@tt-a1i/hive 1.4.4 → 1.5.0
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 +22 -0
- package/README.md +8 -0
- package/assets/qq-group.jpg +0 -0
- package/dist/bin/team.cmd +1 -0
- package/dist/src/cli/hive-update.d.ts +45 -17
- package/dist/src/cli/hive-update.js +63 -25
- package/dist/src/cli/hive.d.ts +25 -0
- package/dist/src/cli/hive.js +41 -3
- package/dist/src/cli/team.d.ts +1 -0
- package/dist/src/cli/team.js +199 -3
- package/dist/src/server/agent-command-resolver.js +3 -19
- package/dist/src/server/agent-manager-support.d.ts +2 -2
- package/dist/src/server/agent-manager-support.js +98 -24
- package/dist/src/server/agent-run-starter.d.ts +7 -1
- package/dist/src/server/agent-run-starter.js +9 -2
- package/dist/src/server/agent-run-store.d.ts +1 -1
- package/dist/src/server/agent-runtime-close.d.ts +1 -0
- package/dist/src/server/agent-runtime-close.js +25 -1
- package/dist/src/server/agent-runtime-contract.d.ts +2 -1
- package/dist/src/server/agent-runtime.d.ts +1 -1
- package/dist/src/server/agent-runtime.js +8 -2
- package/dist/src/server/agent-startup-instructions.d.ts +8 -1
- package/dist/src/server/agent-startup-instructions.js +15 -9
- package/dist/src/server/agent-stdin-dispatcher.d.ts +12 -5
- package/dist/src/server/agent-stdin-dispatcher.js +129 -40
- package/dist/src/server/cron-util.d.ts +7 -0
- package/dist/src/server/cron-util.js +19 -0
- package/dist/src/server/dispatch-ledger-store.d.ts +22 -0
- package/dist/src/server/dispatch-ledger-store.js +51 -3
- package/dist/src/server/env-sync-message.js +9 -9
- package/dist/src/server/fs-pick-folder.js +4 -0
- package/dist/src/server/fs-sandbox.js +36 -7
- package/dist/src/server/hive-team-guidance.d.ts +11 -6
- package/dist/src/server/hive-team-guidance.js +252 -71
- package/dist/src/server/live-run-registry.d.ts +1 -0
- package/dist/src/server/live-run-registry.js +1 -1
- package/dist/src/server/open-target-commands.js +5 -6
- package/dist/src/server/orchestrator-autostart.d.ts +12 -0
- package/dist/src/server/orchestrator-autostart.js +15 -13
- package/dist/src/server/path-canonicalization.d.ts +3 -0
- package/dist/src/server/path-canonicalization.js +29 -0
- package/dist/src/server/platform-path.d.ts +3 -0
- package/dist/src/server/platform-path.js +13 -0
- package/dist/src/server/post-start-input-writer.d.ts +1 -1
- package/dist/src/server/post-start-input-writer.js +110 -13
- package/dist/src/server/preset-launch-support.d.ts +1 -1
- package/dist/src/server/preset-launch-support.js +33 -2
- package/dist/src/server/recovery-summary.d.ts +6 -1
- package/dist/src/server/recovery-summary.js +17 -17
- package/dist/src/server/restart-policy-support.d.ts +6 -1
- package/dist/src/server/restart-policy-support.js +9 -1
- package/dist/src/server/restart-policy.d.ts +2 -2
- package/dist/src/server/restart-policy.js +3 -1
- package/dist/src/server/role-template-store.d.ts +1 -0
- package/dist/src/server/role-template-store.js +11 -1
- package/dist/src/server/route-types.d.ts +43 -0
- package/dist/src/server/routes-runtime.js +2 -1
- package/dist/src/server/routes-settings.js +76 -0
- package/dist/src/server/routes-team.js +211 -1
- package/dist/src/server/routes-workflow-schedules.d.ts +2 -0
- package/dist/src/server/routes-workflow-schedules.js +58 -0
- package/dist/src/server/routes-workflows.d.ts +2 -0
- package/dist/src/server/routes-workflows.js +83 -0
- package/dist/src/server/routes.js +4 -0
- package/dist/src/server/runtime-restart-policy.d.ts +3 -1
- package/dist/src/server/runtime-restart-policy.js +3 -1
- package/dist/src/server/runtime-store-contract.d.ts +122 -0
- package/dist/src/server/runtime-store-contract.js +1 -0
- package/dist/src/server/runtime-store-helpers.d.ts +9 -0
- package/dist/src/server/runtime-store-helpers.js +101 -2
- package/dist/src/server/runtime-store-workflows.d.ts +6 -0
- package/dist/src/server/runtime-store-workflows.js +100 -0
- package/dist/src/server/runtime-store.d.ts +3 -72
- package/dist/src/server/runtime-store.js +70 -4
- package/dist/src/server/session-capture-codex.d.ts +3 -3
- package/dist/src/server/session-capture-codex.js +9 -7
- package/dist/src/server/session-capture-gemini.d.ts +1 -1
- package/dist/src/server/session-capture-gemini.js +6 -3
- package/dist/src/server/settings-store.d.ts +3 -0
- package/dist/src/server/settings-store.js +1 -0
- package/dist/src/server/sqlite-schema-v19.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v19.js +17 -0
- package/dist/src/server/sqlite-schema-v20.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v20.js +20 -0
- package/dist/src/server/sqlite-schema-v21.d.ts +2 -0
- package/dist/src/server/sqlite-schema-v21.js +20 -0
- package/dist/src/server/sqlite-schema.d.ts +1 -1
- package/dist/src/server/sqlite-schema.js +97 -1
- package/dist/src/server/system-message.d.ts +7 -0
- package/dist/src/server/system-message.js +8 -1
- package/dist/src/server/tasks-file-watcher.d.ts +13 -1
- package/dist/src/server/tasks-file-watcher.js +127 -23
- package/dist/src/server/tasks-file.d.ts +2 -1
- package/dist/src/server/tasks-file.js +32 -9
- package/dist/src/server/tasks-websocket-server.js +13 -14
- package/dist/src/server/team-authz.d.ts +1 -1
- package/dist/src/server/team-authz.js +9 -1
- package/dist/src/server/team-autostaff.d.ts +16 -0
- package/dist/src/server/team-autostaff.js +16 -0
- package/dist/src/server/team-list-serializer.d.ts +1 -1
- package/dist/src/server/team-list-serializer.js +3 -1
- package/dist/src/server/team-operations.d.ts +15 -1
- package/dist/src/server/team-operations.js +116 -11
- package/dist/src/server/terminal-protocol.js +9 -3
- package/dist/src/server/terminal-stream-hub.js +16 -10
- package/dist/src/server/terminal-ws-server.js +10 -8
- package/dist/src/server/websocket-upgrade-safety.d.ts +10 -0
- package/dist/src/server/websocket-upgrade-safety.js +35 -0
- package/dist/src/server/windows-command-line.d.ts +3 -0
- package/dist/src/server/windows-command-line.js +9 -0
- package/dist/src/server/windows-filename.d.ts +2 -0
- package/dist/src/server/windows-filename.js +33 -0
- package/dist/src/server/workflow-cli-policy.d.ts +60 -0
- package/dist/src/server/workflow-cli-policy.js +110 -0
- package/dist/src/server/workflow-dispatch-awaiter.d.ts +12 -0
- package/dist/src/server/workflow-dispatch-awaiter.js +80 -0
- package/dist/src/server/workflow-feature.d.ts +15 -0
- package/dist/src/server/workflow-feature.js +15 -0
- package/dist/src/server/workflow-http-serializers.d.ts +64 -0
- package/dist/src/server/workflow-http-serializers.js +58 -0
- package/dist/src/server/workflow-run-log-store.d.ts +19 -0
- package/dist/src/server/workflow-run-log-store.js +45 -0
- package/dist/src/server/workflow-run-store.d.ts +50 -0
- package/dist/src/server/workflow-run-store.js +103 -0
- package/dist/src/server/workflow-runner.d.ts +147 -0
- package/dist/src/server/workflow-runner.js +401 -0
- package/dist/src/server/workflow-schedule-create.d.ts +14 -0
- package/dist/src/server/workflow-schedule-create.js +41 -0
- package/dist/src/server/workflow-schedule-store.d.ts +43 -0
- package/dist/src/server/workflow-schedule-store.js +112 -0
- package/dist/src/server/workflow-scheduler.d.ts +36 -0
- package/dist/src/server/workflow-scheduler.js +97 -0
- package/dist/src/server/workflow-script-loader.d.ts +34 -0
- package/dist/src/server/workflow-script-loader.js +106 -0
- package/dist/src/server/workspace-path-validation.js +16 -4
- package/dist/src/server/workspace-shell-runtime.d.ts +5 -0
- package/dist/src/server/workspace-shell-runtime.js +24 -2
- package/dist/src/server/workspace-store-contract.d.ts +4 -1
- package/dist/src/server/workspace-store-hydration.js +23 -7
- package/dist/src/server/workspace-store-mutations.js +2 -5
- package/dist/src/server/workspace-store-support.d.ts +4 -0
- package/dist/src/server/workspace-store-support.js +13 -1
- package/dist/src/server/workspace-store.js +38 -4
- package/dist/src/shared/types.d.ts +16 -1
- package/package.json +4 -2
- package/web/dist/assets/{AddWorkerDialog-DeZhTQLi.js → AddWorkerDialog-CcC-7kgG.js} +2 -2
- package/web/dist/assets/AddWorkspaceDialog-BDpOTfmt.js +1 -0
- package/web/dist/assets/{FirstRunWizard-B5wLcat5.js → FirstRunWizard-BYX_ocQn.js} +1 -1
- package/web/dist/assets/{MarketplaceDrawer-BC0eBOEW.js → MarketplaceDrawer-DUxSk7db.js} +1 -1
- package/web/dist/assets/WhatsNewDialog-B_RlCXcV.js +1 -0
- package/web/dist/assets/WorkerModal-D9-7YfZZ.js +1 -0
- package/web/dist/assets/WorkspaceTaskDrawer-BCKoF7qc.js +1 -0
- package/web/dist/assets/{WorkspaceTerminalPanels-CvibsPSd.js → WorkspaceTerminalPanels-Dq8y91t2.js} +1 -1
- package/web/dist/assets/index-BiOvKIVw.css +1 -0
- package/web/dist/assets/index-DMRUklT3.js +73 -0
- package/web/dist/assets/path-join-7MR1s7b1.js +1 -0
- package/web/dist/index.html +2 -2
- package/web/dist/sw.js +1 -1
- package/web/dist/assets/AddWorkspaceDialog-DDpXNEKf.js +0 -1
- package/web/dist/assets/WorkerModal-BwMHq-Bi.js +0 -1
- package/web/dist/assets/WorkspaceTaskDrawer-CxvT4nqs.js +0 -1
- package/web/dist/assets/index-BEsTmfrO.css +0 -1
- package/web/dist/assets/index-Ddb7bDN5.js +0 -75
- package/web/dist/assets/path-join-S7qkXQtP.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
All notable user-facing changes will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 1.5.0 - 2026-05-31
|
|
6
|
+
|
|
7
|
+
Workflow runtime, experimental team automation, and Codex reliability.
|
|
8
|
+
|
|
9
|
+
- Adds the experimental Hive workflow runtime: Orchestrators can author
|
|
10
|
+
multi-agent workflow scripts that fan out across real Hive PTY workers, show
|
|
11
|
+
runs in the Workflows drawer, stop runs, inspect run details, schedule
|
|
12
|
+
recurring workflows, and route workflow reports back into the Orchestrator.
|
|
13
|
+
- Adds workflow agent CLI policy settings so users can choose which CLI
|
|
14
|
+
workflow-created agents use by default and which CLIs are allowed.
|
|
15
|
+
- Adds the experimental auto-staff setting, letting the Orchestrator size the
|
|
16
|
+
worker roster to the task and prefer task-scoped ephemeral workers when it
|
|
17
|
+
needs temporary coders, testers, or reviewers.
|
|
18
|
+
- Adds an in-app What's New dialog so future upgrades can surface curated
|
|
19
|
+
release highlights without requiring users to read the changelog manually.
|
|
20
|
+
- Improves Codex reliability by waiting for pasted-content acknowledgements on
|
|
21
|
+
long dispatches while submitting short report/status injections quickly,
|
|
22
|
+
avoiding the several-second delay before reports reach the Orchestrator.
|
|
23
|
+
- Hardens Windows and runtime edge cases, including malformed WebSocket frames,
|
|
24
|
+
stale nvm4w Codex node entrypoints, workflow worker exits, and additional
|
|
25
|
+
workflow/runtime cleanup paths.
|
|
26
|
+
|
|
5
27
|
## 1.4.4 - 2026-05-29
|
|
6
28
|
|
|
7
29
|
Windows portability and team protocol hardening.
|
package/README.md
CHANGED
|
@@ -241,6 +241,14 @@ pnpm release:dry
|
|
|
241
241
|
|
|
242
242
|
Hive 目前处于 alpha 阶段,核心流程已可用。当前重点是继续打磨多 Agent 协作体验、Windows 支持和更清晰的调度可观测性。欢迎试用、提 issue——反馈会直接影响后续节奏。
|
|
243
243
|
|
|
244
|
+
## 交流群
|
|
245
|
+
|
|
246
|
+
有问题、想反馈,或者就想聊聊 Agent 协作,欢迎进 QQ 群:**Ai Native 交流群**(群号 `1098836554`)。
|
|
247
|
+
|
|
248
|
+
<p align="center">
|
|
249
|
+
<img src="./assets/qq-group.jpg" width="240" alt="Ai Native 交流群 QQ 群二维码,群号 1098836554" />
|
|
250
|
+
</p>
|
|
251
|
+
|
|
244
252
|
## 在路上:跨 Agent 的长时记忆
|
|
245
253
|
|
|
246
254
|
<p align="center">
|
|
Binary file
|
package/dist/bin/team.cmd
CHANGED
|
@@ -4,26 +4,36 @@ export interface RunUpdateResult {
|
|
|
4
4
|
spawnError?: Error;
|
|
5
5
|
}
|
|
6
6
|
export type RunUpdate = (command: string, args: readonly string[]) => Promise<RunUpdateResult>;
|
|
7
|
+
export interface SpawnInvocationPlan {
|
|
8
|
+
command: string;
|
|
9
|
+
args: string[];
|
|
10
|
+
options: {
|
|
11
|
+
stdio: 'inherit';
|
|
12
|
+
windowsHide?: boolean;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
7
15
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
16
|
+
* Plan how `defaultRunUpdate` should hand the npm invocation to
|
|
17
|
+
* `child_process.spawn`. Two non-obvious cases collapse here.
|
|
18
|
+
*
|
|
19
|
+
* 1. Node 22+ refuses to spawn `.cmd` / `.bat` files directly after
|
|
20
|
+
* CVE-2024-27980 unless `shell: true` is passed. We do not want
|
|
21
|
+
* `shell: true` though — its arg-stringification path joins argv
|
|
22
|
+
* without quoting, so an install prefix containing spaces (the
|
|
23
|
+
* common Windows case `C:\Program Files\nodejs`) gets word-split
|
|
24
|
+
* by cmd.exe and `--prefix` only sees the first token, so npm
|
|
25
|
+
* silently installs hive to the wrong directory.
|
|
26
|
+
* 2. Wrapping with `cmd.exe /d /s /c <npm.cmd> <args>` solves both:
|
|
27
|
+
* spawning `cmd.exe` (a native exe) avoids the .cmd refusal, and
|
|
28
|
+
* Node's own argv-quoting builds an lpCommandLine where each
|
|
29
|
+
* space-containing arg is wrapped in double quotes that cmd.exe
|
|
30
|
+
* then re-parses correctly. `/d` skips AutoRun, `/s` keeps the
|
|
31
|
+
* quote-handling consistent with `/c`.
|
|
15
32
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* `C:\Program Files\nodejs`) will be tokenized incorrectly. The common
|
|
19
|
-
* Windows prefix `%APPDATA%\npm` does not have this problem; fixing the
|
|
20
|
-
* spaces case requires a verbatim `cmd.exe /d /s /c call npm.cmd …`
|
|
21
|
-
* wrapper similar to `agent-command-resolver` and is tracked separately.
|
|
33
|
+
* Detect by filename suffix instead of `process.platform` so unit
|
|
34
|
+
* tests can inject `platform: 'win32'` and exercise the wrap.
|
|
22
35
|
*/
|
|
23
|
-
export declare const
|
|
24
|
-
shell: boolean;
|
|
25
|
-
stdio: "inherit";
|
|
26
|
-
};
|
|
36
|
+
export declare const planSpawnInvocation: (command: string, args: readonly string[], platform?: NodeJS.Platform) => SpawnInvocationPlan;
|
|
27
37
|
/**
|
|
28
38
|
* Signals the upgrade child should receive when the parent runtime is
|
|
29
39
|
* interrupted. Beyond the POSIX-only SIGTERM/SIGINT, SIGHUP is what
|
|
@@ -33,6 +43,24 @@ export declare const buildSpawnOptionsForCommand: (command: string) => {
|
|
|
33
43
|
* Windows exit paths.
|
|
34
44
|
*/
|
|
35
45
|
export declare const FORWARDED_UPDATE_SIGNALS: readonly NodeJS.Signals[];
|
|
46
|
+
/**
|
|
47
|
+
* Forward a parent-process signal to the spawned npm child. On POSIX
|
|
48
|
+
* we hand the signal straight to the child; on Windows there are no
|
|
49
|
+
* real signals, so `child.kill(SIGTERM)` resolves to TerminateProcess
|
|
50
|
+
* against cmd.exe (our wrapper) only — npm itself, plus any install
|
|
51
|
+
* scripts it spawned, become orphans. `taskkill /pid <pid> /t /f`
|
|
52
|
+
* walks the wrapper's process tree so the whole branch dies together.
|
|
53
|
+
* If taskkill is unavailable (restricted PATH, locked-down policy)
|
|
54
|
+
* we fall back to `child.kill` so the wrapper at least exits.
|
|
55
|
+
*
|
|
56
|
+
* Exported so the win32 path can be unit-tested by injecting a stub
|
|
57
|
+
* `killTree` runner — the real `taskkillProcessTree` shells out, and
|
|
58
|
+
* we don't want that running during the test suite.
|
|
59
|
+
*/
|
|
60
|
+
export declare const killUpdateChild: (child: {
|
|
61
|
+
pid?: number | undefined;
|
|
62
|
+
kill: (signal: NodeJS.Signals) => boolean;
|
|
63
|
+
}, signal: NodeJS.Signals, platform?: NodeJS.Platform, killTree?: (pid: number, onFailure?: () => void) => boolean) => void;
|
|
36
64
|
export declare const defaultRunUpdate: RunUpdate;
|
|
37
65
|
export declare const resolveHiveUpdateInstallArgs: (moduleUrl?: string) => string[];
|
|
38
66
|
interface RunHiveUpdateOptions {
|
|
@@ -2,7 +2,9 @@ import { spawn } from 'node:child_process';
|
|
|
2
2
|
import { existsSync, readFileSync } from 'node:fs';
|
|
3
3
|
import { basename, dirname, join } from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { taskkillProcessTree } from '../server/agent-manager-support.js';
|
|
5
6
|
import { getNpmCommand, INSTALL_COMMAND_ARGS, INSTALL_COMMAND_DISPLAY, PACKAGE_NAME, } from '../server/package-version.js';
|
|
7
|
+
import { buildCmdCallCommand } from '../server/windows-command-line.js';
|
|
6
8
|
export const HIVE_UPDATE_USAGE = [
|
|
7
9
|
'Usage:',
|
|
8
10
|
' hive update',
|
|
@@ -20,25 +22,36 @@ export const HIVE_UPDATE_USAGE = [
|
|
|
20
22
|
' -h, --help Print this help.',
|
|
21
23
|
].join('\n');
|
|
22
24
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* spawn `.cmd` / `.bat` files without `shell: true` after CVE-2024-27980.
|
|
26
|
-
* Detect by file extension rather than by `process.platform` so the same
|
|
27
|
-
* code path works for both real Windows runs and our cross-platform unit
|
|
28
|
-
* tests (which inject `platform: 'win32'` so that `getNpmCommand` returns
|
|
29
|
-
* `npm.cmd`).
|
|
25
|
+
* Plan how `defaultRunUpdate` should hand the npm invocation to
|
|
26
|
+
* `child_process.spawn`. Two non-obvious cases collapse here.
|
|
30
27
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
28
|
+
* 1. Node 22+ refuses to spawn `.cmd` / `.bat` files directly after
|
|
29
|
+
* CVE-2024-27980 unless `shell: true` is passed. We do not want
|
|
30
|
+
* `shell: true` though — its arg-stringification path joins argv
|
|
31
|
+
* without quoting, so an install prefix containing spaces (the
|
|
32
|
+
* common Windows case `C:\Program Files\nodejs`) gets word-split
|
|
33
|
+
* by cmd.exe and `--prefix` only sees the first token, so npm
|
|
34
|
+
* silently installs hive to the wrong directory.
|
|
35
|
+
* 2. Wrapping with `cmd.exe /d /s /c <npm.cmd> <args>` solves both:
|
|
36
|
+
* spawning `cmd.exe` (a native exe) avoids the .cmd refusal, and
|
|
37
|
+
* Node's own argv-quoting builds an lpCommandLine where each
|
|
38
|
+
* space-containing arg is wrapped in double quotes that cmd.exe
|
|
39
|
+
* then re-parses correctly. `/d` skips AutoRun, `/s` keeps the
|
|
40
|
+
* quote-handling consistent with `/c`.
|
|
41
|
+
*
|
|
42
|
+
* Detect by filename suffix instead of `process.platform` so unit
|
|
43
|
+
* tests can inject `platform: 'win32'` and exercise the wrap.
|
|
37
44
|
*/
|
|
38
|
-
export const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
export const planSpawnInvocation = (command, args, platform = process.platform) => {
|
|
46
|
+
if (platform === 'win32' && /\.(cmd|bat)$/i.test(command)) {
|
|
47
|
+
return {
|
|
48
|
+
command: 'cmd.exe',
|
|
49
|
+
args: ['/d', '/s', '/c', buildCmdCallCommand(command, args)],
|
|
50
|
+
options: { stdio: 'inherit', windowsHide: false },
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return { command, args: [...args], options: { stdio: 'inherit' } };
|
|
54
|
+
};
|
|
42
55
|
/**
|
|
43
56
|
* Signals the upgrade child should receive when the parent runtime is
|
|
44
57
|
* interrupted. Beyond the POSIX-only SIGTERM/SIGINT, SIGHUP is what
|
|
@@ -53,21 +66,46 @@ export const FORWARDED_UPDATE_SIGNALS = [
|
|
|
53
66
|
'SIGHUP',
|
|
54
67
|
'SIGBREAK',
|
|
55
68
|
];
|
|
69
|
+
/**
|
|
70
|
+
* Forward a parent-process signal to the spawned npm child. On POSIX
|
|
71
|
+
* we hand the signal straight to the child; on Windows there are no
|
|
72
|
+
* real signals, so `child.kill(SIGTERM)` resolves to TerminateProcess
|
|
73
|
+
* against cmd.exe (our wrapper) only — npm itself, plus any install
|
|
74
|
+
* scripts it spawned, become orphans. `taskkill /pid <pid> /t /f`
|
|
75
|
+
* walks the wrapper's process tree so the whole branch dies together.
|
|
76
|
+
* If taskkill is unavailable (restricted PATH, locked-down policy)
|
|
77
|
+
* we fall back to `child.kill` so the wrapper at least exits.
|
|
78
|
+
*
|
|
79
|
+
* Exported so the win32 path can be unit-tested by injecting a stub
|
|
80
|
+
* `killTree` runner — the real `taskkillProcessTree` shells out, and
|
|
81
|
+
* we don't want that running during the test suite.
|
|
82
|
+
*/
|
|
83
|
+
export const killUpdateChild = (child, signal, platform = process.platform, killTree = (pid, onFailure) => taskkillProcessTree(pid, platform, undefined, onFailure)) => {
|
|
84
|
+
const fallback = () => {
|
|
85
|
+
try {
|
|
86
|
+
child.kill(signal);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// child.kill on Windows throws if the signal name isn't
|
|
90
|
+
// implemented; we forward what we can and ignore the rest.
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
if (platform === 'win32' && typeof child.pid === 'number' && child.pid > 0) {
|
|
94
|
+
if (killTree(child.pid, fallback))
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
fallback();
|
|
98
|
+
};
|
|
56
99
|
export const defaultRunUpdate = (command, args) => new Promise((resolve) => {
|
|
57
|
-
const
|
|
100
|
+
const plan = planSpawnInvocation(command, args);
|
|
101
|
+
const child = spawn(plan.command, plan.args, plan.options);
|
|
58
102
|
let resolved = false;
|
|
59
103
|
// Handlers are registered with `once` so they don't accumulate
|
|
60
104
|
// across invocations and explicitly removed at finalize().
|
|
61
105
|
const handlers = new Map();
|
|
62
106
|
for (const signal of FORWARDED_UPDATE_SIGNALS) {
|
|
63
107
|
const handler = () => {
|
|
64
|
-
|
|
65
|
-
child.kill(signal);
|
|
66
|
-
}
|
|
67
|
-
catch {
|
|
68
|
-
// child.kill on Windows throws if the signal name isn't
|
|
69
|
-
// implemented; we forward what we can and ignore the rest.
|
|
70
|
-
}
|
|
108
|
+
killUpdateChild(child, signal);
|
|
71
109
|
};
|
|
72
110
|
handlers.set(signal, handler);
|
|
73
111
|
process.once(signal, handler);
|
package/dist/src/cli/hive.d.ts
CHANGED
|
@@ -33,6 +33,31 @@ type RunHiveCommandOptions = {
|
|
|
33
33
|
export declare const SHUTDOWN_SIGNALS: readonly ["SIGINT", "SIGTERM", "SIGHUP", "SIGBREAK"];
|
|
34
34
|
export declare const HIVE_USAGE: string;
|
|
35
35
|
export declare const handleHiveInfoCommand: (argv: string[]) => boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Resolve the directory where Hive persists its SQLite DB and supporting
|
|
38
|
+
* state. Platform-aware because `~/.config/hive` is a hidden dot-directory
|
|
39
|
+
* convention that Windows Explorer treats as second-class — Windows users
|
|
40
|
+
* can't navigate there from the address bar without typing the full path,
|
|
41
|
+
* and the standard roaming-profile location is `%APPDATA%\<app>` instead.
|
|
42
|
+
*
|
|
43
|
+
* Resolution order:
|
|
44
|
+
* 1. HIVE_DATA_DIR override (any platform, for tests / opinionated users).
|
|
45
|
+
* 2. Windows: %APPDATA%\hive — roaming user state, follows the user
|
|
46
|
+
* across machines on a domain profile. APPDATA, not LOCALAPPDATA,
|
|
47
|
+
* because Hive's DB is user data, not a machine-local cache.
|
|
48
|
+
* Falls back to homedir()\AppData\Roaming\hive when APPDATA is
|
|
49
|
+
* stripped from the env (some Windows Task Scheduler configs do this).
|
|
50
|
+
* 3. POSIX: $XDG_CONFIG_HOME/hive, falling back to ~/.config/hive.
|
|
51
|
+
*
|
|
52
|
+
* Migration: pre-fix Windows installs wrote to ~/.config/hive. When that
|
|
53
|
+
* legacy directory exists but the new %APPDATA%\hive does not, prefer the
|
|
54
|
+
* legacy path so an upgrade does not surface as an empty workspace list.
|
|
55
|
+
* This is a one-way ratchet — once the new location is populated, it wins.
|
|
56
|
+
*
|
|
57
|
+
* Exported so the resolution rules are unit-testable without touching env
|
|
58
|
+
* or the real filesystem.
|
|
59
|
+
*/
|
|
60
|
+
export declare const resolveDataDir: (platform?: NodeJS.Platform, env?: NodeJS.ProcessEnv, pathExists?: (path: string) => boolean) => string;
|
|
36
61
|
/**
|
|
37
62
|
* Recovery hint formatter for the "port already in use" error. Platform-aware
|
|
38
63
|
* because the lsof / xargs / kill pipeline is POSIX-only; on Windows a user
|
package/dist/src/cli/hive.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { once } from 'node:events';
|
|
3
|
-
import {
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
5
|
import { join } from 'node:path';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { createAgentManager } from '../server/agent-manager.js';
|
|
8
8
|
import { createApp } from '../server/app.js';
|
|
9
9
|
import { readPackageVersion } from '../server/package-version.js';
|
|
10
|
+
import { sameFilesystemPath } from '../server/path-canonicalization.js';
|
|
10
11
|
import { createRuntimeStore } from '../server/runtime-store.js';
|
|
11
12
|
import { createVersionService } from '../server/version-service.js';
|
|
12
13
|
import { runHiveUpdateCommand } from './hive-update.js';
|
|
@@ -80,7 +81,44 @@ const parsePort = (argv) => {
|
|
|
80
81
|
}
|
|
81
82
|
return parsedPort ?? 3000;
|
|
82
83
|
};
|
|
83
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Resolve the directory where Hive persists its SQLite DB and supporting
|
|
86
|
+
* state. Platform-aware because `~/.config/hive` is a hidden dot-directory
|
|
87
|
+
* convention that Windows Explorer treats as second-class — Windows users
|
|
88
|
+
* can't navigate there from the address bar without typing the full path,
|
|
89
|
+
* and the standard roaming-profile location is `%APPDATA%\<app>` instead.
|
|
90
|
+
*
|
|
91
|
+
* Resolution order:
|
|
92
|
+
* 1. HIVE_DATA_DIR override (any platform, for tests / opinionated users).
|
|
93
|
+
* 2. Windows: %APPDATA%\hive — roaming user state, follows the user
|
|
94
|
+
* across machines on a domain profile. APPDATA, not LOCALAPPDATA,
|
|
95
|
+
* because Hive's DB is user data, not a machine-local cache.
|
|
96
|
+
* Falls back to homedir()\AppData\Roaming\hive when APPDATA is
|
|
97
|
+
* stripped from the env (some Windows Task Scheduler configs do this).
|
|
98
|
+
* 3. POSIX: $XDG_CONFIG_HOME/hive, falling back to ~/.config/hive.
|
|
99
|
+
*
|
|
100
|
+
* Migration: pre-fix Windows installs wrote to ~/.config/hive. When that
|
|
101
|
+
* legacy directory exists but the new %APPDATA%\hive does not, prefer the
|
|
102
|
+
* legacy path so an upgrade does not surface as an empty workspace list.
|
|
103
|
+
* This is a one-way ratchet — once the new location is populated, it wins.
|
|
104
|
+
*
|
|
105
|
+
* Exported so the resolution rules are unit-testable without touching env
|
|
106
|
+
* or the real filesystem.
|
|
107
|
+
*/
|
|
108
|
+
export const resolveDataDir = (platform = process.platform, env = process.env, pathExists = existsSync) => {
|
|
109
|
+
const override = env.HIVE_DATA_DIR;
|
|
110
|
+
if (override)
|
|
111
|
+
return override;
|
|
112
|
+
if (platform === 'win32') {
|
|
113
|
+
const appData = env.APPDATA ?? join(homedir(), 'AppData', 'Roaming');
|
|
114
|
+
const target = join(appData, 'hive');
|
|
115
|
+
const legacy = join(homedir(), '.config', 'hive');
|
|
116
|
+
if (!pathExists(target) && pathExists(legacy))
|
|
117
|
+
return legacy;
|
|
118
|
+
return target;
|
|
119
|
+
}
|
|
120
|
+
return join(env.XDG_CONFIG_HOME ?? join(homedir(), '.config'), 'hive');
|
|
121
|
+
};
|
|
84
122
|
const maybePrintUpdateHint = async (versionService) => {
|
|
85
123
|
const info = await versionService.getVersionInfo();
|
|
86
124
|
if (!info.update_available)
|
|
@@ -210,7 +248,7 @@ export const runHiveCommand = async (argv, options = {}) => {
|
|
|
210
248
|
};
|
|
211
249
|
};
|
|
212
250
|
const isMainModule = process.argv[1]
|
|
213
|
-
? fileURLToPath(import.meta.url)
|
|
251
|
+
? sameFilesystemPath(fileURLToPath(import.meta.url), process.argv[1])
|
|
214
252
|
: false;
|
|
215
253
|
if (isMainModule) {
|
|
216
254
|
const argv = process.argv.slice(2);
|
package/dist/src/cli/team.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export interface ParsedReportArgs {
|
|
|
10
10
|
}
|
|
11
11
|
export declare const parseReportArgs: (args: string[], command?: string) => ParsedReportArgs;
|
|
12
12
|
export declare const parseCancelArgs: (args: string[]) => ParsedCancelArgs;
|
|
13
|
+
export declare const decodeStdinBuffer: (buffer: Buffer) => string;
|
|
13
14
|
export declare const readStdinToString: (command?: string) => Promise<string>;
|
|
14
15
|
export declare const runTeamCommand: (argv: string[]) => Promise<void>;
|
|
15
16
|
export {};
|
package/dist/src/cli/team.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { realpathSync } from 'node:fs';
|
|
2
1
|
import { fileURLToPath } from 'node:url';
|
|
2
|
+
import { sameFilesystemPath } from '../server/path-canonicalization.js';
|
|
3
3
|
const REQUIRED_ENV_KEYS = [
|
|
4
4
|
'HIVE_PORT',
|
|
5
5
|
'HIVE_PROJECT_ID',
|
|
@@ -10,6 +10,13 @@ const TEAM_USAGE = [
|
|
|
10
10
|
'Usage:',
|
|
11
11
|
' team list',
|
|
12
12
|
' team send <worker-name> "<task>"',
|
|
13
|
+
' team spawn <role> [--name <name>] [--cli <claude|codex|opencode|gemini>] [--ephemeral]',
|
|
14
|
+
' team dismiss <worker-name>',
|
|
15
|
+
" team workflow run --stdin [--args '<JSON>'] (script from stdin — for multi-line scripts)",
|
|
16
|
+
' team workflow run --inline "<source>" [--args \'<JSON>\']',
|
|
17
|
+
' team workflow stop <run-id>',
|
|
18
|
+
' team workflow show <run-id> (full per-agent transcript for one run)',
|
|
19
|
+
' team workflow schedule --cron "<cron>" --name <n> --stdin (register a recurring run)',
|
|
13
20
|
' team cancel --dispatch <dispatch-id> "<reason>"',
|
|
14
21
|
' team report "<result>" [--dispatch <dispatch-id>] [--artifact <path>]',
|
|
15
22
|
' team report --stdin [--dispatch <dispatch-id>] [--artifact <path>]',
|
|
@@ -35,6 +42,15 @@ const getHiveEnv = () => {
|
|
|
35
42
|
return values;
|
|
36
43
|
};
|
|
37
44
|
const getBaseUrl = (env) => `http://127.0.0.1:${env.HIVE_PORT}`;
|
|
45
|
+
// Read `--flag value` from an argv slice; returns undefined when absent or
|
|
46
|
+
// when the flag is the last token with no following value.
|
|
47
|
+
const readFlag = (args, flag) => {
|
|
48
|
+
const index = args.indexOf(flag);
|
|
49
|
+
if (index === -1)
|
|
50
|
+
return undefined;
|
|
51
|
+
const value = args[index + 1];
|
|
52
|
+
return value && !value.startsWith('--') ? value : undefined;
|
|
53
|
+
};
|
|
38
54
|
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
39
55
|
const describeFetchError = (baseUrl, error) => {
|
|
40
56
|
const cause = error instanceof Error && error.cause instanceof Error ? ` (${error.cause.message})` : '';
|
|
@@ -177,6 +193,20 @@ export const parseCancelArgs = (args) => {
|
|
|
177
193
|
}
|
|
178
194
|
return { dispatchId, reason };
|
|
179
195
|
};
|
|
196
|
+
export const decodeStdinBuffer = (buffer) => {
|
|
197
|
+
if (buffer.length >= 3 && buffer[0] === 0xef && buffer[1] === 0xbb && buffer[2] === 0xbf) {
|
|
198
|
+
return buffer.subarray(3).toString('utf8');
|
|
199
|
+
}
|
|
200
|
+
if (buffer.length >= 2 && buffer[0] === 0xff && buffer[1] === 0xfe) {
|
|
201
|
+
return buffer.subarray(2).toString('utf16le');
|
|
202
|
+
}
|
|
203
|
+
if (buffer.length >= 2 && buffer[0] === 0xfe && buffer[1] === 0xff) {
|
|
204
|
+
const swapped = Buffer.from(buffer.subarray(2));
|
|
205
|
+
swapped.swap16();
|
|
206
|
+
return swapped.toString('utf16le');
|
|
207
|
+
}
|
|
208
|
+
return buffer.toString('utf8');
|
|
209
|
+
};
|
|
180
210
|
export const readStdinToString = async (command = 'report') => {
|
|
181
211
|
if (process.stdin.isTTY) {
|
|
182
212
|
throw new Error(withUsage('--stdin requires piped input, but stdin is a TTY. Did you forget to pipe content in?', command));
|
|
@@ -185,7 +215,7 @@ export const readStdinToString = async (command = 'report') => {
|
|
|
185
215
|
for await (const chunk of process.stdin) {
|
|
186
216
|
chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);
|
|
187
217
|
}
|
|
188
|
-
const content = Buffer.concat(chunks)
|
|
218
|
+
const content = decodeStdinBuffer(Buffer.concat(chunks));
|
|
189
219
|
if (!content.trim()) {
|
|
190
220
|
throw new Error(withUsage('--stdin received empty input', command));
|
|
191
221
|
}
|
|
@@ -241,6 +271,172 @@ export const runTeamCommand = async (argv) => {
|
|
|
241
271
|
console.log(JSON.stringify(payload));
|
|
242
272
|
return;
|
|
243
273
|
}
|
|
274
|
+
if (command === 'spawn') {
|
|
275
|
+
const role = args[0];
|
|
276
|
+
if (!role || role.startsWith('--')) {
|
|
277
|
+
throw new Error('Usage: team spawn <role> [--name <name>] [--cli <claude|codex|opencode|gemini>] [--ephemeral]\n' +
|
|
278
|
+
' Default: persistent member (lives until you `team dismiss` it).\n' +
|
|
279
|
+
' --ephemeral: auto-dismiss after the next dispatch report (one-shot worker).');
|
|
280
|
+
}
|
|
281
|
+
const name = readFlag(args, '--name');
|
|
282
|
+
const cli = readFlag(args, '--cli');
|
|
283
|
+
const ephemeral = args.includes('--ephemeral');
|
|
284
|
+
const env = getHiveEnv();
|
|
285
|
+
const response = await postJson(getBaseUrl(env), '/api/team/spawn', {
|
|
286
|
+
project_id: env.HIVE_PROJECT_ID,
|
|
287
|
+
from_agent_id: env.HIVE_AGENT_ID,
|
|
288
|
+
token: env.HIVE_AGENT_TOKEN,
|
|
289
|
+
role,
|
|
290
|
+
...(name ? { name } : {}),
|
|
291
|
+
...(cli ? { cli } : {}),
|
|
292
|
+
...(ephemeral ? { ephemeral: true } : {}),
|
|
293
|
+
});
|
|
294
|
+
console.log(JSON.stringify(await response.json()));
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
if (command === 'dismiss') {
|
|
298
|
+
const workerName = args[0];
|
|
299
|
+
if (!workerName || workerName.startsWith('--')) {
|
|
300
|
+
throw new Error('Usage: team dismiss <worker-name>');
|
|
301
|
+
}
|
|
302
|
+
const env = getHiveEnv();
|
|
303
|
+
const response = await postJson(getBaseUrl(env), '/api/team/dismiss', {
|
|
304
|
+
project_id: env.HIVE_PROJECT_ID,
|
|
305
|
+
from_agent_id: env.HIVE_AGENT_ID,
|
|
306
|
+
token: env.HIVE_AGENT_TOKEN,
|
|
307
|
+
name: workerName,
|
|
308
|
+
});
|
|
309
|
+
console.log(JSON.stringify(await response.json()));
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (command === 'workflow') {
|
|
313
|
+
const sub = args[0];
|
|
314
|
+
const rest = args.slice(1);
|
|
315
|
+
if (sub === 'run') {
|
|
316
|
+
const inlineFlag = rest.indexOf('--inline');
|
|
317
|
+
const stdinFlag = rest.includes('--stdin');
|
|
318
|
+
const name = readFlag(rest, '--name');
|
|
319
|
+
// TIER 2 #8 — `--args '<JSON>'` makes the script's `args` global a
|
|
320
|
+
// real value instead of always undefined. Parses lazily so a bad
|
|
321
|
+
// JSON gives a clear local error before the HTTP round-trip.
|
|
322
|
+
const rawArgs = readFlag(rest, '--args');
|
|
323
|
+
let parsedArgs;
|
|
324
|
+
if (rawArgs !== undefined) {
|
|
325
|
+
try {
|
|
326
|
+
parsedArgs = JSON.parse(rawArgs);
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
throw new Error(`Usage: team workflow run … --args '<JSON>'\n --args must be valid JSON; got: ${error instanceof Error ? error.message : String(error)}`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
let source;
|
|
333
|
+
if (inlineFlag !== -1) {
|
|
334
|
+
const literal = rest[inlineFlag + 1];
|
|
335
|
+
if (!literal)
|
|
336
|
+
throw new Error('Usage: team workflow run --inline "<script-source>"');
|
|
337
|
+
source = literal;
|
|
338
|
+
}
|
|
339
|
+
else if (stdinFlag) {
|
|
340
|
+
source = await readStdinToString('workflow run');
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
throw new Error('Usage: team workflow run --stdin | team workflow run --inline "<script-source>"\n' +
|
|
344
|
+
' Pass workflow source via stdin (POSIX heredoc / `type x.ts |`) or as one inline arg.\n' +
|
|
345
|
+
" Optional: --args '<JSON>' makes the script's `args` global a real value.");
|
|
346
|
+
}
|
|
347
|
+
const env = getHiveEnv();
|
|
348
|
+
const response = await postJson(getBaseUrl(env), '/api/team/workflow/run', {
|
|
349
|
+
project_id: env.HIVE_PROJECT_ID,
|
|
350
|
+
from_agent_id: env.HIVE_AGENT_ID,
|
|
351
|
+
token: env.HIVE_AGENT_TOKEN,
|
|
352
|
+
source,
|
|
353
|
+
...(name ? { name } : {}),
|
|
354
|
+
...(parsedArgs !== undefined ? { args: parsedArgs } : {}),
|
|
355
|
+
});
|
|
356
|
+
console.log(JSON.stringify(await response.json()));
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
if (sub === 'stop') {
|
|
360
|
+
const runId = rest[0];
|
|
361
|
+
if (!runId)
|
|
362
|
+
throw new Error('Usage: team workflow stop <run-id>');
|
|
363
|
+
const env = getHiveEnv();
|
|
364
|
+
const response = await postJson(getBaseUrl(env), '/api/team/workflow/stop', {
|
|
365
|
+
project_id: env.HIVE_PROJECT_ID,
|
|
366
|
+
from_agent_id: env.HIVE_AGENT_ID,
|
|
367
|
+
token: env.HIVE_AGENT_TOKEN,
|
|
368
|
+
run_id: runId,
|
|
369
|
+
});
|
|
370
|
+
console.log(JSON.stringify(await response.json()));
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
if (sub === 'show') {
|
|
374
|
+
const runId = rest[0];
|
|
375
|
+
if (!runId)
|
|
376
|
+
throw new Error('Usage: team workflow show <run-id>');
|
|
377
|
+
const env = getHiveEnv();
|
|
378
|
+
const response = await postJson(getBaseUrl(env), '/api/team/workflow/show', {
|
|
379
|
+
project_id: env.HIVE_PROJECT_ID,
|
|
380
|
+
from_agent_id: env.HIVE_AGENT_ID,
|
|
381
|
+
token: env.HIVE_AGENT_TOKEN,
|
|
382
|
+
run_id: runId,
|
|
383
|
+
});
|
|
384
|
+
console.log(JSON.stringify(await response.json()));
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
if (sub === 'schedule') {
|
|
388
|
+
const usage = 'Usage: team workflow schedule --cron "<5-field cron>" --name <name> --stdin\n' +
|
|
389
|
+
' team workflow schedule --cron "<cron>" --name <name> --inline "<source>" [--args \'<JSON>\']\n' +
|
|
390
|
+
' Registers a recurring run. Source is persisted so cron can fire it with no orchestrator present.';
|
|
391
|
+
const inlineFlag = rest.indexOf('--inline');
|
|
392
|
+
const stdinFlag = rest.includes('--stdin');
|
|
393
|
+
const cron = readFlag(rest, '--cron');
|
|
394
|
+
const name = readFlag(rest, '--name');
|
|
395
|
+
if (!cron || !name)
|
|
396
|
+
throw new Error(usage);
|
|
397
|
+
const rawArgs = readFlag(rest, '--args');
|
|
398
|
+
let parsedArgs;
|
|
399
|
+
if (rawArgs !== undefined) {
|
|
400
|
+
try {
|
|
401
|
+
parsedArgs = JSON.parse(rawArgs);
|
|
402
|
+
}
|
|
403
|
+
catch (error) {
|
|
404
|
+
throw new Error(`Usage: team workflow schedule … --args '<JSON>'\n --args must be valid JSON; got: ${error instanceof Error ? error.message : String(error)}`);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
let source;
|
|
408
|
+
if (inlineFlag !== -1) {
|
|
409
|
+
const literal = rest[inlineFlag + 1];
|
|
410
|
+
if (!literal)
|
|
411
|
+
throw new Error(usage);
|
|
412
|
+
source = literal;
|
|
413
|
+
}
|
|
414
|
+
else if (stdinFlag) {
|
|
415
|
+
source = await readStdinToString('workflow schedule');
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
throw new Error(usage);
|
|
419
|
+
}
|
|
420
|
+
const env = getHiveEnv();
|
|
421
|
+
const response = await postJson(getBaseUrl(env), '/api/team/workflow/schedule', {
|
|
422
|
+
project_id: env.HIVE_PROJECT_ID,
|
|
423
|
+
from_agent_id: env.HIVE_AGENT_ID,
|
|
424
|
+
token: env.HIVE_AGENT_TOKEN,
|
|
425
|
+
source,
|
|
426
|
+
name,
|
|
427
|
+
cron,
|
|
428
|
+
...(parsedArgs !== undefined ? { args: parsedArgs } : {}),
|
|
429
|
+
});
|
|
430
|
+
console.log(JSON.stringify(await response.json()));
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
throw new Error('Usage:\n' +
|
|
434
|
+
" team workflow run --stdin [--args '<JSON>'] (read script from stdin)\n" +
|
|
435
|
+
' team workflow run --inline "<source>" [--args ...] (one-arg form)\n' +
|
|
436
|
+
' team workflow stop <run-id> (cancel a running workflow)\n' +
|
|
437
|
+
' team workflow show <run-id> (full per-agent transcript)\n' +
|
|
438
|
+
' team workflow schedule --cron "<cron>" --name <n> --stdin (register a recurring run)');
|
|
439
|
+
}
|
|
244
440
|
if (command === 'cancel') {
|
|
245
441
|
const cancel = parseCancelArgs(args);
|
|
246
442
|
const env = getHiveEnv();
|
|
@@ -294,7 +490,7 @@ export const runTeamCommand = async (argv) => {
|
|
|
294
490
|
throw new Error('Unsupported team command');
|
|
295
491
|
};
|
|
296
492
|
const isMainModule = process.argv[1]
|
|
297
|
-
? fileURLToPath(import.meta.url)
|
|
493
|
+
? sameFilesystemPath(fileURLToPath(import.meta.url), process.argv[1])
|
|
298
494
|
: false;
|
|
299
495
|
if (isMainModule) {
|
|
300
496
|
void runTeamCommand(process.argv.slice(2)).catch((error) => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { accessSync, constants } from 'node:fs';
|
|
2
2
|
import { basename, delimiter, extname, isAbsolute, join } from 'node:path';
|
|
3
|
+
import { buildCmdCallCommand } from './windows-command-line.js';
|
|
3
4
|
const hasPathSeparator = (command) => command.includes('/') || command.includes('\\');
|
|
4
5
|
const canExecute = (path, platform = process.platform) => {
|
|
5
6
|
try {
|
|
@@ -54,28 +55,11 @@ const isWindowsBatchFile = (command) => {
|
|
|
54
55
|
const extension = extname(command).toLowerCase();
|
|
55
56
|
return extension === '.cmd' || extension === '.bat';
|
|
56
57
|
};
|
|
57
|
-
/**
|
|
58
|
-
* cmd.exe-style escape: doubles inner `"` (cmd's only literal-quote idiom)
|
|
59
|
-
* and only wraps in `"..."` when the token contains whitespace or a cmd
|
|
60
|
-
* metachar. Plain alnum/path tokens stay unquoted, which is the form
|
|
61
|
-
* cmd.exe parses most predictably.
|
|
62
|
-
*
|
|
63
|
-
* Crucially, we do NOT use the `\"` form that the previous implementation
|
|
64
|
-
* used and that node-pty's `argsToCommandLine` produces: cmd doesn't honor
|
|
65
|
-
* backslash-quote escapes in its own command-line parsing.
|
|
66
|
-
*/
|
|
67
|
-
const escapeCmdToken = (value) => {
|
|
68
|
-
if (value.length === 0)
|
|
69
|
-
return '""';
|
|
70
|
-
const doubled = value.replace(/"/g, '""');
|
|
71
|
-
return /[\s"&<>|^()]/.test(value) ? `"${doubled}"` : doubled;
|
|
72
|
-
};
|
|
73
58
|
const buildWindowsBatchCommandLine = (command, args) => {
|
|
74
|
-
const tokens = [command, ...args].map(escapeCmdToken).join(' ');
|
|
75
59
|
// `call` is cmd's built-in batch invocation; it handles quoted .cmd / .bat
|
|
76
60
|
// paths reliably (this is the same pattern Node.js's child_process uses
|
|
77
61
|
// internally on Windows since the CVE-2024-27980 fix).
|
|
78
|
-
return `/d /s /c
|
|
62
|
+
return `/d /s /c ${buildCmdCallCommand(command, args)}`;
|
|
79
63
|
};
|
|
80
64
|
/**
|
|
81
65
|
* Recognize the exact shape that `createStartupCommandLaunch` produces on
|
|
@@ -93,7 +77,7 @@ const isCmdExeShellLaunch = (resolvedCommand, args) => basename(resolvedCommand)
|
|
|
93
77
|
args.length === 4 &&
|
|
94
78
|
args[0] === '/d' &&
|
|
95
79
|
args[1] === '/s' &&
|
|
96
|
-
args[2] === '/c';
|
|
80
|
+
(args[2] === '/c' || args[2] === '/k');
|
|
97
81
|
export const resolveSpawnCommand = (command, cwd, env, args = [], platform = process.platform) => {
|
|
98
82
|
const resolvedCommand = resolveCommandPath(command, cwd, env, platform);
|
|
99
83
|
if (platform === 'win32' && isWindowsBatchFile(resolvedCommand)) {
|