@tt-a1i/hive 1.7.0 → 2.0.2
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 +60 -0
- package/README.en.md +73 -11
- package/README.md +41 -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 +14 -0
- package/dist/src/server/agent-run-bootstrap.js +11 -4
- 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 +22 -5
- 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 +56 -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 +1 -0
- 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 +17 -0
- package/dist/src/server/session-capture.js +16 -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 +49 -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 +18 -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 +4 -5
- package/package.json +12 -5
- package/scripts/postinstall-native-artifacts.mjs +113 -0
- package/web/dist/assets/AddWorkerDialog-CbV75qUX.js +2 -0
- package/web/dist/assets/AddWorkspaceFlow-CwV-7wPx.js +1 -0
- package/web/dist/assets/FirstRunWizard-a6PWIK3x.js +1 -0
- package/web/dist/assets/MarketplaceDrawer-Dd8WIA8T.js +67 -0
- package/web/dist/assets/TaskGraphDrawer-Bk5WFIk_.js +1 -0
- package/web/dist/assets/{WhatsNewDialog-CHkZeINH.js → WhatsNewDialog-C2VZaip0.js} +1 -1
- package/web/dist/assets/WorkerModal-DucW-9YT.js +1 -0
- package/web/dist/assets/WorkflowsDrawer-Bjf4olbR.js +1 -0
- package/web/dist/assets/WorkspaceMemoryDrawer-DglCy_5f.js +1 -0
- package/web/dist/assets/WorkspaceTaskDrawer-BIWwISvA.js +1 -0
- package/web/dist/assets/index-BAiLYajK.css +1 -0
- package/web/dist/assets/index-BV2k9Dts.js +73 -0
- package/web/dist/assets/search-Bk2HQvO7.js +1 -0
- package/web/dist/assets/square-terminal-D93m9hfY.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/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-BRUxpa3f.js +0 -2
- package/web/dist/assets/AddWorkspaceDialog-D56x5JCb.js +0 -1
- package/web/dist/assets/FirstRunWizard-BFVaMIsE.js +0 -1
- package/web/dist/assets/MarketplaceDrawer-DeEZ35dN.js +0 -76
- package/web/dist/assets/WorkerModal-BBCuMLIa.js +0 -1
- package/web/dist/assets/WorkspaceTaskDrawer-CpZHAcj1.js +0 -1
- package/web/dist/assets/WorkspaceTerminalPanels-7If2mDyp.js +0 -1
- package/web/dist/assets/WorkspaceTerminalPanels-DDGTF8rc.css +0 -1
- package/web/dist/assets/index-5zh61jMg.css +0 -1
- package/web/dist/assets/index-CxNL0O-C.js +0 -73
- package/web/dist/assets/path-join-7MR1s7b1.js +0 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveCommandPresetLaunchConfig } from './agent-launch-resolver.js';
|
|
1
2
|
import { readWorkflowCliPolicy, WORKFLOW_CLI_POLICY_KEY } from './workflow-cli-policy.js';
|
|
2
3
|
import { readWorkflowEnabled, WORKFLOW_ENABLED_KEY } from './workflow-feature.js';
|
|
3
4
|
import { createWorkflowRunner } from './workflow-runner.js';
|
|
@@ -14,6 +15,7 @@ export const createRuntimeStoreWorkflowRuntime = (services, store) => {
|
|
|
14
15
|
append: (runId, message, ts) => services.workflowRunLogStore.append(runId, message, ts),
|
|
15
16
|
},
|
|
16
17
|
getWorkflowCliPolicy: () => readWorkflowCliPolicy(services.settings.getAppState(WORKFLOW_CLI_POLICY_KEY)?.value ?? null),
|
|
18
|
+
resolveCliLaunchConfig: (cli) => resolveCommandPresetLaunchConfig(services.settings, cli),
|
|
17
19
|
roleTemplateResolver: {
|
|
18
20
|
findByName: (name) => {
|
|
19
21
|
const t = services.settings.findRoleTemplateByName(name);
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { createRuntimeStoreDreamMethods } from './runtime-store-dream.js';
|
|
1
2
|
import { createRuntimeStoreLifecycle, createRuntimeStoreServices, logTasksFileWatchStartError, } from './runtime-store-helpers.js';
|
|
3
|
+
import { createRuntimeStoreMemoryMethods } from './runtime-store-memory.js';
|
|
4
|
+
import { createRuntimeStoreRemoteMethods } from './runtime-store-remote.js';
|
|
2
5
|
import { createRuntimeStoreWorkflowRuntime } from './runtime-store-workflows.js';
|
|
3
6
|
import { persistWorkflowSchedule } from './workflow-schedule-create.js';
|
|
4
7
|
export const createRuntimeStore = (options = {}) => {
|
|
@@ -20,6 +23,7 @@ export const createRuntimeStore = (options = {}) => {
|
|
|
20
23
|
const store = {
|
|
21
24
|
close: async () => {
|
|
22
25
|
workflowRuntime?.scheduler.close();
|
|
26
|
+
await services.teamMemoryDreamScheduler.close();
|
|
23
27
|
await lifecycle.close();
|
|
24
28
|
},
|
|
25
29
|
createWorkspace: (path, name) => {
|
|
@@ -40,8 +44,11 @@ export const createRuntimeStore = (options = {}) => {
|
|
|
40
44
|
services.agentRuntime.deleteAgentLaunchConfig(workspaceId, agent.id);
|
|
41
45
|
}
|
|
42
46
|
await services.tasksFileWatcher.stop(workspaceId);
|
|
47
|
+
services.teamMemoryExport.cancel(workspaceId);
|
|
43
48
|
runDataMutation(() => {
|
|
44
49
|
services.dispatchLedgerStore.deleteWorkspaceDispatches(workspaceId);
|
|
50
|
+
services.teamMemoryStore.deleteWorkspaceMemories(workspaceId);
|
|
51
|
+
services.teamMemoryDreamStore.deleteWorkspaceDreamRuns(workspaceId);
|
|
45
52
|
services.workspaceStore.deleteWorkspace(workspaceId);
|
|
46
53
|
});
|
|
47
54
|
if (services.settings.getAppState('active_workspace_id')?.value === workspaceId) {
|
|
@@ -80,13 +87,13 @@ export const createRuntimeStore = (options = {}) => {
|
|
|
80
87
|
renameWorker: (workspaceId, workerId, name) => services.workspaceStore.renameWorker(workspaceId, workerId, name),
|
|
81
88
|
deleteWorker: (workspaceId, workerId) => {
|
|
82
89
|
const activeRun = services.agentRuntime.getActiveRunByAgentId(workspaceId, workerId);
|
|
83
|
-
if (activeRun)
|
|
84
|
-
services.agentRuntime.stopAgentRun(activeRun.runId);
|
|
85
|
-
services.agentRuntime.deleteAgentLaunchConfig(workspaceId, workerId);
|
|
86
90
|
runDataMutation(() => {
|
|
87
91
|
services.dispatchLedgerStore.deleteWorkerDispatches(workspaceId, workerId);
|
|
88
92
|
services.workspaceStore.deleteWorker(workspaceId, workerId);
|
|
89
93
|
});
|
|
94
|
+
services.agentRuntime.deleteAgentLaunchConfig(workspaceId, workerId);
|
|
95
|
+
if (activeRun)
|
|
96
|
+
services.agentRuntime.stopAgentRun(activeRun.runId);
|
|
90
97
|
},
|
|
91
98
|
recordUserInput: services.teamOps.recordUserInput,
|
|
92
99
|
cancelTask: services.teamOps.cancelTask,
|
|
@@ -115,6 +122,9 @@ export const createRuntimeStore = (options = {}) => {
|
|
|
115
122
|
registerTasksListener: lifecycle.registerTasksListener,
|
|
116
123
|
listAgentRuns: (agentId) => services.agentRuntime.listAgentRuns(agentId),
|
|
117
124
|
listMessagesForRecovery: (workspaceId, sinceMs) => services.messageLogStore.listMessagesForRecovery(workspaceId, sinceMs),
|
|
125
|
+
recallMessages: (workspaceId, query, options) => services.teamRecallStore.recallMessages(workspaceId, query, options),
|
|
126
|
+
...createRuntimeStoreMemoryMethods(services),
|
|
127
|
+
...createRuntimeStoreDreamMethods(services),
|
|
118
128
|
peekAgentToken: (agentId) => services.agentRuntime.peekAgentToken(agentId),
|
|
119
129
|
pauseTerminalRun: lifecycle.pauseTerminalRun,
|
|
120
130
|
resizeAgentRun: lifecycle.resizeTerminalRun,
|
|
@@ -125,6 +135,7 @@ export const createRuntimeStore = (options = {}) => {
|
|
|
125
135
|
stopAgentRun: lifecycle.stopTerminalRun,
|
|
126
136
|
validateAgentToken: (agentId, token) => services.agentRuntime.validateAgentToken(agentId, token),
|
|
127
137
|
validateUiToken: (token) => services.uiAuth.validate(token),
|
|
138
|
+
...createRuntimeStoreRemoteMethods(services),
|
|
128
139
|
getWorkflowDispatchAwaiter: () => services.workflowDispatchAwaiter,
|
|
129
140
|
runWorkflow: (input) => getWorkflowRuntime().runner.runWorkflow(input),
|
|
130
141
|
startWorkflow: (input) => getWorkflowRuntime().runner.startWorkflow(input),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const getClaudeProjectsRoot: (pattern?: string) => string;
|
|
1
|
+
export declare const getClaudeProjectsRoot: (pattern?: string, platform?: NodeJS.Platform) => string;
|
|
2
2
|
/**
|
|
3
3
|
* Match the directory-name encoding Claude Code itself uses for its project
|
|
4
4
|
* metadata under `~/.claude/projects/`. Empirically (probed via `claude
|
|
@@ -2,9 +2,10 @@ import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { captureSessionIdWithCoordinator, resetSessionCaptureCoordinatorForTests, } from './claude-session-coordinator.js';
|
|
5
|
+
import { arePathsEqual, expandHomePath } from './platform-path.js';
|
|
5
6
|
const SESSION_FILE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\.jsonl$/i;
|
|
6
7
|
const getDefaultProjectsRoot = () => process.env.HIVE_CLAUDE_PROJECTS_DIR ?? join(homedir(), '.claude/projects');
|
|
7
|
-
export const getClaudeProjectsRoot = (pattern) => {
|
|
8
|
+
export const getClaudeProjectsRoot = (pattern, platform = process.platform) => {
|
|
8
9
|
if (!pattern)
|
|
9
10
|
return getDefaultProjectsRoot();
|
|
10
11
|
const markerIndex = pattern.indexOf('{encoded_cwd}');
|
|
@@ -13,10 +14,12 @@ export const getClaudeProjectsRoot = (pattern) => {
|
|
|
13
14
|
const root = pattern.slice(0, markerIndex).replace(/[\\/]+$/, '');
|
|
14
15
|
if (!root)
|
|
15
16
|
return getDefaultProjectsRoot();
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
const builtInProjectsRoot = join(homedir(), '.claude', 'projects');
|
|
18
|
+
const expandedRoot = expandHomePath(root);
|
|
19
|
+
if (root === '~' || arePathsEqual(expandedRoot, builtInProjectsRoot, platform)) {
|
|
20
|
+
return getDefaultProjectsRoot();
|
|
18
21
|
}
|
|
19
|
-
return
|
|
22
|
+
return expandedRoot;
|
|
20
23
|
};
|
|
21
24
|
/**
|
|
22
25
|
* Match the directory-name encoding Claude Code itself uses for its project
|
|
@@ -2,23 +2,22 @@ import { closeSync, existsSync, openSync, readdirSync, readSync } from 'node:fs'
|
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { captureSessionIdWithCoordinator } from './claude-session-coordinator.js';
|
|
5
|
-
import { arePathsEqual, indexOfPathMarker } from './platform-path.js';
|
|
5
|
+
import { arePathsEqual, expandHomePath, indexOfPathMarker } from './platform-path.js';
|
|
6
6
|
const CODEX_SESSION_FILE = /^rollout-.*\.jsonl$/i;
|
|
7
7
|
const CODEX_SESSIONS_MARKER = '/sessions/';
|
|
8
8
|
const CODEX_HEADER_READ_CHUNK_BYTES = 4096;
|
|
9
9
|
const CODEX_HEADER_MAX_BYTES = 64 * 1024;
|
|
10
10
|
const getDefaultCodexHome = () => process.env.CODEX_HOME ?? join(homedir(), '.codex');
|
|
11
|
-
const expandHome = (path) => path === '~' || path.startsWith('~/') ? join(homedir(), path.slice(2)) : path;
|
|
12
11
|
export const getCodexHome = (pattern, platform = process.platform) => {
|
|
13
12
|
if (!pattern)
|
|
14
13
|
return getDefaultCodexHome();
|
|
15
14
|
const markerIndex = indexOfPathMarker(pattern, CODEX_SESSIONS_MARKER, platform);
|
|
16
15
|
if (markerIndex === -1)
|
|
17
16
|
return getDefaultCodexHome();
|
|
18
|
-
const rawRoot = pattern.slice(0, markerIndex);
|
|
19
|
-
|
|
17
|
+
const rawRoot = pattern.slice(0, markerIndex).replace(/[\\/]+$/u, '');
|
|
18
|
+
const root = expandHomePath(rawRoot);
|
|
19
|
+
if (arePathsEqual(root, join(homedir(), '.codex'), platform))
|
|
20
20
|
return getDefaultCodexHome();
|
|
21
|
-
const root = expandHome(rawRoot);
|
|
22
21
|
return root || getDefaultCodexHome();
|
|
23
22
|
};
|
|
24
23
|
const walkSessionFiles = (dir) => {
|
|
@@ -2,21 +2,20 @@ import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { captureSessionIdWithCoordinator } from './claude-session-coordinator.js';
|
|
5
|
-
import { arePathsEqual, indexOfPathMarker } from './platform-path.js';
|
|
5
|
+
import { arePathsEqual, expandHomePath, indexOfPathMarker } from './platform-path.js';
|
|
6
6
|
const GEMINI_SESSION_FILE = /^session-.*\.json$/i;
|
|
7
7
|
const GEMINI_TMP_MARKER = '/tmp/';
|
|
8
8
|
const getDefaultGeminiHome = () => process.env.HIVE_GEMINI_HOME ?? join(homedir(), '.gemini');
|
|
9
|
-
const expandHome = (path) => path === '~' || path.startsWith('~/') ? join(homedir(), path.slice(2)) : path;
|
|
10
9
|
export const getGeminiHome = (pattern, platform = process.platform) => {
|
|
11
10
|
if (!pattern)
|
|
12
11
|
return getDefaultGeminiHome();
|
|
13
12
|
const markerIndex = indexOfPathMarker(pattern, GEMINI_TMP_MARKER, platform);
|
|
14
13
|
if (markerIndex === -1)
|
|
15
14
|
return getDefaultGeminiHome();
|
|
16
|
-
const rawRoot = pattern.slice(0, markerIndex);
|
|
17
|
-
|
|
15
|
+
const rawRoot = pattern.slice(0, markerIndex).replace(/[\\/]+$/u, '');
|
|
16
|
+
const root = expandHomePath(rawRoot);
|
|
17
|
+
if (arePathsEqual(root, join(homedir(), '.gemini'), platform))
|
|
18
18
|
return getDefaultGeminiHome();
|
|
19
|
-
const root = expandHome(rawRoot);
|
|
20
19
|
return root || getDefaultGeminiHome();
|
|
21
20
|
};
|
|
22
21
|
const readProjectRoot = (projectDir) => {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* needing to mock the underlying SQLite open.
|
|
17
17
|
*/
|
|
18
18
|
export declare const getDefaultOpenCodeDbPath: (platform?: NodeJS.Platform) => string;
|
|
19
|
-
export declare const getOpenCodeDbPath: (pattern?: string) => string;
|
|
20
|
-
export declare const hasOpenCodeSession: (cwd: string, sessionId: string, pattern?: string) => boolean;
|
|
21
|
-
export declare const snapshotOpenCodeSessionIds: (cwd: string, dbPath?: string) => Set<string>;
|
|
22
|
-
export declare const captureOpenCodeSessionId: (cwd: string, knownSessionIds: Set<string>, onCapture: (sessionId: string) => void, timeoutMs?: number, intervalMs?: number, dbPath?: string) => Promise<void>;
|
|
19
|
+
export declare const getOpenCodeDbPath: (pattern?: string, platform?: NodeJS.Platform) => string;
|
|
20
|
+
export declare const hasOpenCodeSession: (cwd: string, sessionId: string, pattern?: string, platform?: NodeJS.Platform, dbPath?: string) => boolean;
|
|
21
|
+
export declare const snapshotOpenCodeSessionIds: (cwd: string, dbPath?: string, platform?: NodeJS.Platform) => Set<string>;
|
|
22
|
+
export declare const captureOpenCodeSessionId: (cwd: string, knownSessionIds: Set<string>, onCapture: (sessionId: string) => void, timeoutMs?: number, intervalMs?: number, dbPath?: string, platform?: NodeJS.Platform) => Promise<void>;
|
|
@@ -3,7 +3,7 @@ import { homedir } from 'node:os';
|
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import Database from 'better-sqlite3';
|
|
5
5
|
import { captureSessionIdWithCoordinator } from './claude-session-coordinator.js';
|
|
6
|
-
|
|
6
|
+
import { arePathsEqual, expandHomePath } from './platform-path.js';
|
|
7
7
|
/**
|
|
8
8
|
* Resolve the path OpenCode upstream writes `opencode.db` to. Branches
|
|
9
9
|
* by platform because XDG_DATA_HOME is not a Windows convention — the
|
|
@@ -31,20 +31,28 @@ export const getDefaultOpenCodeDbPath = (platform = process.platform) => {
|
|
|
31
31
|
}
|
|
32
32
|
return join(process.env.XDG_DATA_HOME ?? join(homedir(), '.local', 'share'), 'opencode', 'opencode.db');
|
|
33
33
|
};
|
|
34
|
-
export const getOpenCodeDbPath = (pattern
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
34
|
+
export const getOpenCodeDbPath = (pattern, platform = process.platform) => {
|
|
35
|
+
if (!pattern)
|
|
36
|
+
return getDefaultOpenCodeDbPath(platform);
|
|
37
|
+
const expanded = expandHomePath(pattern);
|
|
38
|
+
if (arePathsEqual(expanded, join(homedir(), '.local', 'share', 'opencode', 'opencode.db'), platform)) {
|
|
39
|
+
return getDefaultOpenCodeDbPath(platform);
|
|
40
|
+
}
|
|
41
|
+
return expanded;
|
|
42
|
+
};
|
|
43
|
+
const listSessionIds = (cwd, dbPath = getDefaultOpenCodeDbPath(), platform = process.platform) => {
|
|
38
44
|
if (!existsSync(dbPath))
|
|
39
45
|
return [];
|
|
40
46
|
let db;
|
|
41
47
|
try {
|
|
42
48
|
db = new Database(dbPath, { fileMustExist: true, readonly: true });
|
|
43
49
|
return db
|
|
44
|
-
.prepare(`SELECT id FROM session
|
|
45
|
-
WHERE
|
|
50
|
+
.prepare(`SELECT id, directory FROM session
|
|
51
|
+
WHERE time_archived IS NULL
|
|
46
52
|
ORDER BY rowid ASC`)
|
|
47
|
-
.all(
|
|
53
|
+
.all()
|
|
54
|
+
.filter((row) => arePathsEqual(row.directory, cwd, platform))
|
|
55
|
+
.map((row) => row.id);
|
|
48
56
|
}
|
|
49
57
|
catch {
|
|
50
58
|
return [];
|
|
@@ -53,13 +61,13 @@ const listSessionIds = (cwd, dbPath = getDefaultOpenCodeDbPath()) => {
|
|
|
53
61
|
db?.close();
|
|
54
62
|
}
|
|
55
63
|
};
|
|
56
|
-
export const hasOpenCodeSession = (cwd, sessionId, pattern) => listSessionIds(cwd,
|
|
57
|
-
export const snapshotOpenCodeSessionIds = (cwd, dbPath = getDefaultOpenCodeDbPath()) => new Set(listSessionIds(cwd, dbPath));
|
|
58
|
-
export const captureOpenCodeSessionId = async (cwd, knownSessionIds, onCapture, timeoutMs = 5000, intervalMs = 100, dbPath = getDefaultOpenCodeDbPath()) => {
|
|
64
|
+
export const hasOpenCodeSession = (cwd, sessionId, pattern, platform = process.platform, dbPath = getOpenCodeDbPath(pattern, platform)) => listSessionIds(cwd, dbPath, platform).includes(sessionId);
|
|
65
|
+
export const snapshotOpenCodeSessionIds = (cwd, dbPath = getDefaultOpenCodeDbPath(), platform = process.platform) => new Set(listSessionIds(cwd, dbPath, platform));
|
|
66
|
+
export const captureOpenCodeSessionId = async (cwd, knownSessionIds, onCapture, timeoutMs = 5000, intervalMs = 100, dbPath = getDefaultOpenCodeDbPath(), platform = process.platform) => {
|
|
59
67
|
await captureSessionIdWithCoordinator({
|
|
60
68
|
intervalMs,
|
|
61
69
|
knownSessionIds,
|
|
62
|
-
listSessionIds: () => listSessionIds(cwd, dbPath),
|
|
70
|
+
listSessionIds: () => listSessionIds(cwd, dbPath, platform),
|
|
63
71
|
onCapture,
|
|
64
72
|
projectKey: `${dbPath}:${cwd}`,
|
|
65
73
|
timeoutMs,
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const getQwenHome: (pattern?: string, platform?: NodeJS.Platform) => string;
|
|
2
|
+
export declare const hasQwenSession: (cwd: string, sessionId: string, pattern?: string, platform?: NodeJS.Platform, qwenHome?: string) => boolean;
|
|
3
|
+
export declare const snapshotQwenSessionIds: (cwd: string, qwenHome?: string, platform?: NodeJS.Platform) => Set<string>;
|
|
4
|
+
export declare const captureQwenSessionId: (cwd: string, knownSessionIds: Set<string>, onCapture: (sessionId: string) => void, timeoutMs?: number, intervalMs?: number, qwenHome?: string) => Promise<void>;
|
|
5
|
+
export declare const qwenSessionStoreExists: (qwenHome?: string) => boolean;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { closeSync, existsSync, openSync, readdirSync, readSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { captureSessionIdWithCoordinator } from './claude-session-coordinator.js';
|
|
5
|
+
import { arePathsEqual, expandHomePath, indexOfPathMarker } from './platform-path.js';
|
|
6
|
+
const QWEN_SESSION_FILE = /\.json$/i;
|
|
7
|
+
const QWEN_SESSIONS_MARKER = '/sessions/';
|
|
8
|
+
const QWEN_HEADER_READ_CHUNK_BYTES = 4096;
|
|
9
|
+
const QWEN_HEADER_MAX_BYTES = 256 * 1024;
|
|
10
|
+
const getDefaultQwenHome = () => process.env.HIVE_QWEN_HOME ?? join(homedir(), '.qwen');
|
|
11
|
+
export const getQwenHome = (pattern, platform = process.platform) => {
|
|
12
|
+
if (!pattern)
|
|
13
|
+
return getDefaultQwenHome();
|
|
14
|
+
const markerIndex = indexOfPathMarker(pattern, QWEN_SESSIONS_MARKER, platform);
|
|
15
|
+
if (markerIndex === -1)
|
|
16
|
+
return getDefaultQwenHome();
|
|
17
|
+
const rawRoot = pattern.slice(0, markerIndex).replace(/[\\/]+$/u, '');
|
|
18
|
+
const root = expandHomePath(rawRoot);
|
|
19
|
+
if (arePathsEqual(root, join(homedir(), '.qwen'), platform))
|
|
20
|
+
return getDefaultQwenHome();
|
|
21
|
+
return root || getDefaultQwenHome();
|
|
22
|
+
};
|
|
23
|
+
const walkSessionFiles = (dir) => {
|
|
24
|
+
try {
|
|
25
|
+
return readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
|
|
26
|
+
const path = join(dir, entry.name);
|
|
27
|
+
if (entry.isDirectory())
|
|
28
|
+
return walkSessionFiles(path);
|
|
29
|
+
return entry.isFile() && QWEN_SESSION_FILE.test(entry.name) ? [path] : [];
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const readQwenSessionHeader = (filePath, maxBytes = QWEN_HEADER_MAX_BYTES) => {
|
|
37
|
+
const fd = openSync(filePath, 'r');
|
|
38
|
+
try {
|
|
39
|
+
const chunks = [];
|
|
40
|
+
let totalBytes = 0;
|
|
41
|
+
let position = 0;
|
|
42
|
+
while (totalBytes < maxBytes) {
|
|
43
|
+
const bytesToRead = Math.min(QWEN_HEADER_READ_CHUNK_BYTES, maxBytes - totalBytes);
|
|
44
|
+
const buffer = Buffer.allocUnsafe(bytesToRead);
|
|
45
|
+
const bytesRead = readSync(fd, buffer, 0, bytesToRead, position);
|
|
46
|
+
if (bytesRead === 0)
|
|
47
|
+
break;
|
|
48
|
+
const slice = buffer.subarray(0, bytesRead);
|
|
49
|
+
chunks.push(slice);
|
|
50
|
+
totalBytes += bytesRead;
|
|
51
|
+
position += bytesRead;
|
|
52
|
+
}
|
|
53
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
closeSync(fd);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const readJsonStringField = (input, key) => {
|
|
60
|
+
const regex = new RegExp(`"${key}"\\s*:\\s*("(?:\\\\.|[^"\\\\])*")`, 'u');
|
|
61
|
+
const raw = regex.exec(input)?.[1];
|
|
62
|
+
if (!raw)
|
|
63
|
+
return null;
|
|
64
|
+
try {
|
|
65
|
+
const parsed = JSON.parse(raw);
|
|
66
|
+
return typeof parsed === 'string' ? parsed : null;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const parseQwenSession = (filePath) => {
|
|
73
|
+
const header = readQwenSessionHeader(filePath);
|
|
74
|
+
const id = readJsonStringField(header, 'sessionId');
|
|
75
|
+
const cwd = readJsonStringField(header, 'projectRoot');
|
|
76
|
+
return id && cwd ? { cwd, id } : null;
|
|
77
|
+
};
|
|
78
|
+
const listSessionIds = (cwd, qwenHome = getDefaultQwenHome(), platform = process.platform) => {
|
|
79
|
+
const sessionsRoot = join(qwenHome, 'sessions');
|
|
80
|
+
return walkSessionFiles(sessionsRoot)
|
|
81
|
+
.flatMap((filePath) => {
|
|
82
|
+
try {
|
|
83
|
+
const session = parseQwenSession(filePath);
|
|
84
|
+
return session && arePathsEqual(session.cwd, cwd, platform) ? [session.id] : [];
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
.sort((left, right) => left.localeCompare(right));
|
|
91
|
+
};
|
|
92
|
+
export const hasQwenSession = (cwd, sessionId, pattern, platform = process.platform, qwenHome = getQwenHome(pattern, platform)) => listSessionIds(cwd, qwenHome, platform).includes(sessionId);
|
|
93
|
+
export const snapshotQwenSessionIds = (cwd, qwenHome = getDefaultQwenHome(), platform = process.platform) => new Set(listSessionIds(cwd, qwenHome, platform));
|
|
94
|
+
export const captureQwenSessionId = async (cwd, knownSessionIds, onCapture, timeoutMs = 5000, intervalMs = 100, qwenHome = getDefaultQwenHome()) => {
|
|
95
|
+
await captureSessionIdWithCoordinator({
|
|
96
|
+
intervalMs,
|
|
97
|
+
knownSessionIds,
|
|
98
|
+
listSessionIds: () => listSessionIds(cwd, qwenHome),
|
|
99
|
+
onCapture,
|
|
100
|
+
projectKey: join(qwenHome, 'sessions', cwd),
|
|
101
|
+
timeoutMs,
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
export const qwenSessionStoreExists = (qwenHome = getDefaultQwenHome()) => existsSync(join(qwenHome, 'sessions'));
|
|
@@ -10,6 +10,9 @@ export type SessionIdCaptureConfig = {
|
|
|
10
10
|
} | {
|
|
11
11
|
source: 'opencode_session_db';
|
|
12
12
|
pattern: string;
|
|
13
|
+
} | {
|
|
14
|
+
source: 'qwen_session_json_dir';
|
|
15
|
+
pattern: string;
|
|
13
16
|
} | {
|
|
14
17
|
source: 'stdout_regex';
|
|
15
18
|
pattern: string;
|
|
@@ -36,6 +39,7 @@ export declare const snapshotSessionIdsForCapture: (cwd: string, capture: Sessio
|
|
|
36
39
|
CODEX_HOME?: never;
|
|
37
40
|
HIVE_GEMINI_HOME?: never;
|
|
38
41
|
HIVE_OPENCODE_DB_PATH?: never;
|
|
42
|
+
HIVE_QWEN_HOME?: never;
|
|
39
43
|
};
|
|
40
44
|
knownSessionIds: Set<string>;
|
|
41
45
|
root: string;
|
|
@@ -45,6 +49,7 @@ export declare const snapshotSessionIdsForCapture: (cwd: string, capture: Sessio
|
|
|
45
49
|
HIVE_CLAUDE_PROJECTS_DIR?: never;
|
|
46
50
|
HIVE_GEMINI_HOME?: never;
|
|
47
51
|
HIVE_OPENCODE_DB_PATH?: never;
|
|
52
|
+
HIVE_QWEN_HOME?: never;
|
|
48
53
|
};
|
|
49
54
|
knownSessionIds: Set<string>;
|
|
50
55
|
root: string;
|
|
@@ -54,6 +59,7 @@ export declare const snapshotSessionIdsForCapture: (cwd: string, capture: Sessio
|
|
|
54
59
|
HIVE_CLAUDE_PROJECTS_DIR?: never;
|
|
55
60
|
CODEX_HOME?: never;
|
|
56
61
|
HIVE_OPENCODE_DB_PATH?: never;
|
|
62
|
+
HIVE_QWEN_HOME?: never;
|
|
57
63
|
};
|
|
58
64
|
knownSessionIds: Set<string>;
|
|
59
65
|
root: string;
|
|
@@ -63,6 +69,17 @@ export declare const snapshotSessionIdsForCapture: (cwd: string, capture: Sessio
|
|
|
63
69
|
HIVE_CLAUDE_PROJECTS_DIR?: never;
|
|
64
70
|
CODEX_HOME?: never;
|
|
65
71
|
HIVE_GEMINI_HOME?: never;
|
|
72
|
+
HIVE_QWEN_HOME?: never;
|
|
73
|
+
};
|
|
74
|
+
knownSessionIds: Set<string>;
|
|
75
|
+
root: string;
|
|
76
|
+
} | {
|
|
77
|
+
env: {
|
|
78
|
+
HIVE_QWEN_HOME: string;
|
|
79
|
+
HIVE_CLAUDE_PROJECTS_DIR?: never;
|
|
80
|
+
CODEX_HOME?: never;
|
|
81
|
+
HIVE_GEMINI_HOME?: never;
|
|
82
|
+
HIVE_OPENCODE_DB_PATH?: never;
|
|
66
83
|
};
|
|
67
84
|
knownSessionIds: Set<string>;
|
|
68
85
|
root: string;
|
|
@@ -2,6 +2,7 @@ import { captureClaudeSessionId, getClaudeProjectsRoot, hasClaudeSessionFile, sn
|
|
|
2
2
|
import { captureCodexSessionId, getCodexHome, hasCodexSession, snapshotCodexSessionIds, } from './session-capture-codex.js';
|
|
3
3
|
import { captureGeminiSessionId, getGeminiHome, hasGeminiSession, snapshotGeminiSessionIds, } from './session-capture-gemini.js';
|
|
4
4
|
import { captureOpenCodeSessionId, getOpenCodeDbPath, hasOpenCodeSession, snapshotOpenCodeSessionIds, } from './session-capture-opencode.js';
|
|
5
|
+
import { captureQwenSessionId, getQwenHome, hasQwenSession, snapshotQwenSessionIds, } from './session-capture-qwen.js';
|
|
5
6
|
const hasSource = (value) => Boolean(value && typeof value === 'object' && 'source' in value);
|
|
6
7
|
export const parseSessionIdCapture = (value) => {
|
|
7
8
|
if (!hasSource(value))
|
|
@@ -13,6 +14,7 @@ export const parseSessionIdCapture = (value) => {
|
|
|
13
14
|
value.source === 'codex_session_jsonl_dir' ||
|
|
14
15
|
value.source === 'gemini_session_json_dir' ||
|
|
15
16
|
value.source === 'opencode_session_db' ||
|
|
17
|
+
value.source === 'qwen_session_json_dir' ||
|
|
16
18
|
value.source === 'stdout_regex') {
|
|
17
19
|
return typeof pattern === 'string' ? { pattern, source: value.source } : null;
|
|
18
20
|
}
|
|
@@ -57,6 +59,14 @@ export const snapshotSessionIdsForCapture = (cwd, capture, discriminator) => {
|
|
|
57
59
|
root: dbPath,
|
|
58
60
|
};
|
|
59
61
|
}
|
|
62
|
+
if (capture.source === 'qwen_session_json_dir') {
|
|
63
|
+
const qwenHome = getQwenHome(capture.pattern);
|
|
64
|
+
return {
|
|
65
|
+
env: { HIVE_QWEN_HOME: qwenHome },
|
|
66
|
+
knownSessionIds: snapshotQwenSessionIds(cwd, qwenHome),
|
|
67
|
+
root: qwenHome,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
60
70
|
if (capture.source === 'stdout_regex') {
|
|
61
71
|
return {
|
|
62
72
|
knownSessionIds: new Set(),
|
|
@@ -78,6 +88,9 @@ export const captureSessionIdForCapture = async (cwd, capture, snapshot, onCaptu
|
|
|
78
88
|
if (capture.source === 'opencode_session_db') {
|
|
79
89
|
await captureOpenCodeSessionId(cwd, snapshot.knownSessionIds, onCapture, timeoutMs, intervalMs, snapshot.root);
|
|
80
90
|
}
|
|
91
|
+
if (capture.source === 'qwen_session_json_dir') {
|
|
92
|
+
await captureQwenSessionId(cwd, snapshot.knownSessionIds, onCapture, timeoutMs, intervalMs, snapshot.root);
|
|
93
|
+
}
|
|
81
94
|
if (capture.source === 'stdout_regex') {
|
|
82
95
|
await captureStdoutRegexSessionId(capture.pattern, snapshot.getOutput, snapshot.knownSessionIds, onCapture, timeoutMs, intervalMs);
|
|
83
96
|
}
|
|
@@ -95,6 +108,9 @@ export const doesCapturedSessionExist = (cwd, capture, sessionId, discriminator)
|
|
|
95
108
|
if (capture.source === 'opencode_session_db') {
|
|
96
109
|
return hasOpenCodeSession(cwd, sessionId, capture.pattern);
|
|
97
110
|
}
|
|
111
|
+
if (capture.source === 'qwen_session_json_dir') {
|
|
112
|
+
return hasQwenSession(cwd, sessionId, capture.pattern);
|
|
113
|
+
}
|
|
98
114
|
return false;
|
|
99
115
|
};
|
|
100
116
|
const compileCaptureRegex = (pattern) => {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// v23 — remote-access (mobile) audit trail. The tunnel/bridge layer is the
|
|
2
|
+
// SINGLE collection point for every remote HTTP request + WS-input event
|
|
3
|
+
// (see the plan's 审计规格): one place to record so no per-route handler has
|
|
4
|
+
// to know about remote at all. Rows are append-only and written async off the
|
|
5
|
+
// forwarding path (remote-audit-store.ts), so a slow disk never stalls a frame.
|
|
6
|
+
//
|
|
7
|
+
// Schema notes:
|
|
8
|
+
// - remote_device_id is the M1 session's device id; it's an audit/revocation
|
|
9
|
+
// tag, NEVER a permission branch (Authority Model: paired device == local).
|
|
10
|
+
// - action is a coarse category, not a full URL ('http' for bridged API
|
|
11
|
+
// requests, 'ws_input' for terminal/tasks stdin, plus lifecycle/control
|
|
12
|
+
// actions like 'session_open' / 'revoke' / 'reject'). endpoint holds the
|
|
13
|
+
// whitelisted path ('/api/...' '/ws/...') when there is one.
|
|
14
|
+
// - result is 'ok' | 'rejected' | 'error'. reject_reason is set ONLY on a
|
|
15
|
+
// rejection (off-whitelist path, revoked device, forged secret, …) so the
|
|
16
|
+
// security tests can assert the reason text, not just that a row exists.
|
|
17
|
+
// - WS input is summarised: byte_count + a short truncated preview. We NEVER
|
|
18
|
+
// persist full stdin (that's the user typing into a YOLO terminal). The
|
|
19
|
+
// preview is bounded by the store before it reaches here.
|
|
20
|
+
//
|
|
21
|
+
// Index covers the Settings audit-stream view: newest-first, optionally scoped
|
|
22
|
+
// to one device.
|
|
23
|
+
export const applySchemaVersion23 = (db) => {
|
|
24
|
+
db.exec(`
|
|
25
|
+
CREATE TABLE IF NOT EXISTS remote_audit (
|
|
26
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
27
|
+
remote_device_id TEXT,
|
|
28
|
+
ts INTEGER NOT NULL,
|
|
29
|
+
workspace_id TEXT,
|
|
30
|
+
action TEXT NOT NULL,
|
|
31
|
+
endpoint TEXT,
|
|
32
|
+
result TEXT NOT NULL,
|
|
33
|
+
reject_reason TEXT,
|
|
34
|
+
byte_count INTEGER,
|
|
35
|
+
preview TEXT
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
CREATE INDEX IF NOT EXISTS idx_remote_audit_recent
|
|
39
|
+
ON remote_audit (id DESC);
|
|
40
|
+
CREATE INDEX IF NOT EXISTS idx_remote_audit_device
|
|
41
|
+
ON remote_audit (remote_device_id, id DESC);
|
|
42
|
+
`);
|
|
43
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// v24 — paired remote devices. A row exists ONLY after a human confirmed the pairing at the desktop
|
|
2
|
+
// (Authority Model trust root): the pending handshake lives in memory in remote-pairing.ts and is
|
|
3
|
+
// INSERTed here on confirm, NEVER before.
|
|
4
|
+
//
|
|
5
|
+
// key_d2p/key_p2d are the M3 DeviceSession.keys — M6.1 reinterprets these as the directional ROOT keys:
|
|
6
|
+
// the bridge derives a fresh per-connection AEAD key from them on every connect (deriveConnectionKeys)
|
|
7
|
+
// and never seals/opens under the stored bytes directly. The at-rest format is byte-identical — no
|
|
8
|
+
// schema change, no version bump; v24 rows are forward-compatible (their stored material IS the root).
|
|
9
|
+
// They are a stored secret in the sense that they decrypt all of that device's E2E traffic, but the
|
|
10
|
+
// protection here is
|
|
11
|
+
// exactly the same filesystem-local posture as remote_daemon_token (plaintext in app_state, see
|
|
12
|
+
// remote-config-keys.ts): the runtime.sqlite file lives next to the daemon on 127.0.0.1. This is NOT
|
|
13
|
+
// encryption-at-rest. If at-rest encryption is later wanted it should cover daemon_token + these keys
|
|
14
|
+
// together as a single hardening item, not be implied here.
|
|
15
|
+
//
|
|
16
|
+
// revoked_at != NULL = dead device: the persistent provider returns null for it at once (live streams
|
|
17
|
+
// drop on the next frame). Rows are kept (soft tombstone) so the device list / audit can still show a
|
|
18
|
+
// revoked device.
|
|
19
|
+
export const applySchemaVersion24 = (db) => {
|
|
20
|
+
db.exec(`
|
|
21
|
+
CREATE TABLE IF NOT EXISTS remote_devices (
|
|
22
|
+
id TEXT PRIMARY KEY,
|
|
23
|
+
name TEXT NOT NULL,
|
|
24
|
+
key_d2p TEXT NOT NULL, -- base64url(32) daemon->phone (daemon SEALS)
|
|
25
|
+
key_p2d TEXT NOT NULL, -- base64url(32) phone->daemon (daemon OPENS)
|
|
26
|
+
device_pubkey TEXT NOT NULL, -- base64url(32); audit/debug, not secret
|
|
27
|
+
created_at INTEGER NOT NULL,
|
|
28
|
+
last_active INTEGER,
|
|
29
|
+
revoked_at INTEGER
|
|
30
|
+
);
|
|
31
|
+
CREATE INDEX IF NOT EXISTS idx_remote_devices_active
|
|
32
|
+
ON remote_devices (revoked_at, created_at DESC);
|
|
33
|
+
`);
|
|
34
|
+
};
|