mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-01 16:03:19 -04:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 99b484bb64 |
@@ -0,0 +1,7 @@
|
||||
export function isTrustedIpcUrl(value: string, devUrl = process.env.ELECTRON_RENDERER_URL) {
|
||||
if (!URL.canParse(value)) return false
|
||||
const url = new URL(value)
|
||||
if (url.protocol === "oc:" && url.host === "renderer") return true
|
||||
if (!devUrl || !URL.canParse(devUrl)) return false
|
||||
return url.origin === new URL(devUrl).origin
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { isTrustedIpcUrl } from "./ipc-origin"
|
||||
|
||||
describe("isTrustedIpcUrl", () => {
|
||||
test("allows packaged renderer pages", () => {
|
||||
expect(isTrustedIpcUrl("oc://renderer/index.html", undefined)).toBe(true)
|
||||
})
|
||||
|
||||
test("rejects other packaged and remote origins", () => {
|
||||
expect(isTrustedIpcUrl("oc://attacker/index.html", undefined)).toBe(false)
|
||||
expect(isTrustedIpcUrl("https://example.com", undefined)).toBe(false)
|
||||
expect(isTrustedIpcUrl("not a url", undefined)).toBe(false)
|
||||
})
|
||||
|
||||
test("allows only the configured development origin", () => {
|
||||
const devUrl = "http://localhost:5173"
|
||||
expect(isTrustedIpcUrl("http://localhost:5173/index.html", devUrl)).toBe(true)
|
||||
expect(isTrustedIpcUrl("http://localhost:5174/index.html", devUrl)).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,31 @@
|
||||
import { ipcMain } from "electron"
|
||||
import type { IpcMainEvent, IpcMainInvokeEvent } from "electron"
|
||||
import { isTrustedIpcUrl } from "./ipc-origin"
|
||||
|
||||
type IpcEvent = IpcMainEvent | IpcMainInvokeEvent
|
||||
|
||||
export function handle<Args extends unknown[], Result>(
|
||||
channel: string,
|
||||
listener: (event: IpcMainInvokeEvent, ...args: Args) => Result,
|
||||
) {
|
||||
ipcMain.handle(channel, (event, ...args) => {
|
||||
requireTrustedIpcSender(event)
|
||||
return listener(event, ...(args as Args))
|
||||
})
|
||||
}
|
||||
|
||||
export function on<Args extends unknown[]>(
|
||||
channel: string,
|
||||
listener: (event: IpcMainEvent, ...args: Args) => void,
|
||||
) {
|
||||
ipcMain.on(channel, (event, ...args) => {
|
||||
requireTrustedIpcSender(event)
|
||||
listener(event, ...(args as Args))
|
||||
})
|
||||
}
|
||||
|
||||
function requireTrustedIpcSender(event: IpcEvent) {
|
||||
const frame = event.senderFrame
|
||||
if (frame === event.sender.mainFrame && isTrustedIpcUrl(frame.url)) return
|
||||
throw new Error("Rejected IPC from untrusted renderer")
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
import { execFile } from "node:child_process"
|
||||
import { stat } from "node:fs/promises"
|
||||
import { basename } from "node:path"
|
||||
import { app, BrowserWindow, Notification, clipboard, dialog, ipcMain, shell } from "electron"
|
||||
import { app, BrowserWindow, Notification, clipboard, dialog, shell } from "electron"
|
||||
import type { IpcMainEvent, IpcMainInvokeEvent } from "electron"
|
||||
import type { DesktopMenuAction } from "@opencode-ai/app/desktop-menu"
|
||||
|
||||
import type { FatalRendererError, ServerReadyData, TitlebarTheme } from "../preload/types"
|
||||
import { runDesktopMenuAction } from "./desktop-menu-actions"
|
||||
import { setForceFocus } from "./debug"
|
||||
import { handle, on } from "./ipc-policy"
|
||||
import { assertAttachmentBudget, createPickedFileAuthorizations } from "./attachment-picker"
|
||||
import { getStore, removeStoreFileIfEmpty } from "./store"
|
||||
import { getPinchZoomEnabled, getWindowID, setPinchZoomEnabled, setTitlebar, updateTitlebar } from "./windows"
|
||||
@@ -47,26 +48,26 @@ export function registerIpcHandlers(deps: Deps) {
|
||||
const updaterSubscriptions = createUpdaterSubscriptions()
|
||||
app.once("will-quit", updaterSubscriptions.clear)
|
||||
|
||||
ipcMain.handle("kill-sidecar", () => deps.killSidecar())
|
||||
ipcMain.handle("await-initialization", () => deps.awaitInitialization())
|
||||
ipcMain.handle("consume-initial-deep-links", () => deps.consumeInitialDeepLinks())
|
||||
ipcMain.handle("get-default-server-url", () => deps.getDefaultServerUrl())
|
||||
ipcMain.handle("set-default-server-url", (_event: IpcMainInvokeEvent, url: string | null) =>
|
||||
handle("kill-sidecar", () => deps.killSidecar())
|
||||
handle("await-initialization", () => deps.awaitInitialization())
|
||||
handle("consume-initial-deep-links", () => deps.consumeInitialDeepLinks())
|
||||
handle("get-default-server-url", () => deps.getDefaultServerUrl())
|
||||
handle("set-default-server-url", (_event: IpcMainInvokeEvent, url: string | null) =>
|
||||
deps.setDefaultServerUrl(url),
|
||||
)
|
||||
ipcMain.handle("is-first-launch-onboarding-pending", () => deps.isFirstLaunchOnboardingPending())
|
||||
ipcMain.handle("finish-first-launch-onboarding", (_event: IpcMainInvokeEvent, createDefaultProject: boolean) =>
|
||||
handle("is-first-launch-onboarding-pending", () => deps.isFirstLaunchOnboardingPending())
|
||||
handle("finish-first-launch-onboarding", (_event: IpcMainInvokeEvent, createDefaultProject: boolean) =>
|
||||
deps.finishFirstLaunchOnboarding(createDefaultProject),
|
||||
)
|
||||
ipcMain.handle("is-old-layout-eligible", () => deps.isOldLayoutEligible())
|
||||
ipcMain.handle("get-display-backend", () => deps.getDisplayBackend())
|
||||
ipcMain.handle("set-display-backend", (_event: IpcMainInvokeEvent, backend: string | null) =>
|
||||
handle("is-old-layout-eligible", () => deps.isOldLayoutEligible())
|
||||
handle("get-display-backend", () => deps.getDisplayBackend())
|
||||
handle("set-display-backend", (_event: IpcMainInvokeEvent, backend: string | null) =>
|
||||
deps.setDisplayBackend(backend),
|
||||
)
|
||||
ipcMain.handle("parse-markdown", (_event: IpcMainInvokeEvent, markdown: string) => deps.parseMarkdown(markdown))
|
||||
ipcMain.handle("check-app-exists", (_event: IpcMainInvokeEvent, appName: string) => deps.checkAppExists(appName))
|
||||
ipcMain.handle("resolve-app-path", (_event: IpcMainInvokeEvent, appName: string) => deps.resolveAppPath(appName))
|
||||
ipcMain.handle("updater-subscribe", (event) => {
|
||||
handle("parse-markdown", (_event: IpcMainInvokeEvent, markdown: string) => deps.parseMarkdown(markdown))
|
||||
handle("check-app-exists", (_event: IpcMainInvokeEvent, appName: string) => deps.checkAppExists(appName))
|
||||
handle("resolve-app-path", (_event: IpcMainInvokeEvent, appName: string) => deps.resolveAppPath(appName))
|
||||
handle("updater-subscribe", (event) => {
|
||||
const id = event.sender.id
|
||||
updaterSubscriptions.set(
|
||||
id,
|
||||
@@ -77,18 +78,18 @@ export function registerIpcHandlers(deps: Deps) {
|
||||
)
|
||||
event.sender.once("destroyed", () => updaterSubscriptions.delete(id))
|
||||
})
|
||||
ipcMain.handle("updater-unsubscribe", (event) => updaterSubscriptions.delete(event.sender.id))
|
||||
ipcMain.handle("updater-check", () => deps.updater.check())
|
||||
ipcMain.handle("updater-install", () => deps.updater.install())
|
||||
ipcMain.handle("set-background-color", (_event: IpcMainInvokeEvent, color: string) => deps.setBackgroundColor(color))
|
||||
ipcMain.handle("export-debug-logs", () => deps.exportDebugLogs())
|
||||
ipcMain.handle("set-force-focus", (event: IpcMainInvokeEvent, enabled: boolean) =>
|
||||
handle("updater-unsubscribe", (event) => updaterSubscriptions.delete(event.sender.id))
|
||||
handle("updater-check", () => deps.updater.check())
|
||||
handle("updater-install", () => deps.updater.install())
|
||||
handle("set-background-color", (_event: IpcMainInvokeEvent, color: string) => deps.setBackgroundColor(color))
|
||||
handle("export-debug-logs", () => deps.exportDebugLogs())
|
||||
handle("set-force-focus", (event: IpcMainInvokeEvent, enabled: boolean) =>
|
||||
setForceFocus(event.sender, enabled),
|
||||
)
|
||||
ipcMain.handle("record-fatal-renderer-error", (_event: IpcMainInvokeEvent, error: FatalRendererError) =>
|
||||
handle("record-fatal-renderer-error", (_event: IpcMainInvokeEvent, error: FatalRendererError) =>
|
||||
deps.recordFatalRendererError(error),
|
||||
)
|
||||
ipcMain.handle("store-get", (_event: IpcMainInvokeEvent, name: string, key: string) => {
|
||||
handle("store-get", (_event: IpcMainInvokeEvent, name: string, key: string) => {
|
||||
try {
|
||||
const store = getStore(name)
|
||||
const value = store.get(key)
|
||||
@@ -98,27 +99,27 @@ export function registerIpcHandlers(deps: Deps) {
|
||||
return null
|
||||
}
|
||||
})
|
||||
ipcMain.handle("store-set", (_event: IpcMainInvokeEvent, name: string, key: string, value: string) => {
|
||||
handle("store-set", (_event: IpcMainInvokeEvent, name: string, key: string, value: string) => {
|
||||
getStore(name).set(key, value)
|
||||
})
|
||||
ipcMain.handle("store-delete", (_event: IpcMainInvokeEvent, name: string, key: string) => {
|
||||
handle("store-delete", (_event: IpcMainInvokeEvent, name: string, key: string) => {
|
||||
getStore(name).delete(key)
|
||||
void removeStoreFileIfEmpty(name)
|
||||
})
|
||||
ipcMain.handle("store-clear", (_event: IpcMainInvokeEvent, name: string) => {
|
||||
handle("store-clear", (_event: IpcMainInvokeEvent, name: string) => {
|
||||
getStore(name).clear()
|
||||
void removeStoreFileIfEmpty(name)
|
||||
})
|
||||
ipcMain.handle("store-keys", (_event: IpcMainInvokeEvent, name: string) => {
|
||||
handle("store-keys", (_event: IpcMainInvokeEvent, name: string) => {
|
||||
const store = getStore(name)
|
||||
return Object.keys(store.store)
|
||||
})
|
||||
ipcMain.handle("store-length", (_event: IpcMainInvokeEvent, name: string) => {
|
||||
handle("store-length", (_event: IpcMainInvokeEvent, name: string) => {
|
||||
const store = getStore(name)
|
||||
return Object.keys(store.store).length
|
||||
})
|
||||
|
||||
ipcMain.handle(
|
||||
handle(
|
||||
"open-directory-picker",
|
||||
async (_event: IpcMainInvokeEvent, opts?: { multiple?: boolean; title?: string; defaultPath?: string }) => {
|
||||
const result = await dialog.showOpenDialog({
|
||||
@@ -131,7 +132,7 @@ export function registerIpcHandlers(deps: Deps) {
|
||||
},
|
||||
)
|
||||
|
||||
ipcMain.handle(
|
||||
handle(
|
||||
"open-file-picker",
|
||||
async (
|
||||
event: IpcMainInvokeEvent,
|
||||
@@ -157,15 +158,15 @@ export function registerIpcHandlers(deps: Deps) {
|
||||
},
|
||||
)
|
||||
|
||||
ipcMain.handle("read-picked-file", async (event: IpcMainInvokeEvent, token: string, filePath: string) => {
|
||||
handle("read-picked-file", async (event: IpcMainInvokeEvent, token: string, filePath: string) => {
|
||||
return pickedFiles.read(event.sender.id, token, filePath)
|
||||
})
|
||||
|
||||
ipcMain.handle("release-picked-files", (event: IpcMainInvokeEvent, token: string) => {
|
||||
handle("release-picked-files", (event: IpcMainInvokeEvent, token: string) => {
|
||||
pickedFiles.release(event.sender.id, token)
|
||||
})
|
||||
|
||||
ipcMain.handle(
|
||||
handle(
|
||||
"save-file-picker",
|
||||
async (_event: IpcMainInvokeEvent, opts?: { title?: string; defaultPath?: string }) => {
|
||||
const result = await dialog.showSaveDialog({
|
||||
@@ -177,11 +178,11 @@ export function registerIpcHandlers(deps: Deps) {
|
||||
},
|
||||
)
|
||||
|
||||
ipcMain.on("open-link", (_event: IpcMainEvent, url: string) => {
|
||||
on("open-link", (_event: IpcMainEvent, url: string) => {
|
||||
void shell.openExternal(url)
|
||||
})
|
||||
|
||||
ipcMain.handle("open-path", async (_event: IpcMainInvokeEvent, path: string, app?: string) => {
|
||||
handle("open-path", async (_event: IpcMainInvokeEvent, path: string, app?: string) => {
|
||||
if (!app) return shell.openPath(path)
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const [cmd, args] =
|
||||
@@ -190,7 +191,7 @@ export function registerIpcHandlers(deps: Deps) {
|
||||
})
|
||||
})
|
||||
|
||||
ipcMain.handle("reveal-path", async (_event: IpcMainInvokeEvent, path: string) => {
|
||||
handle("reveal-path", async (_event: IpcMainInvokeEvent, path: string) => {
|
||||
const exists = await stat(path).then(
|
||||
() => true,
|
||||
() => false,
|
||||
@@ -200,7 +201,7 @@ export function registerIpcHandlers(deps: Deps) {
|
||||
return true
|
||||
})
|
||||
|
||||
ipcMain.handle("read-clipboard-image", () => {
|
||||
handle("read-clipboard-image", () => {
|
||||
const image = clipboard.readImage()
|
||||
if (image.isEmpty()) return null
|
||||
const buffer = image.toPNG().buffer
|
||||
@@ -208,13 +209,13 @@ export function registerIpcHandlers(deps: Deps) {
|
||||
return { buffer, width: size.width, height: size.height }
|
||||
})
|
||||
|
||||
ipcMain.on("show-notification", (_event: IpcMainEvent, title: string, body?: string) => {
|
||||
on("show-notification", (_event: IpcMainEvent, title: string, body?: string) => {
|
||||
new Notification({ title, body }).show()
|
||||
})
|
||||
|
||||
ipcMain.handle("get-window-count", () => BrowserWindow.getAllWindows().length)
|
||||
handle("get-window-count", () => BrowserWindow.getAllWindows().length)
|
||||
|
||||
ipcMain.handle("get-window-id", (event: IpcMainInvokeEvent) => {
|
||||
handle("get-window-id", (event: IpcMainInvokeEvent) => {
|
||||
const win = BrowserWindow.fromWebContents(event.sender)
|
||||
if (!win) throw new Error("Window not found")
|
||||
const id = getWindowID(win)
|
||||
@@ -222,42 +223,42 @@ export function registerIpcHandlers(deps: Deps) {
|
||||
return id
|
||||
})
|
||||
|
||||
ipcMain.handle("get-window-focused", (event: IpcMainInvokeEvent) => {
|
||||
handle("get-window-focused", (event: IpcMainInvokeEvent) => {
|
||||
const win = BrowserWindow.fromWebContents(event.sender)
|
||||
return win?.isFocused() ?? false
|
||||
})
|
||||
|
||||
ipcMain.handle("set-window-focus", (event: IpcMainInvokeEvent) => {
|
||||
handle("set-window-focus", (event: IpcMainInvokeEvent) => {
|
||||
const win = BrowserWindow.fromWebContents(event.sender)
|
||||
win?.focus()
|
||||
})
|
||||
|
||||
ipcMain.handle("show-window", (event: IpcMainInvokeEvent) => {
|
||||
handle("show-window", (event: IpcMainInvokeEvent) => {
|
||||
const win = BrowserWindow.fromWebContents(event.sender)
|
||||
win?.show()
|
||||
})
|
||||
|
||||
ipcMain.on("relaunch", () => {
|
||||
on("relaunch", () => {
|
||||
deps.relaunch()
|
||||
})
|
||||
|
||||
ipcMain.handle("get-zoom-factor", (event: IpcMainInvokeEvent) => event.sender.getZoomFactor())
|
||||
ipcMain.handle("set-zoom-factor", (event: IpcMainInvokeEvent, factor: number) => {
|
||||
handle("get-zoom-factor", (event: IpcMainInvokeEvent) => event.sender.getZoomFactor())
|
||||
handle("set-zoom-factor", (event: IpcMainInvokeEvent, factor: number) => {
|
||||
event.sender.setZoomFactor(factor)
|
||||
const win = BrowserWindow.fromWebContents(event.sender)
|
||||
if (!win) return
|
||||
updateTitlebar(win)
|
||||
})
|
||||
ipcMain.handle("get-pinch-zoom-enabled", () => getPinchZoomEnabled())
|
||||
ipcMain.handle("set-pinch-zoom-enabled", (_event: IpcMainInvokeEvent, enabled: boolean) => {
|
||||
handle("get-pinch-zoom-enabled", () => getPinchZoomEnabled())
|
||||
handle("set-pinch-zoom-enabled", (_event: IpcMainInvokeEvent, enabled: boolean) => {
|
||||
setPinchZoomEnabled(enabled)
|
||||
})
|
||||
ipcMain.handle("set-titlebar", (event: IpcMainInvokeEvent, theme: TitlebarTheme) => {
|
||||
handle("set-titlebar", (event: IpcMainInvokeEvent, theme: TitlebarTheme) => {
|
||||
const win = BrowserWindow.fromWebContents(event.sender)
|
||||
if (!win) return
|
||||
setTitlebar(win, theme)
|
||||
})
|
||||
ipcMain.handle("run-desktop-menu-action", (event: IpcMainInvokeEvent, action: DesktopMenuAction) => {
|
||||
handle("run-desktop-menu-action", (event: IpcMainInvokeEvent, action: DesktopMenuAction) => {
|
||||
runDesktopMenuAction(BrowserWindow.fromWebContents(event.sender), action, {
|
||||
checkForUpdates: () => void deps.showUpdater(),
|
||||
relaunch: deps.relaunch,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { app, ipcMain } from "electron"
|
||||
import { app } from "electron"
|
||||
import type { IpcMainInvokeEvent } from "electron"
|
||||
import type { WslServersController } from "./servers"
|
||||
import { requireWslIpcString, requireWslIpcStrings } from "./policy"
|
||||
import type { WslServersState } from "../../preload/types"
|
||||
import { handle } from "../ipc-policy"
|
||||
|
||||
export function registerWslIpcHandlers(controller: WslServersController) {
|
||||
if (process.platform !== "win32") {
|
||||
@@ -23,7 +24,7 @@ export function registerWslIpcHandlers(controller: WslServersController) {
|
||||
subscriptions.clear()
|
||||
})
|
||||
|
||||
ipcMain.handle("wsl-servers-subscribe", (event) => {
|
||||
handle("wsl-servers-subscribe", (event) => {
|
||||
const id = event.sender.id
|
||||
if (subscriptions.has(id)) return
|
||||
subscriptions.set(
|
||||
@@ -38,30 +39,30 @@ export function registerWslIpcHandlers(controller: WslServersController) {
|
||||
)
|
||||
event.sender.once("destroyed", () => unsubscribe(id))
|
||||
})
|
||||
ipcMain.handle("wsl-servers-unsubscribe", (event) => unsubscribe(event.sender.id))
|
||||
ipcMain.handle("wsl-servers-get-state", () => controller.getState())
|
||||
ipcMain.handle("wsl-servers-probe-runtime", () => controller.probeRuntime())
|
||||
ipcMain.handle("wsl-servers-refresh-distros", () => controller.refreshDistros())
|
||||
ipcMain.handle("wsl-servers-install-wsl", () => controller.installWsl())
|
||||
ipcMain.handle("wsl-servers-install-distro", (_event: IpcMainInvokeEvent, name: string) =>
|
||||
handle("wsl-servers-unsubscribe", (event) => unsubscribe(event.sender.id))
|
||||
handle("wsl-servers-get-state", () => controller.getState())
|
||||
handle("wsl-servers-probe-runtime", () => controller.probeRuntime())
|
||||
handle("wsl-servers-refresh-distros", () => controller.refreshDistros())
|
||||
handle("wsl-servers-install-wsl", () => controller.installWsl())
|
||||
handle("wsl-servers-install-distro", (_event: IpcMainInvokeEvent, name: string) =>
|
||||
controller.installDistro(requireWslIpcString("distro", name)),
|
||||
)
|
||||
ipcMain.handle("wsl-servers-probe-addable", (_event: IpcMainInvokeEvent, distros: string[]) =>
|
||||
handle("wsl-servers-probe-addable", (_event: IpcMainInvokeEvent, distros: string[]) =>
|
||||
controller.probeAddable(requireWslIpcStrings("distro", distros)),
|
||||
)
|
||||
ipcMain.handle("wsl-servers-install-opencode", (_event: IpcMainInvokeEvent, name: string) =>
|
||||
handle("wsl-servers-install-opencode", (_event: IpcMainInvokeEvent, name: string) =>
|
||||
controller.installOpencode(requireWslIpcString("distro", name)),
|
||||
)
|
||||
ipcMain.handle("wsl-servers-open-terminal", (_event: IpcMainInvokeEvent, name: string) =>
|
||||
handle("wsl-servers-open-terminal", (_event: IpcMainInvokeEvent, name: string) =>
|
||||
controller.openTerminal(requireWslIpcString("distro", name)),
|
||||
)
|
||||
ipcMain.handle("wsl-servers-add", (_event: IpcMainInvokeEvent, distro: string) =>
|
||||
handle("wsl-servers-add", (_event: IpcMainInvokeEvent, distro: string) =>
|
||||
controller.addServer(requireWslIpcString("distro", distro)),
|
||||
)
|
||||
ipcMain.handle("wsl-servers-remove", (_event: IpcMainInvokeEvent, id: string) =>
|
||||
handle("wsl-servers-remove", (_event: IpcMainInvokeEvent, id: string) =>
|
||||
controller.removeServer(requireWslIpcString("server id", id)),
|
||||
)
|
||||
ipcMain.handle("wsl-servers-start", (_event: IpcMainInvokeEvent, id: string) =>
|
||||
handle("wsl-servers-start", (_event: IpcMainInvokeEvent, id: string) =>
|
||||
controller.startServer(requireWslIpcString("server id", id)),
|
||||
)
|
||||
}
|
||||
@@ -85,19 +86,19 @@ function registerUnavailableWslIpcHandlers() {
|
||||
job: null,
|
||||
})
|
||||
|
||||
ipcMain.handle("wsl-servers-subscribe", (event) => {
|
||||
handle("wsl-servers-subscribe", (event) => {
|
||||
event.sender.send("wsl-servers-event", { type: "state", state: state() })
|
||||
})
|
||||
ipcMain.handle("wsl-servers-unsubscribe", () => undefined)
|
||||
ipcMain.handle("wsl-servers-get-state", () => state())
|
||||
ipcMain.handle("wsl-servers-probe-runtime", unavailable)
|
||||
ipcMain.handle("wsl-servers-refresh-distros", unavailable)
|
||||
ipcMain.handle("wsl-servers-install-wsl", unavailable)
|
||||
ipcMain.handle("wsl-servers-install-distro", unavailable)
|
||||
ipcMain.handle("wsl-servers-probe-addable", unavailable)
|
||||
ipcMain.handle("wsl-servers-install-opencode", unavailable)
|
||||
ipcMain.handle("wsl-servers-open-terminal", unavailable)
|
||||
ipcMain.handle("wsl-servers-add", unavailable)
|
||||
ipcMain.handle("wsl-servers-remove", unavailable)
|
||||
ipcMain.handle("wsl-servers-start", unavailable)
|
||||
handle("wsl-servers-unsubscribe", () => undefined)
|
||||
handle("wsl-servers-get-state", () => state())
|
||||
handle("wsl-servers-probe-runtime", unavailable)
|
||||
handle("wsl-servers-refresh-distros", unavailable)
|
||||
handle("wsl-servers-install-wsl", unavailable)
|
||||
handle("wsl-servers-install-distro", unavailable)
|
||||
handle("wsl-servers-probe-addable", unavailable)
|
||||
handle("wsl-servers-install-opencode", unavailable)
|
||||
handle("wsl-servers-open-terminal", unavailable)
|
||||
handle("wsl-servers-add", unavailable)
|
||||
handle("wsl-servers-remove", unavailable)
|
||||
handle("wsl-servers-start", unavailable)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user