@versatly/workgraph 0.3.0 → 1.1.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/README.md +31 -2
- package/dist/chunk-OTSECVE5.js +6045 -0
- package/dist/chunk-TOFSHG5S.js +3107 -0
- package/dist/cli.js +794 -12
- package/dist/index.d.ts +847 -11
- package/dist/index.js +27 -3
- package/dist/mcp-server.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-65ZMX2WM.js +0 -2846
- package/dist/chunk-E3QU5Y53.js +0 -1062
package/dist/cli.js
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
|
+
agent_exports,
|
|
3
|
+
autonomy_daemon_exports,
|
|
2
4
|
bases_exports,
|
|
3
5
|
board_exports,
|
|
4
6
|
clawdapus_exports,
|
|
5
7
|
command_center_exports,
|
|
8
|
+
diagnostics_exports,
|
|
6
9
|
integration_exports,
|
|
10
|
+
lens_exports,
|
|
7
11
|
onboard_exports,
|
|
8
12
|
search_qmd_adapter_exports,
|
|
9
13
|
skill_exports,
|
|
10
14
|
trigger_exports,
|
|
11
15
|
workspace_exports
|
|
12
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-TOFSHG5S.js";
|
|
13
17
|
import {
|
|
18
|
+
autonomy_exports,
|
|
14
19
|
dispatch_exports,
|
|
20
|
+
gate_exports,
|
|
15
21
|
graph_exports,
|
|
16
22
|
ledger_exports,
|
|
17
23
|
mcp_server_exports,
|
|
@@ -20,11 +26,14 @@ import {
|
|
|
20
26
|
query_exports,
|
|
21
27
|
registry_exports,
|
|
22
28
|
store_exports,
|
|
23
|
-
|
|
24
|
-
|
|
29
|
+
thread_audit_exports,
|
|
30
|
+
thread_exports,
|
|
31
|
+
trigger_engine_exports
|
|
32
|
+
} from "./chunk-OTSECVE5.js";
|
|
25
33
|
|
|
26
34
|
// src/cli.ts
|
|
27
35
|
import fs from "fs";
|
|
36
|
+
import os from "os";
|
|
28
37
|
import path from "path";
|
|
29
38
|
import { Command } from "commander";
|
|
30
39
|
var DEFAULT_ACTOR = process.env.WORKGRAPH_AGENT || process.env.USER || "anonymous";
|
|
@@ -46,7 +55,7 @@ addWorkspaceOption(
|
|
|
46
55
|
(targetPath, opts) => runCommand(
|
|
47
56
|
opts,
|
|
48
57
|
() => {
|
|
49
|
-
const workspacePath =
|
|
58
|
+
const workspacePath = resolveInitTargetPath(targetPath, opts);
|
|
50
59
|
const result = workspace_exports.initWorkspace(workspacePath, {
|
|
51
60
|
name: opts.name,
|
|
52
61
|
createTypeDirs: opts.typeDirs,
|
|
@@ -168,13 +177,17 @@ addWorkspaceOption(
|
|
|
168
177
|
)
|
|
169
178
|
);
|
|
170
179
|
addWorkspaceOption(
|
|
171
|
-
threadCmd.command("claim <threadPath>").description("Claim a thread for this agent").option("-a, --actor <name>", "Agent name", DEFAULT_ACTOR).option("--json", "Emit structured JSON output")
|
|
180
|
+
threadCmd.command("claim <threadPath>").description("Claim a thread for this agent").option("-a, --actor <name>", "Agent name", DEFAULT_ACTOR).option("--lease-ttl-minutes <n>", "Claim lease TTL in minutes", "30").option("--json", "Emit structured JSON output")
|
|
172
181
|
).action(
|
|
173
182
|
(threadPath, opts) => runCommand(
|
|
174
183
|
opts,
|
|
175
184
|
() => {
|
|
176
185
|
const workspacePath = resolveWorkspacePath(opts);
|
|
177
|
-
return {
|
|
186
|
+
return {
|
|
187
|
+
thread: thread_exports.claim(workspacePath, threadPath, opts.actor, {
|
|
188
|
+
leaseTtlMinutes: Number.parseFloat(String(opts.leaseTtlMinutes))
|
|
189
|
+
})
|
|
190
|
+
};
|
|
178
191
|
},
|
|
179
192
|
(result) => [`Claimed: ${result.thread.path}`, `Owner: ${String(result.thread.fields.owner)}`]
|
|
180
193
|
)
|
|
@@ -203,6 +216,18 @@ addWorkspaceOption(
|
|
|
203
216
|
(result) => [`Done: ${result.thread.path}`]
|
|
204
217
|
)
|
|
205
218
|
);
|
|
219
|
+
addWorkspaceOption(
|
|
220
|
+
threadCmd.command("reopen <threadPath>").description("Reopen a done/cancelled thread via compensating ledger op").option("-a, --actor <name>", "Agent name", DEFAULT_ACTOR).option("--reason <reason>", "Why the thread is being reopened").option("--json", "Emit structured JSON output")
|
|
221
|
+
).action(
|
|
222
|
+
(threadPath, opts) => runCommand(
|
|
223
|
+
opts,
|
|
224
|
+
() => {
|
|
225
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
226
|
+
return { thread: thread_exports.reopen(workspacePath, threadPath, opts.actor, opts.reason) };
|
|
227
|
+
},
|
|
228
|
+
(result) => [`Reopened: ${result.thread.path}`, `Status: ${String(result.thread.fields.status)}`]
|
|
229
|
+
)
|
|
230
|
+
);
|
|
206
231
|
addWorkspaceOption(
|
|
207
232
|
threadCmd.command("block <threadPath>").description("Mark a thread blocked").requiredOption("-b, --blocked-by <dep>", "Dependency blocking this thread").option("-a, --actor <name>", "Agent name", DEFAULT_ACTOR).option("--reason <reason>", "Why it is blocked").option("--json", "Emit structured JSON output")
|
|
208
233
|
).action(
|
|
@@ -229,6 +254,63 @@ addWorkspaceOption(
|
|
|
229
254
|
(result) => [`Unblocked: ${result.thread.path}`]
|
|
230
255
|
)
|
|
231
256
|
);
|
|
257
|
+
addWorkspaceOption(
|
|
258
|
+
threadCmd.command("heartbeat [threadPath]").description("Refresh thread claim lease heartbeat (one thread or all active claims for actor)").option("-a, --actor <name>", "Agent name", DEFAULT_ACTOR).option("--ttl-minutes <n>", "Lease TTL in minutes", "30").option("--json", "Emit structured JSON output")
|
|
259
|
+
).action(
|
|
260
|
+
(threadPath, opts) => runCommand(
|
|
261
|
+
opts,
|
|
262
|
+
() => {
|
|
263
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
264
|
+
return thread_exports.heartbeatClaim(
|
|
265
|
+
workspacePath,
|
|
266
|
+
opts.actor,
|
|
267
|
+
threadPath,
|
|
268
|
+
{
|
|
269
|
+
ttlMinutes: Number.parseFloat(String(opts.ttlMinutes))
|
|
270
|
+
}
|
|
271
|
+
);
|
|
272
|
+
},
|
|
273
|
+
(result) => [
|
|
274
|
+
`Heartbeat actor: ${result.actor}`,
|
|
275
|
+
`Touched leases: ${result.touched.length}`,
|
|
276
|
+
...result.touched.length > 0 ? result.touched.map((entry) => `- ${entry.threadPath} expires=${entry.expiresAt}`) : [],
|
|
277
|
+
...result.skipped.length > 0 ? result.skipped.map((entry) => `SKIP ${entry.threadPath}: ${entry.reason}`) : []
|
|
278
|
+
]
|
|
279
|
+
)
|
|
280
|
+
);
|
|
281
|
+
addWorkspaceOption(
|
|
282
|
+
threadCmd.command("reap-stale").description("Reopen/release stale claimed threads whose leases expired").option("-a, --actor <name>", "Agent name", DEFAULT_ACTOR).option("--limit <n>", "Max stale leases to reap this run").option("--json", "Emit structured JSON output")
|
|
283
|
+
).action(
|
|
284
|
+
(opts) => runCommand(
|
|
285
|
+
opts,
|
|
286
|
+
() => {
|
|
287
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
288
|
+
return thread_exports.reapStaleClaims(workspacePath, opts.actor, {
|
|
289
|
+
limit: opts.limit ? Number.parseInt(String(opts.limit), 10) : void 0
|
|
290
|
+
});
|
|
291
|
+
},
|
|
292
|
+
(result) => [
|
|
293
|
+
`Reaper actor: ${result.actor}`,
|
|
294
|
+
`Scanned stale leases: ${result.scanned}`,
|
|
295
|
+
`Reaped: ${result.reaped.length}`,
|
|
296
|
+
...result.reaped.length > 0 ? result.reaped.map((entry) => `- ${entry.threadPath} (prev=${entry.previousOwner})`) : [],
|
|
297
|
+
...result.skipped.length > 0 ? result.skipped.map((entry) => `SKIP ${entry.threadPath}: ${entry.reason}`) : []
|
|
298
|
+
]
|
|
299
|
+
)
|
|
300
|
+
);
|
|
301
|
+
addWorkspaceOption(
|
|
302
|
+
threadCmd.command("leases").description("List claim leases and staleness state").option("--json", "Emit structured JSON output")
|
|
303
|
+
).action(
|
|
304
|
+
(opts) => runCommand(
|
|
305
|
+
opts,
|
|
306
|
+
() => {
|
|
307
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
308
|
+
const leases = thread_exports.listClaimLeaseStatus(workspacePath);
|
|
309
|
+
return { leases, count: leases.length };
|
|
310
|
+
},
|
|
311
|
+
(result) => result.leases.map((lease) => `${lease.stale ? "STALE" : "LIVE"} ${lease.owner} -> ${lease.target} expires=${lease.expiresAt}`)
|
|
312
|
+
)
|
|
313
|
+
);
|
|
232
314
|
addWorkspaceOption(
|
|
233
315
|
threadCmd.command("decompose <threadPath>").description("Break a thread into sub-threads").requiredOption("--sub <specs...>", 'Sub-thread specs as "title|goal"').option("-a, --actor <name>", "Agent name", DEFAULT_ACTOR).option("--json", "Emit structured JSON output")
|
|
234
316
|
).action(
|
|
@@ -246,6 +328,58 @@ addWorkspaceOption(
|
|
|
246
328
|
(result) => [`Created ${result.children.length} sub-thread(s).`]
|
|
247
329
|
)
|
|
248
330
|
);
|
|
331
|
+
var agentCmd = program.command("agent").description("Track agent presence heartbeats");
|
|
332
|
+
addWorkspaceOption(
|
|
333
|
+
agentCmd.command("heartbeat <name>").description("Create/update an agent presence heartbeat").option("-a, --actor <name>", "Actor writing the heartbeat", DEFAULT_ACTOR).option("--status <status>", "online | busy | offline", "online").option("--current-task <threadRef>", "Current task/thread slug for this agent").option("--capabilities <items>", "Comma-separated capability tags").option("--json", "Emit structured JSON output")
|
|
334
|
+
).action(
|
|
335
|
+
(name, opts) => runCommand(
|
|
336
|
+
opts,
|
|
337
|
+
() => {
|
|
338
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
339
|
+
return {
|
|
340
|
+
presence: agent_exports.heartbeat(workspacePath, name, {
|
|
341
|
+
actor: opts.actor,
|
|
342
|
+
status: normalizeAgentPresenceStatus(opts.status),
|
|
343
|
+
currentTask: opts.currentTask,
|
|
344
|
+
capabilities: csv(opts.capabilities)
|
|
345
|
+
})
|
|
346
|
+
};
|
|
347
|
+
},
|
|
348
|
+
(result) => [
|
|
349
|
+
`Heartbeat: ${String(result.presence.fields.name)} [${String(result.presence.fields.status)}]`,
|
|
350
|
+
`Last seen: ${String(result.presence.fields.last_seen)}`,
|
|
351
|
+
`Current task: ${String(result.presence.fields.current_task ?? "none")}`
|
|
352
|
+
]
|
|
353
|
+
)
|
|
354
|
+
);
|
|
355
|
+
addWorkspaceOption(
|
|
356
|
+
agentCmd.command("list").description("List known agent presence entries").option("--json", "Emit structured JSON output")
|
|
357
|
+
).action(
|
|
358
|
+
(opts) => runCommand(
|
|
359
|
+
opts,
|
|
360
|
+
() => {
|
|
361
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
362
|
+
const agents = agent_exports.list(workspacePath);
|
|
363
|
+
return {
|
|
364
|
+
agents,
|
|
365
|
+
count: agents.length
|
|
366
|
+
};
|
|
367
|
+
},
|
|
368
|
+
(result) => {
|
|
369
|
+
if (result.agents.length === 0) return ["No agent presence entries found."];
|
|
370
|
+
return [
|
|
371
|
+
...result.agents.map((entry) => {
|
|
372
|
+
const name = String(entry.fields.name ?? entry.path);
|
|
373
|
+
const status = String(entry.fields.status ?? "unknown");
|
|
374
|
+
const task = String(entry.fields.current_task ?? "none");
|
|
375
|
+
const lastSeen = String(entry.fields.last_seen ?? "unknown");
|
|
376
|
+
return `${name} [${status}] task=${task} last_seen=${lastSeen}`;
|
|
377
|
+
}),
|
|
378
|
+
`${result.count} agent(s)`
|
|
379
|
+
];
|
|
380
|
+
}
|
|
381
|
+
)
|
|
382
|
+
);
|
|
249
383
|
var primitiveCmd = program.command("primitive").description("Manage primitive type definitions and instances");
|
|
250
384
|
addWorkspaceOption(
|
|
251
385
|
primitiveCmd.command("define <name>").description("Define a new primitive type").requiredOption("-d, --description <desc>", "Type description").option("-a, --actor <name>", "Agent name", DEFAULT_ACTOR).option("--fields <specs...>", 'Field definitions as "name:type"').option("--dir <directory>", "Storage directory override").option("--json", "Emit structured JSON output")
|
|
@@ -283,6 +417,8 @@ addWorkspaceOption(
|
|
|
283
417
|
]
|
|
284
418
|
)
|
|
285
419
|
);
|
|
420
|
+
registerPrimitiveSchemaCommand("schema", "Show supported fields for a primitive type");
|
|
421
|
+
registerPrimitiveSchemaCommand("fields", "Alias for schema");
|
|
286
422
|
var basesCmd = program.command("bases").description("Generate Obsidian .base files from primitive-registry.yaml");
|
|
287
423
|
addWorkspaceOption(
|
|
288
424
|
basesCmd.command("sync-registry").description("Sync .workgraph/primitive-registry.yaml from active registry").option("--json", "Emit structured JSON output")
|
|
@@ -337,6 +473,46 @@ addWorkspaceOption(
|
|
|
337
473
|
(result) => result.types.map((t) => `${t.name} (${t.directory}/) ${t.builtIn ? "[built-in]" : ""}`)
|
|
338
474
|
)
|
|
339
475
|
);
|
|
476
|
+
function registerPrimitiveSchemaCommand(commandName, description) {
|
|
477
|
+
addWorkspaceOption(
|
|
478
|
+
primitiveCmd.command(`${commandName} <typeName>`).description(description).option("--json", "Emit structured JSON output")
|
|
479
|
+
).action(
|
|
480
|
+
(typeName, opts) => runCommand(
|
|
481
|
+
opts,
|
|
482
|
+
() => {
|
|
483
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
484
|
+
const typeDef = registry_exports.getType(workspacePath, typeName);
|
|
485
|
+
if (!typeDef) {
|
|
486
|
+
throw new Error(`Unknown primitive type "${typeName}". Use \`workgraph primitive list\` to inspect available types.`);
|
|
487
|
+
}
|
|
488
|
+
const fields = Object.entries(typeDef.fields).map(([name, definition]) => ({
|
|
489
|
+
name,
|
|
490
|
+
type: definition.type,
|
|
491
|
+
required: definition.required === true,
|
|
492
|
+
default: definition.default,
|
|
493
|
+
enum: definition.enum ?? [],
|
|
494
|
+
description: definition.description ?? "",
|
|
495
|
+
template: definition.template ?? void 0,
|
|
496
|
+
pattern: definition.pattern ?? void 0,
|
|
497
|
+
refTypes: definition.refTypes ?? []
|
|
498
|
+
}));
|
|
499
|
+
return {
|
|
500
|
+
type: typeDef.name,
|
|
501
|
+
description: typeDef.description,
|
|
502
|
+
directory: typeDef.directory,
|
|
503
|
+
builtIn: typeDef.builtIn,
|
|
504
|
+
fields
|
|
505
|
+
};
|
|
506
|
+
},
|
|
507
|
+
(result) => [
|
|
508
|
+
`Type: ${result.type}`,
|
|
509
|
+
`Directory: ${result.directory}/`,
|
|
510
|
+
`Built-in: ${result.builtIn}`,
|
|
511
|
+
...result.fields.map((field) => `- ${field.name}: ${field.type}${field.required ? " (required)" : ""}${field.description ? ` \u2014 ${field.description}` : ""}`)
|
|
512
|
+
]
|
|
513
|
+
)
|
|
514
|
+
);
|
|
515
|
+
}
|
|
340
516
|
addWorkspaceOption(
|
|
341
517
|
primitiveCmd.command("create <type> <title>").description("Create an instance of any primitive type").option("-a, --actor <name>", "Agent name", DEFAULT_ACTOR).option("--set <fields...>", 'Set fields as "key=value"').option("--body <text>", "Markdown body content", "").option("--json", "Emit structured JSON output")
|
|
342
518
|
).action(
|
|
@@ -353,7 +529,7 @@ addWorkspaceOption(
|
|
|
353
529
|
)
|
|
354
530
|
);
|
|
355
531
|
addWorkspaceOption(
|
|
356
|
-
primitiveCmd.command("update <path>").description("Update an existing primitive instance").option("-a, --actor <name>", "Agent name", DEFAULT_ACTOR).option("--set <fields...>", 'Set fields as "key=value"').option("--body <text>", "Replace markdown body content").option("--body-file <path>", "Read markdown body content from file").option("--json", "Emit structured JSON output")
|
|
532
|
+
primitiveCmd.command("update <path>").description("Update an existing primitive instance").option("-a, --actor <name>", "Agent name", DEFAULT_ACTOR).option("--set <fields...>", 'Set fields as "key=value"').option("--etag <etag>", "Expected etag for optimistic concurrency").option("--body <text>", "Replace markdown body content").option("--body-file <path>", "Read markdown body content from file").option("--json", "Emit structured JSON output")
|
|
357
533
|
).action(
|
|
358
534
|
(targetPath, opts) => runCommand(
|
|
359
535
|
opts,
|
|
@@ -365,7 +541,9 @@ addWorkspaceOption(
|
|
|
365
541
|
body = fs.readFileSync(path.resolve(opts.bodyFile), "utf-8");
|
|
366
542
|
}
|
|
367
543
|
return {
|
|
368
|
-
instance: store_exports.update(workspacePath, targetPath, updates, body, opts.actor
|
|
544
|
+
instance: store_exports.update(workspacePath, targetPath, updates, body, opts.actor, {
|
|
545
|
+
expectedEtag: opts.etag
|
|
546
|
+
})
|
|
369
547
|
};
|
|
370
548
|
},
|
|
371
549
|
(result) => [`Updated ${result.instance.type}: ${result.instance.path}`]
|
|
@@ -653,6 +831,26 @@ addWorkspaceOption(
|
|
|
653
831
|
]
|
|
654
832
|
)
|
|
655
833
|
);
|
|
834
|
+
addWorkspaceOption(
|
|
835
|
+
ledgerCmd.command("reconcile").description("Audit thread files against ledger claims, leases, and dependency wiring").option("--fail-on-issues", "Exit non-zero when issues are found").option("--json", "Emit structured JSON output")
|
|
836
|
+
).action(
|
|
837
|
+
(opts) => runCommand(
|
|
838
|
+
opts,
|
|
839
|
+
() => {
|
|
840
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
841
|
+
const report = thread_audit_exports.reconcileThreadState(workspacePath);
|
|
842
|
+
if (opts.failOnIssues && !report.ok) {
|
|
843
|
+
throw new Error(`Ledger reconcile found ${report.issues.length} issue(s).`);
|
|
844
|
+
}
|
|
845
|
+
return report;
|
|
846
|
+
},
|
|
847
|
+
(result) => [
|
|
848
|
+
`Reconcile ok: ${result.ok}`,
|
|
849
|
+
`Threads: ${result.totalThreads} Claims: ${result.totalClaims} Leases: ${result.totalLeases}`,
|
|
850
|
+
...result.issues.length > 0 ? result.issues.map((issue) => `${issue.kind}: ${issue.path} \u2014 ${issue.message}`) : ["No reconcile issues found."]
|
|
851
|
+
]
|
|
852
|
+
)
|
|
853
|
+
);
|
|
656
854
|
addWorkspaceOption(
|
|
657
855
|
ledgerCmd.command("seal").description("Rebuild ledger index + hash-chain state from ledger.jsonl").option("--json", "Emit structured JSON output")
|
|
658
856
|
).action(
|
|
@@ -674,6 +872,96 @@ addWorkspaceOption(
|
|
|
674
872
|
]
|
|
675
873
|
)
|
|
676
874
|
);
|
|
875
|
+
addWorkspaceOption(
|
|
876
|
+
program.command("doctor").description("Diagnose vault health, warnings, and repairable issues").option("--fix", "Auto-repair safe issues (orphan links, stale claims/runs)").option("--stale-after-minutes <n>", "Threshold for stale claims/runs in minutes", "60").option("-a, --actor <name>", "Actor used for --fix mutations", DEFAULT_ACTOR).option("--json", "Emit structured JSON output")
|
|
877
|
+
).action(
|
|
878
|
+
(opts) => runCommand(
|
|
879
|
+
opts,
|
|
880
|
+
() => {
|
|
881
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
882
|
+
const staleAfterMinutes = Number.parseInt(String(opts.staleAfterMinutes), 10);
|
|
883
|
+
const safeStaleAfterMinutes = Number.isNaN(staleAfterMinutes) ? 60 : Math.max(1, staleAfterMinutes);
|
|
884
|
+
return diagnostics_exports.diagnoseVaultHealth(workspacePath, {
|
|
885
|
+
fix: !!opts.fix,
|
|
886
|
+
actor: opts.actor,
|
|
887
|
+
staleAfterMs: safeStaleAfterMinutes * 60 * 1e3
|
|
888
|
+
});
|
|
889
|
+
},
|
|
890
|
+
(result) => diagnostics_exports.renderDoctorReport(result)
|
|
891
|
+
)
|
|
892
|
+
);
|
|
893
|
+
addWorkspaceOption(
|
|
894
|
+
program.command("replay").description("Replay ledger events chronologically with typed filters").option("--type <type>", "create | update | transition").option("--actor <name>", "Filter by actor").option("--primitive <ref>", "Filter by primitive path/type substring").option("--since <iso>", "Filter events on/after ISO timestamp").option("--until <iso>", "Filter events on/before ISO timestamp").option("--no-color", "Disable colorized output").option("--json", "Emit structured JSON output")
|
|
895
|
+
).action(
|
|
896
|
+
(opts) => runCommand(
|
|
897
|
+
opts,
|
|
898
|
+
() => {
|
|
899
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
900
|
+
return diagnostics_exports.replayLedger(workspacePath, {
|
|
901
|
+
type: opts.type,
|
|
902
|
+
actor: opts.actor,
|
|
903
|
+
primitive: opts.primitive,
|
|
904
|
+
since: opts.since,
|
|
905
|
+
until: opts.until
|
|
906
|
+
});
|
|
907
|
+
},
|
|
908
|
+
(result) => diagnostics_exports.renderReplayText(result, {
|
|
909
|
+
color: opts.color !== false && !wantsJson(opts)
|
|
910
|
+
})
|
|
911
|
+
)
|
|
912
|
+
);
|
|
913
|
+
addWorkspaceOption(
|
|
914
|
+
program.command("viz").description("Render an ASCII wiki-link graph of primitives in this vault").option("--focus <slugOrPath>", "Center the graph on a specific node").option("--depth <n>", "Traversal depth from each root", "2").option("--top <n>", "When large, show top N most-connected roots", "10").option("--no-color", "Disable colorized output").option("--json", "Emit structured JSON output")
|
|
915
|
+
).action(
|
|
916
|
+
(opts) => runCommand(
|
|
917
|
+
opts,
|
|
918
|
+
() => {
|
|
919
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
920
|
+
const parsedDepth = Number.parseInt(String(opts.depth), 10);
|
|
921
|
+
const parsedTop = Number.parseInt(String(opts.top), 10);
|
|
922
|
+
return diagnostics_exports.visualizeVaultGraph(workspacePath, {
|
|
923
|
+
focus: opts.focus,
|
|
924
|
+
depth: Number.isNaN(parsedDepth) ? 2 : Math.max(1, parsedDepth),
|
|
925
|
+
top: Number.isNaN(parsedTop) ? 10 : Math.max(1, parsedTop),
|
|
926
|
+
color: opts.color !== false && !wantsJson(opts)
|
|
927
|
+
});
|
|
928
|
+
},
|
|
929
|
+
(result) => [
|
|
930
|
+
...result.rendered.split("\n"),
|
|
931
|
+
"",
|
|
932
|
+
`Nodes: ${result.nodeCount}`,
|
|
933
|
+
`Edges: ${result.edgeCount}`,
|
|
934
|
+
...result.focus ? [`Focus: ${result.focus}`] : []
|
|
935
|
+
]
|
|
936
|
+
)
|
|
937
|
+
);
|
|
938
|
+
addWorkspaceOption(
|
|
939
|
+
program.command("stats").description("Show detailed vault statistics and graph/ledger health metrics").option("--json", "Emit structured JSON output")
|
|
940
|
+
).action(
|
|
941
|
+
(opts) => runCommand(
|
|
942
|
+
opts,
|
|
943
|
+
() => {
|
|
944
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
945
|
+
return diagnostics_exports.computeVaultStats(workspacePath);
|
|
946
|
+
},
|
|
947
|
+
(result) => diagnostics_exports.renderStatsReport(result)
|
|
948
|
+
)
|
|
949
|
+
);
|
|
950
|
+
addWorkspaceOption(
|
|
951
|
+
program.command("changelog").description("Generate a human-readable changelog from ledger events").requiredOption("--since <date>", "Include entries on/after this date (ISO-8601)").option("--until <date>", "Include entries on/before this date (ISO-8601)").option("--json", "Emit structured JSON output")
|
|
952
|
+
).action(
|
|
953
|
+
(opts) => runCommand(
|
|
954
|
+
opts,
|
|
955
|
+
() => {
|
|
956
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
957
|
+
return diagnostics_exports.generateLedgerChangelog(workspacePath, {
|
|
958
|
+
since: opts.since,
|
|
959
|
+
until: opts.until
|
|
960
|
+
});
|
|
961
|
+
},
|
|
962
|
+
(result) => diagnostics_exports.renderChangelogText(result)
|
|
963
|
+
)
|
|
964
|
+
);
|
|
677
965
|
addWorkspaceOption(
|
|
678
966
|
program.command("command-center").description("Generate a markdown command center from workgraph state").option("-a, --actor <name>", "Agent name", DEFAULT_ACTOR).option("-o, --output <path>", "Output markdown path", "Command Center.md").option("-n, --recent <count>", "Recent ledger entries to include", "15").option("--json", "Emit structured JSON output")
|
|
679
967
|
).action(
|
|
@@ -766,6 +1054,57 @@ addWorkspaceOption(
|
|
|
766
1054
|
(result) => [`Captured intake: ${result.intake.path}`]
|
|
767
1055
|
)
|
|
768
1056
|
);
|
|
1057
|
+
var lensCmd = program.command("lens").description("Generate deterministic context lenses for situational awareness");
|
|
1058
|
+
addWorkspaceOption(
|
|
1059
|
+
lensCmd.command("list").description("List built-in context lenses").option("--json", "Emit structured JSON output")
|
|
1060
|
+
).action(
|
|
1061
|
+
(opts) => runCommand(
|
|
1062
|
+
opts,
|
|
1063
|
+
() => ({
|
|
1064
|
+
lenses: lens_exports.listContextLenses()
|
|
1065
|
+
}),
|
|
1066
|
+
(result) => result.lenses.map((lens) => `lens://${lens.id} - ${lens.description}`)
|
|
1067
|
+
)
|
|
1068
|
+
);
|
|
1069
|
+
addWorkspaceOption(
|
|
1070
|
+
lensCmd.command("show <lensId>").description("Generate one context lens snapshot").option("-a, --actor <name>", "Actor identity for actor-scoped lenses", DEFAULT_ACTOR).option("--lookback-hours <hours>", "Lookback window in hours", "24").option("--stale-hours <hours>", "Stale threshold in hours", "24").option("--limit <n>", "Maximum items per section", "10").option("-o, --output <path>", "Write lens markdown to workspace-relative output path").option("--json", "Emit structured JSON output")
|
|
1071
|
+
).action(
|
|
1072
|
+
(lensId, opts) => runCommand(
|
|
1073
|
+
opts,
|
|
1074
|
+
() => {
|
|
1075
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1076
|
+
const lensOptions = {
|
|
1077
|
+
actor: opts.actor,
|
|
1078
|
+
lookbackHours: parsePositiveNumberOption(opts.lookbackHours, "lookback-hours"),
|
|
1079
|
+
staleHours: parsePositiveNumberOption(opts.staleHours, "stale-hours"),
|
|
1080
|
+
limit: parsePositiveIntegerOption(opts.limit, "limit")
|
|
1081
|
+
};
|
|
1082
|
+
if (opts.output) {
|
|
1083
|
+
return lens_exports.materializeContextLens(workspacePath, lensId, {
|
|
1084
|
+
...lensOptions,
|
|
1085
|
+
outputPath: opts.output
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
return lens_exports.generateContextLens(workspacePath, lensId, lensOptions);
|
|
1089
|
+
},
|
|
1090
|
+
(result) => {
|
|
1091
|
+
const metricSummary = Object.entries(result.metrics).map(([metric, value]) => `${metric}=${value}`).join(" ");
|
|
1092
|
+
const sectionSummary = result.sections.map((section) => `${section.id}:${section.items.length}`).join(" ");
|
|
1093
|
+
const lines = [
|
|
1094
|
+
`Lens: ${result.lens}`,
|
|
1095
|
+
`Generated: ${result.generatedAt}`,
|
|
1096
|
+
...result.actor ? [`Actor: ${result.actor}`] : [],
|
|
1097
|
+
`Metrics: ${metricSummary || "none"}`,
|
|
1098
|
+
`Sections: ${sectionSummary || "none"}`
|
|
1099
|
+
];
|
|
1100
|
+
if (isMaterializedLensResult(result)) {
|
|
1101
|
+
lines.push(`Saved markdown: ${result.outputPath}`);
|
|
1102
|
+
return lines;
|
|
1103
|
+
}
|
|
1104
|
+
return [...lines, "", ...result.markdown.split("\n")];
|
|
1105
|
+
}
|
|
1106
|
+
)
|
|
1107
|
+
);
|
|
769
1108
|
addWorkspaceOption(
|
|
770
1109
|
program.command("query").description("Query primitive instances with multi-field filters").option("--type <type>", "Primitive type").option("--status <status>", "Status value").option("--owner <owner>", "Owner/actor value").option("--tag <tag>", "Tag filter").option("--text <text>", "Full-text contains filter").option("--path-includes <text>", "Path substring filter").option("--updated-after <iso>", "Updated at or after").option("--updated-before <iso>", "Updated at or before").option("--created-after <iso>", "Created at or after").option("--created-before <iso>", "Created at or before").option("--limit <n>", "Result limit").option("--offset <n>", "Result offset").option("--json", "Emit structured JSON output")
|
|
771
1110
|
).action(
|
|
@@ -886,6 +1225,114 @@ addWorkspaceOption(
|
|
|
886
1225
|
]
|
|
887
1226
|
)
|
|
888
1227
|
);
|
|
1228
|
+
addWorkspaceOption(
|
|
1229
|
+
graphCmd.command("neighborhood <slug>").description("Find connected primitives within N wiki-link hops").option("--depth <n>", "Traversal depth (default: 2)", "2").option("--refresh", "Refresh graph index before querying").option("--json", "Emit structured JSON output")
|
|
1230
|
+
).action(
|
|
1231
|
+
(slug, opts) => runCommand(
|
|
1232
|
+
opts,
|
|
1233
|
+
() => {
|
|
1234
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1235
|
+
return graph_exports.graphNeighborhoodQuery(workspacePath, slug, {
|
|
1236
|
+
depth: parseNonNegativeIntOption(opts.depth, "depth"),
|
|
1237
|
+
refresh: !!opts.refresh
|
|
1238
|
+
});
|
|
1239
|
+
},
|
|
1240
|
+
(result) => [
|
|
1241
|
+
`Center: ${result.center.path} (${result.center.exists ? "exists" : "missing"})`,
|
|
1242
|
+
`Depth: ${result.depth}`,
|
|
1243
|
+
`Connected nodes: ${result.connectedNodes.length}`,
|
|
1244
|
+
`Edges in neighborhood: ${result.edges.length}`
|
|
1245
|
+
]
|
|
1246
|
+
)
|
|
1247
|
+
);
|
|
1248
|
+
addWorkspaceOption(
|
|
1249
|
+
graphCmd.command("impact <slug>").description("Analyze reverse-link impact for a primitive").option("--refresh", "Refresh graph index before querying").option("--json", "Emit structured JSON output")
|
|
1250
|
+
).action(
|
|
1251
|
+
(slug, opts) => runCommand(
|
|
1252
|
+
opts,
|
|
1253
|
+
() => {
|
|
1254
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1255
|
+
return graph_exports.graphImpactAnalysis(workspacePath, slug, {
|
|
1256
|
+
refresh: !!opts.refresh
|
|
1257
|
+
});
|
|
1258
|
+
},
|
|
1259
|
+
(result) => [
|
|
1260
|
+
`Target: ${result.target.path} (${result.target.exists ? "exists" : "missing"})`,
|
|
1261
|
+
`Total references: ${result.totalReferences}`,
|
|
1262
|
+
...result.groups.map((group) => `${group.type}: ${group.referenceCount}`)
|
|
1263
|
+
]
|
|
1264
|
+
)
|
|
1265
|
+
);
|
|
1266
|
+
addWorkspaceOption(
|
|
1267
|
+
graphCmd.command("context <slug>").description("Assemble token-budgeted markdown context from graph neighborhood").option("--budget <tokens>", "Approx token budget (chars/4)", "2000").option("--refresh", "Refresh graph index before querying").option("--json", "Emit structured JSON output")
|
|
1268
|
+
).action(
|
|
1269
|
+
(slug, opts) => runCommand(
|
|
1270
|
+
opts,
|
|
1271
|
+
() => {
|
|
1272
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1273
|
+
return graph_exports.graphContextAssembly(workspacePath, slug, {
|
|
1274
|
+
budgetTokens: parsePositiveIntOption(opts.budget, "budget"),
|
|
1275
|
+
refresh: !!opts.refresh
|
|
1276
|
+
});
|
|
1277
|
+
},
|
|
1278
|
+
(result) => [
|
|
1279
|
+
`Center: ${result.center.path}`,
|
|
1280
|
+
`Budget: ${result.budgetTokens} tokens`,
|
|
1281
|
+
`Used: ${result.usedTokens} tokens`,
|
|
1282
|
+
`Sections: ${result.sections.length}`,
|
|
1283
|
+
"",
|
|
1284
|
+
result.markdown
|
|
1285
|
+
]
|
|
1286
|
+
)
|
|
1287
|
+
);
|
|
1288
|
+
addWorkspaceOption(
|
|
1289
|
+
graphCmd.command("edges <slug>").description("Show typed incoming/outgoing edges for one primitive").option("--refresh", "Refresh graph index before querying").option("--json", "Emit structured JSON output")
|
|
1290
|
+
).action(
|
|
1291
|
+
(slug, opts) => runCommand(
|
|
1292
|
+
opts,
|
|
1293
|
+
() => {
|
|
1294
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1295
|
+
return graph_exports.graphTypedEdges(workspacePath, slug, {
|
|
1296
|
+
refresh: !!opts.refresh
|
|
1297
|
+
});
|
|
1298
|
+
},
|
|
1299
|
+
(result) => [
|
|
1300
|
+
`Node: ${result.node.path} (${result.node.exists ? "exists" : "missing"})`,
|
|
1301
|
+
`Outgoing edges: ${result.outgoing.length}`,
|
|
1302
|
+
`Incoming edges: ${result.incoming.length}`,
|
|
1303
|
+
...result.outgoing.map((edge) => `OUT ${edge.type} ${edge.from} -> ${edge.to}`),
|
|
1304
|
+
...result.incoming.map((edge) => `IN ${edge.type} ${edge.from} -> ${edge.to}`)
|
|
1305
|
+
]
|
|
1306
|
+
)
|
|
1307
|
+
);
|
|
1308
|
+
addWorkspaceOption(
|
|
1309
|
+
graphCmd.command("export <slug>").description("Export a markdown subgraph directory around a center primitive").option("--depth <n>", "Traversal depth (default: 2)", "2").option("--format <format>", "Export format (default: md)", "md").option("--output-dir <path>", "Output directory (default under .workgraph/graph-exports)").option("--refresh", "Refresh graph index before querying").option("--json", "Emit structured JSON output")
|
|
1310
|
+
).action(
|
|
1311
|
+
(slug, opts) => runCommand(
|
|
1312
|
+
opts,
|
|
1313
|
+
() => {
|
|
1314
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1315
|
+
const format = String(opts.format ?? "md").trim().toLowerCase();
|
|
1316
|
+
if (format !== "md") {
|
|
1317
|
+
throw new Error(`Invalid --format "${opts.format}". Supported formats: md.`);
|
|
1318
|
+
}
|
|
1319
|
+
return graph_exports.graphExportSubgraph(workspacePath, slug, {
|
|
1320
|
+
depth: parseNonNegativeIntOption(opts.depth, "depth"),
|
|
1321
|
+
format,
|
|
1322
|
+
outputDir: opts.outputDir,
|
|
1323
|
+
refresh: !!opts.refresh
|
|
1324
|
+
});
|
|
1325
|
+
},
|
|
1326
|
+
(result) => [
|
|
1327
|
+
`Exported subgraph: ${result.outputDirectory}`,
|
|
1328
|
+
`Center: ${result.center.path}`,
|
|
1329
|
+
`Depth: ${result.depth}`,
|
|
1330
|
+
`Nodes: ${result.exportedNodes.length}`,
|
|
1331
|
+
`Edges: ${result.exportedEdgeCount}`,
|
|
1332
|
+
`Manifest: ${result.manifestPath}`
|
|
1333
|
+
]
|
|
1334
|
+
)
|
|
1335
|
+
);
|
|
889
1336
|
addWorkspaceOption(
|
|
890
1337
|
graphCmd.command("neighbors <nodePath>").description("Query incoming/outgoing wiki-link neighbors for one node").option("--refresh", "Refresh graph index before querying").option("--json", "Emit structured JSON output")
|
|
891
1338
|
).action(
|
|
@@ -952,6 +1399,33 @@ addWorkspaceOption(
|
|
|
952
1399
|
(result) => result.parties.map((party) => `${party.id} [${party.roles.join(", ")}]`)
|
|
953
1400
|
)
|
|
954
1401
|
);
|
|
1402
|
+
var gateCmd = program.command("gate").description("Evaluate thread quality gates before claim");
|
|
1403
|
+
addWorkspaceOption(
|
|
1404
|
+
gateCmd.command("check <threadRef>").description("Check policy-gate status for one thread").option("--json", "Emit structured JSON output")
|
|
1405
|
+
).action(
|
|
1406
|
+
(threadRef, opts) => runCommand(
|
|
1407
|
+
opts,
|
|
1408
|
+
() => {
|
|
1409
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1410
|
+
return gate_exports.checkThreadGates(workspacePath, threadRef);
|
|
1411
|
+
},
|
|
1412
|
+
(result) => {
|
|
1413
|
+
const header = [`Gate check for ${result.threadPath}: ${result.allowed ? "PASSED" : "FAILED"}`];
|
|
1414
|
+
if (result.gates.length === 0) {
|
|
1415
|
+
return [...header, "No gates configured."];
|
|
1416
|
+
}
|
|
1417
|
+
const details = result.gates.map((gate) => {
|
|
1418
|
+
const failingRules = gate.rules.filter((rule) => !rule.ok);
|
|
1419
|
+
const gateLabel = gate.gatePath ?? gate.gateRef;
|
|
1420
|
+
if (failingRules.length === 0) {
|
|
1421
|
+
return `[pass] ${gateLabel}`;
|
|
1422
|
+
}
|
|
1423
|
+
return `[fail] ${gateLabel} :: ${failingRules.map((rule) => rule.message).join("; ")}`;
|
|
1424
|
+
});
|
|
1425
|
+
return [...header, ...details];
|
|
1426
|
+
}
|
|
1427
|
+
)
|
|
1428
|
+
);
|
|
955
1429
|
var dispatchCmd = program.command("dispatch").description("Programmatic runtime dispatch contract");
|
|
956
1430
|
addWorkspaceOption(
|
|
957
1431
|
dispatchCmd.command("create <objective>").description("Create a new run dispatch request").option("-a, --actor <name>", "Actor", DEFAULT_ACTOR).option("--adapter <name>", "Adapter name", "cursor-cloud").option("--idempotency-key <key>", "Idempotency key").option("--json", "Emit structured JSON output")
|
|
@@ -972,6 +1446,21 @@ addWorkspaceOption(
|
|
|
972
1446
|
(result) => [`Run created: ${result.run.id} [${result.run.status}]`]
|
|
973
1447
|
)
|
|
974
1448
|
);
|
|
1449
|
+
addWorkspaceOption(
|
|
1450
|
+
dispatchCmd.command("claim <threadRef>").description("Claim a thread after passing quality gates").option("-a, --actor <name>", "Actor", DEFAULT_ACTOR).option("--json", "Emit structured JSON output")
|
|
1451
|
+
).action(
|
|
1452
|
+
(threadRef, opts) => runCommand(
|
|
1453
|
+
opts,
|
|
1454
|
+
() => {
|
|
1455
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1456
|
+
return dispatch_exports.claimThread(workspacePath, threadRef, opts.actor);
|
|
1457
|
+
},
|
|
1458
|
+
(result) => [
|
|
1459
|
+
`Claimed thread: ${result.thread.path}`,
|
|
1460
|
+
`Gates checked: ${result.gateCheck.gates.length}`
|
|
1461
|
+
]
|
|
1462
|
+
)
|
|
1463
|
+
);
|
|
975
1464
|
addWorkspaceOption(
|
|
976
1465
|
dispatchCmd.command("create-execute <objective>").description("Create and execute a run with autonomous multi-agent coordination").option("-a, --actor <name>", "Actor", DEFAULT_ACTOR).option("--adapter <name>", "Adapter name", "cursor-cloud").option("--idempotency-key <key>", "Idempotency key").option("--agents <actors>", "Comma-separated agent identities for autonomous execution").option("--max-steps <n>", "Maximum scheduler steps", "200").option("--step-delay-ms <ms>", "Delay between scheduling steps", "25").option("--space <spaceRef>", "Restrict execution to one space").option("--no-checkpoint", "Skip automatic checkpoint generation after execution").option("--json", "Emit structured JSON output")
|
|
977
1466
|
).action(
|
|
@@ -1089,6 +1578,65 @@ addWorkspaceOption(
|
|
|
1089
1578
|
(result) => [`Stopped run: ${result.run.id} [${result.run.status}]`]
|
|
1090
1579
|
)
|
|
1091
1580
|
);
|
|
1581
|
+
addWorkspaceOption(
|
|
1582
|
+
dispatchCmd.command("heartbeat <runId>").description("Heartbeat a running run lease and extend lease_expiry").option("-a, --actor <name>", "Actor", DEFAULT_ACTOR).option("--lease-minutes <n>", "Lease extension in minutes").option("--json", "Emit structured JSON output")
|
|
1583
|
+
).action(
|
|
1584
|
+
(runId, opts) => runCommand(
|
|
1585
|
+
opts,
|
|
1586
|
+
() => {
|
|
1587
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1588
|
+
return {
|
|
1589
|
+
run: dispatch_exports.heartbeat(workspacePath, runId, {
|
|
1590
|
+
actor: opts.actor,
|
|
1591
|
+
leaseMinutes: opts.leaseMinutes ? Number.parseInt(String(opts.leaseMinutes), 10) : void 0
|
|
1592
|
+
})
|
|
1593
|
+
};
|
|
1594
|
+
},
|
|
1595
|
+
(result) => [
|
|
1596
|
+
`Heartbeated run: ${result.run.id}`,
|
|
1597
|
+
`Lease expires: ${String(result.run.leaseExpires ?? "none")}`,
|
|
1598
|
+
`Heartbeats: ${(result.run.heartbeats ?? []).length}`
|
|
1599
|
+
]
|
|
1600
|
+
)
|
|
1601
|
+
);
|
|
1602
|
+
addWorkspaceOption(
|
|
1603
|
+
dispatchCmd.command("reconcile").description("Requeue runs with expired leases").option("-a, --actor <name>", "Actor", DEFAULT_ACTOR).option("--json", "Emit structured JSON output")
|
|
1604
|
+
).action(
|
|
1605
|
+
(opts) => runCommand(
|
|
1606
|
+
opts,
|
|
1607
|
+
() => {
|
|
1608
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1609
|
+
return dispatch_exports.reconcileExpiredLeases(workspacePath, opts.actor);
|
|
1610
|
+
},
|
|
1611
|
+
(result) => [
|
|
1612
|
+
`Reconciled at: ${result.reconciledAt}`,
|
|
1613
|
+
`Inspected runs: ${result.inspectedRuns}`,
|
|
1614
|
+
`Requeued runs: ${result.requeuedRuns.length}`,
|
|
1615
|
+
...result.requeuedRuns.map((run) => `- ${run.id}`)
|
|
1616
|
+
]
|
|
1617
|
+
)
|
|
1618
|
+
);
|
|
1619
|
+
addWorkspaceOption(
|
|
1620
|
+
dispatchCmd.command("handoff <runId>").description("Create a structured run handoff to another agent").requiredOption("--to <agent>", "Target agent").requiredOption("--reason <text>", "Reason for handoff").option("-a, --actor <name>", "Actor", DEFAULT_ACTOR).option("--adapter <name>", "Adapter override for handoff run").option("--json", "Emit structured JSON output")
|
|
1621
|
+
).action(
|
|
1622
|
+
(runId, opts) => runCommand(
|
|
1623
|
+
opts,
|
|
1624
|
+
() => {
|
|
1625
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1626
|
+
return dispatch_exports.handoffRun(workspacePath, runId, {
|
|
1627
|
+
actor: opts.actor,
|
|
1628
|
+
to: opts.to,
|
|
1629
|
+
reason: opts.reason,
|
|
1630
|
+
adapter: opts.adapter
|
|
1631
|
+
});
|
|
1632
|
+
},
|
|
1633
|
+
(result) => [
|
|
1634
|
+
`Handoff created: ${result.handoffRun.id} (from ${result.sourceRun.id})`,
|
|
1635
|
+
`Target agent: ${result.handoffRun.actor}`,
|
|
1636
|
+
`Objective: ${result.handoffRun.objective}`
|
|
1637
|
+
]
|
|
1638
|
+
)
|
|
1639
|
+
);
|
|
1092
1640
|
addWorkspaceOption(
|
|
1093
1641
|
dispatchCmd.command("mark <runId>").description("Set run status transition explicitly").requiredOption("--status <status>", "running|succeeded|failed|cancelled").option("-a, --actor <name>", "Actor", DEFAULT_ACTOR).option("--output <text>", "Optional output payload").option("--error <text>", "Optional error payload").option("--json", "Emit structured JSON output")
|
|
1094
1642
|
).action(
|
|
@@ -1142,6 +1690,28 @@ addWorkspaceOption(
|
|
|
1142
1690
|
]
|
|
1143
1691
|
)
|
|
1144
1692
|
);
|
|
1693
|
+
var triggerEngineCmd = triggerCmd.command("engine").description("Run trigger engine");
|
|
1694
|
+
addWorkspaceOption(
|
|
1695
|
+
triggerEngineCmd.command("run").description("Process trigger events once or continuously").option("-a, --actor <name>", "Actor", DEFAULT_ACTOR).option("--watch", "Continuously process new events").option("--poll-ms <ms>", "Poll interval in watch mode", "2000").option("--max-cycles <n>", "Maximum cycles before exiting").option("--entry-limit <n>", "Maximum ledger entries per cycle").option("--agents <actors>", "Comma-separated agents used when executing runs").option("--max-steps <n>", "Maximum adapter scheduler steps", "200").option("--step-delay-ms <ms>", "Adapter scheduler delay", "25").option("--space <spaceRef>", "Restrict run execution to one space").option("--stale-claim-minutes <m>", "Drift warning threshold for stale claims", "30").option("--no-execute-runs", "Do not execute dispatched runs").option("--json", "Emit structured JSON output")
|
|
1696
|
+
).action(
|
|
1697
|
+
(opts) => runCommand(
|
|
1698
|
+
opts,
|
|
1699
|
+
() => {
|
|
1700
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1701
|
+
return trigger_engine_exports.runTriggerEngineCycle(workspacePath, {
|
|
1702
|
+
actor: opts.actor
|
|
1703
|
+
});
|
|
1704
|
+
},
|
|
1705
|
+
(result) => [
|
|
1706
|
+
`Evaluated: ${result.evaluated} triggers`,
|
|
1707
|
+
`Fired: ${result.fired}`,
|
|
1708
|
+
`Errors: ${result.errors}`,
|
|
1709
|
+
...result.triggers.map(
|
|
1710
|
+
(t) => ` ${t.triggerPath}: ${t.fired ? "FIRED" : "skipped"} (${t.reason})${t.error ? ` error: ${t.error}` : ""}`
|
|
1711
|
+
)
|
|
1712
|
+
]
|
|
1713
|
+
)
|
|
1714
|
+
);
|
|
1145
1715
|
addWorkspaceOption(
|
|
1146
1716
|
program.command("onboard").description("Guided agent-first workspace setup and starter artifacts").option("-a, --actor <name>", "Actor", DEFAULT_ACTOR).option("--spaces <list>", "Comma-separated space names").option("--no-demo-threads", "Skip starter onboarding threads").option("--json", "Emit structured JSON output")
|
|
1147
1717
|
).action(
|
|
@@ -1204,12 +1774,124 @@ addWorkspaceOption(
|
|
|
1204
1774
|
(result) => [`Updated onboarding: ${result.onboarding.path} [${String(result.onboarding.fields.status)}]`]
|
|
1205
1775
|
)
|
|
1206
1776
|
);
|
|
1777
|
+
var autonomyCmd = program.command("autonomy").description("Run long-lived autonomous collaboration loops");
|
|
1778
|
+
addWorkspaceOption(
|
|
1779
|
+
autonomyCmd.command("run").description("Run autonomy cycles (trigger engine + ready-thread execution)").option("-a, --actor <name>", "Actor", DEFAULT_ACTOR).option("--adapter <name>", "Dispatch adapter name", "cursor-cloud").option("--agents <actors>", "Comma-separated autonomous worker identities").option("--watch", "Run continuously instead of stopping when idle").option("--poll-ms <ms>", "Cycle poll interval", "2000").option("--max-cycles <n>", "Maximum cycles before exit").option("--max-idle-cycles <n>", "Idle cycles before exit in non-watch mode", "2").option("--max-steps <n>", "Maximum adapter scheduler steps", "200").option("--step-delay-ms <ms>", "Adapter scheduler delay", "25").option("--space <spaceRef>", "Restrict autonomy to one space").option("--stale-claim-minutes <m>", "Drift warning threshold", "30").option("--heartbeat-file <path>", "Write daemon heartbeat JSON to this path").option("--no-execute-triggers", "Disable trigger engine actions").option("--no-execute-ready-threads", "Disable ready-thread dispatch execution").option("--json", "Emit structured JSON output")
|
|
1780
|
+
).action(
|
|
1781
|
+
(opts) => runCommand(
|
|
1782
|
+
opts,
|
|
1783
|
+
async () => {
|
|
1784
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1785
|
+
return autonomy_exports.runAutonomyLoop(workspacePath, {
|
|
1786
|
+
actor: opts.actor,
|
|
1787
|
+
adapter: opts.adapter,
|
|
1788
|
+
agents: csv(opts.agents),
|
|
1789
|
+
watch: !!opts.watch,
|
|
1790
|
+
pollMs: Number.parseInt(String(opts.pollMs), 10),
|
|
1791
|
+
maxCycles: opts.maxCycles ? Number.parseInt(String(opts.maxCycles), 10) : void 0,
|
|
1792
|
+
maxIdleCycles: Number.parseInt(String(opts.maxIdleCycles), 10),
|
|
1793
|
+
maxSteps: Number.parseInt(String(opts.maxSteps), 10),
|
|
1794
|
+
stepDelayMs: Number.parseInt(String(opts.stepDelayMs), 10),
|
|
1795
|
+
space: opts.space,
|
|
1796
|
+
staleClaimMinutes: Number.parseInt(String(opts.staleClaimMinutes), 10),
|
|
1797
|
+
heartbeatFile: opts.heartbeatFile,
|
|
1798
|
+
executeTriggers: opts.executeTriggers,
|
|
1799
|
+
executeReadyThreads: opts.executeReadyThreads
|
|
1800
|
+
});
|
|
1801
|
+
},
|
|
1802
|
+
(result) => [
|
|
1803
|
+
`Cycles: ${result.cycles.length}`,
|
|
1804
|
+
`Final ready threads: ${result.finalReadyThreads}`,
|
|
1805
|
+
`Final drift status: ${result.finalDriftOk ? "ok" : "issues"}`,
|
|
1806
|
+
...result.cycles.map(
|
|
1807
|
+
(cycle) => `Cycle ${cycle.cycle}: ready=${cycle.readyThreads} trigger_actions=${cycle.triggerActions} run=${cycle.runStatus ?? "none"} drift_issues=${cycle.driftIssues}`
|
|
1808
|
+
)
|
|
1809
|
+
]
|
|
1810
|
+
)
|
|
1811
|
+
);
|
|
1812
|
+
var autonomyDaemonCmd = autonomyCmd.command("daemon").description("Manage autonomy process lifecycle (pid + heartbeat + logs)");
|
|
1813
|
+
addWorkspaceOption(
|
|
1814
|
+
autonomyDaemonCmd.command("start").description("Start autonomy in detached daemon mode").option("-a, --actor <name>", "Actor", DEFAULT_ACTOR).option("--adapter <name>", "Dispatch adapter name", "cursor-cloud").option("--agents <actors>", "Comma-separated autonomous worker identities").option("--poll-ms <ms>", "Cycle poll interval", "2000").option("--max-cycles <n>", "Maximum cycles before daemon exits").option("--max-steps <n>", "Maximum adapter scheduler steps", "200").option("--step-delay-ms <ms>", "Adapter scheduler delay", "25").option("--space <spaceRef>", "Restrict autonomy to one space").option("--stale-claim-minutes <m>", "Drift warning threshold", "30").option("--log-path <path>", "Daemon log file path (workspace-relative)").option("--heartbeat-path <path>", "Heartbeat file path (workspace-relative)").option("--no-execute-triggers", "Disable trigger engine actions").option("--no-execute-ready-threads", "Disable ready-thread dispatch execution").option("--json", "Emit structured JSON output")
|
|
1815
|
+
).action(
|
|
1816
|
+
(opts) => runCommand(
|
|
1817
|
+
opts,
|
|
1818
|
+
() => {
|
|
1819
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1820
|
+
return autonomy_daemon_exports.startAutonomyDaemon(workspacePath, {
|
|
1821
|
+
cliEntrypointPath: process.argv[1] ?? path.resolve("bin/workgraph.js"),
|
|
1822
|
+
actor: opts.actor,
|
|
1823
|
+
adapter: opts.adapter,
|
|
1824
|
+
agents: csv(opts.agents),
|
|
1825
|
+
pollMs: Number.parseInt(String(opts.pollMs), 10),
|
|
1826
|
+
maxCycles: opts.maxCycles ? Number.parseInt(String(opts.maxCycles), 10) : void 0,
|
|
1827
|
+
maxSteps: Number.parseInt(String(opts.maxSteps), 10),
|
|
1828
|
+
stepDelayMs: Number.parseInt(String(opts.stepDelayMs), 10),
|
|
1829
|
+
space: opts.space,
|
|
1830
|
+
staleClaimMinutes: Number.parseInt(String(opts.staleClaimMinutes), 10),
|
|
1831
|
+
logPath: opts.logPath,
|
|
1832
|
+
heartbeatPath: opts.heartbeatPath,
|
|
1833
|
+
executeTriggers: opts.executeTriggers,
|
|
1834
|
+
executeReadyThreads: opts.executeReadyThreads
|
|
1835
|
+
});
|
|
1836
|
+
},
|
|
1837
|
+
(result) => [
|
|
1838
|
+
`Daemon running: ${result.running}`,
|
|
1839
|
+
...result.pid ? [`PID: ${result.pid}`] : [],
|
|
1840
|
+
`PID file: ${result.pidPath}`,
|
|
1841
|
+
`Heartbeat: ${result.heartbeatPath}`,
|
|
1842
|
+
`Log: ${result.logPath}`
|
|
1843
|
+
]
|
|
1844
|
+
)
|
|
1845
|
+
);
|
|
1846
|
+
addWorkspaceOption(
|
|
1847
|
+
autonomyDaemonCmd.command("status").description("Show autonomy daemon status").option("--json", "Emit structured JSON output")
|
|
1848
|
+
).action(
|
|
1849
|
+
(opts) => runCommand(
|
|
1850
|
+
opts,
|
|
1851
|
+
() => {
|
|
1852
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1853
|
+
return autonomy_daemon_exports.readAutonomyDaemonStatus(workspacePath);
|
|
1854
|
+
},
|
|
1855
|
+
(result) => [
|
|
1856
|
+
`Daemon running: ${result.running}`,
|
|
1857
|
+
...result.pid ? [`PID: ${result.pid}`] : [],
|
|
1858
|
+
...result.heartbeat ? [`Last heartbeat: ${result.heartbeat.ts}`] : ["Last heartbeat: none"],
|
|
1859
|
+
`PID file: ${result.pidPath}`,
|
|
1860
|
+
`Heartbeat: ${result.heartbeatPath}`,
|
|
1861
|
+
`Log: ${result.logPath}`
|
|
1862
|
+
]
|
|
1863
|
+
)
|
|
1864
|
+
);
|
|
1865
|
+
addWorkspaceOption(
|
|
1866
|
+
autonomyDaemonCmd.command("stop").description("Stop autonomy daemon by PID").option("--signal <signal>", "Signal for graceful stop", "SIGTERM").option("--timeout-ms <ms>", "Graceful wait timeout", "5000").option("--json", "Emit structured JSON output")
|
|
1867
|
+
).action(
|
|
1868
|
+
(opts) => runCommand(
|
|
1869
|
+
opts,
|
|
1870
|
+
async () => {
|
|
1871
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1872
|
+
return autonomy_daemon_exports.stopAutonomyDaemon(workspacePath, {
|
|
1873
|
+
signal: String(opts.signal),
|
|
1874
|
+
timeoutMs: Number.parseInt(String(opts.timeoutMs), 10)
|
|
1875
|
+
});
|
|
1876
|
+
},
|
|
1877
|
+
(result) => [
|
|
1878
|
+
`Stopped: ${result.stopped}`,
|
|
1879
|
+
`Previously running: ${result.previouslyRunning}`,
|
|
1880
|
+
...result.pid ? [`PID: ${result.pid}`] : [],
|
|
1881
|
+
`Daemon running now: ${result.status.running}`
|
|
1882
|
+
]
|
|
1883
|
+
)
|
|
1884
|
+
);
|
|
1207
1885
|
var mcpCmd = program.command("mcp").description("Run Workgraph MCP server");
|
|
1208
1886
|
addWorkspaceOption(
|
|
1209
|
-
mcpCmd.command("serve").description("Serve stdio MCP tools/resources for this workspace").option("-a, --actor <name>", "Default actor for MCP write tools", DEFAULT_ACTOR).option("--read-only", "Disable all MCP write tools")
|
|
1887
|
+
mcpCmd.command("serve").description("Serve stdio MCP tools/resources for this workspace").option("-a, --actor <name>", "Default actor for MCP write tools", DEFAULT_ACTOR).option("--read-only", "Disable all MCP write tools").option("--sse-port <port>", "Optional SSE event stream port").option("--sse-host <host>", "SSE bind host (default: 127.0.0.1)").option("--sse-path <path>", "SSE endpoint path (default: /events)").option("--sse-poll-ms <ms>", "Ledger poll interval for SSE stream (default: 250ms)").option("--sse-heartbeat-ms <ms>", "SSE heartbeat interval (default: 15000ms)")
|
|
1210
1888
|
).action(async (opts) => {
|
|
1211
1889
|
const workspacePath = resolveWorkspacePath(opts);
|
|
1212
1890
|
console.error(`Starting MCP server for workspace: ${workspacePath}`);
|
|
1891
|
+
const ssePort = opts.ssePort !== void 0 ? Number.parseInt(String(opts.ssePort), 10) : void 0;
|
|
1892
|
+
const ssePollMs = opts.ssePollMs !== void 0 ? Number.parseInt(String(opts.ssePollMs), 10) : void 0;
|
|
1893
|
+
const sseHeartbeatMs = opts.sseHeartbeatMs !== void 0 ? Number.parseInt(String(opts.sseHeartbeatMs), 10) : void 0;
|
|
1894
|
+
const sseEnabled = ssePort !== void 0 || opts.sseHost !== void 0 || opts.ssePath !== void 0;
|
|
1213
1895
|
await mcp_server_exports.startWorkgraphMcpServer({
|
|
1214
1896
|
workspacePath,
|
|
1215
1897
|
defaultActor: opts.actor,
|
|
@@ -1218,15 +1900,51 @@ addWorkspaceOption(
|
|
|
1218
1900
|
});
|
|
1219
1901
|
await program.parseAsync();
|
|
1220
1902
|
function addWorkspaceOption(command) {
|
|
1221
|
-
return command.option("-w, --workspace <path>", "Workgraph workspace path").option("--vault <path>", "Alias for --workspace").option("--shared-vault <path>", "Shared vault path (e.g. mounted via Tailscale)");
|
|
1903
|
+
return command.option("-w, --workspace <path>", "Workgraph workspace path").option("--vault <path>", "Alias for --workspace").option("--shared-vault <path>", "Shared vault path (e.g. mounted via Tailscale)").option("--dry-run", "Execute against a temporary workspace copy and discard changes");
|
|
1222
1904
|
}
|
|
1223
1905
|
function resolveWorkspacePath(opts) {
|
|
1906
|
+
const originalWorkspacePath = resolveWorkspacePathBase(opts);
|
|
1907
|
+
if (!opts.dryRun) return originalWorkspacePath;
|
|
1908
|
+
if (opts.__dryRunWorkspace) return opts.__dryRunWorkspace;
|
|
1909
|
+
const sandboxRoot = fs.mkdtempSync(path.join(os.tmpdir(), "workgraph-dry-run-"));
|
|
1910
|
+
const sandboxWorkspace = path.join(sandboxRoot, "workspace");
|
|
1911
|
+
if (fs.existsSync(originalWorkspacePath)) {
|
|
1912
|
+
fs.cpSync(originalWorkspacePath, sandboxWorkspace, {
|
|
1913
|
+
recursive: true,
|
|
1914
|
+
force: true
|
|
1915
|
+
});
|
|
1916
|
+
} else {
|
|
1917
|
+
fs.mkdirSync(sandboxWorkspace, { recursive: true });
|
|
1918
|
+
}
|
|
1919
|
+
opts.__dryRunWorkspaceRoot = sandboxRoot;
|
|
1920
|
+
opts.__dryRunWorkspace = sandboxWorkspace;
|
|
1921
|
+
opts.__dryRunOriginal = originalWorkspacePath;
|
|
1922
|
+
return sandboxWorkspace;
|
|
1923
|
+
}
|
|
1924
|
+
function resolveWorkspacePathBase(opts) {
|
|
1224
1925
|
const explicit = opts.workspace || opts.vault || opts.sharedVault;
|
|
1225
1926
|
if (explicit) return path.resolve(explicit);
|
|
1226
1927
|
if (process.env.WORKGRAPH_SHARED_VAULT) return path.resolve(process.env.WORKGRAPH_SHARED_VAULT);
|
|
1227
1928
|
if (process.env.WORKGRAPH_PATH) return path.resolve(process.env.WORKGRAPH_PATH);
|
|
1228
1929
|
return process.cwd();
|
|
1229
1930
|
}
|
|
1931
|
+
function resolveInitTargetPath(targetPath, opts) {
|
|
1932
|
+
const requestedPath = path.resolve(targetPath || resolveWorkspacePathBase(opts));
|
|
1933
|
+
if (!opts.dryRun) return requestedPath;
|
|
1934
|
+
if (opts.__dryRunWorkspace) return opts.__dryRunWorkspace;
|
|
1935
|
+
const sandboxRoot = fs.mkdtempSync(path.join(os.tmpdir(), "workgraph-init-dry-run-"));
|
|
1936
|
+
const sandboxWorkspace = path.join(sandboxRoot, path.basename(requestedPath));
|
|
1937
|
+
if (fs.existsSync(requestedPath)) {
|
|
1938
|
+
fs.cpSync(requestedPath, sandboxWorkspace, {
|
|
1939
|
+
recursive: true,
|
|
1940
|
+
force: true
|
|
1941
|
+
});
|
|
1942
|
+
}
|
|
1943
|
+
opts.__dryRunWorkspaceRoot = sandboxRoot;
|
|
1944
|
+
opts.__dryRunWorkspace = sandboxWorkspace;
|
|
1945
|
+
opts.__dryRunOriginal = requestedPath;
|
|
1946
|
+
return sandboxWorkspace;
|
|
1947
|
+
}
|
|
1230
1948
|
function parseSetPairs(pairs) {
|
|
1231
1949
|
const fields = {};
|
|
1232
1950
|
for (const pair of pairs) {
|
|
@@ -1279,6 +1997,37 @@ function parseScalar(value) {
|
|
|
1279
1997
|
return value;
|
|
1280
1998
|
}
|
|
1281
1999
|
}
|
|
2000
|
+
function parsePositiveIntOption(value, name) {
|
|
2001
|
+
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
2002
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
2003
|
+
throw new Error(`Invalid --${name} value "${String(value)}". Expected a positive integer.`);
|
|
2004
|
+
}
|
|
2005
|
+
return parsed;
|
|
2006
|
+
}
|
|
2007
|
+
function parsePositiveNumberOption(value, optionName) {
|
|
2008
|
+
const parsed = Number(value);
|
|
2009
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
2010
|
+
throw new Error(`Invalid --${optionName}. Expected a positive number.`);
|
|
2011
|
+
}
|
|
2012
|
+
return parsed;
|
|
2013
|
+
}
|
|
2014
|
+
function parseNonNegativeIntOption(value, name) {
|
|
2015
|
+
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
2016
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
2017
|
+
throw new Error(`Invalid --${name} value "${String(value)}". Expected a non-negative integer.`);
|
|
2018
|
+
}
|
|
2019
|
+
return parsed;
|
|
2020
|
+
}
|
|
2021
|
+
function parsePositiveIntegerOption(value, optionName) {
|
|
2022
|
+
const parsed = Number.parseInt(String(value), 10);
|
|
2023
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
2024
|
+
throw new Error(`Invalid --${optionName}. Expected a positive integer.`);
|
|
2025
|
+
}
|
|
2026
|
+
return parsed;
|
|
2027
|
+
}
|
|
2028
|
+
function isMaterializedLensResult(value) {
|
|
2029
|
+
return typeof value.outputPath === "string";
|
|
2030
|
+
}
|
|
1282
2031
|
function normalizeRunStatus(status) {
|
|
1283
2032
|
const normalized = String(status).toLowerCase();
|
|
1284
2033
|
if (normalized === "running" || normalized === "succeeded" || normalized === "failed" || normalized === "cancelled") {
|
|
@@ -1286,6 +2035,13 @@ function normalizeRunStatus(status) {
|
|
|
1286
2035
|
}
|
|
1287
2036
|
throw new Error(`Invalid run status "${status}". Expected running|succeeded|failed|cancelled.`);
|
|
1288
2037
|
}
|
|
2038
|
+
function normalizeAgentPresenceStatus(status) {
|
|
2039
|
+
const normalized = String(status).toLowerCase();
|
|
2040
|
+
if (normalized === "online" || normalized === "busy" || normalized === "offline") {
|
|
2041
|
+
return normalized;
|
|
2042
|
+
}
|
|
2043
|
+
throw new Error(`Invalid agent status "${status}". Expected online|busy|offline.`);
|
|
2044
|
+
}
|
|
1289
2045
|
function normalizeOnboardingStatus(status) {
|
|
1290
2046
|
const normalized = String(status).toLowerCase();
|
|
1291
2047
|
if (normalized === "active" || normalized === "paused" || normalized === "completed") {
|
|
@@ -1301,19 +2057,45 @@ function wantsJson(opts) {
|
|
|
1301
2057
|
async function runCommand(opts, action, renderText) {
|
|
1302
2058
|
try {
|
|
1303
2059
|
const result = await action();
|
|
2060
|
+
const dryRunMetadata = opts.dryRun ? {
|
|
2061
|
+
dryRun: true,
|
|
2062
|
+
targetWorkspace: opts.__dryRunOriginal ?? resolveWorkspacePathBase(opts),
|
|
2063
|
+
sandboxWorkspace: opts.__dryRunWorkspace
|
|
2064
|
+
} : {};
|
|
1304
2065
|
if (wantsJson(opts)) {
|
|
1305
|
-
console.log(JSON.stringify({ ok: true, data: result }, null, 2));
|
|
2066
|
+
console.log(JSON.stringify({ ok: true, ...dryRunMetadata, data: result }, null, 2));
|
|
1306
2067
|
return;
|
|
1307
2068
|
}
|
|
2069
|
+
if (opts.dryRun) {
|
|
2070
|
+
console.log(
|
|
2071
|
+
[
|
|
2072
|
+
"[dry-run] Executed against sandbox workspace and discarded changes.",
|
|
2073
|
+
`Target: ${opts.__dryRunOriginal ?? resolveWorkspacePathBase(opts)}`,
|
|
2074
|
+
`Sandbox: ${opts.__dryRunWorkspace ?? "n/a"}`
|
|
2075
|
+
].join(" ")
|
|
2076
|
+
);
|
|
2077
|
+
}
|
|
1308
2078
|
const lines = renderText(result);
|
|
1309
2079
|
for (const line of lines) console.log(line);
|
|
1310
2080
|
} catch (error) {
|
|
1311
2081
|
const message = error instanceof Error ? error.message : String(error);
|
|
1312
2082
|
if (wantsJson(opts)) {
|
|
1313
|
-
console.error(JSON.stringify({ ok: false, error: message }, null, 2));
|
|
2083
|
+
console.error(JSON.stringify({ ok: false, dryRun: !!opts.dryRun, error: message }, null, 2));
|
|
1314
2084
|
} else {
|
|
1315
2085
|
console.error(`Error: ${message}`);
|
|
1316
2086
|
}
|
|
2087
|
+
cleanupDryRunSandbox(opts);
|
|
1317
2088
|
process.exit(1);
|
|
2089
|
+
} finally {
|
|
2090
|
+
cleanupDryRunSandbox(opts);
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
function cleanupDryRunSandbox(opts) {
|
|
2094
|
+
if (!opts.dryRun || !opts.__dryRunWorkspaceRoot) return;
|
|
2095
|
+
if (fs.existsSync(opts.__dryRunWorkspaceRoot)) {
|
|
2096
|
+
fs.rmSync(opts.__dryRunWorkspaceRoot, { recursive: true, force: true });
|
|
1318
2097
|
}
|
|
2098
|
+
delete opts.__dryRunWorkspaceRoot;
|
|
2099
|
+
delete opts.__dryRunWorkspace;
|
|
2100
|
+
delete opts.__dryRunOriginal;
|
|
1319
2101
|
}
|