@versatly/workgraph 0.2.0 → 1.0.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 +56 -2
- package/dist/chunk-OJ6KOGB2.js +2638 -0
- package/dist/chunk-R2MLGBHB.js +6043 -0
- package/dist/cli.js +855 -17
- package/dist/index.d.ts +921 -12
- package/dist/index.js +43 -7
- package/dist/mcp-server-fU6U6ht8.d.ts +20 -0
- package/dist/mcp-server.d.ts +2 -0
- package/dist/mcp-server.js +8 -0
- package/package.json +9 -3
- package/dist/chunk-XUMA4O2Z.js +0 -2817
package/dist/cli.js
CHANGED
|
@@ -1,25 +1,38 @@
|
|
|
1
1
|
import {
|
|
2
|
+
agent_exports,
|
|
3
|
+
autonomy_daemon_exports,
|
|
2
4
|
bases_exports,
|
|
3
5
|
board_exports,
|
|
6
|
+
clawdapus_exports,
|
|
4
7
|
command_center_exports,
|
|
8
|
+
diagnostics_exports,
|
|
9
|
+
integration_exports,
|
|
10
|
+
onboard_exports,
|
|
11
|
+
search_qmd_adapter_exports,
|
|
12
|
+
skill_exports,
|
|
13
|
+
trigger_exports,
|
|
14
|
+
workspace_exports
|
|
15
|
+
} from "./chunk-OJ6KOGB2.js";
|
|
16
|
+
import {
|
|
17
|
+
autonomy_exports,
|
|
5
18
|
dispatch_exports,
|
|
19
|
+
gate_exports,
|
|
6
20
|
graph_exports,
|
|
7
21
|
ledger_exports,
|
|
8
|
-
|
|
22
|
+
mcp_server_exports,
|
|
9
23
|
orientation_exports,
|
|
10
24
|
policy_exports,
|
|
11
25
|
query_exports,
|
|
12
26
|
registry_exports,
|
|
13
|
-
search_qmd_adapter_exports,
|
|
14
|
-
skill_exports,
|
|
15
27
|
store_exports,
|
|
28
|
+
thread_audit_exports,
|
|
16
29
|
thread_exports,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
} from "./chunk-XUMA4O2Z.js";
|
|
30
|
+
trigger_engine_exports
|
|
31
|
+
} from "./chunk-R2MLGBHB.js";
|
|
20
32
|
|
|
21
33
|
// src/cli.ts
|
|
22
34
|
import fs from "fs";
|
|
35
|
+
import os from "os";
|
|
23
36
|
import path from "path";
|
|
24
37
|
import { Command } from "commander";
|
|
25
38
|
var DEFAULT_ACTOR = process.env.WORKGRAPH_AGENT || process.env.USER || "anonymous";
|
|
@@ -41,7 +54,7 @@ addWorkspaceOption(
|
|
|
41
54
|
(targetPath, opts) => runCommand(
|
|
42
55
|
opts,
|
|
43
56
|
() => {
|
|
44
|
-
const workspacePath =
|
|
57
|
+
const workspacePath = resolveInitTargetPath(targetPath, opts);
|
|
45
58
|
const result = workspace_exports.initWorkspace(workspacePath, {
|
|
46
59
|
name: opts.name,
|
|
47
60
|
createTypeDirs: opts.typeDirs,
|
|
@@ -163,13 +176,17 @@ addWorkspaceOption(
|
|
|
163
176
|
)
|
|
164
177
|
);
|
|
165
178
|
addWorkspaceOption(
|
|
166
|
-
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")
|
|
179
|
+
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")
|
|
167
180
|
).action(
|
|
168
181
|
(threadPath, opts) => runCommand(
|
|
169
182
|
opts,
|
|
170
183
|
() => {
|
|
171
184
|
const workspacePath = resolveWorkspacePath(opts);
|
|
172
|
-
return {
|
|
185
|
+
return {
|
|
186
|
+
thread: thread_exports.claim(workspacePath, threadPath, opts.actor, {
|
|
187
|
+
leaseTtlMinutes: Number.parseFloat(String(opts.leaseTtlMinutes))
|
|
188
|
+
})
|
|
189
|
+
};
|
|
173
190
|
},
|
|
174
191
|
(result) => [`Claimed: ${result.thread.path}`, `Owner: ${String(result.thread.fields.owner)}`]
|
|
175
192
|
)
|
|
@@ -198,6 +215,18 @@ addWorkspaceOption(
|
|
|
198
215
|
(result) => [`Done: ${result.thread.path}`]
|
|
199
216
|
)
|
|
200
217
|
);
|
|
218
|
+
addWorkspaceOption(
|
|
219
|
+
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")
|
|
220
|
+
).action(
|
|
221
|
+
(threadPath, opts) => runCommand(
|
|
222
|
+
opts,
|
|
223
|
+
() => {
|
|
224
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
225
|
+
return { thread: thread_exports.reopen(workspacePath, threadPath, opts.actor, opts.reason) };
|
|
226
|
+
},
|
|
227
|
+
(result) => [`Reopened: ${result.thread.path}`, `Status: ${String(result.thread.fields.status)}`]
|
|
228
|
+
)
|
|
229
|
+
);
|
|
201
230
|
addWorkspaceOption(
|
|
202
231
|
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")
|
|
203
232
|
).action(
|
|
@@ -224,6 +253,63 @@ addWorkspaceOption(
|
|
|
224
253
|
(result) => [`Unblocked: ${result.thread.path}`]
|
|
225
254
|
)
|
|
226
255
|
);
|
|
256
|
+
addWorkspaceOption(
|
|
257
|
+
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")
|
|
258
|
+
).action(
|
|
259
|
+
(threadPath, opts) => runCommand(
|
|
260
|
+
opts,
|
|
261
|
+
() => {
|
|
262
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
263
|
+
return thread_exports.heartbeatClaim(
|
|
264
|
+
workspacePath,
|
|
265
|
+
opts.actor,
|
|
266
|
+
threadPath,
|
|
267
|
+
{
|
|
268
|
+
ttlMinutes: Number.parseFloat(String(opts.ttlMinutes))
|
|
269
|
+
}
|
|
270
|
+
);
|
|
271
|
+
},
|
|
272
|
+
(result) => [
|
|
273
|
+
`Heartbeat actor: ${result.actor}`,
|
|
274
|
+
`Touched leases: ${result.touched.length}`,
|
|
275
|
+
...result.touched.length > 0 ? result.touched.map((entry) => `- ${entry.threadPath} expires=${entry.expiresAt}`) : [],
|
|
276
|
+
...result.skipped.length > 0 ? result.skipped.map((entry) => `SKIP ${entry.threadPath}: ${entry.reason}`) : []
|
|
277
|
+
]
|
|
278
|
+
)
|
|
279
|
+
);
|
|
280
|
+
addWorkspaceOption(
|
|
281
|
+
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")
|
|
282
|
+
).action(
|
|
283
|
+
(opts) => runCommand(
|
|
284
|
+
opts,
|
|
285
|
+
() => {
|
|
286
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
287
|
+
return thread_exports.reapStaleClaims(workspacePath, opts.actor, {
|
|
288
|
+
limit: opts.limit ? Number.parseInt(String(opts.limit), 10) : void 0
|
|
289
|
+
});
|
|
290
|
+
},
|
|
291
|
+
(result) => [
|
|
292
|
+
`Reaper actor: ${result.actor}`,
|
|
293
|
+
`Scanned stale leases: ${result.scanned}`,
|
|
294
|
+
`Reaped: ${result.reaped.length}`,
|
|
295
|
+
...result.reaped.length > 0 ? result.reaped.map((entry) => `- ${entry.threadPath} (prev=${entry.previousOwner})`) : [],
|
|
296
|
+
...result.skipped.length > 0 ? result.skipped.map((entry) => `SKIP ${entry.threadPath}: ${entry.reason}`) : []
|
|
297
|
+
]
|
|
298
|
+
)
|
|
299
|
+
);
|
|
300
|
+
addWorkspaceOption(
|
|
301
|
+
threadCmd.command("leases").description("List claim leases and staleness state").option("--json", "Emit structured JSON output")
|
|
302
|
+
).action(
|
|
303
|
+
(opts) => runCommand(
|
|
304
|
+
opts,
|
|
305
|
+
() => {
|
|
306
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
307
|
+
const leases = thread_exports.listClaimLeaseStatus(workspacePath);
|
|
308
|
+
return { leases, count: leases.length };
|
|
309
|
+
},
|
|
310
|
+
(result) => result.leases.map((lease) => `${lease.stale ? "STALE" : "LIVE"} ${lease.owner} -> ${lease.target} expires=${lease.expiresAt}`)
|
|
311
|
+
)
|
|
312
|
+
);
|
|
227
313
|
addWorkspaceOption(
|
|
228
314
|
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")
|
|
229
315
|
).action(
|
|
@@ -241,6 +327,58 @@ addWorkspaceOption(
|
|
|
241
327
|
(result) => [`Created ${result.children.length} sub-thread(s).`]
|
|
242
328
|
)
|
|
243
329
|
);
|
|
330
|
+
var agentCmd = program.command("agent").description("Track agent presence heartbeats");
|
|
331
|
+
addWorkspaceOption(
|
|
332
|
+
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")
|
|
333
|
+
).action(
|
|
334
|
+
(name, opts) => runCommand(
|
|
335
|
+
opts,
|
|
336
|
+
() => {
|
|
337
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
338
|
+
return {
|
|
339
|
+
presence: agent_exports.heartbeat(workspacePath, name, {
|
|
340
|
+
actor: opts.actor,
|
|
341
|
+
status: normalizeAgentPresenceStatus(opts.status),
|
|
342
|
+
currentTask: opts.currentTask,
|
|
343
|
+
capabilities: csv(opts.capabilities)
|
|
344
|
+
})
|
|
345
|
+
};
|
|
346
|
+
},
|
|
347
|
+
(result) => [
|
|
348
|
+
`Heartbeat: ${String(result.presence.fields.name)} [${String(result.presence.fields.status)}]`,
|
|
349
|
+
`Last seen: ${String(result.presence.fields.last_seen)}`,
|
|
350
|
+
`Current task: ${String(result.presence.fields.current_task ?? "none")}`
|
|
351
|
+
]
|
|
352
|
+
)
|
|
353
|
+
);
|
|
354
|
+
addWorkspaceOption(
|
|
355
|
+
agentCmd.command("list").description("List known agent presence entries").option("--json", "Emit structured JSON output")
|
|
356
|
+
).action(
|
|
357
|
+
(opts) => runCommand(
|
|
358
|
+
opts,
|
|
359
|
+
() => {
|
|
360
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
361
|
+
const agents = agent_exports.list(workspacePath);
|
|
362
|
+
return {
|
|
363
|
+
agents,
|
|
364
|
+
count: agents.length
|
|
365
|
+
};
|
|
366
|
+
},
|
|
367
|
+
(result) => {
|
|
368
|
+
if (result.agents.length === 0) return ["No agent presence entries found."];
|
|
369
|
+
return [
|
|
370
|
+
...result.agents.map((entry) => {
|
|
371
|
+
const name = String(entry.fields.name ?? entry.path);
|
|
372
|
+
const status = String(entry.fields.status ?? "unknown");
|
|
373
|
+
const task = String(entry.fields.current_task ?? "none");
|
|
374
|
+
const lastSeen = String(entry.fields.last_seen ?? "unknown");
|
|
375
|
+
return `${name} [${status}] task=${task} last_seen=${lastSeen}`;
|
|
376
|
+
}),
|
|
377
|
+
`${result.count} agent(s)`
|
|
378
|
+
];
|
|
379
|
+
}
|
|
380
|
+
)
|
|
381
|
+
);
|
|
244
382
|
var primitiveCmd = program.command("primitive").description("Manage primitive type definitions and instances");
|
|
245
383
|
addWorkspaceOption(
|
|
246
384
|
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")
|
|
@@ -278,6 +416,8 @@ addWorkspaceOption(
|
|
|
278
416
|
]
|
|
279
417
|
)
|
|
280
418
|
);
|
|
419
|
+
registerPrimitiveSchemaCommand("schema", "Show supported fields for a primitive type");
|
|
420
|
+
registerPrimitiveSchemaCommand("fields", "Alias for schema");
|
|
281
421
|
var basesCmd = program.command("bases").description("Generate Obsidian .base files from primitive-registry.yaml");
|
|
282
422
|
addWorkspaceOption(
|
|
283
423
|
basesCmd.command("sync-registry").description("Sync .workgraph/primitive-registry.yaml from active registry").option("--json", "Emit structured JSON output")
|
|
@@ -332,6 +472,46 @@ addWorkspaceOption(
|
|
|
332
472
|
(result) => result.types.map((t) => `${t.name} (${t.directory}/) ${t.builtIn ? "[built-in]" : ""}`)
|
|
333
473
|
)
|
|
334
474
|
);
|
|
475
|
+
function registerPrimitiveSchemaCommand(commandName, description) {
|
|
476
|
+
addWorkspaceOption(
|
|
477
|
+
primitiveCmd.command(`${commandName} <typeName>`).description(description).option("--json", "Emit structured JSON output")
|
|
478
|
+
).action(
|
|
479
|
+
(typeName, opts) => runCommand(
|
|
480
|
+
opts,
|
|
481
|
+
() => {
|
|
482
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
483
|
+
const typeDef = registry_exports.getType(workspacePath, typeName);
|
|
484
|
+
if (!typeDef) {
|
|
485
|
+
throw new Error(`Unknown primitive type "${typeName}". Use \`workgraph primitive list\` to inspect available types.`);
|
|
486
|
+
}
|
|
487
|
+
const fields = Object.entries(typeDef.fields).map(([name, definition]) => ({
|
|
488
|
+
name,
|
|
489
|
+
type: definition.type,
|
|
490
|
+
required: definition.required === true,
|
|
491
|
+
default: definition.default,
|
|
492
|
+
enum: definition.enum ?? [],
|
|
493
|
+
description: definition.description ?? "",
|
|
494
|
+
template: definition.template ?? void 0,
|
|
495
|
+
pattern: definition.pattern ?? void 0,
|
|
496
|
+
refTypes: definition.refTypes ?? []
|
|
497
|
+
}));
|
|
498
|
+
return {
|
|
499
|
+
type: typeDef.name,
|
|
500
|
+
description: typeDef.description,
|
|
501
|
+
directory: typeDef.directory,
|
|
502
|
+
builtIn: typeDef.builtIn,
|
|
503
|
+
fields
|
|
504
|
+
};
|
|
505
|
+
},
|
|
506
|
+
(result) => [
|
|
507
|
+
`Type: ${result.type}`,
|
|
508
|
+
`Directory: ${result.directory}/`,
|
|
509
|
+
`Built-in: ${result.builtIn}`,
|
|
510
|
+
...result.fields.map((field) => `- ${field.name}: ${field.type}${field.required ? " (required)" : ""}${field.description ? ` \u2014 ${field.description}` : ""}`)
|
|
511
|
+
]
|
|
512
|
+
)
|
|
513
|
+
);
|
|
514
|
+
}
|
|
335
515
|
addWorkspaceOption(
|
|
336
516
|
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")
|
|
337
517
|
).action(
|
|
@@ -348,7 +528,7 @@ addWorkspaceOption(
|
|
|
348
528
|
)
|
|
349
529
|
);
|
|
350
530
|
addWorkspaceOption(
|
|
351
|
-
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")
|
|
531
|
+
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")
|
|
352
532
|
).action(
|
|
353
533
|
(targetPath, opts) => runCommand(
|
|
354
534
|
opts,
|
|
@@ -360,7 +540,9 @@ addWorkspaceOption(
|
|
|
360
540
|
body = fs.readFileSync(path.resolve(opts.bodyFile), "utf-8");
|
|
361
541
|
}
|
|
362
542
|
return {
|
|
363
|
-
instance: store_exports.update(workspacePath, targetPath, updates, body, opts.actor
|
|
543
|
+
instance: store_exports.update(workspacePath, targetPath, updates, body, opts.actor, {
|
|
544
|
+
expectedEtag: opts.etag
|
|
545
|
+
})
|
|
364
546
|
};
|
|
365
547
|
},
|
|
366
548
|
(result) => [`Updated ${result.instance.type}: ${result.instance.path}`]
|
|
@@ -512,6 +694,40 @@ addWorkspaceOption(
|
|
|
512
694
|
]
|
|
513
695
|
)
|
|
514
696
|
);
|
|
697
|
+
var integrationCmd = program.command("integration").description("Manage optional third-party integrations");
|
|
698
|
+
addWorkspaceOption(
|
|
699
|
+
integrationCmd.command("list").description("List supported optional integrations").option("--json", "Emit structured JSON output")
|
|
700
|
+
).action(
|
|
701
|
+
(opts) => runCommand(
|
|
702
|
+
opts,
|
|
703
|
+
() => ({
|
|
704
|
+
integrations: integration_exports.listIntegrations()
|
|
705
|
+
}),
|
|
706
|
+
(result) => result.integrations.map((integration) => `${integration.id} (${integration.defaultTitle}) -> ${integration.defaultSourceUrl}`)
|
|
707
|
+
)
|
|
708
|
+
);
|
|
709
|
+
addWorkspaceOption(
|
|
710
|
+
integrationCmd.command("install <integrationName>").description("Install an optional integration into this workspace").option("-a, --actor <name>", "Agent name", DEFAULT_ACTOR).option("--owner <name>", "Skill owner override").option("--title <title>", "Skill title to store in workgraph").option("--source-url <url>", "Source URL override for integration content").option("--force", "Overwrite an existing imported integration skill").option("--json", "Emit structured JSON output")
|
|
711
|
+
).action(
|
|
712
|
+
(integrationName, opts) => runCommand(
|
|
713
|
+
opts,
|
|
714
|
+
() => installNamedIntegration(resolveWorkspacePath(opts), integrationName, opts),
|
|
715
|
+
renderInstalledIntegrationResult
|
|
716
|
+
)
|
|
717
|
+
);
|
|
718
|
+
addWorkspaceOption(
|
|
719
|
+
integrationCmd.command("clawdapus").description("Import Clawdapus SKILL.md into this workspace").option("-a, --actor <name>", "Agent name", DEFAULT_ACTOR).option("--owner <name>", "Skill owner override").option("--title <title>", "Skill title to store in workgraph", "clawdapus").option(
|
|
720
|
+
"--source-url <url>",
|
|
721
|
+
"Source URL for Clawdapus SKILL.md",
|
|
722
|
+
clawdapus_exports.DEFAULT_CLAWDAPUS_SKILL_URL
|
|
723
|
+
).option("--force", "Overwrite an existing imported Clawdapus skill").option("--json", "Emit structured JSON output")
|
|
724
|
+
).action(
|
|
725
|
+
(opts) => runCommand(
|
|
726
|
+
opts,
|
|
727
|
+
() => installNamedIntegration(resolveWorkspacePath(opts), "clawdapus", opts),
|
|
728
|
+
renderInstalledIntegrationResult
|
|
729
|
+
)
|
|
730
|
+
);
|
|
515
731
|
var ledgerCmd = program.command("ledger").description("Inspect the append-only workgraph ledger");
|
|
516
732
|
addWorkspaceOption(
|
|
517
733
|
ledgerCmd.command("show").description("Show recent ledger entries").option("-n, --count <n>", "Number of entries", "20").option("--actor <name>", "Filter by actor").option("--json", "Emit structured JSON output")
|
|
@@ -614,6 +830,26 @@ addWorkspaceOption(
|
|
|
614
830
|
]
|
|
615
831
|
)
|
|
616
832
|
);
|
|
833
|
+
addWorkspaceOption(
|
|
834
|
+
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")
|
|
835
|
+
).action(
|
|
836
|
+
(opts) => runCommand(
|
|
837
|
+
opts,
|
|
838
|
+
() => {
|
|
839
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
840
|
+
const report = thread_audit_exports.reconcileThreadState(workspacePath);
|
|
841
|
+
if (opts.failOnIssues && !report.ok) {
|
|
842
|
+
throw new Error(`Ledger reconcile found ${report.issues.length} issue(s).`);
|
|
843
|
+
}
|
|
844
|
+
return report;
|
|
845
|
+
},
|
|
846
|
+
(result) => [
|
|
847
|
+
`Reconcile ok: ${result.ok}`,
|
|
848
|
+
`Threads: ${result.totalThreads} Claims: ${result.totalClaims} Leases: ${result.totalLeases}`,
|
|
849
|
+
...result.issues.length > 0 ? result.issues.map((issue) => `${issue.kind}: ${issue.path} \u2014 ${issue.message}`) : ["No reconcile issues found."]
|
|
850
|
+
]
|
|
851
|
+
)
|
|
852
|
+
);
|
|
617
853
|
addWorkspaceOption(
|
|
618
854
|
ledgerCmd.command("seal").description("Rebuild ledger index + hash-chain state from ledger.jsonl").option("--json", "Emit structured JSON output")
|
|
619
855
|
).action(
|
|
@@ -635,6 +871,96 @@ addWorkspaceOption(
|
|
|
635
871
|
]
|
|
636
872
|
)
|
|
637
873
|
);
|
|
874
|
+
addWorkspaceOption(
|
|
875
|
+
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")
|
|
876
|
+
).action(
|
|
877
|
+
(opts) => runCommand(
|
|
878
|
+
opts,
|
|
879
|
+
() => {
|
|
880
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
881
|
+
const staleAfterMinutes = Number.parseInt(String(opts.staleAfterMinutes), 10);
|
|
882
|
+
const safeStaleAfterMinutes = Number.isNaN(staleAfterMinutes) ? 60 : Math.max(1, staleAfterMinutes);
|
|
883
|
+
return diagnostics_exports.diagnoseVaultHealth(workspacePath, {
|
|
884
|
+
fix: !!opts.fix,
|
|
885
|
+
actor: opts.actor,
|
|
886
|
+
staleAfterMs: safeStaleAfterMinutes * 60 * 1e3
|
|
887
|
+
});
|
|
888
|
+
},
|
|
889
|
+
(result) => diagnostics_exports.renderDoctorReport(result)
|
|
890
|
+
)
|
|
891
|
+
);
|
|
892
|
+
addWorkspaceOption(
|
|
893
|
+
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")
|
|
894
|
+
).action(
|
|
895
|
+
(opts) => runCommand(
|
|
896
|
+
opts,
|
|
897
|
+
() => {
|
|
898
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
899
|
+
return diagnostics_exports.replayLedger(workspacePath, {
|
|
900
|
+
type: opts.type,
|
|
901
|
+
actor: opts.actor,
|
|
902
|
+
primitive: opts.primitive,
|
|
903
|
+
since: opts.since,
|
|
904
|
+
until: opts.until
|
|
905
|
+
});
|
|
906
|
+
},
|
|
907
|
+
(result) => diagnostics_exports.renderReplayText(result, {
|
|
908
|
+
color: opts.color !== false && !wantsJson(opts)
|
|
909
|
+
})
|
|
910
|
+
)
|
|
911
|
+
);
|
|
912
|
+
addWorkspaceOption(
|
|
913
|
+
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")
|
|
914
|
+
).action(
|
|
915
|
+
(opts) => runCommand(
|
|
916
|
+
opts,
|
|
917
|
+
() => {
|
|
918
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
919
|
+
const parsedDepth = Number.parseInt(String(opts.depth), 10);
|
|
920
|
+
const parsedTop = Number.parseInt(String(opts.top), 10);
|
|
921
|
+
return diagnostics_exports.visualizeVaultGraph(workspacePath, {
|
|
922
|
+
focus: opts.focus,
|
|
923
|
+
depth: Number.isNaN(parsedDepth) ? 2 : Math.max(1, parsedDepth),
|
|
924
|
+
top: Number.isNaN(parsedTop) ? 10 : Math.max(1, parsedTop),
|
|
925
|
+
color: opts.color !== false && !wantsJson(opts)
|
|
926
|
+
});
|
|
927
|
+
},
|
|
928
|
+
(result) => [
|
|
929
|
+
...result.rendered.split("\n"),
|
|
930
|
+
"",
|
|
931
|
+
`Nodes: ${result.nodeCount}`,
|
|
932
|
+
`Edges: ${result.edgeCount}`,
|
|
933
|
+
...result.focus ? [`Focus: ${result.focus}`] : []
|
|
934
|
+
]
|
|
935
|
+
)
|
|
936
|
+
);
|
|
937
|
+
addWorkspaceOption(
|
|
938
|
+
program.command("stats").description("Show detailed vault statistics and graph/ledger health metrics").option("--json", "Emit structured JSON output")
|
|
939
|
+
).action(
|
|
940
|
+
(opts) => runCommand(
|
|
941
|
+
opts,
|
|
942
|
+
() => {
|
|
943
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
944
|
+
return diagnostics_exports.computeVaultStats(workspacePath);
|
|
945
|
+
},
|
|
946
|
+
(result) => diagnostics_exports.renderStatsReport(result)
|
|
947
|
+
)
|
|
948
|
+
);
|
|
949
|
+
addWorkspaceOption(
|
|
950
|
+
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")
|
|
951
|
+
).action(
|
|
952
|
+
(opts) => runCommand(
|
|
953
|
+
opts,
|
|
954
|
+
() => {
|
|
955
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
956
|
+
return diagnostics_exports.generateLedgerChangelog(workspacePath, {
|
|
957
|
+
since: opts.since,
|
|
958
|
+
until: opts.until
|
|
959
|
+
});
|
|
960
|
+
},
|
|
961
|
+
(result) => diagnostics_exports.renderChangelogText(result)
|
|
962
|
+
)
|
|
963
|
+
);
|
|
638
964
|
addWorkspaceOption(
|
|
639
965
|
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")
|
|
640
966
|
).action(
|
|
@@ -847,6 +1173,114 @@ addWorkspaceOption(
|
|
|
847
1173
|
]
|
|
848
1174
|
)
|
|
849
1175
|
);
|
|
1176
|
+
addWorkspaceOption(
|
|
1177
|
+
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")
|
|
1178
|
+
).action(
|
|
1179
|
+
(slug, opts) => runCommand(
|
|
1180
|
+
opts,
|
|
1181
|
+
() => {
|
|
1182
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1183
|
+
return graph_exports.graphNeighborhoodQuery(workspacePath, slug, {
|
|
1184
|
+
depth: parseNonNegativeIntOption(opts.depth, "depth"),
|
|
1185
|
+
refresh: !!opts.refresh
|
|
1186
|
+
});
|
|
1187
|
+
},
|
|
1188
|
+
(result) => [
|
|
1189
|
+
`Center: ${result.center.path} (${result.center.exists ? "exists" : "missing"})`,
|
|
1190
|
+
`Depth: ${result.depth}`,
|
|
1191
|
+
`Connected nodes: ${result.connectedNodes.length}`,
|
|
1192
|
+
`Edges in neighborhood: ${result.edges.length}`
|
|
1193
|
+
]
|
|
1194
|
+
)
|
|
1195
|
+
);
|
|
1196
|
+
addWorkspaceOption(
|
|
1197
|
+
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")
|
|
1198
|
+
).action(
|
|
1199
|
+
(slug, opts) => runCommand(
|
|
1200
|
+
opts,
|
|
1201
|
+
() => {
|
|
1202
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1203
|
+
return graph_exports.graphImpactAnalysis(workspacePath, slug, {
|
|
1204
|
+
refresh: !!opts.refresh
|
|
1205
|
+
});
|
|
1206
|
+
},
|
|
1207
|
+
(result) => [
|
|
1208
|
+
`Target: ${result.target.path} (${result.target.exists ? "exists" : "missing"})`,
|
|
1209
|
+
`Total references: ${result.totalReferences}`,
|
|
1210
|
+
...result.groups.map((group) => `${group.type}: ${group.referenceCount}`)
|
|
1211
|
+
]
|
|
1212
|
+
)
|
|
1213
|
+
);
|
|
1214
|
+
addWorkspaceOption(
|
|
1215
|
+
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")
|
|
1216
|
+
).action(
|
|
1217
|
+
(slug, opts) => runCommand(
|
|
1218
|
+
opts,
|
|
1219
|
+
() => {
|
|
1220
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1221
|
+
return graph_exports.graphContextAssembly(workspacePath, slug, {
|
|
1222
|
+
budgetTokens: parsePositiveIntOption(opts.budget, "budget"),
|
|
1223
|
+
refresh: !!opts.refresh
|
|
1224
|
+
});
|
|
1225
|
+
},
|
|
1226
|
+
(result) => [
|
|
1227
|
+
`Center: ${result.center.path}`,
|
|
1228
|
+
`Budget: ${result.budgetTokens} tokens`,
|
|
1229
|
+
`Used: ${result.usedTokens} tokens`,
|
|
1230
|
+
`Sections: ${result.sections.length}`,
|
|
1231
|
+
"",
|
|
1232
|
+
result.markdown
|
|
1233
|
+
]
|
|
1234
|
+
)
|
|
1235
|
+
);
|
|
1236
|
+
addWorkspaceOption(
|
|
1237
|
+
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")
|
|
1238
|
+
).action(
|
|
1239
|
+
(slug, opts) => runCommand(
|
|
1240
|
+
opts,
|
|
1241
|
+
() => {
|
|
1242
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1243
|
+
return graph_exports.graphTypedEdges(workspacePath, slug, {
|
|
1244
|
+
refresh: !!opts.refresh
|
|
1245
|
+
});
|
|
1246
|
+
},
|
|
1247
|
+
(result) => [
|
|
1248
|
+
`Node: ${result.node.path} (${result.node.exists ? "exists" : "missing"})`,
|
|
1249
|
+
`Outgoing edges: ${result.outgoing.length}`,
|
|
1250
|
+
`Incoming edges: ${result.incoming.length}`,
|
|
1251
|
+
...result.outgoing.map((edge) => `OUT ${edge.type} ${edge.from} -> ${edge.to}`),
|
|
1252
|
+
...result.incoming.map((edge) => `IN ${edge.type} ${edge.from} -> ${edge.to}`)
|
|
1253
|
+
]
|
|
1254
|
+
)
|
|
1255
|
+
);
|
|
1256
|
+
addWorkspaceOption(
|
|
1257
|
+
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")
|
|
1258
|
+
).action(
|
|
1259
|
+
(slug, opts) => runCommand(
|
|
1260
|
+
opts,
|
|
1261
|
+
() => {
|
|
1262
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1263
|
+
const format = String(opts.format ?? "md").trim().toLowerCase();
|
|
1264
|
+
if (format !== "md") {
|
|
1265
|
+
throw new Error(`Invalid --format "${opts.format}". Supported formats: md.`);
|
|
1266
|
+
}
|
|
1267
|
+
return graph_exports.graphExportSubgraph(workspacePath, slug, {
|
|
1268
|
+
depth: parseNonNegativeIntOption(opts.depth, "depth"),
|
|
1269
|
+
format,
|
|
1270
|
+
outputDir: opts.outputDir,
|
|
1271
|
+
refresh: !!opts.refresh
|
|
1272
|
+
});
|
|
1273
|
+
},
|
|
1274
|
+
(result) => [
|
|
1275
|
+
`Exported subgraph: ${result.outputDirectory}`,
|
|
1276
|
+
`Center: ${result.center.path}`,
|
|
1277
|
+
`Depth: ${result.depth}`,
|
|
1278
|
+
`Nodes: ${result.exportedNodes.length}`,
|
|
1279
|
+
`Edges: ${result.exportedEdgeCount}`,
|
|
1280
|
+
`Manifest: ${result.manifestPath}`
|
|
1281
|
+
]
|
|
1282
|
+
)
|
|
1283
|
+
);
|
|
850
1284
|
addWorkspaceOption(
|
|
851
1285
|
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")
|
|
852
1286
|
).action(
|
|
@@ -913,6 +1347,33 @@ addWorkspaceOption(
|
|
|
913
1347
|
(result) => result.parties.map((party) => `${party.id} [${party.roles.join(", ")}]`)
|
|
914
1348
|
)
|
|
915
1349
|
);
|
|
1350
|
+
var gateCmd = program.command("gate").description("Evaluate thread quality gates before claim");
|
|
1351
|
+
addWorkspaceOption(
|
|
1352
|
+
gateCmd.command("check <threadRef>").description("Check policy-gate status for one thread").option("--json", "Emit structured JSON output")
|
|
1353
|
+
).action(
|
|
1354
|
+
(threadRef, opts) => runCommand(
|
|
1355
|
+
opts,
|
|
1356
|
+
() => {
|
|
1357
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1358
|
+
return gate_exports.checkThreadGates(workspacePath, threadRef);
|
|
1359
|
+
},
|
|
1360
|
+
(result) => {
|
|
1361
|
+
const header = [`Gate check for ${result.threadPath}: ${result.allowed ? "PASSED" : "FAILED"}`];
|
|
1362
|
+
if (result.gates.length === 0) {
|
|
1363
|
+
return [...header, "No gates configured."];
|
|
1364
|
+
}
|
|
1365
|
+
const details = result.gates.map((gate) => {
|
|
1366
|
+
const failingRules = gate.rules.filter((rule) => !rule.ok);
|
|
1367
|
+
const gateLabel = gate.gatePath ?? gate.gateRef;
|
|
1368
|
+
if (failingRules.length === 0) {
|
|
1369
|
+
return `[pass] ${gateLabel}`;
|
|
1370
|
+
}
|
|
1371
|
+
return `[fail] ${gateLabel} :: ${failingRules.map((rule) => rule.message).join("; ")}`;
|
|
1372
|
+
});
|
|
1373
|
+
return [...header, ...details];
|
|
1374
|
+
}
|
|
1375
|
+
)
|
|
1376
|
+
);
|
|
916
1377
|
var dispatchCmd = program.command("dispatch").description("Programmatic runtime dispatch contract");
|
|
917
1378
|
addWorkspaceOption(
|
|
918
1379
|
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")
|
|
@@ -933,6 +1394,54 @@ addWorkspaceOption(
|
|
|
933
1394
|
(result) => [`Run created: ${result.run.id} [${result.run.status}]`]
|
|
934
1395
|
)
|
|
935
1396
|
);
|
|
1397
|
+
addWorkspaceOption(
|
|
1398
|
+
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")
|
|
1399
|
+
).action(
|
|
1400
|
+
(threadRef, opts) => runCommand(
|
|
1401
|
+
opts,
|
|
1402
|
+
() => {
|
|
1403
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1404
|
+
return dispatch_exports.claimThread(workspacePath, threadRef, opts.actor);
|
|
1405
|
+
},
|
|
1406
|
+
(result) => [
|
|
1407
|
+
`Claimed thread: ${result.thread.path}`,
|
|
1408
|
+
`Gates checked: ${result.gateCheck.gates.length}`
|
|
1409
|
+
]
|
|
1410
|
+
)
|
|
1411
|
+
);
|
|
1412
|
+
addWorkspaceOption(
|
|
1413
|
+
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")
|
|
1414
|
+
).action(
|
|
1415
|
+
(objective, opts) => runCommand(
|
|
1416
|
+
opts,
|
|
1417
|
+
async () => {
|
|
1418
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1419
|
+
return {
|
|
1420
|
+
run: await dispatch_exports.createAndExecuteRun(
|
|
1421
|
+
workspacePath,
|
|
1422
|
+
{
|
|
1423
|
+
actor: opts.actor,
|
|
1424
|
+
adapter: opts.adapter,
|
|
1425
|
+
objective,
|
|
1426
|
+
idempotencyKey: opts.idempotencyKey
|
|
1427
|
+
},
|
|
1428
|
+
{
|
|
1429
|
+
agents: csv(opts.agents),
|
|
1430
|
+
maxSteps: Number.parseInt(String(opts.maxSteps), 10),
|
|
1431
|
+
stepDelayMs: Number.parseInt(String(opts.stepDelayMs), 10),
|
|
1432
|
+
space: opts.space,
|
|
1433
|
+
createCheckpoint: opts.checkpoint
|
|
1434
|
+
}
|
|
1435
|
+
)
|
|
1436
|
+
};
|
|
1437
|
+
},
|
|
1438
|
+
(result) => [
|
|
1439
|
+
`Run executed: ${result.run.id} [${result.run.status}]`,
|
|
1440
|
+
...result.run.output ? [`Output: ${result.run.output}`] : [],
|
|
1441
|
+
...result.run.error ? [`Error: ${result.run.error}`] : []
|
|
1442
|
+
]
|
|
1443
|
+
)
|
|
1444
|
+
);
|
|
936
1445
|
addWorkspaceOption(
|
|
937
1446
|
dispatchCmd.command("list").description("List runs").option("--status <status>", "queued|running|succeeded|failed|cancelled").option("--limit <n>", "Result limit").option("--json", "Emit structured JSON output")
|
|
938
1447
|
).action(
|
|
@@ -964,6 +1473,31 @@ addWorkspaceOption(
|
|
|
964
1473
|
(result) => [`${result.run.id} [${result.run.status}]`]
|
|
965
1474
|
)
|
|
966
1475
|
);
|
|
1476
|
+
addWorkspaceOption(
|
|
1477
|
+
dispatchCmd.command("execute <runId>").description("Execute a queued/running run via adapter autonomous scheduling").option("-a, --actor <name>", "Actor", DEFAULT_ACTOR).option("--agents <actors>", "Comma-separated agent identities").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")
|
|
1478
|
+
).action(
|
|
1479
|
+
(runId, opts) => runCommand(
|
|
1480
|
+
opts,
|
|
1481
|
+
async () => {
|
|
1482
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1483
|
+
return {
|
|
1484
|
+
run: await dispatch_exports.executeRun(workspacePath, runId, {
|
|
1485
|
+
actor: opts.actor,
|
|
1486
|
+
agents: csv(opts.agents),
|
|
1487
|
+
maxSteps: Number.parseInt(String(opts.maxSteps), 10),
|
|
1488
|
+
stepDelayMs: Number.parseInt(String(opts.stepDelayMs), 10),
|
|
1489
|
+
space: opts.space,
|
|
1490
|
+
createCheckpoint: opts.checkpoint
|
|
1491
|
+
})
|
|
1492
|
+
};
|
|
1493
|
+
},
|
|
1494
|
+
(result) => [
|
|
1495
|
+
`Run executed: ${result.run.id} [${result.run.status}]`,
|
|
1496
|
+
...result.run.output ? [`Output: ${result.run.output}`] : [],
|
|
1497
|
+
...result.run.error ? [`Error: ${result.run.error}`] : []
|
|
1498
|
+
]
|
|
1499
|
+
)
|
|
1500
|
+
);
|
|
967
1501
|
addWorkspaceOption(
|
|
968
1502
|
dispatchCmd.command("followup <runId> <input>").description("Send follow-up input to a run").option("-a, --actor <name>", "Actor", DEFAULT_ACTOR).option("--json", "Emit structured JSON output")
|
|
969
1503
|
).action(
|
|
@@ -992,6 +1526,65 @@ addWorkspaceOption(
|
|
|
992
1526
|
(result) => [`Stopped run: ${result.run.id} [${result.run.status}]`]
|
|
993
1527
|
)
|
|
994
1528
|
);
|
|
1529
|
+
addWorkspaceOption(
|
|
1530
|
+
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")
|
|
1531
|
+
).action(
|
|
1532
|
+
(runId, opts) => runCommand(
|
|
1533
|
+
opts,
|
|
1534
|
+
() => {
|
|
1535
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1536
|
+
return {
|
|
1537
|
+
run: dispatch_exports.heartbeat(workspacePath, runId, {
|
|
1538
|
+
actor: opts.actor,
|
|
1539
|
+
leaseMinutes: opts.leaseMinutes ? Number.parseInt(String(opts.leaseMinutes), 10) : void 0
|
|
1540
|
+
})
|
|
1541
|
+
};
|
|
1542
|
+
},
|
|
1543
|
+
(result) => [
|
|
1544
|
+
`Heartbeated run: ${result.run.id}`,
|
|
1545
|
+
`Lease expires: ${String(result.run.leaseExpires ?? "none")}`,
|
|
1546
|
+
`Heartbeats: ${(result.run.heartbeats ?? []).length}`
|
|
1547
|
+
]
|
|
1548
|
+
)
|
|
1549
|
+
);
|
|
1550
|
+
addWorkspaceOption(
|
|
1551
|
+
dispatchCmd.command("reconcile").description("Requeue runs with expired leases").option("-a, --actor <name>", "Actor", DEFAULT_ACTOR).option("--json", "Emit structured JSON output")
|
|
1552
|
+
).action(
|
|
1553
|
+
(opts) => runCommand(
|
|
1554
|
+
opts,
|
|
1555
|
+
() => {
|
|
1556
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1557
|
+
return dispatch_exports.reconcileExpiredLeases(workspacePath, opts.actor);
|
|
1558
|
+
},
|
|
1559
|
+
(result) => [
|
|
1560
|
+
`Reconciled at: ${result.reconciledAt}`,
|
|
1561
|
+
`Inspected runs: ${result.inspectedRuns}`,
|
|
1562
|
+
`Requeued runs: ${result.requeuedRuns.length}`,
|
|
1563
|
+
...result.requeuedRuns.map((run) => `- ${run.id}`)
|
|
1564
|
+
]
|
|
1565
|
+
)
|
|
1566
|
+
);
|
|
1567
|
+
addWorkspaceOption(
|
|
1568
|
+
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")
|
|
1569
|
+
).action(
|
|
1570
|
+
(runId, opts) => runCommand(
|
|
1571
|
+
opts,
|
|
1572
|
+
() => {
|
|
1573
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1574
|
+
return dispatch_exports.handoffRun(workspacePath, runId, {
|
|
1575
|
+
actor: opts.actor,
|
|
1576
|
+
to: opts.to,
|
|
1577
|
+
reason: opts.reason,
|
|
1578
|
+
adapter: opts.adapter
|
|
1579
|
+
});
|
|
1580
|
+
},
|
|
1581
|
+
(result) => [
|
|
1582
|
+
`Handoff created: ${result.handoffRun.id} (from ${result.sourceRun.id})`,
|
|
1583
|
+
`Target agent: ${result.handoffRun.actor}`,
|
|
1584
|
+
`Objective: ${result.handoffRun.objective}`
|
|
1585
|
+
]
|
|
1586
|
+
)
|
|
1587
|
+
);
|
|
995
1588
|
addWorkspaceOption(
|
|
996
1589
|
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")
|
|
997
1590
|
).action(
|
|
@@ -1045,6 +1638,28 @@ addWorkspaceOption(
|
|
|
1045
1638
|
]
|
|
1046
1639
|
)
|
|
1047
1640
|
);
|
|
1641
|
+
var triggerEngineCmd = triggerCmd.command("engine").description("Run trigger engine");
|
|
1642
|
+
addWorkspaceOption(
|
|
1643
|
+
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")
|
|
1644
|
+
).action(
|
|
1645
|
+
(opts) => runCommand(
|
|
1646
|
+
opts,
|
|
1647
|
+
() => {
|
|
1648
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1649
|
+
return trigger_engine_exports.runTriggerEngineCycle(workspacePath, {
|
|
1650
|
+
actor: opts.actor
|
|
1651
|
+
});
|
|
1652
|
+
},
|
|
1653
|
+
(result) => [
|
|
1654
|
+
`Evaluated: ${result.evaluated} triggers`,
|
|
1655
|
+
`Fired: ${result.fired}`,
|
|
1656
|
+
`Errors: ${result.errors}`,
|
|
1657
|
+
...result.triggers.map(
|
|
1658
|
+
(t) => ` ${t.triggerPath}: ${t.fired ? "FIRED" : "skipped"} (${t.reason})${t.error ? ` error: ${t.error}` : ""}`
|
|
1659
|
+
)
|
|
1660
|
+
]
|
|
1661
|
+
)
|
|
1662
|
+
);
|
|
1048
1663
|
addWorkspaceOption(
|
|
1049
1664
|
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")
|
|
1050
1665
|
).action(
|
|
@@ -1107,17 +1722,177 @@ addWorkspaceOption(
|
|
|
1107
1722
|
(result) => [`Updated onboarding: ${result.onboarding.path} [${String(result.onboarding.fields.status)}]`]
|
|
1108
1723
|
)
|
|
1109
1724
|
);
|
|
1110
|
-
program.
|
|
1725
|
+
var autonomyCmd = program.command("autonomy").description("Run long-lived autonomous collaboration loops");
|
|
1726
|
+
addWorkspaceOption(
|
|
1727
|
+
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")
|
|
1728
|
+
).action(
|
|
1729
|
+
(opts) => runCommand(
|
|
1730
|
+
opts,
|
|
1731
|
+
async () => {
|
|
1732
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1733
|
+
return autonomy_exports.runAutonomyLoop(workspacePath, {
|
|
1734
|
+
actor: opts.actor,
|
|
1735
|
+
adapter: opts.adapter,
|
|
1736
|
+
agents: csv(opts.agents),
|
|
1737
|
+
watch: !!opts.watch,
|
|
1738
|
+
pollMs: Number.parseInt(String(opts.pollMs), 10),
|
|
1739
|
+
maxCycles: opts.maxCycles ? Number.parseInt(String(opts.maxCycles), 10) : void 0,
|
|
1740
|
+
maxIdleCycles: Number.parseInt(String(opts.maxIdleCycles), 10),
|
|
1741
|
+
maxSteps: Number.parseInt(String(opts.maxSteps), 10),
|
|
1742
|
+
stepDelayMs: Number.parseInt(String(opts.stepDelayMs), 10),
|
|
1743
|
+
space: opts.space,
|
|
1744
|
+
staleClaimMinutes: Number.parseInt(String(opts.staleClaimMinutes), 10),
|
|
1745
|
+
heartbeatFile: opts.heartbeatFile,
|
|
1746
|
+
executeTriggers: opts.executeTriggers,
|
|
1747
|
+
executeReadyThreads: opts.executeReadyThreads
|
|
1748
|
+
});
|
|
1749
|
+
},
|
|
1750
|
+
(result) => [
|
|
1751
|
+
`Cycles: ${result.cycles.length}`,
|
|
1752
|
+
`Final ready threads: ${result.finalReadyThreads}`,
|
|
1753
|
+
`Final drift status: ${result.finalDriftOk ? "ok" : "issues"}`,
|
|
1754
|
+
...result.cycles.map(
|
|
1755
|
+
(cycle) => `Cycle ${cycle.cycle}: ready=${cycle.readyThreads} trigger_actions=${cycle.triggerActions} run=${cycle.runStatus ?? "none"} drift_issues=${cycle.driftIssues}`
|
|
1756
|
+
)
|
|
1757
|
+
]
|
|
1758
|
+
)
|
|
1759
|
+
);
|
|
1760
|
+
var autonomyDaemonCmd = autonomyCmd.command("daemon").description("Manage autonomy process lifecycle (pid + heartbeat + logs)");
|
|
1761
|
+
addWorkspaceOption(
|
|
1762
|
+
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")
|
|
1763
|
+
).action(
|
|
1764
|
+
(opts) => runCommand(
|
|
1765
|
+
opts,
|
|
1766
|
+
() => {
|
|
1767
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1768
|
+
return autonomy_daemon_exports.startAutonomyDaemon(workspacePath, {
|
|
1769
|
+
cliEntrypointPath: process.argv[1] ?? path.resolve("bin/workgraph.js"),
|
|
1770
|
+
actor: opts.actor,
|
|
1771
|
+
adapter: opts.adapter,
|
|
1772
|
+
agents: csv(opts.agents),
|
|
1773
|
+
pollMs: Number.parseInt(String(opts.pollMs), 10),
|
|
1774
|
+
maxCycles: opts.maxCycles ? Number.parseInt(String(opts.maxCycles), 10) : void 0,
|
|
1775
|
+
maxSteps: Number.parseInt(String(opts.maxSteps), 10),
|
|
1776
|
+
stepDelayMs: Number.parseInt(String(opts.stepDelayMs), 10),
|
|
1777
|
+
space: opts.space,
|
|
1778
|
+
staleClaimMinutes: Number.parseInt(String(opts.staleClaimMinutes), 10),
|
|
1779
|
+
logPath: opts.logPath,
|
|
1780
|
+
heartbeatPath: opts.heartbeatPath,
|
|
1781
|
+
executeTriggers: opts.executeTriggers,
|
|
1782
|
+
executeReadyThreads: opts.executeReadyThreads
|
|
1783
|
+
});
|
|
1784
|
+
},
|
|
1785
|
+
(result) => [
|
|
1786
|
+
`Daemon running: ${result.running}`,
|
|
1787
|
+
...result.pid ? [`PID: ${result.pid}`] : [],
|
|
1788
|
+
`PID file: ${result.pidPath}`,
|
|
1789
|
+
`Heartbeat: ${result.heartbeatPath}`,
|
|
1790
|
+
`Log: ${result.logPath}`
|
|
1791
|
+
]
|
|
1792
|
+
)
|
|
1793
|
+
);
|
|
1794
|
+
addWorkspaceOption(
|
|
1795
|
+
autonomyDaemonCmd.command("status").description("Show autonomy daemon status").option("--json", "Emit structured JSON output")
|
|
1796
|
+
).action(
|
|
1797
|
+
(opts) => runCommand(
|
|
1798
|
+
opts,
|
|
1799
|
+
() => {
|
|
1800
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1801
|
+
return autonomy_daemon_exports.readAutonomyDaemonStatus(workspacePath);
|
|
1802
|
+
},
|
|
1803
|
+
(result) => [
|
|
1804
|
+
`Daemon running: ${result.running}`,
|
|
1805
|
+
...result.pid ? [`PID: ${result.pid}`] : [],
|
|
1806
|
+
...result.heartbeat ? [`Last heartbeat: ${result.heartbeat.ts}`] : ["Last heartbeat: none"],
|
|
1807
|
+
`PID file: ${result.pidPath}`,
|
|
1808
|
+
`Heartbeat: ${result.heartbeatPath}`,
|
|
1809
|
+
`Log: ${result.logPath}`
|
|
1810
|
+
]
|
|
1811
|
+
)
|
|
1812
|
+
);
|
|
1813
|
+
addWorkspaceOption(
|
|
1814
|
+
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")
|
|
1815
|
+
).action(
|
|
1816
|
+
(opts) => runCommand(
|
|
1817
|
+
opts,
|
|
1818
|
+
async () => {
|
|
1819
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1820
|
+
return autonomy_daemon_exports.stopAutonomyDaemon(workspacePath, {
|
|
1821
|
+
signal: String(opts.signal),
|
|
1822
|
+
timeoutMs: Number.parseInt(String(opts.timeoutMs), 10)
|
|
1823
|
+
});
|
|
1824
|
+
},
|
|
1825
|
+
(result) => [
|
|
1826
|
+
`Stopped: ${result.stopped}`,
|
|
1827
|
+
`Previously running: ${result.previouslyRunning}`,
|
|
1828
|
+
...result.pid ? [`PID: ${result.pid}`] : [],
|
|
1829
|
+
`Daemon running now: ${result.status.running}`
|
|
1830
|
+
]
|
|
1831
|
+
)
|
|
1832
|
+
);
|
|
1833
|
+
var mcpCmd = program.command("mcp").description("Run Workgraph MCP server");
|
|
1834
|
+
addWorkspaceOption(
|
|
1835
|
+
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)")
|
|
1836
|
+
).action(async (opts) => {
|
|
1837
|
+
const workspacePath = resolveWorkspacePath(opts);
|
|
1838
|
+
console.error(`Starting MCP server for workspace: ${workspacePath}`);
|
|
1839
|
+
const ssePort = opts.ssePort !== void 0 ? Number.parseInt(String(opts.ssePort), 10) : void 0;
|
|
1840
|
+
const ssePollMs = opts.ssePollMs !== void 0 ? Number.parseInt(String(opts.ssePollMs), 10) : void 0;
|
|
1841
|
+
const sseHeartbeatMs = opts.sseHeartbeatMs !== void 0 ? Number.parseInt(String(opts.sseHeartbeatMs), 10) : void 0;
|
|
1842
|
+
const sseEnabled = ssePort !== void 0 || opts.sseHost !== void 0 || opts.ssePath !== void 0;
|
|
1843
|
+
await mcp_server_exports.startWorkgraphMcpServer({
|
|
1844
|
+
workspacePath,
|
|
1845
|
+
defaultActor: opts.actor,
|
|
1846
|
+
readOnly: !!opts.readOnly
|
|
1847
|
+
});
|
|
1848
|
+
});
|
|
1849
|
+
await program.parseAsync();
|
|
1111
1850
|
function addWorkspaceOption(command) {
|
|
1112
|
-
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)");
|
|
1851
|
+
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");
|
|
1113
1852
|
}
|
|
1114
1853
|
function resolveWorkspacePath(opts) {
|
|
1854
|
+
const originalWorkspacePath = resolveWorkspacePathBase(opts);
|
|
1855
|
+
if (!opts.dryRun) return originalWorkspacePath;
|
|
1856
|
+
if (opts.__dryRunWorkspace) return opts.__dryRunWorkspace;
|
|
1857
|
+
const sandboxRoot = fs.mkdtempSync(path.join(os.tmpdir(), "workgraph-dry-run-"));
|
|
1858
|
+
const sandboxWorkspace = path.join(sandboxRoot, "workspace");
|
|
1859
|
+
if (fs.existsSync(originalWorkspacePath)) {
|
|
1860
|
+
fs.cpSync(originalWorkspacePath, sandboxWorkspace, {
|
|
1861
|
+
recursive: true,
|
|
1862
|
+
force: true
|
|
1863
|
+
});
|
|
1864
|
+
} else {
|
|
1865
|
+
fs.mkdirSync(sandboxWorkspace, { recursive: true });
|
|
1866
|
+
}
|
|
1867
|
+
opts.__dryRunWorkspaceRoot = sandboxRoot;
|
|
1868
|
+
opts.__dryRunWorkspace = sandboxWorkspace;
|
|
1869
|
+
opts.__dryRunOriginal = originalWorkspacePath;
|
|
1870
|
+
return sandboxWorkspace;
|
|
1871
|
+
}
|
|
1872
|
+
function resolveWorkspacePathBase(opts) {
|
|
1115
1873
|
const explicit = opts.workspace || opts.vault || opts.sharedVault;
|
|
1116
1874
|
if (explicit) return path.resolve(explicit);
|
|
1117
1875
|
if (process.env.WORKGRAPH_SHARED_VAULT) return path.resolve(process.env.WORKGRAPH_SHARED_VAULT);
|
|
1118
1876
|
if (process.env.WORKGRAPH_PATH) return path.resolve(process.env.WORKGRAPH_PATH);
|
|
1119
1877
|
return process.cwd();
|
|
1120
1878
|
}
|
|
1879
|
+
function resolveInitTargetPath(targetPath, opts) {
|
|
1880
|
+
const requestedPath = path.resolve(targetPath || resolveWorkspacePathBase(opts));
|
|
1881
|
+
if (!opts.dryRun) return requestedPath;
|
|
1882
|
+
if (opts.__dryRunWorkspace) return opts.__dryRunWorkspace;
|
|
1883
|
+
const sandboxRoot = fs.mkdtempSync(path.join(os.tmpdir(), "workgraph-init-dry-run-"));
|
|
1884
|
+
const sandboxWorkspace = path.join(sandboxRoot, path.basename(requestedPath));
|
|
1885
|
+
if (fs.existsSync(requestedPath)) {
|
|
1886
|
+
fs.cpSync(requestedPath, sandboxWorkspace, {
|
|
1887
|
+
recursive: true,
|
|
1888
|
+
force: true
|
|
1889
|
+
});
|
|
1890
|
+
}
|
|
1891
|
+
opts.__dryRunWorkspaceRoot = sandboxRoot;
|
|
1892
|
+
opts.__dryRunWorkspace = sandboxWorkspace;
|
|
1893
|
+
opts.__dryRunOriginal = requestedPath;
|
|
1894
|
+
return sandboxWorkspace;
|
|
1895
|
+
}
|
|
1121
1896
|
function parseSetPairs(pairs) {
|
|
1122
1897
|
const fields = {};
|
|
1123
1898
|
for (const pair of pairs) {
|
|
@@ -1134,6 +1909,22 @@ function csv(value) {
|
|
|
1134
1909
|
if (!value) return void 0;
|
|
1135
1910
|
return String(value).split(",").map((s) => s.trim()).filter(Boolean);
|
|
1136
1911
|
}
|
|
1912
|
+
function installNamedIntegration(workspacePath, integrationName, opts) {
|
|
1913
|
+
return integration_exports.installIntegration(workspacePath, integrationName, {
|
|
1914
|
+
actor: opts.actor,
|
|
1915
|
+
owner: opts.owner,
|
|
1916
|
+
title: opts.title,
|
|
1917
|
+
sourceUrl: opts.sourceUrl,
|
|
1918
|
+
force: !!opts.force
|
|
1919
|
+
});
|
|
1920
|
+
}
|
|
1921
|
+
function renderInstalledIntegrationResult(result) {
|
|
1922
|
+
return [
|
|
1923
|
+
`${result.replacedExisting ? "Updated" : "Installed"} ${result.provider} integration skill: ${result.skill.path}`,
|
|
1924
|
+
`Source: ${result.sourceUrl}`,
|
|
1925
|
+
`Status: ${String(result.skill.fields.status)}`
|
|
1926
|
+
];
|
|
1927
|
+
}
|
|
1137
1928
|
function parseScalar(value) {
|
|
1138
1929
|
if (value === "true") return true;
|
|
1139
1930
|
if (value === "false") return false;
|
|
@@ -1154,6 +1945,20 @@ function parseScalar(value) {
|
|
|
1154
1945
|
return value;
|
|
1155
1946
|
}
|
|
1156
1947
|
}
|
|
1948
|
+
function parsePositiveIntOption(value, name) {
|
|
1949
|
+
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
1950
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
1951
|
+
throw new Error(`Invalid --${name} value "${String(value)}". Expected a positive integer.`);
|
|
1952
|
+
}
|
|
1953
|
+
return parsed;
|
|
1954
|
+
}
|
|
1955
|
+
function parseNonNegativeIntOption(value, name) {
|
|
1956
|
+
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
1957
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
1958
|
+
throw new Error(`Invalid --${name} value "${String(value)}". Expected a non-negative integer.`);
|
|
1959
|
+
}
|
|
1960
|
+
return parsed;
|
|
1961
|
+
}
|
|
1157
1962
|
function normalizeRunStatus(status) {
|
|
1158
1963
|
const normalized = String(status).toLowerCase();
|
|
1159
1964
|
if (normalized === "running" || normalized === "succeeded" || normalized === "failed" || normalized === "cancelled") {
|
|
@@ -1161,6 +1966,13 @@ function normalizeRunStatus(status) {
|
|
|
1161
1966
|
}
|
|
1162
1967
|
throw new Error(`Invalid run status "${status}". Expected running|succeeded|failed|cancelled.`);
|
|
1163
1968
|
}
|
|
1969
|
+
function normalizeAgentPresenceStatus(status) {
|
|
1970
|
+
const normalized = String(status).toLowerCase();
|
|
1971
|
+
if (normalized === "online" || normalized === "busy" || normalized === "offline") {
|
|
1972
|
+
return normalized;
|
|
1973
|
+
}
|
|
1974
|
+
throw new Error(`Invalid agent status "${status}". Expected online|busy|offline.`);
|
|
1975
|
+
}
|
|
1164
1976
|
function normalizeOnboardingStatus(status) {
|
|
1165
1977
|
const normalized = String(status).toLowerCase();
|
|
1166
1978
|
if (normalized === "active" || normalized === "paused" || normalized === "completed") {
|
|
@@ -1173,22 +1985,48 @@ function wantsJson(opts) {
|
|
|
1173
1985
|
if (process.env.WORKGRAPH_JSON === "1") return true;
|
|
1174
1986
|
return false;
|
|
1175
1987
|
}
|
|
1176
|
-
function runCommand(opts, action, renderText) {
|
|
1988
|
+
async function runCommand(opts, action, renderText) {
|
|
1177
1989
|
try {
|
|
1178
|
-
const result = action();
|
|
1990
|
+
const result = await action();
|
|
1991
|
+
const dryRunMetadata = opts.dryRun ? {
|
|
1992
|
+
dryRun: true,
|
|
1993
|
+
targetWorkspace: opts.__dryRunOriginal ?? resolveWorkspacePathBase(opts),
|
|
1994
|
+
sandboxWorkspace: opts.__dryRunWorkspace
|
|
1995
|
+
} : {};
|
|
1179
1996
|
if (wantsJson(opts)) {
|
|
1180
|
-
console.log(JSON.stringify({ ok: true, data: result }, null, 2));
|
|
1997
|
+
console.log(JSON.stringify({ ok: true, ...dryRunMetadata, data: result }, null, 2));
|
|
1181
1998
|
return;
|
|
1182
1999
|
}
|
|
2000
|
+
if (opts.dryRun) {
|
|
2001
|
+
console.log(
|
|
2002
|
+
[
|
|
2003
|
+
"[dry-run] Executed against sandbox workspace and discarded changes.",
|
|
2004
|
+
`Target: ${opts.__dryRunOriginal ?? resolveWorkspacePathBase(opts)}`,
|
|
2005
|
+
`Sandbox: ${opts.__dryRunWorkspace ?? "n/a"}`
|
|
2006
|
+
].join(" ")
|
|
2007
|
+
);
|
|
2008
|
+
}
|
|
1183
2009
|
const lines = renderText(result);
|
|
1184
2010
|
for (const line of lines) console.log(line);
|
|
1185
2011
|
} catch (error) {
|
|
1186
2012
|
const message = error instanceof Error ? error.message : String(error);
|
|
1187
2013
|
if (wantsJson(opts)) {
|
|
1188
|
-
console.error(JSON.stringify({ ok: false, error: message }, null, 2));
|
|
2014
|
+
console.error(JSON.stringify({ ok: false, dryRun: !!opts.dryRun, error: message }, null, 2));
|
|
1189
2015
|
} else {
|
|
1190
2016
|
console.error(`Error: ${message}`);
|
|
1191
2017
|
}
|
|
2018
|
+
cleanupDryRunSandbox(opts);
|
|
1192
2019
|
process.exit(1);
|
|
2020
|
+
} finally {
|
|
2021
|
+
cleanupDryRunSandbox(opts);
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
function cleanupDryRunSandbox(opts) {
|
|
2025
|
+
if (!opts.dryRun || !opts.__dryRunWorkspaceRoot) return;
|
|
2026
|
+
if (fs.existsSync(opts.__dryRunWorkspaceRoot)) {
|
|
2027
|
+
fs.rmSync(opts.__dryRunWorkspaceRoot, { recursive: true, force: true });
|
|
1193
2028
|
}
|
|
2029
|
+
delete opts.__dryRunWorkspaceRoot;
|
|
2030
|
+
delete opts.__dryRunWorkspace;
|
|
2031
|
+
delete opts.__dryRunOriginal;
|
|
1194
2032
|
}
|