diff --git a/packages/explorer.core/README.md b/packages/explorer.core/README.md index 2405cf0..1dde270 100644 --- a/packages/explorer.core/README.md +++ b/packages/explorer.core/README.md @@ -196,7 +196,8 @@ React lifecycle and StrictMode concerns. `ConfirmCopyMoveCommand` (`file-explorer:rename-prompt`, `mkdir-prompt`, `confirm-delete`, `confirm-copy-move`) are declared as prompts that resolve on confirm and reject on cancel, but no package registers a handler for them. - They are `Command.silent`, so a call never settles. + They use the `async` dispatch policy, so a call fails at once with a + `CommandError` with `kind: "no-handlers"`. ### Constraints diff --git a/packages/explorer.core/src/public/commands.ts b/packages/explorer.core/src/public/commands.ts index e9633c5..5e28328 100644 --- a/packages/explorer.core/src/public/commands.ts +++ b/packages/explorer.core/src/public/commands.ts @@ -15,7 +15,7 @@ export interface RenamePromptPayload { * basename on confirm, rejects on cancel. The handler lives in * `file-explorer-react` so the panel renderer owns the dialog. */ -export const RenamePromptCommand = Command.silent("file-explorer:rename-prompt") +export const RenamePromptCommand = Command.async("file-explorer:rename-prompt") .input(passthrough()) .output(passthrough<{ name: string }>()) .build(); @@ -27,7 +27,7 @@ export interface MkdirPromptPayload { * Open the mkdir prompt UI under `parentPath`. Resolves with the * new directory name on confirm, rejects on cancel. */ -export const MkdirPromptCommand = Command.silent("file-explorer:mkdir-prompt") +export const MkdirPromptCommand = Command.async("file-explorer:mkdir-prompt") .input(passthrough()) .output(passthrough<{ name: string }>()) .build(); @@ -40,7 +40,7 @@ export interface ConfirmDeletePayload { * on cancel. The actual deletion is performed by the caller via * `files:delete-file`. */ -export const ConfirmDeleteCommand = Command.silent("file-explorer:confirm-delete") +export const ConfirmDeleteCommand = Command.async("file-explorer:confirm-delete") .input(passthrough()) .output(passthrough()) .build(); @@ -55,7 +55,7 @@ export interface ConfirmCopyMovePayload { * rejects on cancel. The caller iterates and dispatches * `files:move-file` (move) or single-file copy operations. */ -export const ConfirmCopyMoveCommand = Command.silent("file-explorer:confirm-copy-move") +export const ConfirmCopyMoveCommand = Command.async("file-explorer:confirm-copy-move") .input(passthrough()) .output(passthrough()) .build(); @@ -77,7 +77,7 @@ export interface NewFileExplorerPanelPayload { * (the suffix after `file-explorer:`) so callers can compose with * `dock:focus-panel` etc. */ -export const NewFileExplorerPanelCommand = Command.silent("file-explorer:new-panel") +export const NewFileExplorerPanelCommand = Command.async("file-explorer:new-panel") .input(passthrough()) .output(passthrough<{ panelId: string }>()) .build(); diff --git a/packages/mime.core/README.md b/packages/mime.core/README.md index b19bf30..e02fdd9 100644 --- a/packages/mime.core/README.md +++ b/packages/mime.core/README.md @@ -134,9 +134,9 @@ re-implementing the policy, so all callers agree on the winner. `No mime-renderer registered for ""`. - Unknown extension: the MIME type is `application/octet-stream`, which usually has no renderer, so the error above names `application/octet-stream`. -- The commands are `Command.silent`: if no handler is registered (the fragment - was not booted, or for `files:open`, `@statewalker/explorer.core` was not), - the returned promise never settles. There is no error. +- If no handler is registered (the fragment was not booted, or for + `files:open`, `@statewalker/explorer.core` was not), the returned promise + rejects at once with a `CommandError` with `kind: "no-handlers"`. ### Constraints diff --git a/packages/mime.core/src/public/commands.ts b/packages/mime.core/src/public/commands.ts index c62dda1..7a5af8b 100644 --- a/packages/mime.core/src/public/commands.ts +++ b/packages/mime.core/src/public/commands.ts @@ -16,7 +16,7 @@ export interface VisualizeFilePayload { */ referencePanelId?: string; } -export const VisualizeFileCommand = Command.silent("files:visualize") +export const VisualizeFileCommand = Command.async("files:visualize") .input(passthrough()) .output(passthrough()) .build(); @@ -43,7 +43,7 @@ export interface OpenPayload { * — directories navigate the panel identified by `target`, files * delegate to `files:visualize`. */ -export const OpenCommand = Command.silent("files:open") +export const OpenCommand = Command.async("files:open") .input(passthrough()) .output(passthrough()) .build(); diff --git a/packages/platform.browser/tests/activate.browser.test.ts b/packages/platform.browser/tests/activate.browser.test.ts index 870f074..2fce404 100644 --- a/packages/platform.browser/tests/activate.browser.test.ts +++ b/packages/platform.browser/tests/activate.browser.test.ts @@ -51,18 +51,16 @@ describe("platform.browser initPlatformWeb(ctx)", () => { getCommands(ctx).call(PreferenceSetCommand, { key: "t", value: 1 }).promise, ).resolves.toBeUndefined(); - // After cleanup, every platform command stays unsettled (no listener, - // silent policy leaves it pending so the caller can supply external - // resolution if needed). + // After cleanup no platform command has a handler left: a call fails at + // once with "no-handlers" instead of waiting forever. await cleanup(); for (const key of PLATFORM_COMMAND_KEYS) { - const decl = Command.silent(key) + const decl = Command.async(key) .input(passthrough>()) .output(passthrough()) .build(); const cmd = getCommands(ctx).call(decl, {}); - await Promise.resolve(); - expect(cmd.settled).toBe(false); + await expect(cmd.promise).rejects.toMatchObject({ kind: "no-handlers", commandKey: key }); } }); }); diff --git a/packages/platform.browser/tests/copy-to-clipboard.browser.test.ts b/packages/platform.browser/tests/copy-to-clipboard.browser.test.ts index 700a75a..229e005 100644 --- a/packages/platform.browser/tests/copy-to-clipboard.browser.test.ts +++ b/packages/platform.browser/tests/copy-to-clipboard.browser.test.ts @@ -41,11 +41,11 @@ describe("copy-to-clipboard browser handler", () => { } }); - it("unregister leaves subsequent fires unsettled (noop default)", () => { + it("after unregister a call fails at once with no-handlers", async () => { const ctx = {}; const unregister = registerCopyToClipboardBrowser(getCommands(ctx)); unregister(); const command = getCommands(ctx).call(CopyToClipboardCommand, { text: "x" }); - expect(command.settled).toBe(false); + await expect(command.promise).rejects.toMatchObject({ kind: "no-handlers" }); }); }); diff --git a/packages/platform.core/README.md b/packages/platform.core/README.md index 3348c83..99ad8ad 100644 --- a/packages/platform.core/README.md +++ b/packages/platform.core/README.md @@ -97,9 +97,9 @@ command.reject(new UserCancelledError()); ## Internals -### A call with no handler waits forever +### A call with no handler fails at once -Every declaration uses the `silent` dispatch policy of `@statewalker/shared-commands`: when no handler is registered at call time, the call neither resolves nor rejects, and a handler registered later does not pick it up. On a host that does not implement a command (for example the pickers or the clipboard under `@statewalker/platform.node`), or when the call is made before the host fragment has started, `await ...promise` hangs with no error. Start the platform fragment before the fragments that call it, and guard calls with a timeout where the host may lack the command. +Every declaration uses the `async` dispatch policy of `@statewalker/shared-commands`: when no handler is registered at call time, the call rejects immediately with `CommandError` with `kind: "no-handlers"` (`commandKey` names the command). A handler registered later does not pick up earlier calls. This happens on a host that does not implement a command (for example the pickers or the clipboard under `@statewalker/platform.node`), or when the call is made before the host fragment has started: start the platform fragment before the fragments that call it, and catch the error where the host may lack the command. A handler that takes the call without settling it (a listener that only observes) still leaves it pending. ### Cancellation is a type, not a string diff --git a/packages/platform.core/src/commands/copy-to-clipboard/index.ts b/packages/platform.core/src/commands/copy-to-clipboard/index.ts index 4781ce1..3645f7e 100644 --- a/packages/platform.core/src/commands/copy-to-clipboard/index.ts +++ b/packages/platform.core/src/commands/copy-to-clipboard/index.ts @@ -6,7 +6,7 @@ export interface CopyToClipboardPayload { text: string; } -export const CopyToClipboardCommand = Command.silent(COPY_TO_CLIPBOARD_COMMAND_KEY) +export const CopyToClipboardCommand = Command.async(COPY_TO_CLIPBOARD_COMMAND_KEY) .input(passthrough()) .output(passthrough()) .build(); diff --git a/packages/platform.core/src/commands/download-blob/index.ts b/packages/platform.core/src/commands/download-blob/index.ts index 35829ad..f8e2fb8 100644 --- a/packages/platform.core/src/commands/download-blob/index.ts +++ b/packages/platform.core/src/commands/download-blob/index.ts @@ -7,7 +7,7 @@ export interface DownloadBlobPayload { filename: string; } -export const DownloadBlobCommand = Command.silent(DOWNLOAD_BLOB_COMMAND_KEY) +export const DownloadBlobCommand = Command.async(DOWNLOAD_BLOB_COMMAND_KEY) .input(passthrough()) .output(passthrough()) .build(); diff --git a/packages/platform.core/src/commands/download-to-files/index.ts b/packages/platform.core/src/commands/download-to-files/index.ts index 9c68da8..68021f4 100644 --- a/packages/platform.core/src/commands/download-to-files/index.ts +++ b/packages/platform.core/src/commands/download-to-files/index.ts @@ -21,7 +21,7 @@ export interface DownloadToFilesResult { bytes: number; } -export const DownloadToFilesCommand = Command.silent(DOWNLOAD_TO_FILES_COMMAND_KEY) +export const DownloadToFilesCommand = Command.async(DOWNLOAD_TO_FILES_COMMAND_KEY) .input(passthrough()) .output(passthrough()) .build(); diff --git a/packages/platform.core/src/commands/pick-directory/index.ts b/packages/platform.core/src/commands/pick-directory/index.ts index c2a7dff..1212403 100644 --- a/packages/platform.core/src/commands/pick-directory/index.ts +++ b/packages/platform.core/src/commands/pick-directory/index.ts @@ -12,7 +12,7 @@ export interface PickDirectoryResult { label: string; } -export const PickDirectoryCommand = Command.silent(PICK_DIRECTORY_COMMAND_KEY) +export const PickDirectoryCommand = Command.async(PICK_DIRECTORY_COMMAND_KEY) .input(passthrough()) .output(passthrough()) .build(); diff --git a/packages/platform.core/src/commands/pick-file/index.ts b/packages/platform.core/src/commands/pick-file/index.ts index bfc9d7f..d15b575 100644 --- a/packages/platform.core/src/commands/pick-file/index.ts +++ b/packages/platform.core/src/commands/pick-file/index.ts @@ -13,7 +13,7 @@ export interface PickFileResult { names: string[]; } -export const PickFileCommand = Command.silent(PICK_FILE_COMMAND_KEY) +export const PickFileCommand = Command.async(PICK_FILE_COMMAND_KEY) .input(passthrough()) .output(passthrough()) .build(); diff --git a/packages/platform.core/src/commands/preference-get/index.ts b/packages/platform.core/src/commands/preference-get/index.ts index 98826a4..e5dd367 100644 --- a/packages/platform.core/src/commands/preference-get/index.ts +++ b/packages/platform.core/src/commands/preference-get/index.ts @@ -10,7 +10,7 @@ export interface PreferenceGetResult { value: unknown | undefined; } -export const PreferenceGetCommand = Command.silent(PREFERENCE_GET_COMMAND_KEY) +export const PreferenceGetCommand = Command.async(PREFERENCE_GET_COMMAND_KEY) .input(passthrough()) .output(passthrough()) .build(); diff --git a/packages/platform.core/src/commands/preference-set/index.ts b/packages/platform.core/src/commands/preference-set/index.ts index 444bfa1..35b9b3f 100644 --- a/packages/platform.core/src/commands/preference-set/index.ts +++ b/packages/platform.core/src/commands/preference-set/index.ts @@ -7,7 +7,7 @@ export interface PreferenceSetPayload { value: unknown; } -export const PreferenceSetCommand = Command.silent(PREFERENCE_SET_COMMAND_KEY) +export const PreferenceSetCommand = Command.async(PREFERENCE_SET_COMMAND_KEY) .input(passthrough()) .output(passthrough()) .build(); diff --git a/packages/platform.node/README.md b/packages/platform.node/README.md index 102fd73..70c2561 100644 --- a/packages/platform.node/README.md +++ b/packages/platform.node/README.md @@ -52,7 +52,7 @@ Every get reads `/preferences.json` and every set reads, changes one key and wri ### Commands this package does not answer -Pickers, clipboard, `download-blob`, `download-to-files` and URL state have no Node handler. Their declarations use the `silent` policy, so a call to one of them on Node never settles: no result, no error. Do not await them in code that may run headless without a timeout. +Pickers, clipboard, `download-blob`, `download-to-files` and URL state have no Node handler. A call to one of them on Node rejects at once with a `CommandError` with `kind: "no-handlers"`; code that may run headless should catch it. ### Dependencies diff --git a/packages/render.core/README.md b/packages/render.core/README.md index 4c009e5..d37da57 100644 --- a/packages/render.core/README.md +++ b/packages/render.core/README.md @@ -152,7 +152,7 @@ when its last panel closes, unless `meta.persistent === true`. starts empty. Read and write failures log `[layout-store] failed to read dock-layout.json` / `[layout-store] failed to persist dock-layout.json`; `connect()` never rejects. -- The commands are `Command.silent`: without `initSpecStore`, their promises never settle. +- Without `initSpecStore`, a call rejects at once with a `CommandError` with `kind: "no-handlers"`. ### Constraints diff --git a/packages/render.core/src/internal/spec-store.test.ts b/packages/render.core/src/internal/spec-store.test.ts index 1a4503a..6309798 100644 --- a/packages/render.core/src/internal/spec-store.test.ts +++ b/packages/render.core/src/internal/spec-store.test.ts @@ -153,11 +153,11 @@ describe("spec:create / spec:patch commands", () => { const store = ws.requireAdapter(SpecStore); const sizeBefore = countSpecs(store); await cleanup(); - // After cleanup, commands.call(CreateSpecCommand, ...) falls through to - // CreateSpecCommand's declared default (() => {}), which silently - // resolves with no payload — critically, no SpecStore.create is - // called anymore. - commands.call(CreateSpecCommand, { catalogId: "c", spec: 1 }); + // After cleanup no handler is left: the call fails at once with + // "no-handlers", and no SpecStore.create is called anymore. + await expect( + commands.call(CreateSpecCommand, { catalogId: "c", spec: 1 }).promise, + ).rejects.toMatchObject({ kind: "no-handlers", commandKey: "spec:create" }); expect(countSpecs(store)).toBe(sizeBefore); }); }); diff --git a/packages/render.core/src/public/commands.ts b/packages/render.core/src/public/commands.ts index 6fcaa41..ec6070a 100644 --- a/packages/render.core/src/public/commands.ts +++ b/packages/render.core/src/public/commands.ts @@ -11,7 +11,7 @@ export interface CreateSpecResult { specId: string; } -export const CreateSpecCommand = Command.silent("spec:create") +export const CreateSpecCommand = Command.async("spec:create") .input(passthrough()) .output(passthrough()) .build(); @@ -21,7 +21,7 @@ export interface PatchSpecPayload { patch: SpecPatch; } -export const PatchSpecCommand = Command.silent("spec:patch") +export const PatchSpecCommand = Command.async("spec:patch") .input(passthrough()) .output(passthrough()) .build(); diff --git a/packages/settings.core/README.md b/packages/settings.core/README.md index 7428be5..802ef8e 100644 --- a/packages/settings.core/README.md +++ b/packages/settings.core/README.md @@ -93,8 +93,8 @@ across workspace `onLoad` / `onUnload` until `cleanup` runs. ### What breaks -- Both commands are `Command.silent`. If `initSettings` was not booted, the - promise returned by `commands.call(...)` never settles; nothing is logged. +- If `initSettings` was not booted, the promise returned by + `commands.call(...)` rejects at once with a `CommandError` with `kind: "no-handlers"`. - State is in memory only. A reload opens with the dialog closed and no active tab. ### Dependencies diff --git a/packages/settings.core/src/public/commands.ts b/packages/settings.core/src/public/commands.ts index d855c7d..8828fe5 100644 --- a/packages/settings.core/src/public/commands.ts +++ b/packages/settings.core/src/public/commands.ts @@ -11,13 +11,13 @@ export interface OpenSettingsPayload { * `SettingsManager`; consumers fire the command without importing * the adapter directly. */ -export const OpenSettingsCommand = Command.silent("settings:open") +export const OpenSettingsCommand = Command.async("settings:open") .input(passthrough()) .output(passthrough()) .build(); /** Close the settings dialog. */ -export const CloseSettingsCommand = Command.silent("settings:close") +export const CloseSettingsCommand = Command.async("settings:close") .input(passthrough()) .output(passthrough()) .build(); diff --git a/packages/shell.core/README.md b/packages/shell.core/README.md index d8ddb3e..9f2780a 100644 --- a/packages/shell.core/README.md +++ b/packages/shell.core/README.md @@ -144,8 +144,8 @@ or HMR remount does not lose panels added since the last save. - A layout that DockView cannot apply logs `[chat-mini:dock] failed to restore in-memory layout` and falls back to the saved layout. Save errors log `[chat-mini:dock] failed to persist layout`. -- The commands are `Command.silent`: without `initDock`, their promises never - settle. +- Without `initDock`, a call rejects at once with a `CommandError` with + `kind: "no-handlers"`. - `onActivePanelChange` is not called on subscribe; read `getActivePanelId()` first if you need the current value. Before mount, `getPanelIds()` is empty. diff --git a/packages/shell.core/src/public/commands.ts b/packages/shell.core/src/public/commands.ts index 471f807..253476c 100644 --- a/packages/shell.core/src/public/commands.ts +++ b/packages/shell.core/src/public/commands.ts @@ -24,7 +24,7 @@ export interface ShowDockPanelPayload { * (vision audit C7 / proposal §5.5) — the dock fragment is the only * place that knows the panel kind is `"json"`. */ -export const ShowDockPanelCommand = Command.silent("dock:show-panel") +export const ShowDockPanelCommand = Command.async("dock:show-panel") .input(passthrough()) .output(passthrough()) .build(); @@ -32,7 +32,7 @@ export const ShowDockPanelCommand = Command.silent("dock:show-panel") export interface ClosePanelPayload { panelId: string; } -export const ClosePanelCommand = Command.silent("dock:close-panel") +export const ClosePanelCommand = Command.async("dock:close-panel") .input(passthrough()) .output(passthrough()) .build(); @@ -40,7 +40,7 @@ export const ClosePanelCommand = Command.silent("dock:close-panel") export interface FocusPanelPayload { panelId: string; } -export const FocusPanelCommand = Command.silent("dock:focus-panel") +export const FocusPanelCommand = Command.async("dock:focus-panel") .input(passthrough()) .output(passthrough()) .build(); @@ -55,7 +55,7 @@ export interface SetPanelTitlePayload { * file tabs, etc. — so the dock fragment owns the mechanism rather * than each catalog reinventing it. */ -export const SetPanelTitleCommand = Command.silent("dock:set-panel-title") +export const SetPanelTitleCommand = Command.async("dock:set-panel-title") .input(passthrough()) .output(passthrough()) .build(); diff --git a/packages/webapp.browser/src/open-web-app.ts b/packages/webapp.browser/src/open-web-app.ts index 0733ddb..b239cf8 100644 --- a/packages/webapp.browser/src/open-web-app.ts +++ b/packages/webapp.browser/src/open-web-app.ts @@ -24,7 +24,7 @@ export interface OpenWebAppPayload { } /** Command: host `project` and open (or focus) its web-app dock tab. */ -export const OpenWebAppCommand = Command.silent("webapp:open") +export const OpenWebAppCommand = Command.async("webapp:open") .input(passthrough()) .output(passthrough()) .build(); diff --git a/packages/workspace.browser/src/public/commands.ts b/packages/workspace.browser/src/public/commands.ts index 9cb2942..16d7798 100644 --- a/packages/workspace.browser/src/public/commands.ts +++ b/packages/workspace.browser/src/public/commands.ts @@ -32,7 +32,7 @@ export type WorkspaceVoidResult = Record; * `requestPermission()` call is allowed by the browser. */ export const WORKSPACE_RECONNECT_COMMAND_KEY = "workspace:reconnect"; -export const WorkspaceReconnectCommand = Command.silent(WORKSPACE_RECONNECT_COMMAND_KEY) +export const WorkspaceReconnectCommand = Command.async(WORKSPACE_RECONNECT_COMMAND_KEY) .input(passthrough()) .output(passthrough()) .build(); @@ -45,7 +45,7 @@ export const WorkspaceReconnectCommand = Command.silent(WORKSPACE_RECONNECT_COMM * runtime is fully torn down before the next pick lands. */ export const WORKSPACE_DISCONNECT_COMMAND_KEY = "workspace:disconnect"; -export const WorkspaceDisconnectCommand = Command.silent(WORKSPACE_DISCONNECT_COMMAND_KEY) +export const WorkspaceDisconnectCommand = Command.async(WORKSPACE_DISCONNECT_COMMAND_KEY) .input(passthrough()) .output(passthrough()) .build(); diff --git a/packages/workspace.core/src/public/commands.ts b/packages/workspace.core/src/public/commands.ts index dbae19b..490e2d3 100644 --- a/packages/workspace.core/src/public/commands.ts +++ b/packages/workspace.core/src/public/commands.ts @@ -26,7 +26,7 @@ export interface ChangeWorkspaceResult { workspace: Workspace; } -export const ChangeWorkspaceCommand = Command.silent(CHANGE_WORKSPACE_COMMAND_KEY) +export const ChangeWorkspaceCommand = Command.async(CHANGE_WORKSPACE_COMMAND_KEY) .input(passthrough()) .output(passthrough()) .build(); diff --git a/packages/workspace.core/src/public/files-commands.ts b/packages/workspace.core/src/public/files-commands.ts index cc5ad95..7377b73 100644 --- a/packages/workspace.core/src/public/files-commands.ts +++ b/packages/workspace.core/src/public/files-commands.ts @@ -31,7 +31,7 @@ export interface LoadDirectoryPayload { /** Recursive listing (descends into subdirectories). */ recursive?: boolean; } -export const LoadDirectoryCommand = Command.silent("files:load-directory") +export const LoadDirectoryCommand = Command.async("files:load-directory") .input(passthrough()) .output(passthrough()) .build(); @@ -39,7 +39,7 @@ export const LoadDirectoryCommand = Command.silent("files:load-directory") export interface LoadFilePayload { path: string; } -export const LoadFileCommand = Command.silent("files:load-file") +export const LoadFileCommand = Command.async("files:load-file") .input(passthrough()) .output(passthrough()) .build(); @@ -48,7 +48,7 @@ export interface WriteFilePayload { path: string; content: Uint8Array | string; } -export const WriteFileCommand = Command.silent("files:write-file") +export const WriteFileCommand = Command.async("files:write-file") .input(passthrough()) .output(passthrough()) .build(); @@ -57,7 +57,7 @@ export interface MoveFilePayload { fromPath: string; toPath: string; } -export const MoveFileCommand = Command.silent("files:move-file") +export const MoveFileCommand = Command.async("files:move-file") .input(passthrough()) .output(passthrough()) .build(); @@ -65,7 +65,7 @@ export const MoveFileCommand = Command.silent("files:move-file") export interface DeleteFilePayload { path: string; } -export const DeleteFileCommand = Command.silent("files:delete-file") +export const DeleteFileCommand = Command.async("files:delete-file") .input(passthrough()) .output(passthrough()) .build(); @@ -74,7 +74,7 @@ export interface MkdirPayload { /** Workspace-relative path of the directory to create. */ path: string; } -export const MkdirCommand = Command.silent("files:mkdir") +export const MkdirCommand = Command.async("files:mkdir") .input(passthrough()) .output(passthrough()) .build(); @@ -83,7 +83,7 @@ export interface RenamePayload { fromPath: string; toPath: string; } -export const RenameCommand = Command.silent("files:rename") +export const RenameCommand = Command.async("files:rename") .input(passthrough()) .output(passthrough()) .build(); diff --git a/packages/workspace.core/src/public/pick-directory.ts b/packages/workspace.core/src/public/pick-directory.ts index f7fbc32..8bcf480 100644 --- a/packages/workspace.core/src/public/pick-directory.ts +++ b/packages/workspace.core/src/public/pick-directory.ts @@ -21,7 +21,7 @@ export interface PickDirectoryResult { label: string; } -export const PickDirectoryCommand = Command.silent(PICK_DIRECTORY_COMMAND_KEY) +export const PickDirectoryCommand = Command.async(PICK_DIRECTORY_COMMAND_KEY) .input(passthrough()) .output(passthrough()) .build();