@tt-a1i/hive 1.6.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/README.en.md +74 -11
- package/README.md +42 -8
- package/dist/src/cli/hive-remote.d.ts +46 -0
- package/dist/src/cli/hive-remote.js +257 -0
- package/dist/src/cli/hive-update.js +7 -2
- package/dist/src/cli/hive.d.ts +6 -0
- package/dist/src/cli/hive.js +64 -0
- package/dist/src/cli/team.d.ts +22 -0
- package/dist/src/cli/team.js +255 -5
- package/dist/src/server/agent-command-resolver.js +10 -3
- package/dist/src/server/agent-exit-classification.d.ts +6 -0
- package/dist/src/server/agent-exit-classification.js +6 -0
- package/dist/src/server/agent-manager-support.d.ts +2 -1
- package/dist/src/server/agent-manager-support.js +59 -15
- package/dist/src/server/agent-manager.d.ts +3 -0
- package/dist/src/server/agent-manager.js +22 -7
- package/dist/src/server/agent-run-bootstrap.d.ts +20 -1
- package/dist/src/server/agent-run-bootstrap.js +16 -6
- package/dist/src/server/agent-run-exit-handler.js +14 -8
- package/dist/src/server/agent-run-starter.d.ts +3 -1
- package/dist/src/server/agent-run-starter.js +37 -6
- package/dist/src/server/agent-run-sync.js +13 -5
- package/dist/src/server/agent-runtime-types.d.ts +1 -0
- package/dist/src/server/agent-runtime.d.ts +2 -1
- package/dist/src/server/agent-runtime.js +9 -2
- package/dist/src/server/agent-startup-instructions.d.ts +2 -1
- package/dist/src/server/agent-startup-instructions.js +8 -4
- package/dist/src/server/agent-stdin-dispatcher.d.ts +4 -2
- package/dist/src/server/agent-stdin-dispatcher.js +35 -3
- package/dist/src/server/command-preset-defaults.d.ts +6 -1
- package/dist/src/server/command-preset-defaults.js +68 -0
- package/dist/src/server/fs-browse.d.ts +2 -0
- package/dist/src/server/fs-browse.js +165 -31
- package/dist/src/server/fs-pick-folder.js +6 -69
- package/dist/src/server/fs-sandbox.d.ts +5 -3
- package/dist/src/server/fs-sandbox.js +5 -3
- package/dist/src/server/hive-team-guidance.js +18 -6
- package/dist/src/server/machine-name.d.ts +2 -0
- package/dist/src/server/machine-name.js +13 -0
- package/dist/src/server/open-target-commands.d.ts +1 -0
- package/dist/src/server/open-target-commands.js +4 -1
- package/dist/src/server/orchestrator-autostart.js +1 -1
- package/dist/src/server/platform-path.d.ts +1 -0
- package/dist/src/server/platform-path.js +14 -1
- package/dist/src/server/post-start-input-writer.js +50 -13
- package/dist/src/server/preset-launch-support.js +3 -1
- package/dist/src/server/recovery-summary.d.ts +2 -1
- package/dist/src/server/recovery-summary.js +2 -1
- package/dist/src/server/remote-audit-store.d.ts +51 -0
- package/dist/src/server/remote-audit-store.js +108 -0
- package/dist/src/server/remote-config-keys.d.ts +17 -0
- package/dist/src/server/remote-config-keys.js +27 -0
- package/dist/src/server/remote-control-constants.d.ts +30 -0
- package/dist/src/server/remote-control-constants.js +29 -0
- package/dist/src/server/remote-device-session.d.ts +40 -0
- package/dist/src/server/remote-device-session.js +22 -0
- package/dist/src/server/remote-device-store.d.ts +36 -0
- package/dist/src/server/remote-device-store.js +67 -0
- package/dist/src/server/remote-frame-bridge.d.ts +102 -0
- package/dist/src/server/remote-frame-bridge.js +791 -0
- package/dist/src/server/remote-gateway-client.d.ts +14 -0
- package/dist/src/server/remote-gateway-client.js +36 -0
- package/dist/src/server/remote-loopback-auth.d.ts +6 -0
- package/dist/src/server/remote-loopback-auth.js +112 -0
- package/dist/src/server/remote-pairing-tunnel.d.ts +59 -0
- package/dist/src/server/remote-pairing-tunnel.js +146 -0
- package/dist/src/server/remote-pairing.d.ts +58 -0
- package/dist/src/server/remote-pairing.js +237 -0
- package/dist/src/server/remote-tunnel.d.ts +113 -0
- package/dist/src/server/remote-tunnel.js +514 -0
- package/dist/src/server/restart-policy-support.d.ts +4 -1
- package/dist/src/server/restart-policy-support.js +3 -1
- package/dist/src/server/restart-policy.d.ts +1 -1
- package/dist/src/server/restart-policy.js +19 -3
- package/dist/src/server/route-types.d.ts +1 -1
- package/dist/src/server/routes-dispatches.js +1 -1
- package/dist/src/server/routes-fs.js +3 -3
- package/dist/src/server/routes-marketplace.js +2 -2
- package/dist/src/server/routes-open-workspace.js +1 -1
- package/dist/src/server/routes-remote.d.ts +2 -0
- package/dist/src/server/routes-remote.js +166 -0
- package/dist/src/server/routes-runtime.js +6 -6
- package/dist/src/server/routes-settings.js +16 -16
- package/dist/src/server/routes-tasks.js +2 -2
- package/dist/src/server/routes-team-memory.d.ts +2 -0
- package/dist/src/server/routes-team-memory.js +154 -0
- package/dist/src/server/routes-team-recall.d.ts +2 -0
- package/dist/src/server/routes-team-recall.js +119 -0
- package/dist/src/server/routes-team.js +31 -9
- package/dist/src/server/routes-ui.js +11 -1
- package/dist/src/server/routes-workflow-schedules.js +3 -3
- package/dist/src/server/routes-workflows.js +5 -5
- package/dist/src/server/routes-workspace-memory-dreams.d.ts +2 -0
- package/dist/src/server/routes-workspace-memory-dreams.js +105 -0
- package/dist/src/server/routes-workspace-memory.d.ts +2 -0
- package/dist/src/server/routes-workspace-memory.js +215 -0
- package/dist/src/server/routes-workspaces.js +9 -9
- package/dist/src/server/routes.js +10 -0
- package/dist/src/server/runtime-database.d.ts +1 -0
- package/dist/src/server/runtime-database.js +27 -2
- package/dist/src/server/runtime-restart-policy.d.ts +3 -1
- package/dist/src/server/runtime-restart-policy.js +2 -1
- package/dist/src/server/runtime-store-contract.d.ts +37 -0
- package/dist/src/server/runtime-store-dream.d.ts +23 -0
- package/dist/src/server/runtime-store-dream.js +16 -0
- package/dist/src/server/runtime-store-helpers.d.ts +20 -0
- package/dist/src/server/runtime-store-helpers.js +81 -7
- package/dist/src/server/runtime-store-memory.d.ts +33 -0
- package/dist/src/server/runtime-store-memory.js +37 -0
- package/dist/src/server/runtime-store-remote.d.ts +5 -0
- package/dist/src/server/runtime-store-remote.js +45 -0
- package/dist/src/server/runtime-store-workflows.js +2 -0
- package/dist/src/server/runtime-store.js +14 -3
- package/dist/src/server/session-capture-claude.d.ts +1 -1
- package/dist/src/server/session-capture-claude.js +7 -4
- package/dist/src/server/session-capture-codex.js +4 -5
- package/dist/src/server/session-capture-gemini.js +4 -5
- package/dist/src/server/session-capture-opencode.d.ts +4 -4
- package/dist/src/server/session-capture-opencode.js +20 -12
- package/dist/src/server/session-capture-qwen.d.ts +5 -0
- package/dist/src/server/session-capture-qwen.js +104 -0
- package/dist/src/server/session-capture.d.ts +23 -0
- package/dist/src/server/session-capture.js +48 -0
- package/dist/src/server/sqlite-schema-v22.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v22.js +27 -0
- package/dist/src/server/sqlite-schema-v23.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v23.js +43 -0
- package/dist/src/server/sqlite-schema-v24.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v24.js +34 -0
- package/dist/src/server/sqlite-schema-v25.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v25.js +127 -0
- package/dist/src/server/sqlite-schema-v26.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v26.js +56 -0
- package/dist/src/server/sqlite-schema-v27.d.ts +6 -0
- package/dist/src/server/sqlite-schema-v27.js +92 -0
- package/dist/src/server/sqlite-schema-v28.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v28.js +19 -0
- package/dist/src/server/sqlite-schema-v29.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v29.js +27 -0
- package/dist/src/server/sqlite-schema-v30.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v30.js +27 -0
- package/dist/src/server/sqlite-schema-v31.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v31.js +30 -0
- package/dist/src/server/sqlite-schema.d.ts +1 -1
- package/dist/src/server/sqlite-schema.js +54 -1
- package/dist/src/server/startup-command-parser.js +5 -1
- package/dist/src/server/tasks-file-watcher.d.ts +2 -0
- package/dist/src/server/tasks-file-watcher.js +15 -6
- package/dist/src/server/tasks-file.js +30 -5
- package/dist/src/server/tasks-websocket-server.js +4 -0
- package/dist/src/server/team-authz.d.ts +1 -1
- package/dist/src/server/team-authz.js +13 -1
- package/dist/src/server/team-list-enrichment.js +3 -1
- package/dist/src/server/team-memory-digest.d.ts +52 -0
- package/dist/src/server/team-memory-digest.js +200 -0
- package/dist/src/server/team-memory-dream-applier.d.ts +5 -0
- package/dist/src/server/team-memory-dream-applier.js +234 -0
- package/dist/src/server/team-memory-dream-http-serializers.d.ts +13 -0
- package/dist/src/server/team-memory-dream-http-serializers.js +12 -0
- package/dist/src/server/team-memory-dream-ops.d.ts +40 -0
- package/dist/src/server/team-memory-dream-ops.js +153 -0
- package/dist/src/server/team-memory-dream-reverter.d.ts +22 -0
- package/dist/src/server/team-memory-dream-reverter.js +221 -0
- package/dist/src/server/team-memory-dream-run-store.d.ts +23 -0
- package/dist/src/server/team-memory-dream-run-store.js +211 -0
- package/dist/src/server/team-memory-dream-runner.d.ts +37 -0
- package/dist/src/server/team-memory-dream-runner.js +178 -0
- package/dist/src/server/team-memory-dream-scheduler.d.ts +32 -0
- package/dist/src/server/team-memory-dream-scheduler.js +115 -0
- package/dist/src/server/team-memory-dream-store.d.ts +19 -0
- package/dist/src/server/team-memory-dream-store.js +16 -0
- package/dist/src/server/team-memory-dream-types.d.ts +104 -0
- package/dist/src/server/team-memory-dream-types.js +23 -0
- package/dist/src/server/team-memory-export.d.ts +22 -0
- package/dist/src/server/team-memory-export.js +220 -0
- package/dist/src/server/team-memory-feature.d.ts +12 -0
- package/dist/src/server/team-memory-feature.js +12 -0
- package/dist/src/server/team-memory-http-serializers.d.ts +102 -0
- package/dist/src/server/team-memory-http-serializers.js +46 -0
- package/dist/src/server/team-memory-injection.d.ts +31 -0
- package/dist/src/server/team-memory-injection.js +49 -0
- package/dist/src/server/team-memory-store.d.ts +116 -0
- package/dist/src/server/team-memory-store.js +513 -0
- package/dist/src/server/team-operations.d.ts +5 -1
- package/dist/src/server/team-operations.js +46 -16
- package/dist/src/server/team-recall-store.d.ts +38 -0
- package/dist/src/server/team-recall-store.js +205 -0
- package/dist/src/server/terminal-input-profile.d.ts +1 -1
- package/dist/src/server/terminal-input-profile.js +8 -0
- package/dist/src/server/terminal-ws-server.js +6 -0
- package/dist/src/server/ui-auth-helpers.d.ts +1 -1
- package/dist/src/server/ui-auth-helpers.js +7 -1
- package/dist/src/server/ui-auth.d.ts +3 -0
- package/dist/src/server/ui-auth.js +21 -1
- package/dist/src/server/workflow-cli-policy.d.ts +2 -3
- package/dist/src/server/workflow-cli-policy.js +3 -3
- package/dist/src/server/workflow-runner.d.ts +1 -0
- package/dist/src/server/workflow-runner.js +9 -4
- package/dist/src/server/workspace-path-validation.js +6 -2
- package/dist/src/server/workspace-store.d.ts +1 -1
- package/dist/src/server/workspace-store.js +35 -9
- package/dist/src/shared/fs-browse.d.ts +1 -0
- package/dist/src/shared/fs-browse.js +1 -0
- package/dist/src/shared/path-input.d.ts +12 -0
- package/dist/src/shared/path-input.js +22 -0
- package/dist/src/shared/remote-bridge-routing.d.ts +19 -0
- package/dist/src/shared/remote-bridge-routing.js +141 -0
- package/dist/src/shared/remote-crypto.d.ts +138 -0
- package/dist/src/shared/remote-crypto.js +427 -0
- package/dist/src/shared/remote-pairing-code.d.ts +7 -0
- package/dist/src/shared/remote-pairing-code.js +47 -0
- package/dist/src/shared/remote-protocol.d.ts +160 -0
- package/dist/src/shared/remote-protocol.js +526 -0
- package/dist/src/shared/team-memory.d.ts +11 -0
- package/dist/src/shared/team-memory.js +10 -0
- package/dist/src/shared/team-recall.d.ts +1 -0
- package/dist/src/shared/team-recall.js +1 -0
- package/dist/src/shared/types.d.ts +5 -6
- package/package.json +12 -5
- package/scripts/postinstall-native-artifacts.mjs +113 -0
- package/web/dist/assets/AddWorkerDialog-C86CwNgQ.js +2 -0
- package/web/dist/assets/AddWorkspaceFlow-Bm2Jz34D.js +1 -0
- package/web/dist/assets/FirstRunWizard-XzBoEpA5.js +1 -0
- package/web/dist/assets/MarketplaceDrawer-BFfGT8hH.js +67 -0
- package/web/dist/assets/TaskGraphDrawer-_uVH_0C1.js +1 -0
- package/web/dist/assets/{WhatsNewDialog-CSGzk-2U.js → WhatsNewDialog-DkJHmkMs.js} +1 -1
- package/web/dist/assets/WorkerModal-BtMJEOG9.js +1 -0
- package/web/dist/assets/WorkflowsDrawer-CiIdHS6_.js +1 -0
- package/web/dist/assets/WorkspaceMemoryDrawer-C6sNocl_.js +1 -0
- package/web/dist/assets/WorkspaceTaskDrawer-CyhhEB1Z.js +1 -0
- package/web/dist/assets/index-BAiLYajK.css +1 -0
- package/web/dist/assets/index-K-GG8UwR.js +73 -0
- package/web/dist/assets/search-BtRkkEmS.js +1 -0
- package/web/dist/assets/square-terminal-lEeQUWb3.js +1 -0
- package/web/dist/cli-icons/agy.png +0 -0
- package/web/dist/cli-icons/cursor.ico +0 -0
- package/web/dist/cli-icons/grok.ico +0 -0
- package/web/dist/cli-icons/hermes.png +0 -0
- package/web/dist/cli-icons/qwen.png +0 -0
- package/web/dist/index.html +8 -3
- package/web/dist/sw.js +1 -1
- package/scripts/fix-runtime-artifacts.mjs +0 -33
- package/web/dist/assets/AddWorkerDialog-CGbaxu0T.js +0 -2
- package/web/dist/assets/AddWorkspaceDialog-CNgExu6b.js +0 -1
- package/web/dist/assets/FirstRunWizard-DxGApUNc.js +0 -1
- package/web/dist/assets/MarketplaceDrawer-Bk6cpukn.js +0 -76
- package/web/dist/assets/WorkerModal-i2F3n3nZ.js +0 -1
- package/web/dist/assets/WorkspaceTaskDrawer-C_Ta_K13.js +0 -1
- package/web/dist/assets/WorkspaceTerminalPanels-DDGTF8rc.css +0 -1
- package/web/dist/assets/WorkspaceTerminalPanels-VdDxtrQF.js +0 -1
- package/web/dist/assets/index-5zh61jMg.css +0 -1
- package/web/dist/assets/index-CAgGM6nb.js +0 -75
- package/web/dist/assets/path-join-7MR1s7b1.js +0 -1
|
@@ -2,6 +2,7 @@ import { FEATURE_FLAGS_ALL_OFF } from './feature-flags.js';
|
|
|
2
2
|
import { buildOrchestratorReminderTail, buildWorkerReminderTail } from './hive-team-guidance.js';
|
|
3
3
|
import { PtyInactiveError } from './http-errors.js';
|
|
4
4
|
import { createPostStartInputWriter } from './post-start-input-writer.js';
|
|
5
|
+
import { buildDispatchMemoryDigestSafely, logMemoryDigestInjection, rollbackMemoryDigestInjection, } from './team-memory-injection.js';
|
|
5
6
|
export const buildOrchestratorReportPayload = (workerName, text, artifacts, flags = FEATURE_FLAGS_ALL_OFF) => {
|
|
6
7
|
const lines = [`<hive-message kind="report" from="@${workerName}">`, text];
|
|
7
8
|
for (const artifact of artifacts)
|
|
@@ -17,7 +18,7 @@ export const buildOrchestratorStatusPayload = (workerName, text, artifacts, flag
|
|
|
17
18
|
return lines.join('\n');
|
|
18
19
|
};
|
|
19
20
|
export const buildOrchestratorUserInputPayload = (text, flags = FEATURE_FLAGS_ALL_OFF) => [text, '', buildOrchestratorReminderTail(flags), ''].join('\n');
|
|
20
|
-
export const buildWorkerDispatchPayload = (fromAgentName, workerDescription, dispatchId, text) => [
|
|
21
|
+
export const buildWorkerDispatchPayload = (fromAgentName, workerDescription, dispatchId, text, memoryDigest) => [
|
|
21
22
|
`<hive-message kind="dispatch" from="@${fromAgentName}">`,
|
|
22
23
|
'',
|
|
23
24
|
`Your role: ${workerDescription}`,
|
|
@@ -28,6 +29,7 @@ export const buildWorkerDispatchPayload = (fromAgentName, workerDescription, dis
|
|
|
28
29
|
'',
|
|
29
30
|
`dispatch_id: ${dispatchId}`,
|
|
30
31
|
'',
|
|
32
|
+
...(memoryDigest ? [memoryDigest, ''] : []),
|
|
31
33
|
'Task:',
|
|
32
34
|
text,
|
|
33
35
|
'</hive-message>',
|
|
@@ -45,7 +47,7 @@ export const buildWorkerCancelPayload = (dispatchId, reason) => [
|
|
|
45
47
|
'</hive-message>',
|
|
46
48
|
'',
|
|
47
49
|
].join('\n');
|
|
48
|
-
export const createAgentStdinDispatcher = ({ agentManager, getLaunchConfig, getWorkspaceId, registry, syncRun, getFlags, }) => {
|
|
50
|
+
export const createAgentStdinDispatcher = ({ agentManager, getLaunchConfig, getWorkspaceId, registry, syncRun, memoryInjection, getFlags, }) => {
|
|
49
51
|
const flags = () => getFlags?.() ?? FEATURE_FLAGS_ALL_OFF;
|
|
50
52
|
const chains = new Map();
|
|
51
53
|
const getChain = (agentId) => {
|
|
@@ -163,7 +165,37 @@ export const createAgentStdinDispatcher = ({ agentManager, getLaunchConfig, getW
|
|
|
163
165
|
swallowQueuedFailure(writeToActiveAgentRun(workspaceId, `${workspaceId}:orchestrator`, buildOrchestratorStatusPayload(workerName, text, artifacts, flags()), input));
|
|
164
166
|
},
|
|
165
167
|
writeSendPrompt(workspaceId, workerId, dispatchId, fromAgentName, workerDescription, text) {
|
|
166
|
-
|
|
168
|
+
if (!resolveActiveRun(workspaceId, workerId)) {
|
|
169
|
+
throw new PtyInactiveError(`No active run for agent: ${workerId}`);
|
|
170
|
+
}
|
|
171
|
+
const memoryDigest = buildDispatchMemoryDigestSafely({
|
|
172
|
+
memoryInjection,
|
|
173
|
+
taskText: text,
|
|
174
|
+
workerDescription,
|
|
175
|
+
workspaceId,
|
|
176
|
+
});
|
|
177
|
+
const injectionIds = logMemoryDigestInjection({
|
|
178
|
+
agentId: workerId,
|
|
179
|
+
contextType: 'dispatch',
|
|
180
|
+
dispatchId,
|
|
181
|
+
memoryDigest,
|
|
182
|
+
memoryInjection,
|
|
183
|
+
workspaceId,
|
|
184
|
+
});
|
|
185
|
+
const rollback = () => rollbackMemoryDigestInjection({
|
|
186
|
+
injectionIds,
|
|
187
|
+
memoryInjection,
|
|
188
|
+
});
|
|
189
|
+
try {
|
|
190
|
+
return writeToActiveAgentRun(workspaceId, workerId, buildWorkerDispatchPayload(fromAgentName, workerDescription, dispatchId, text, injectionIds ? memoryDigest?.text : null), { requireActiveRun: true }).catch((error) => {
|
|
191
|
+
rollback();
|
|
192
|
+
throw error;
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
rollback();
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
167
199
|
},
|
|
168
200
|
writeCancelPrompt(workspaceId, workerId, dispatchId, reason, input = {}) {
|
|
169
201
|
swallowQueuedFailure(writeToActiveAgentRun(workspaceId, workerId, buildWorkerCancelPayload(dispatchId, reason), input));
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { SessionIdCaptureConfig } from './session-capture.js';
|
|
2
|
+
export declare const BUILTIN_COMMAND_PRESET_IDS: readonly ["claude", "codex", "opencode", "gemini", "hermes", "qwen", "agy", "cursor", "grok"];
|
|
3
|
+
export type BuiltinCommandPresetId = (typeof BUILTIN_COMMAND_PRESET_IDS)[number];
|
|
2
4
|
export interface BuiltinCommandPresetDefaults {
|
|
3
|
-
id:
|
|
5
|
+
id: BuiltinCommandPresetId;
|
|
4
6
|
displayName: string;
|
|
5
7
|
command: string;
|
|
6
8
|
resumeArgsTemplate: string | null;
|
|
@@ -9,3 +11,6 @@ export interface BuiltinCommandPresetDefaults {
|
|
|
9
11
|
}
|
|
10
12
|
export declare const BUILTIN_COMMAND_PRESETS: BuiltinCommandPresetDefaults[];
|
|
11
13
|
export declare const getBuiltinCommandPreset: (id: string) => BuiltinCommandPresetDefaults | undefined;
|
|
14
|
+
export declare const getBuiltinCommandPresetByCommand: (command: string) => BuiltinCommandPresetDefaults | undefined;
|
|
15
|
+
export declare const BUILTIN_COMMAND_PRESET_CLI_LIST: string;
|
|
16
|
+
export declare const BUILTIN_INTERACTIVE_COMMANDS: Set<string>;
|
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
import { CLAUDE_DEFAULT_YOLO_ARGS } from './claude-command-defaults.js';
|
|
2
|
+
export const BUILTIN_COMMAND_PRESET_IDS = [
|
|
3
|
+
'claude',
|
|
4
|
+
'codex',
|
|
5
|
+
'opencode',
|
|
6
|
+
'gemini',
|
|
7
|
+
'hermes',
|
|
8
|
+
'qwen',
|
|
9
|
+
'agy',
|
|
10
|
+
'cursor',
|
|
11
|
+
'grok',
|
|
12
|
+
];
|
|
2
13
|
const CODEX_DEFAULT_YOLO_ARGS = ['--dangerously-bypass-approvals-and-sandbox'];
|
|
3
14
|
const OPENCODE_DEFAULT_YOLO_ARGS = [];
|
|
4
15
|
const GEMINI_DEFAULT_YOLO_ARGS = ['--yolo'];
|
|
16
|
+
const HERMES_DEFAULT_YOLO_ARGS = ['--yolo'];
|
|
17
|
+
const QWEN_DEFAULT_YOLO_ARGS = ['--approval-mode', 'yolo'];
|
|
18
|
+
const AGY_DEFAULT_YOLO_ARGS = ['--dangerously-skip-permissions'];
|
|
19
|
+
const CURSOR_DEFAULT_YOLO_ARGS = ['--force'];
|
|
20
|
+
const GROK_DEFAULT_YOLO_ARGS = ['--always-approve'];
|
|
5
21
|
export const BUILTIN_COMMAND_PRESETS = [
|
|
6
22
|
{
|
|
7
23
|
command: 'claude',
|
|
@@ -47,5 +63,57 @@ export const BUILTIN_COMMAND_PRESETS = [
|
|
|
47
63
|
},
|
|
48
64
|
yoloArgsTemplate: GEMINI_DEFAULT_YOLO_ARGS,
|
|
49
65
|
},
|
|
66
|
+
{
|
|
67
|
+
command: 'hermes',
|
|
68
|
+
displayName: 'Hermes',
|
|
69
|
+
id: 'hermes',
|
|
70
|
+
resumeArgsTemplate: '--resume {session_id}',
|
|
71
|
+
sessionIdCapture: {
|
|
72
|
+
pattern: String.raw `Session:\s*([A-Za-z0-9_-]+)`,
|
|
73
|
+
source: 'stdout_regex',
|
|
74
|
+
},
|
|
75
|
+
yoloArgsTemplate: HERMES_DEFAULT_YOLO_ARGS,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
command: 'qwen',
|
|
79
|
+
displayName: 'Qwen Code',
|
|
80
|
+
id: 'qwen',
|
|
81
|
+
resumeArgsTemplate: '--resume {session_id}',
|
|
82
|
+
sessionIdCapture: {
|
|
83
|
+
pattern: '~/.qwen/sessions/**/*.json',
|
|
84
|
+
source: 'qwen_session_json_dir',
|
|
85
|
+
},
|
|
86
|
+
yoloArgsTemplate: QWEN_DEFAULT_YOLO_ARGS,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
command: 'agy',
|
|
90
|
+
displayName: 'Antigravity CLI',
|
|
91
|
+
id: 'agy',
|
|
92
|
+
resumeArgsTemplate: '--conversation {session_id}',
|
|
93
|
+
sessionIdCapture: {
|
|
94
|
+
pattern: String.raw `(?:^|\s)(?:\S*[\\/])?agy(?:\.(?:cmd|exe))?\s+--conversation\s+([0-9a-fA-F-]{36})\b`,
|
|
95
|
+
source: 'stdout_regex',
|
|
96
|
+
},
|
|
97
|
+
yoloArgsTemplate: AGY_DEFAULT_YOLO_ARGS,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
command: 'cursor-agent',
|
|
101
|
+
displayName: 'Cursor CLI',
|
|
102
|
+
id: 'cursor',
|
|
103
|
+
resumeArgsTemplate: null,
|
|
104
|
+
sessionIdCapture: null,
|
|
105
|
+
yoloArgsTemplate: CURSOR_DEFAULT_YOLO_ARGS,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
command: 'grok',
|
|
109
|
+
displayName: 'Grok Build',
|
|
110
|
+
id: 'grok',
|
|
111
|
+
resumeArgsTemplate: null,
|
|
112
|
+
sessionIdCapture: null,
|
|
113
|
+
yoloArgsTemplate: GROK_DEFAULT_YOLO_ARGS,
|
|
114
|
+
},
|
|
50
115
|
];
|
|
51
116
|
export const getBuiltinCommandPreset = (id) => BUILTIN_COMMAND_PRESETS.find((preset) => preset.id === id);
|
|
117
|
+
export const getBuiltinCommandPresetByCommand = (command) => BUILTIN_COMMAND_PRESETS.find((preset) => preset.command === command);
|
|
118
|
+
export const BUILTIN_COMMAND_PRESET_CLI_LIST = BUILTIN_COMMAND_PRESET_IDS.join('|');
|
|
119
|
+
export const BUILTIN_INTERACTIVE_COMMANDS = new Set(BUILTIN_COMMAND_PRESETS.flatMap((preset) => [preset.id, preset.command]));
|
|
@@ -21,6 +21,8 @@ export interface FsProbeResponse {
|
|
|
21
21
|
path: string;
|
|
22
22
|
suggested_name: string;
|
|
23
23
|
}
|
|
24
|
+
export declare const getWindowsBrowseParentPath: (candidate: string) => string;
|
|
25
|
+
export declare const getSuggestedWorkspaceNameFromPath: (path: string) => string;
|
|
24
26
|
export declare const browseDirectory: (requestedPath: string) => Promise<FsBrowseResponse>;
|
|
25
27
|
export interface ProbeDirectoryOptions {
|
|
26
28
|
/**
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { dirname, resolve } from 'node:path';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
const GIT_BRANCH_TIMEOUT_MS = 800;
|
|
1
|
+
import { readdir, readFile, stat } from 'node:fs/promises';
|
|
2
|
+
import { dirname, isAbsolute, resolve } from 'node:path';
|
|
3
|
+
import { dirname as dirnameWin32Path, parse as parseWin32Path, resolve as resolveWin32Path, } from 'node:path/win32';
|
|
4
|
+
import { WINDOWS_DRIVES_ROOT } from '../shared/fs-browse.js';
|
|
5
|
+
import { sanitizePastedPath } from '../shared/path-input.js';
|
|
6
|
+
import { getFsBrowseRoot, hasFsBrowseRootOverride, isPathWithinRoot } from './fs-sandbox.js';
|
|
8
7
|
/**
|
|
9
8
|
* Map a filesystem rejection (from `readdir`, `stat`, etc.) to a string
|
|
10
9
|
* suitable for surfacing in the browse response. The common Windows
|
|
@@ -28,31 +27,94 @@ const formatFilesystemError = (error) => {
|
|
|
28
27
|
}
|
|
29
28
|
return error.message;
|
|
30
29
|
};
|
|
31
|
-
const
|
|
30
|
+
const resolveGitDirPath = async (repoPath) => {
|
|
31
|
+
const dotGitPath = resolve(repoPath, '.git');
|
|
32
32
|
try {
|
|
33
|
-
const info = await stat(
|
|
34
|
-
|
|
33
|
+
const info = await stat(dotGitPath);
|
|
34
|
+
if (info.isDirectory())
|
|
35
|
+
return dotGitPath;
|
|
36
|
+
if (!info.isFile())
|
|
37
|
+
return null;
|
|
38
|
+
const text = await readFile(dotGitPath, 'utf8');
|
|
39
|
+
const firstLine = text.split(/\r?\n/u)[0]?.trim() ?? '';
|
|
40
|
+
const match = /^gitdir:\s*(?<path>.+)$/iu.exec(firstLine);
|
|
41
|
+
const gitDirPath = match?.groups?.path?.trim();
|
|
42
|
+
if (!gitDirPath)
|
|
43
|
+
return null;
|
|
44
|
+
return isAbsolute(gitDirPath) ? gitDirPath : resolve(repoPath, gitDirPath);
|
|
35
45
|
}
|
|
36
46
|
catch {
|
|
37
|
-
return
|
|
47
|
+
return null;
|
|
38
48
|
}
|
|
39
49
|
};
|
|
50
|
+
const detectGitRepository = async (entryPath) => (await resolveGitDirPath(entryPath)) !== null;
|
|
40
51
|
const readCurrentBranch = async (repoPath) => {
|
|
52
|
+
const gitDirPath = await resolveGitDirPath(repoPath);
|
|
53
|
+
if (!gitDirPath)
|
|
54
|
+
return null;
|
|
41
55
|
try {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return branch.length > 0 ? branch : null;
|
|
56
|
+
const head = (await readFile(resolve(gitDirPath, 'HEAD'), 'utf8')).split(/\r?\n/u)[0]?.trim();
|
|
57
|
+
const branchRef = head?.startsWith('ref: refs/heads/')
|
|
58
|
+
? head.slice('ref: refs/heads/'.length)
|
|
59
|
+
: '';
|
|
60
|
+
return branchRef.length > 0 ? branchRef : null;
|
|
48
61
|
}
|
|
49
62
|
catch {
|
|
50
63
|
return null;
|
|
51
64
|
}
|
|
52
65
|
};
|
|
66
|
+
const isFullWindowsBrowseEnabled = () => process.platform === 'win32' && !hasFsBrowseRootOverride();
|
|
67
|
+
const trimTrailingWindowsSeparators = (path) => path.replace(/[\\/]+$/u, '');
|
|
68
|
+
const isWindowsRootPath = (path) => {
|
|
69
|
+
const resolved = resolveWin32Path(path);
|
|
70
|
+
const parsed = parseWin32Path(resolved);
|
|
71
|
+
return (parsed.root.length > 0 &&
|
|
72
|
+
trimTrailingWindowsSeparators(resolved).toLowerCase() ===
|
|
73
|
+
trimTrailingWindowsSeparators(parsed.root).toLowerCase());
|
|
74
|
+
};
|
|
75
|
+
const listWindowsDriveRoots = async () => {
|
|
76
|
+
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
|
|
77
|
+
const roots = await Promise.all(letters.map(async (letter) => {
|
|
78
|
+
const root = `${letter}:\\`;
|
|
79
|
+
try {
|
|
80
|
+
const info = await stat(root);
|
|
81
|
+
if (!info.isDirectory())
|
|
82
|
+
return null;
|
|
83
|
+
return {
|
|
84
|
+
is_dir: true,
|
|
85
|
+
is_git_repository: false,
|
|
86
|
+
name: `${letter}:`,
|
|
87
|
+
path: root,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}));
|
|
94
|
+
return roots.filter((root) => root !== null);
|
|
95
|
+
};
|
|
96
|
+
const browseWindowsDrivesRoot = async () => ({
|
|
97
|
+
current_path: WINDOWS_DRIVES_ROOT,
|
|
98
|
+
entries: await listWindowsDriveRoots(),
|
|
99
|
+
error: null,
|
|
100
|
+
ok: true,
|
|
101
|
+
parent_path: null,
|
|
102
|
+
root_path: WINDOWS_DRIVES_ROOT,
|
|
103
|
+
});
|
|
104
|
+
const getSandboxParentPath = (rootPath, candidate) => {
|
|
105
|
+
if (candidate === rootPath)
|
|
106
|
+
return null;
|
|
107
|
+
const rawParent = dirname(candidate);
|
|
108
|
+
return isPathWithinRoot(rootPath, rawParent) ? rawParent : null;
|
|
109
|
+
};
|
|
110
|
+
export const getWindowsBrowseParentPath = (candidate) => isWindowsRootPath(candidate) ? WINDOWS_DRIVES_ROOT : dirnameWin32Path(candidate);
|
|
111
|
+
export const getSuggestedWorkspaceNameFromPath = (path) => (path.split(/[\\/]/).filter(Boolean).pop() ?? '').replace(/:$/u, '');
|
|
53
112
|
export const browseDirectory = async (requestedPath) => {
|
|
113
|
+
if (isFullWindowsBrowseEnabled()) {
|
|
114
|
+
return browseWindowsDirectory(requestedPath);
|
|
115
|
+
}
|
|
54
116
|
const rootPath = getFsBrowseRoot();
|
|
55
|
-
const trimmed = requestedPath
|
|
117
|
+
const trimmed = sanitizePastedPath(requestedPath);
|
|
56
118
|
const candidate = trimmed.length === 0 ? rootPath : resolve(rootPath, trimmed);
|
|
57
119
|
if (!isPathWithinRoot(rootPath, candidate)) {
|
|
58
120
|
return {
|
|
@@ -74,7 +136,7 @@ export const browseDirectory = async (requestedPath) => {
|
|
|
74
136
|
entries: [],
|
|
75
137
|
error: formatFilesystemError(error),
|
|
76
138
|
ok: false,
|
|
77
|
-
parent_path:
|
|
139
|
+
parent_path: getSandboxParentPath(rootPath, candidate),
|
|
78
140
|
root_path: rootPath,
|
|
79
141
|
};
|
|
80
142
|
}
|
|
@@ -84,7 +146,7 @@ export const browseDirectory = async (requestedPath) => {
|
|
|
84
146
|
entries: [],
|
|
85
147
|
error: 'The specified path is not a directory.',
|
|
86
148
|
ok: false,
|
|
87
|
-
parent_path:
|
|
149
|
+
parent_path: getSandboxParentPath(rootPath, candidate),
|
|
88
150
|
root_path: rootPath,
|
|
89
151
|
};
|
|
90
152
|
}
|
|
@@ -102,7 +164,7 @@ export const browseDirectory = async (requestedPath) => {
|
|
|
102
164
|
entries: [],
|
|
103
165
|
error: formatFilesystemError(error),
|
|
104
166
|
ok: false,
|
|
105
|
-
parent_path:
|
|
167
|
+
parent_path: getSandboxParentPath(rootPath, candidate),
|
|
106
168
|
root_path: rootPath,
|
|
107
169
|
};
|
|
108
170
|
}
|
|
@@ -118,25 +180,97 @@ export const browseDirectory = async (requestedPath) => {
|
|
|
118
180
|
path: entryPath,
|
|
119
181
|
};
|
|
120
182
|
}));
|
|
121
|
-
const isAtRoot = candidate === rootPath;
|
|
122
|
-
const rawParent = dirname(candidate);
|
|
123
|
-
const parentIsWithinRoot = isPathWithinRoot(rootPath, rawParent);
|
|
124
|
-
const parent_path = isAtRoot ? null : parentIsWithinRoot ? rawParent : null;
|
|
125
183
|
return {
|
|
126
184
|
current_path: candidate,
|
|
127
185
|
entries,
|
|
128
186
|
error: null,
|
|
129
187
|
ok: true,
|
|
130
|
-
parent_path,
|
|
188
|
+
parent_path: getSandboxParentPath(rootPath, candidate),
|
|
131
189
|
root_path: rootPath,
|
|
132
190
|
};
|
|
133
191
|
};
|
|
192
|
+
const browseWindowsDirectory = async (requestedPath) => {
|
|
193
|
+
const trimmed = sanitizePastedPath(requestedPath);
|
|
194
|
+
if (trimmed.length === 0 || trimmed === WINDOWS_DRIVES_ROOT) {
|
|
195
|
+
return browseWindowsDrivesRoot();
|
|
196
|
+
}
|
|
197
|
+
const candidate = resolveWin32Path(trimmed);
|
|
198
|
+
let dirStat;
|
|
199
|
+
try {
|
|
200
|
+
dirStat = await stat(candidate);
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
return {
|
|
204
|
+
current_path: candidate,
|
|
205
|
+
entries: [],
|
|
206
|
+
error: formatFilesystemError(error),
|
|
207
|
+
ok: false,
|
|
208
|
+
parent_path: getWindowsBrowseParentPath(candidate),
|
|
209
|
+
root_path: WINDOWS_DRIVES_ROOT,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
if (!dirStat.isDirectory()) {
|
|
213
|
+
return {
|
|
214
|
+
current_path: candidate,
|
|
215
|
+
entries: [],
|
|
216
|
+
error: 'The specified path is not a directory.',
|
|
217
|
+
ok: false,
|
|
218
|
+
parent_path: getWindowsBrowseParentPath(candidate),
|
|
219
|
+
root_path: WINDOWS_DRIVES_ROOT,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
let rawEntries;
|
|
223
|
+
try {
|
|
224
|
+
rawEntries = await readdir(candidate, { withFileTypes: true });
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
return {
|
|
228
|
+
current_path: candidate,
|
|
229
|
+
entries: [],
|
|
230
|
+
error: formatFilesystemError(error),
|
|
231
|
+
ok: false,
|
|
232
|
+
parent_path: getWindowsBrowseParentPath(candidate),
|
|
233
|
+
root_path: WINDOWS_DRIVES_ROOT,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
const directoryEntries = rawEntries
|
|
237
|
+
.filter((entry) => entry.isDirectory() && !entry.name.startsWith('.'))
|
|
238
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
239
|
+
const entries = await Promise.all(directoryEntries.map(async (entry) => {
|
|
240
|
+
const entryPath = resolveWin32Path(candidate, entry.name);
|
|
241
|
+
return {
|
|
242
|
+
is_dir: true,
|
|
243
|
+
is_git_repository: await detectGitRepository(entryPath),
|
|
244
|
+
name: entry.name,
|
|
245
|
+
path: entryPath,
|
|
246
|
+
};
|
|
247
|
+
}));
|
|
248
|
+
return {
|
|
249
|
+
current_path: candidate,
|
|
250
|
+
entries,
|
|
251
|
+
error: null,
|
|
252
|
+
ok: true,
|
|
253
|
+
parent_path: getWindowsBrowseParentPath(candidate),
|
|
254
|
+
root_path: WINDOWS_DRIVES_ROOT,
|
|
255
|
+
};
|
|
256
|
+
};
|
|
134
257
|
export const probeDirectory = async (requestedPath, options = {}) => {
|
|
135
258
|
const enforceSandbox = options.enforceSandbox ?? true;
|
|
259
|
+
const fullWindowsBrowse = isFullWindowsBrowseEnabled();
|
|
260
|
+
const requested = sanitizePastedPath(requestedPath);
|
|
261
|
+
if (requested === WINDOWS_DRIVES_ROOT) {
|
|
262
|
+
return {
|
|
263
|
+
current_branch: null,
|
|
264
|
+
exists: false,
|
|
265
|
+
is_dir: false,
|
|
266
|
+
is_git_repository: false,
|
|
267
|
+
ok: false,
|
|
268
|
+
path: '',
|
|
269
|
+
suggested_name: '',
|
|
270
|
+
};
|
|
271
|
+
}
|
|
136
272
|
const rootPath = getFsBrowseRoot();
|
|
137
|
-
const candidate = enforceSandbox
|
|
138
|
-
? resolve(rootPath, requestedPath.trim())
|
|
139
|
-
: resolve(requestedPath.trim());
|
|
273
|
+
const candidate = enforceSandbox && !fullWindowsBrowse ? resolve(rootPath, requested) : resolve(requested);
|
|
140
274
|
const base = {
|
|
141
275
|
current_branch: null,
|
|
142
276
|
exists: false,
|
|
@@ -144,9 +278,9 @@ export const probeDirectory = async (requestedPath, options = {}) => {
|
|
|
144
278
|
is_git_repository: false,
|
|
145
279
|
ok: false,
|
|
146
280
|
path: candidate,
|
|
147
|
-
suggested_name: candidate
|
|
281
|
+
suggested_name: getSuggestedWorkspaceNameFromPath(candidate),
|
|
148
282
|
};
|
|
149
|
-
if (enforceSandbox && !isPathWithinRoot(rootPath, candidate)) {
|
|
283
|
+
if (enforceSandbox && !fullWindowsBrowse && !isPathWithinRoot(rootPath, candidate)) {
|
|
150
284
|
return base;
|
|
151
285
|
}
|
|
152
286
|
try {
|
|
@@ -95,73 +95,6 @@ const linuxPick = async (run) => {
|
|
|
95
95
|
return emptyResponse({ canceled: true });
|
|
96
96
|
return finalizeWithProbe(picked);
|
|
97
97
|
};
|
|
98
|
-
const windowsPick = async (run) => {
|
|
99
|
-
/* Hive's PowerShell child has no visible main window, so a bare
|
|
100
|
-
`$dialog.ShowDialog()` inherits the desktop as IWin32Window parent —
|
|
101
|
-
and ends up below the foreground browser in z-order. To the user
|
|
102
|
-
that looks like a hang ("Add Workspace pops up then nothing"); the
|
|
103
|
-
picker is open, just occluded.
|
|
104
|
-
|
|
105
|
-
Fix: build a TopMost invisible owner Form, `Show()` it so it has a
|
|
106
|
-
real HWND (an unshown Form has none, and ShowDialog silently falls
|
|
107
|
-
back to desktop-parent), then pass the owner to `ShowDialog($owner)`.
|
|
108
|
-
The owner inherits TopMost z-order onto the dialog. The owner itself
|
|
109
|
-
stays invisible — Opacity 0, parked at (-32000, -32000), 1x1 size,
|
|
110
|
-
no taskbar entry — so the user only sees the picker. Dispose in
|
|
111
|
-
`finally` to release the HWND each invocation.
|
|
112
|
-
|
|
113
|
-
`Add-Type -AssemblyName System.Drawing` is required because Point /
|
|
114
|
-
Size live in System.Drawing.dll, not System.Windows.Forms.dll. */
|
|
115
|
-
// PS 5.1 on zh-CN Windows defaults [Console]::OutputEncoding to cp936/GBK;
|
|
116
|
-
// Node decodes stdout as UTF-8 and CJK paths arrive mojibake'd. Force UTF-8
|
|
117
|
-
// before any output. No-op on PS 7+ which already defaults to UTF-8.
|
|
118
|
-
const script = [
|
|
119
|
-
'[Console]::OutputEncoding = [System.Text.Encoding]::UTF8',
|
|
120
|
-
'Add-Type -AssemblyName System.Windows.Forms',
|
|
121
|
-
'Add-Type -AssemblyName System.Drawing',
|
|
122
|
-
'$owner = New-Object System.Windows.Forms.Form',
|
|
123
|
-
"$owner.FormBorderStyle = 'None'",
|
|
124
|
-
'$owner.Opacity = 0',
|
|
125
|
-
'$owner.ShowInTaskbar = $false',
|
|
126
|
-
"$owner.StartPosition = 'Manual'",
|
|
127
|
-
'$owner.Location = New-Object System.Drawing.Point(-32000, -32000)',
|
|
128
|
-
'$owner.Size = New-Object System.Drawing.Size(1, 1)',
|
|
129
|
-
'$owner.TopMost = $true',
|
|
130
|
-
'$owner.Show()',
|
|
131
|
-
'try {',
|
|
132
|
-
' $dialog = New-Object System.Windows.Forms.FolderBrowserDialog',
|
|
133
|
-
' $dialog.Description = "Select Hive workspace"',
|
|
134
|
-
' $dialog.ShowNewFolderButton = $false',
|
|
135
|
-
' $result = $dialog.ShowDialog($owner)',
|
|
136
|
-
' if ($result -eq [System.Windows.Forms.DialogResult]::OK) { [Console]::Out.WriteLine($dialog.SelectedPath); exit 0 }',
|
|
137
|
-
' exit 1',
|
|
138
|
-
'} finally {',
|
|
139
|
-
' $owner.Close()',
|
|
140
|
-
' $owner.Dispose()',
|
|
141
|
-
'}',
|
|
142
|
-
].join('; ');
|
|
143
|
-
const result = await run('powershell.exe', ['-NoProfile', '-STA', '-ExecutionPolicy', 'Bypass', '-Command', script], { timeout: PICKER_TIMEOUT_MS });
|
|
144
|
-
if (result.spawnError?.code === 'ENOENT') {
|
|
145
|
-
return emptyResponse({
|
|
146
|
-
error: 'PowerShell is unavailable on this host. Use Advanced: paste path.',
|
|
147
|
-
supported: false,
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
if (result.timedOut) {
|
|
151
|
-
return emptyResponse({ error: 'Folder picker timed out before a folder was selected.' });
|
|
152
|
-
}
|
|
153
|
-
if (result.status !== 0) {
|
|
154
|
-
const stderr = result.stderr.trim();
|
|
155
|
-
if (stderr.length > 0) {
|
|
156
|
-
return emptyResponse({ error: `Folder picker failed: ${stderr}` });
|
|
157
|
-
}
|
|
158
|
-
return emptyResponse({ canceled: true });
|
|
159
|
-
}
|
|
160
|
-
const picked = result.stdout.trim();
|
|
161
|
-
if (picked.length === 0)
|
|
162
|
-
return emptyResponse({ canceled: true });
|
|
163
|
-
return finalizeWithProbe(picked);
|
|
164
|
-
};
|
|
165
98
|
export const pickFolder = async (options = {}) => {
|
|
166
99
|
const platform = options.platform ?? process.platform;
|
|
167
100
|
const run = options.runCommand ?? defaultRunCommand;
|
|
@@ -169,8 +102,12 @@ export const pickFolder = async (options = {}) => {
|
|
|
169
102
|
return macOsPick(run);
|
|
170
103
|
if (platform === 'linux')
|
|
171
104
|
return linuxPick(run);
|
|
172
|
-
if (platform === 'win32')
|
|
173
|
-
return
|
|
105
|
+
if (platform === 'win32') {
|
|
106
|
+
return emptyResponse({
|
|
107
|
+
error: 'Native folder picker is disabled on Windows. Use Browse server filesystem or paste path.',
|
|
108
|
+
supported: false,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
174
111
|
return emptyResponse({
|
|
175
112
|
error: 'Native folder picker not supported on this platform. Use Advanced: paste path.',
|
|
176
113
|
supported: false,
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Root directory the FS-browse API is allowed to reveal
|
|
3
|
-
*
|
|
4
|
-
* this
|
|
2
|
+
* Root directory the FS-browse API is allowed to reveal when sandboxing is
|
|
3
|
+
* enabled. Tests and remote/headless setups can set `HIVE_FS_BROWSE_ROOT`.
|
|
4
|
+
* Normal Windows runs skip this sandbox root and browse from the virtual
|
|
5
|
+
* "This PC" drive list; POSIX sandboxed browsing defaults to `$HOME`.
|
|
5
6
|
*/
|
|
6
7
|
export declare const getFsBrowseRoot: () => string;
|
|
8
|
+
export declare const hasFsBrowseRootOverride: () => boolean;
|
|
7
9
|
/**
|
|
8
10
|
* True when `candidatePath` is `rootPath` itself or a descendant of it.
|
|
9
11
|
* Uses `path.relative` + separator check so Windows back-slashes and drive
|
|
@@ -2,9 +2,10 @@ import { homedir } from 'node:os';
|
|
|
2
2
|
import { isAbsolute, relative, resolve, sep } from 'node:path';
|
|
3
3
|
import { realpathNative } from './path-canonicalization.js';
|
|
4
4
|
/**
|
|
5
|
-
* Root directory the FS-browse API is allowed to reveal
|
|
6
|
-
*
|
|
7
|
-
* this
|
|
5
|
+
* Root directory the FS-browse API is allowed to reveal when sandboxing is
|
|
6
|
+
* enabled. Tests and remote/headless setups can set `HIVE_FS_BROWSE_ROOT`.
|
|
7
|
+
* Normal Windows runs skip this sandbox root and browse from the virtual
|
|
8
|
+
* "This PC" drive list; POSIX sandboxed browsing defaults to `$HOME`.
|
|
8
9
|
*/
|
|
9
10
|
export const getFsBrowseRoot = () => {
|
|
10
11
|
const override = process.env.HIVE_FS_BROWSE_ROOT;
|
|
@@ -16,6 +17,7 @@ export const getFsBrowseRoot = () => {
|
|
|
16
17
|
return root;
|
|
17
18
|
}
|
|
18
19
|
};
|
|
20
|
+
export const hasFsBrowseRootOverride = () => (process.env.HIVE_FS_BROWSE_ROOT ?? '').length > 0;
|
|
19
21
|
const isResolvedPathWithinRoot = (rootPath, candidatePath) => {
|
|
20
22
|
if (candidatePath === rootPath)
|
|
21
23
|
return true;
|