@tt-a1i/hive 1.4.3 → 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 +44 -0
- package/README.en.md +5 -4
- package/README.md +9 -1
- package/assets/qq-group.jpg +0 -0
- package/dist/bin/team.cmd +1 -0
- package/dist/src/cli/hive-update.d.ts +57 -0
- package/dist/src/cli/hive-update.js +92 -15
- package/dist/src/cli/hive.d.ts +57 -0
- package/dist/src/cli/hive.js +113 -20
- package/dist/src/cli/team.d.ts +1 -0
- package/dist/src/cli/team.js +215 -7
- package/dist/src/server/agent-command-resolver.d.ts +10 -1
- package/dist/src/server/agent-command-resolver.js +32 -4
- package/dist/src/server/agent-launch-resolver.js +9 -3
- package/dist/src/server/agent-manager-support.d.ts +28 -0
- package/dist/src/server/agent-manager-support.js +138 -10
- package/dist/src/server/agent-run-bootstrap.d.ts +17 -1
- package/dist/src/server/agent-run-bootstrap.js +30 -2
- 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/app.d.ts +1 -0
- package/dist/src/server/app.js +12 -2
- 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-browse.d.ts +14 -1
- package/dist/src/server/fs-browse.js +48 -5
- package/dist/src/server/fs-pick-folder.js +58 -11
- 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 -70
- 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 +29 -4
- 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 +116 -16
- 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 +221 -2
- 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 -70
- package/dist/src/server/runtime-store.js +70 -4
- package/dist/src/server/session-capture-claude.d.ts +23 -0
- package/dist/src/server/session-capture-claude.js +24 -1
- 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/session-capture-opencode.d.ts +18 -0
- package/dist/src/server/session-capture-opencode.js +27 -2
- 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/startup-command-parser.d.ts +15 -0
- package/dist/src/server/startup-command-parser.js +33 -2
- 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 +39 -1
- package/dist/src/server/tasks-file-watcher.js +155 -25
- 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 +20 -2
- package/dist/src/server/team-operations.js +160 -14
- package/dist/src/server/terminal-input-profile.js +2 -8
- 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 +36 -16
- 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-DmkDOdp6.js → AddWorkerDialog-CcC-7kgG.js} +2 -2
- package/web/dist/assets/AddWorkspaceDialog-BDpOTfmt.js +1 -0
- package/web/dist/assets/{FirstRunWizard-SAd1wsH4.js → FirstRunWizard-BYX_ocQn.js} +1 -1
- package/web/dist/assets/{MarketplaceDrawer-B_8aG2uT.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-BReWh1YL.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-BsVnH3Xe.js +0 -1
- package/web/dist/assets/WorkerModal-CQmjiPme.js +0 -1
- package/web/dist/assets/WorkspaceTaskDrawer-B0DmCWcV.js +0 -1
- package/web/dist/assets/chevron-right-CtLjVEl7.js +0 -1
- package/web/dist/assets/index-BEsTmfrO.css +0 -1
- package/web/dist/assets/index-Cn8X3get.js +0 -76
|
@@ -4,8 +4,33 @@ import { join } from 'node:path';
|
|
|
4
4
|
import Database from 'better-sqlite3';
|
|
5
5
|
import { captureSessionIdWithCoordinator } from './claude-session-coordinator.js';
|
|
6
6
|
const expandHome = (path) => path === '~' || path.startsWith('~/') ? join(homedir(), path.slice(2)) : path;
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Resolve the path OpenCode upstream writes `opencode.db` to. Branches
|
|
9
|
+
* by platform because XDG_DATA_HOME is not a Windows convention — the
|
|
10
|
+
* previous unconditional ~/.local/share/opencode/opencode.db default
|
|
11
|
+
* pointed at a path that never exists on Windows, silently breaking
|
|
12
|
+
* Layer A native session resume for OpenCode workers there.
|
|
13
|
+
*
|
|
14
|
+
* Resolution order:
|
|
15
|
+
* 1. HIVE_OPENCODE_DB_PATH override (any platform, for tests/users).
|
|
16
|
+
* 2. Windows: %LOCALAPPDATA%\opencode\opencode.db (with a homedir
|
|
17
|
+
* fallback when LOCALAPPDATA is missing — some shells strip env).
|
|
18
|
+
* 3. POSIX: $XDG_DATA_HOME/opencode/opencode.db, falling back to
|
|
19
|
+
* ~/.local/share/opencode/opencode.db.
|
|
20
|
+
*
|
|
21
|
+
* Exported so the path-resolution rules can be unit-tested without
|
|
22
|
+
* needing to mock the underlying SQLite open.
|
|
23
|
+
*/
|
|
24
|
+
export const getDefaultOpenCodeDbPath = (platform = process.platform) => {
|
|
25
|
+
const override = process.env.HIVE_OPENCODE_DB_PATH;
|
|
26
|
+
if (override)
|
|
27
|
+
return override;
|
|
28
|
+
if (platform === 'win32') {
|
|
29
|
+
const localAppData = process.env.LOCALAPPDATA ?? join(homedir(), 'AppData', 'Local');
|
|
30
|
+
return join(localAppData, 'opencode', 'opencode.db');
|
|
31
|
+
}
|
|
32
|
+
return join(process.env.XDG_DATA_HOME ?? join(homedir(), '.local', 'share'), 'opencode', 'opencode.db');
|
|
33
|
+
};
|
|
9
34
|
export const getOpenCodeDbPath = (pattern) => pattern === '~/.local/share/opencode/opencode.db' || !pattern
|
|
10
35
|
? getDefaultOpenCodeDbPath()
|
|
11
36
|
: expandHome(pattern);
|
|
@@ -7,6 +7,9 @@ export interface SettingsStore {
|
|
|
7
7
|
createRoleTemplate: (input: RoleTemplateInput) => RoleTemplateRecord;
|
|
8
8
|
deleteCommandPreset: (id: string) => void;
|
|
9
9
|
deleteRoleTemplate: (id: string) => void;
|
|
10
|
+
/** TIER 2 #4 — workflow runner uses this to resolve a non-built-in
|
|
11
|
+
* agentType against the user's curated role library. */
|
|
12
|
+
findRoleTemplateByName: (name: string) => RoleTemplateRecord | undefined;
|
|
10
13
|
getAppState: (key: string) => AppStateRecord | undefined;
|
|
11
14
|
getCommandPreset: (id: string) => CommandPresetRecord | undefined;
|
|
12
15
|
listCommandPresets: () => CommandPresetRecord[];
|
|
@@ -10,6 +10,7 @@ export const createSettingsStore = (db) => {
|
|
|
10
10
|
createRoleTemplate: roleTemplateStore.create,
|
|
11
11
|
deleteCommandPreset: commandPresetStore.remove,
|
|
12
12
|
deleteRoleTemplate: roleTemplateStore.remove,
|
|
13
|
+
findRoleTemplateByName: roleTemplateStore.findByName,
|
|
13
14
|
getAppState: appStateStore.get,
|
|
14
15
|
getCommandPreset: commandPresetStore.get,
|
|
15
16
|
listCommandPresets: commandPresetStore.list,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const applySchemaVersion19 = (db) => {
|
|
2
|
+
const workerColumns = new Set(db.prepare('PRAGMA table_info(workers)').all().map((c) => c.name));
|
|
3
|
+
if (!workerColumns.has('ephemeral')) {
|
|
4
|
+
db.exec('ALTER TABLE workers ADD COLUMN ephemeral INTEGER NOT NULL DEFAULT 0');
|
|
5
|
+
}
|
|
6
|
+
if (!workerColumns.has('spawned_by')) {
|
|
7
|
+
db.exec('ALTER TABLE workers ADD COLUMN spawned_by TEXT');
|
|
8
|
+
}
|
|
9
|
+
const dispatchColumns = new Set(db.prepare('PRAGMA table_info(dispatches)').all().map((c) => c.name));
|
|
10
|
+
if (!dispatchColumns.has('workflow_run_id')) {
|
|
11
|
+
db.exec('ALTER TABLE dispatches ADD COLUMN workflow_run_id TEXT');
|
|
12
|
+
}
|
|
13
|
+
if (!dispatchColumns.has('step_index')) {
|
|
14
|
+
db.exec('ALTER TABLE dispatches ADD COLUMN step_index INTEGER');
|
|
15
|
+
}
|
|
16
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_dispatches_workflow ON dispatches (workflow_run_id, step_index)');
|
|
17
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const applySchemaVersion20 = (db) => {
|
|
2
|
+
db.exec(`
|
|
3
|
+
CREATE TABLE IF NOT EXISTS workflow_runs (
|
|
4
|
+
id TEXT PRIMARY KEY,
|
|
5
|
+
workspace_id TEXT NOT NULL,
|
|
6
|
+
script_path TEXT NOT NULL,
|
|
7
|
+
script_hash TEXT,
|
|
8
|
+
name TEXT NOT NULL,
|
|
9
|
+
status TEXT NOT NULL,
|
|
10
|
+
phase TEXT,
|
|
11
|
+
args TEXT,
|
|
12
|
+
started_at INTEGER NOT NULL,
|
|
13
|
+
finished_at INTEGER,
|
|
14
|
+
error TEXT,
|
|
15
|
+
created_at INTEGER NOT NULL
|
|
16
|
+
);
|
|
17
|
+
CREATE INDEX IF NOT EXISTS idx_workflow_runs_workspace
|
|
18
|
+
ON workflow_runs (workspace_id, created_at);
|
|
19
|
+
`);
|
|
20
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const applySchemaVersion21 = (db) => {
|
|
2
|
+
db.exec(`
|
|
3
|
+
CREATE TABLE IF NOT EXISTS workflow_schedules (
|
|
4
|
+
id TEXT PRIMARY KEY,
|
|
5
|
+
workspace_id TEXT NOT NULL,
|
|
6
|
+
script_path TEXT NOT NULL,
|
|
7
|
+
cron TEXT NOT NULL,
|
|
8
|
+
args TEXT,
|
|
9
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
10
|
+
last_run_at INTEGER,
|
|
11
|
+
next_run_at INTEGER NOT NULL,
|
|
12
|
+
created_at INTEGER NOT NULL,
|
|
13
|
+
updated_at INTEGER NOT NULL
|
|
14
|
+
);
|
|
15
|
+
CREATE INDEX IF NOT EXISTS idx_workflow_schedules_due
|
|
16
|
+
ON workflow_schedules (enabled, next_run_at);
|
|
17
|
+
CREATE INDEX IF NOT EXISTS idx_workflow_schedules_workspace
|
|
18
|
+
ON workflow_schedules (workspace_id, created_at);
|
|
19
|
+
`);
|
|
20
|
+
};
|
|
@@ -11,7 +11,19 @@ import { applySchemaVersion15 } from './sqlite-schema-v15.js';
|
|
|
11
11
|
import { applySchemaVersion16 } from './sqlite-schema-v16.js';
|
|
12
12
|
import { applySchemaVersion17 } from './sqlite-schema-v17.js';
|
|
13
13
|
import { applySchemaVersion18 } from './sqlite-schema-v18.js';
|
|
14
|
-
|
|
14
|
+
import { applySchemaVersion19 } from './sqlite-schema-v19.js';
|
|
15
|
+
import { applySchemaVersion20 } from './sqlite-schema-v20.js';
|
|
16
|
+
import { applySchemaVersion21 } from './sqlite-schema-v21.js';
|
|
17
|
+
export const CURRENT_SCHEMA_VERSION = 21;
|
|
18
|
+
// Idempotent column-add helper. SQLite doesn't have `ALTER TABLE … ADD COLUMN
|
|
19
|
+
// IF NOT EXISTS`, so PRAGMA-check first. Safe to call on every init; required
|
|
20
|
+
// for foreign-built DBs where the version-gated migration is skipped.
|
|
21
|
+
const ensureColumn = (db, table, column, definition) => {
|
|
22
|
+
const present = new Set(db.prepare(`PRAGMA table_info(${table})`).all().map((r) => r.name));
|
|
23
|
+
if (!present.has(column)) {
|
|
24
|
+
db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
15
27
|
export const initializeRuntimeDatabase = (db) => {
|
|
16
28
|
db.exec(`
|
|
17
29
|
CREATE TABLE IF NOT EXISTS schema_version (
|
|
@@ -105,7 +117,79 @@ export const initializeRuntimeDatabase = (db) => {
|
|
|
105
117
|
|
|
106
118
|
CREATE INDEX IF NOT EXISTS idx_dispatches_open_by_worker
|
|
107
119
|
ON dispatches (workspace_id, to_agent_id, status, sequence);
|
|
120
|
+
|
|
121
|
+
CREATE TABLE IF NOT EXISTS workflow_runs (
|
|
122
|
+
id TEXT PRIMARY KEY,
|
|
123
|
+
workspace_id TEXT NOT NULL,
|
|
124
|
+
script_path TEXT NOT NULL,
|
|
125
|
+
script_hash TEXT,
|
|
126
|
+
name TEXT NOT NULL,
|
|
127
|
+
status TEXT NOT NULL,
|
|
128
|
+
phase TEXT,
|
|
129
|
+
args TEXT,
|
|
130
|
+
started_at INTEGER NOT NULL,
|
|
131
|
+
finished_at INTEGER,
|
|
132
|
+
error TEXT,
|
|
133
|
+
created_at INTEGER NOT NULL
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
CREATE INDEX IF NOT EXISTS idx_workflow_runs_workspace
|
|
137
|
+
ON workflow_runs (workspace_id, created_at);
|
|
138
|
+
|
|
139
|
+
CREATE TABLE IF NOT EXISTS workflow_schedules (
|
|
140
|
+
id TEXT PRIMARY KEY,
|
|
141
|
+
workspace_id TEXT NOT NULL,
|
|
142
|
+
script_path TEXT NOT NULL,
|
|
143
|
+
cron TEXT NOT NULL,
|
|
144
|
+
args TEXT,
|
|
145
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
146
|
+
last_run_at INTEGER,
|
|
147
|
+
next_run_at INTEGER NOT NULL,
|
|
148
|
+
created_at INTEGER NOT NULL,
|
|
149
|
+
updated_at INTEGER NOT NULL
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
CREATE INDEX IF NOT EXISTS idx_workflow_schedules_due
|
|
153
|
+
ON workflow_schedules (enabled, next_run_at);
|
|
154
|
+
CREATE INDEX IF NOT EXISTS idx_workflow_schedules_workspace
|
|
155
|
+
ON workflow_schedules (workspace_id, created_at);
|
|
108
156
|
`);
|
|
157
|
+
// Idempotent column additions — run on every init regardless of
|
|
158
|
+
// schema_version. The v19 migration adds these columns but is gated on
|
|
159
|
+
// !appliedVersions.has(19); a foreign-built DB whose v19 was a DIFFERENT
|
|
160
|
+
// migration leaves these absent. PRAGMA + ALTER here makes the writes safe.
|
|
161
|
+
ensureColumn(db, 'workers', 'ephemeral', 'INTEGER NOT NULL DEFAULT 0');
|
|
162
|
+
ensureColumn(db, 'workers', 'spawned_by', 'TEXT');
|
|
163
|
+
ensureColumn(db, 'dispatches', 'workflow_run_id', 'TEXT');
|
|
164
|
+
ensureColumn(db, 'dispatches', 'step_index', 'INTEGER');
|
|
165
|
+
// M9 — phase + label on dispatches so the workflow UI can render the
|
|
166
|
+
// phase tree + agent fleet view (mirrors Claude Code's /workflows view).
|
|
167
|
+
ensureColumn(db, 'dispatches', 'phase', 'TEXT');
|
|
168
|
+
ensureColumn(db, 'dispatches', 'label', 'TEXT');
|
|
169
|
+
// M10: capture the workflow script's `return` value so the UI can render
|
|
170
|
+
// a single canonical "Result" panel and the orchestrator notification can
|
|
171
|
+
// include it.
|
|
172
|
+
ensureColumn(db, 'workflow_runs', 'result', 'TEXT');
|
|
173
|
+
// TIER 2 #5: parent_run_id lets the Drawer render nested workflow() calls
|
|
174
|
+
// as a tree (child runs indented under their parent). Without it,
|
|
175
|
+
// nested runs were flat and the user couldn't tell which child
|
|
176
|
+
// belonged to which parent. Null on top-level runs.
|
|
177
|
+
ensureColumn(db, 'workflow_runs', 'parent_run_id', 'TEXT');
|
|
178
|
+
// TIER 2 #3: log() narrator pipeline. Authors call `log('Discovered 47
|
|
179
|
+
// endpoints')` from a workflow script; rows live here and stream to
|
|
180
|
+
// the Drawer's narrator lane + the last few lines are appended to
|
|
181
|
+
// the orchestrator's completion notification.
|
|
182
|
+
db.exec(`
|
|
183
|
+
CREATE TABLE IF NOT EXISTS workflow_run_logs (
|
|
184
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
185
|
+
run_id TEXT NOT NULL,
|
|
186
|
+
ts INTEGER NOT NULL,
|
|
187
|
+
message TEXT NOT NULL
|
|
188
|
+
);
|
|
189
|
+
CREATE INDEX IF NOT EXISTS idx_workflow_run_logs_run
|
|
190
|
+
ON workflow_run_logs (run_id, id);
|
|
191
|
+
`);
|
|
192
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_dispatches_workflow ON dispatches (workflow_run_id, step_index)');
|
|
109
193
|
const versions = db
|
|
110
194
|
.prepare('SELECT version FROM schema_version ORDER BY version ASC')
|
|
111
195
|
.all();
|
|
@@ -203,4 +287,16 @@ export const initializeRuntimeDatabase = (db) => {
|
|
|
203
287
|
applySchemaVersion18(db);
|
|
204
288
|
db.prepare('INSERT INTO schema_version (version, applied_at) VALUES (?, ?)').run(18, Date.now());
|
|
205
289
|
}
|
|
290
|
+
if (!appliedVersions.has(19)) {
|
|
291
|
+
applySchemaVersion19(db);
|
|
292
|
+
db.prepare('INSERT INTO schema_version (version, applied_at) VALUES (?, ?)').run(19, Date.now());
|
|
293
|
+
}
|
|
294
|
+
if (!appliedVersions.has(20)) {
|
|
295
|
+
applySchemaVersion20(db);
|
|
296
|
+
db.prepare('INSERT INTO schema_version (version, applied_at) VALUES (?, ?)').run(20, Date.now());
|
|
297
|
+
}
|
|
298
|
+
if (!appliedVersions.has(21)) {
|
|
299
|
+
applySchemaVersion21(db);
|
|
300
|
+
db.prepare('INSERT INTO schema_version (version, applied_at) VALUES (?, ?)').run(21, Date.now());
|
|
301
|
+
}
|
|
206
302
|
};
|
|
@@ -3,3 +3,18 @@ export declare const createStartupCommandLaunch: (startupCommand: string, env?:
|
|
|
3
3
|
command: string;
|
|
4
4
|
};
|
|
5
5
|
export declare const getStartupCommandExecutable: (startupCommand: string) => string | null;
|
|
6
|
+
/**
|
|
7
|
+
* Reduce an executable token to its canonical brand id for preset lookup
|
|
8
|
+
* and CLI-behavior dispatch. Splits on both '\\' and '/' so Windows paths
|
|
9
|
+
* normalize even when the host runs `node:path.posix` (macOS test runners,
|
|
10
|
+
* mixed-slash paths from npm-installed shims).
|
|
11
|
+
*
|
|
12
|
+
* Examples (return value in parens):
|
|
13
|
+
* 'claude' → 'claude'
|
|
14
|
+
* 'claude.CMD' → 'claude'
|
|
15
|
+
* 'C:\\Program Files\\nodejs\\claude.cmd' → 'claude'
|
|
16
|
+
* '/usr/local/bin/codex' → 'codex'
|
|
17
|
+
* 'C:/Users/me/opencode.CMD' → 'opencode'
|
|
18
|
+
* 'my-custom-runner.bat' → 'my-custom-runner'
|
|
19
|
+
*/
|
|
20
|
+
export declare const normalizeExecutableToken: (token: string | null | undefined) => string | null;
|
|
@@ -32,6 +32,37 @@ export const getStartupCommandExecutable = (startupCommand) => {
|
|
|
32
32
|
const command = startupCommand.trim();
|
|
33
33
|
if (!command)
|
|
34
34
|
return null;
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
// Match alternates, in order:
|
|
36
|
+
// 1. Double-quoted token (may contain spaces, backslashes, slashes)
|
|
37
|
+
// 2. Single-quoted token
|
|
38
|
+
// 3. Bare token: no whitespace, no embedded quotes
|
|
39
|
+
// An unbalanced opening quote falls through all three and returns null.
|
|
40
|
+
// The previous regex used `[^'"\s]+` for the captured run, which forbade
|
|
41
|
+
// spaces and therefore returned null for `"C:\Program Files\…claude.cmd"`
|
|
42
|
+
// — the standard Windows install layout. See the parser tests for the
|
|
43
|
+
// contract; brand identification belongs in agent-launch-resolver.
|
|
44
|
+
const match = /^"([^"]+)"|^'([^']+)'|^([^\s'"]+)/.exec(command);
|
|
45
|
+
return match?.[1] ?? match?.[2] ?? match?.[3] ?? null;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Reduce an executable token to its canonical brand id for preset lookup
|
|
49
|
+
* and CLI-behavior dispatch. Splits on both '\\' and '/' so Windows paths
|
|
50
|
+
* normalize even when the host runs `node:path.posix` (macOS test runners,
|
|
51
|
+
* mixed-slash paths from npm-installed shims).
|
|
52
|
+
*
|
|
53
|
+
* Examples (return value in parens):
|
|
54
|
+
* 'claude' → 'claude'
|
|
55
|
+
* 'claude.CMD' → 'claude'
|
|
56
|
+
* 'C:\\Program Files\\nodejs\\claude.cmd' → 'claude'
|
|
57
|
+
* '/usr/local/bin/codex' → 'codex'
|
|
58
|
+
* 'C:/Users/me/opencode.CMD' → 'opencode'
|
|
59
|
+
* 'my-custom-runner.bat' → 'my-custom-runner'
|
|
60
|
+
*/
|
|
61
|
+
export const normalizeExecutableToken = (token) => {
|
|
62
|
+
if (!token)
|
|
63
|
+
return null;
|
|
64
|
+
const lastSegment = token.replace(/^.*[\\/]/, '');
|
|
65
|
+
if (!lastSegment)
|
|
66
|
+
return null;
|
|
67
|
+
return lastSegment.toLowerCase().replace(/\.(cmd|bat|exe|ps1)$/u, '');
|
|
37
68
|
};
|
|
@@ -1 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wraps a system-injected message body in an out-of-band XML envelope so the
|
|
3
|
+
* agent attends to it as system content rather than mistaking it for user
|
|
4
|
+
* input or its own output. `<hive-system-reminder>` is reserved for the short
|
|
5
|
+
* re-anchoring action menu appended at a message tail; this `<hive-system-message>`
|
|
6
|
+
* tag carries larger injected bodies (crash-recovery / restart env-sync).
|
|
7
|
+
*/
|
|
1
8
|
export declare const wrapSystemMessage: (content: string) => string;
|
|
@@ -1 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Wraps a system-injected message body in an out-of-band XML envelope so the
|
|
3
|
+
* agent attends to it as system content rather than mistaking it for user
|
|
4
|
+
* input or its own output. `<hive-system-reminder>` is reserved for the short
|
|
5
|
+
* re-anchoring action menu appended at a message tail; this `<hive-system-message>`
|
|
6
|
+
* tag carries larger injected bodies (crash-recovery / restart env-sync).
|
|
7
|
+
*/
|
|
8
|
+
export const wrapSystemMessage = (content) => `<hive-system-message>\n${content}\n</hive-system-message>`;
|
|
@@ -1,8 +1,46 @@
|
|
|
1
|
+
import { type ChokidarOptions } from 'chokidar';
|
|
2
|
+
import type { WorkflowCliPolicy } from './workflow-cli-policy.js';
|
|
3
|
+
/**
|
|
4
|
+
* Watcher configuration. The atomic-save option matters on Windows: VS
|
|
5
|
+
* Code, Cursor, Notepad++, and the editor inside Hive itself all save
|
|
6
|
+
* by writing a temp file and renaming it over the target. On
|
|
7
|
+
* ReadDirectoryChangesW (Windows' native fs-event backend) that rename
|
|
8
|
+
* invalidates the file-handle the chokidar watcher held for the
|
|
9
|
+
* single-file path, and subsequent edits would emit no events at all.
|
|
10
|
+
*
|
|
11
|
+
* `atomic: 100` tells chokidar to correlate an unlink+add pair within
|
|
12
|
+
* a 100ms window as a single `change` event — collapsing the rename
|
|
13
|
+
* into one logical "the file was modified" notification and rebinding
|
|
14
|
+
* the underlying watch handle. Without it the tasks panel goes deaf
|
|
15
|
+
* to changes the user makes outside the app after the first save.
|
|
16
|
+
*
|
|
17
|
+
* We intentionally do NOT set `awaitWriteFinish` here. It would help
|
|
18
|
+
* even more on Windows (waits for the file size to stabilise before
|
|
19
|
+
* emitting), but it also throttles every emission by `stabilityThreshold`
|
|
20
|
+
* — clashing with continuous-write workflows (a worker streaming
|
|
21
|
+
* updates into tasks.md every 100ms would never see an emit). The
|
|
22
|
+
* `atomic` option alone covers atomic-save; if a future workspace
|
|
23
|
+
* needs stronger settling behaviour we can promote it then.
|
|
24
|
+
*
|
|
25
|
+
* Exported so the configuration is testable in isolation.
|
|
26
|
+
*/
|
|
27
|
+
export declare const TASKS_WATCHER_OPTIONS: ChokidarOptions;
|
|
28
|
+
export declare const buildTasksWatcherOptions: (workspacePath: string, platform?: NodeJS.Platform) => ChokidarOptions;
|
|
1
29
|
export interface TasksFileWatcher {
|
|
2
30
|
close: () => Promise<void>;
|
|
3
31
|
start: (workspaceId: string, workspacePath: string) => Promise<void>;
|
|
4
32
|
stop: (workspaceId: string) => Promise<void>;
|
|
5
33
|
}
|
|
6
|
-
export declare const createTasksFileWatcher: ({ onTasksUpdated, }: {
|
|
34
|
+
export declare const createTasksFileWatcher: ({ onTasksUpdated, getWorkflowCliPolicy, getWorkflowsEnabled, getAutostaffEnabled, }: {
|
|
7
35
|
onTasksUpdated: (workspaceId: string, content: string) => void;
|
|
36
|
+
/** Lets the freshly-written `.hive/PROTOCOL.md` state the workspace's
|
|
37
|
+
* workflow CLI default + allowlist. Optional: omitted → the doc renders
|
|
38
|
+
* the unrestricted default. */
|
|
39
|
+
getWorkflowCliPolicy?: () => WorkflowCliPolicy;
|
|
40
|
+
/** Whether the experimental workflow feature is on. Off → PROTOCOL.md omits
|
|
41
|
+
* the workflow DSL + `team workflow` commands entirely. Defaults to off. */
|
|
42
|
+
getWorkflowsEnabled?: () => boolean;
|
|
43
|
+
/** Whether the experimental auto-staff feature is on (default on). Off →
|
|
44
|
+
* PROTOCOL.md omits the team-sizing rule. */
|
|
45
|
+
getAutostaffEnabled?: () => boolean;
|
|
8
46
|
}) => TasksFileWatcher;
|
|
@@ -1,11 +1,79 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { basename, dirname, normalize } from 'node:path';
|
|
3
4
|
import chokidar from 'chokidar';
|
|
4
|
-
import { ensureProtocolFile, ensureTasksFile, getTasksFilePath } from './tasks-file.js';
|
|
5
|
+
import { ensureProtocolFile, ensureTasksFile, getTasksFilePath, TASKS_FILE_NAME, } from './tasks-file.js';
|
|
5
6
|
const DEBOUNCE_MS = 100;
|
|
6
|
-
|
|
7
|
+
const WATCHER_RETRY_MS = 5000;
|
|
8
|
+
const WATCHER_CLOSE_TIMEOUT_MS = 2000;
|
|
9
|
+
const WATCHER_READY_TIMEOUT_MS = 15000;
|
|
10
|
+
/**
|
|
11
|
+
* Watcher configuration. The atomic-save option matters on Windows: VS
|
|
12
|
+
* Code, Cursor, Notepad++, and the editor inside Hive itself all save
|
|
13
|
+
* by writing a temp file and renaming it over the target. On
|
|
14
|
+
* ReadDirectoryChangesW (Windows' native fs-event backend) that rename
|
|
15
|
+
* invalidates the file-handle the chokidar watcher held for the
|
|
16
|
+
* single-file path, and subsequent edits would emit no events at all.
|
|
17
|
+
*
|
|
18
|
+
* `atomic: 100` tells chokidar to correlate an unlink+add pair within
|
|
19
|
+
* a 100ms window as a single `change` event — collapsing the rename
|
|
20
|
+
* into one logical "the file was modified" notification and rebinding
|
|
21
|
+
* the underlying watch handle. Without it the tasks panel goes deaf
|
|
22
|
+
* to changes the user makes outside the app after the first save.
|
|
23
|
+
*
|
|
24
|
+
* We intentionally do NOT set `awaitWriteFinish` here. It would help
|
|
25
|
+
* even more on Windows (waits for the file size to stabilise before
|
|
26
|
+
* emitting), but it also throttles every emission by `stabilityThreshold`
|
|
27
|
+
* — clashing with continuous-write workflows (a worker streaming
|
|
28
|
+
* updates into tasks.md every 100ms would never see an emit). The
|
|
29
|
+
* `atomic` option alone covers atomic-save; if a future workspace
|
|
30
|
+
* needs stronger settling behaviour we can promote it then.
|
|
31
|
+
*
|
|
32
|
+
* Exported so the configuration is testable in isolation.
|
|
33
|
+
*/
|
|
34
|
+
export const TASKS_WATCHER_OPTIONS = {
|
|
35
|
+
atomic: 100,
|
|
36
|
+
ignoreInitial: true,
|
|
37
|
+
};
|
|
38
|
+
const isWindowsUncPath = (path, platform = process.platform) => platform === 'win32' && /^[\\/]{2}[^\\/]+[\\/]+[^\\/]+/u.test(path);
|
|
39
|
+
export const buildTasksWatcherOptions = (workspacePath, platform = process.platform) => ({
|
|
40
|
+
...TASKS_WATCHER_OPTIONS,
|
|
41
|
+
...(platform === 'win32' || isWindowsUncPath(workspacePath, platform)
|
|
42
|
+
? { interval: 500, usePolling: true }
|
|
43
|
+
: {}),
|
|
44
|
+
});
|
|
45
|
+
const closeWatcherWithTimeout = async (watcher) => {
|
|
46
|
+
if (!watcher)
|
|
47
|
+
return;
|
|
48
|
+
let timer;
|
|
49
|
+
try {
|
|
50
|
+
await Promise.race([
|
|
51
|
+
watcher.close(),
|
|
52
|
+
new Promise((resolve) => {
|
|
53
|
+
timer = setTimeout(resolve, WATCHER_CLOSE_TIMEOUT_MS);
|
|
54
|
+
timer.unref?.();
|
|
55
|
+
}),
|
|
56
|
+
]);
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
if (timer)
|
|
60
|
+
clearTimeout(timer);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
const isTasksFileEvent = (tasksPath, changedPath) => {
|
|
64
|
+
if (!changedPath)
|
|
65
|
+
return true;
|
|
66
|
+
const text = Buffer.isBuffer(changedPath) ? changedPath.toString() : changedPath;
|
|
67
|
+
return normalize(text) === normalize(tasksPath) || basename(text) === TASKS_FILE_NAME;
|
|
68
|
+
};
|
|
69
|
+
export const createTasksFileWatcher = ({ onTasksUpdated, getWorkflowCliPolicy, getWorkflowsEnabled, getAutostaffEnabled, }) => {
|
|
7
70
|
const watchers = new Map();
|
|
8
71
|
const timers = new Map();
|
|
72
|
+
const retryTimers = new Map();
|
|
73
|
+
let closed = false;
|
|
74
|
+
const logWatcherError = (workspaceId, error) => {
|
|
75
|
+
console.error(`[hive] tasks watcher error for workspace ${workspaceId}`, error);
|
|
76
|
+
};
|
|
9
77
|
const clearTimer = (workspaceId) => {
|
|
10
78
|
const timer = timers.get(workspaceId);
|
|
11
79
|
if (!timer)
|
|
@@ -13,6 +81,13 @@ export const createTasksFileWatcher = ({ onTasksUpdated, }) => {
|
|
|
13
81
|
clearTimeout(timer);
|
|
14
82
|
timers.delete(workspaceId);
|
|
15
83
|
};
|
|
84
|
+
const clearRetryTimer = (workspaceId) => {
|
|
85
|
+
const timer = retryTimers.get(workspaceId);
|
|
86
|
+
if (!timer)
|
|
87
|
+
return;
|
|
88
|
+
clearTimeout(timer);
|
|
89
|
+
retryTimers.delete(workspaceId);
|
|
90
|
+
};
|
|
16
91
|
const emitCurrentContent = async (workspaceId, workspacePath) => {
|
|
17
92
|
const tasksPath = getTasksFilePath(workspacePath);
|
|
18
93
|
try {
|
|
@@ -20,41 +95,96 @@ export const createTasksFileWatcher = ({ onTasksUpdated, }) => {
|
|
|
20
95
|
onTasksUpdated(workspaceId, content);
|
|
21
96
|
}
|
|
22
97
|
catch (error) {
|
|
23
|
-
if (error.code !== 'ENOENT')
|
|
24
|
-
|
|
98
|
+
if (error.code !== 'ENOENT') {
|
|
99
|
+
logWatcherError(workspaceId, error);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
25
102
|
onTasksUpdated(workspaceId, '');
|
|
26
103
|
}
|
|
27
104
|
};
|
|
28
105
|
const stop = async (workspaceId) => {
|
|
29
106
|
clearTimer(workspaceId);
|
|
107
|
+
clearRetryTimer(workspaceId);
|
|
30
108
|
const watcher = watchers.get(workspaceId);
|
|
31
109
|
watchers.delete(workspaceId);
|
|
32
|
-
await watcher
|
|
110
|
+
await closeWatcherWithTimeout(watcher);
|
|
111
|
+
};
|
|
112
|
+
const scheduleRetry = (workspaceId, workspacePath) => {
|
|
113
|
+
if (closed || retryTimers.has(workspaceId))
|
|
114
|
+
return;
|
|
115
|
+
const timer = setTimeout(() => {
|
|
116
|
+
retryTimers.delete(workspaceId);
|
|
117
|
+
void start(workspaceId, workspacePath).catch((error) => logWatcherError(workspaceId, error));
|
|
118
|
+
}, WATCHER_RETRY_MS);
|
|
119
|
+
timer.unref?.();
|
|
120
|
+
retryTimers.set(workspaceId, timer);
|
|
121
|
+
};
|
|
122
|
+
const waitForReady = async (watcher) => await new Promise((resolve, reject) => {
|
|
123
|
+
const cleanup = () => {
|
|
124
|
+
watcher.off('ready', handleReady);
|
|
125
|
+
watcher.off('error', handleError);
|
|
126
|
+
clearTimeout(timeout);
|
|
127
|
+
};
|
|
128
|
+
const handleReady = () => {
|
|
129
|
+
cleanup();
|
|
130
|
+
resolve();
|
|
131
|
+
};
|
|
132
|
+
const handleError = (error) => {
|
|
133
|
+
cleanup();
|
|
134
|
+
reject(error);
|
|
135
|
+
};
|
|
136
|
+
const timeout = setTimeout(() => {
|
|
137
|
+
cleanup();
|
|
138
|
+
reject(new Error(`Timed out waiting for tasks watcher ready after ${WATCHER_READY_TIMEOUT_MS}ms`));
|
|
139
|
+
}, WATCHER_READY_TIMEOUT_MS);
|
|
140
|
+
timeout.unref?.();
|
|
141
|
+
watcher.once('ready', handleReady);
|
|
142
|
+
watcher.once('error', handleError);
|
|
143
|
+
});
|
|
144
|
+
const start = async (workspaceId, workspacePath) => {
|
|
145
|
+
closed = false;
|
|
146
|
+
await stop(workspaceId);
|
|
147
|
+
ensureTasksFile(workspacePath);
|
|
148
|
+
ensureProtocolFile(workspacePath, getWorkflowCliPolicy?.(), getWorkflowsEnabled?.() ?? false, getAutostaffEnabled?.() ?? false);
|
|
149
|
+
const tasksPath = getTasksFilePath(workspacePath);
|
|
150
|
+
const watcher = chokidar.watch(dirname(tasksPath), buildTasksWatcherOptions(workspacePath));
|
|
151
|
+
const scheduleEmit = (changedPath) => {
|
|
152
|
+
if (!isTasksFileEvent(tasksPath, changedPath))
|
|
153
|
+
return;
|
|
154
|
+
clearTimer(workspaceId);
|
|
155
|
+
timers.set(workspaceId, setTimeout(() => {
|
|
156
|
+
timers.delete(workspaceId);
|
|
157
|
+
void emitCurrentContent(workspaceId, workspacePath);
|
|
158
|
+
}, DEBOUNCE_MS));
|
|
159
|
+
};
|
|
160
|
+
watcher.on('add', scheduleEmit);
|
|
161
|
+
watcher.on('change', scheduleEmit);
|
|
162
|
+
watcher.on('unlink', scheduleEmit);
|
|
163
|
+
watcher.on('error', (error) => {
|
|
164
|
+
logWatcherError(workspaceId, error);
|
|
165
|
+
void stop(workspaceId)
|
|
166
|
+
.catch((closeError) => logWatcherError(workspaceId, closeError))
|
|
167
|
+
.finally(() => scheduleRetry(workspaceId, workspacePath));
|
|
168
|
+
});
|
|
169
|
+
watchers.set(workspaceId, watcher);
|
|
170
|
+
try {
|
|
171
|
+
await waitForReady(watcher);
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
watchers.delete(workspaceId);
|
|
175
|
+
await closeWatcherWithTimeout(watcher);
|
|
176
|
+
scheduleRetry(workspaceId, workspacePath);
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
33
179
|
};
|
|
34
180
|
return {
|
|
35
181
|
close: async () => {
|
|
182
|
+
closed = true;
|
|
183
|
+
for (const workspaceId of retryTimers.keys())
|
|
184
|
+
clearRetryTimer(workspaceId);
|
|
36
185
|
await Promise.all(Array.from(watchers.keys(), (workspaceId) => stop(workspaceId)));
|
|
37
186
|
},
|
|
38
|
-
start
|
|
39
|
-
await stop(workspaceId);
|
|
40
|
-
ensureTasksFile(workspacePath);
|
|
41
|
-
ensureProtocolFile(workspacePath);
|
|
42
|
-
const watcher = chokidar.watch(getTasksFilePath(workspacePath), {
|
|
43
|
-
ignoreInitial: true,
|
|
44
|
-
});
|
|
45
|
-
const scheduleEmit = () => {
|
|
46
|
-
clearTimer(workspaceId);
|
|
47
|
-
timers.set(workspaceId, setTimeout(() => {
|
|
48
|
-
timers.delete(workspaceId);
|
|
49
|
-
void emitCurrentContent(workspaceId, workspacePath);
|
|
50
|
-
}, DEBOUNCE_MS));
|
|
51
|
-
};
|
|
52
|
-
watcher.on('add', scheduleEmit);
|
|
53
|
-
watcher.on('change', scheduleEmit);
|
|
54
|
-
watcher.on('unlink', scheduleEmit);
|
|
55
|
-
watchers.set(workspaceId, watcher);
|
|
56
|
-
await new Promise((resolve) => watcher.once('ready', () => resolve()));
|
|
57
|
-
},
|
|
187
|
+
start,
|
|
58
188
|
stop,
|
|
59
189
|
};
|
|
60
190
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { WorkflowCliPolicy } from './workflow-cli-policy.js';
|
|
1
2
|
interface TasksFileService {
|
|
2
3
|
readTasks: (workspacePath: string) => string;
|
|
3
4
|
writeTasks: (workspacePath: string, content: string) => void;
|
|
@@ -16,6 +17,6 @@ export declare const ensureTasksFile: (workspacePath: string) => string;
|
|
|
16
17
|
* on every workspace open means a Hive version bump that changes the rules
|
|
17
18
|
* propagates without manual intervention.
|
|
18
19
|
*/
|
|
19
|
-
export declare const ensureProtocolFile: (workspacePath: string) => string;
|
|
20
|
+
export declare const ensureProtocolFile: (workspacePath: string, cliPolicy?: WorkflowCliPolicy, workflowsEnabled?: boolean, autostaffEnabled?: boolean) => string;
|
|
20
21
|
export declare const createTasksFileService: () => TasksFileService;
|
|
21
22
|
export type { TasksFileService };
|