@crouton-kit/crouter 0.3.8 → 0.3.12
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/bin/crtrd +2 -0
- package/dist/builtin-personas/design/base.md +9 -0
- package/dist/builtin-personas/design/orchestrator.md +10 -0
- package/dist/builtin-personas/developer/base.md +9 -0
- package/dist/builtin-personas/developer/orchestrator.md +12 -0
- package/dist/builtin-personas/explore/base.md +9 -0
- package/dist/builtin-personas/explore/orchestrator.md +9 -0
- package/dist/builtin-personas/general/base.md +5 -0
- package/dist/builtin-personas/general/orchestrator.md +7 -0
- package/dist/builtin-personas/orchestration-kernel.md +71 -0
- package/dist/builtin-personas/plan/base.md +7 -0
- package/dist/builtin-personas/plan/orchestrator.md +12 -0
- package/dist/builtin-personas/review/base.md +7 -0
- package/dist/builtin-personas/review/orchestrator.md +9 -0
- package/dist/builtin-personas/runtime-base.md +39 -0
- package/dist/builtin-personas/spec/base.md +7 -0
- package/dist/builtin-personas/spec/orchestrator.md +10 -0
- package/dist/builtin-skills/skills/design/SKILL.md +51 -0
- package/dist/builtin-skills/skills/development/SKILL.md +109 -0
- package/dist/builtin-skills/skills/planning/SKILL.md +59 -0
- package/dist/builtin-skills/skills/spec/SKILL.md +83 -0
- package/dist/cli.js +25 -27
- package/dist/commands/{job.d.ts → attention.d.ts} +1 -1
- package/dist/commands/attention.js +152 -0
- package/dist/commands/canvas.d.ts +2 -0
- package/dist/commands/canvas.js +35 -0
- package/dist/commands/{agent.d.ts → daemon.d.ts} +1 -1
- package/dist/commands/daemon.js +111 -0
- package/dist/commands/dashboard.d.ts +2 -0
- package/dist/commands/dashboard.js +65 -0
- package/dist/commands/human/prompts.d.ts +5 -0
- package/dist/commands/human/prompts.js +269 -0
- package/dist/commands/human/queue.d.ts +3 -0
- package/dist/commands/human/queue.js +133 -0
- package/dist/commands/human/shared.d.ts +43 -0
- package/dist/commands/human/shared.js +107 -0
- package/dist/commands/human.js +15 -427
- package/dist/commands/node.d.ts +2 -0
- package/dist/commands/node.js +354 -0
- package/dist/commands/pkg/market-inspect.d.ts +1 -0
- package/dist/commands/pkg/market-inspect.js +157 -0
- package/dist/commands/pkg/market-manage.d.ts +1 -0
- package/dist/commands/pkg/market-manage.js +316 -0
- package/dist/commands/pkg/market.d.ts +1 -0
- package/dist/commands/pkg/market.js +16 -0
- package/dist/commands/pkg/plugin-inspect.d.ts +1 -0
- package/dist/commands/pkg/plugin-inspect.js +142 -0
- package/dist/commands/pkg/plugin-manage.d.ts +1 -0
- package/dist/commands/pkg/plugin-manage.js +294 -0
- package/dist/commands/pkg/plugin.d.ts +1 -0
- package/dist/commands/pkg/plugin.js +16 -0
- package/dist/commands/pkg/shared.d.ts +5 -0
- package/dist/commands/pkg/shared.js +61 -0
- package/dist/commands/pkg.js +8 -1004
- package/dist/commands/push.d.ts +3 -0
- package/dist/commands/push.js +159 -0
- package/dist/commands/revive.d.ts +2 -0
- package/dist/commands/revive.js +64 -0
- package/dist/commands/skill/author.d.ts +3 -0
- package/dist/commands/skill/author.js +147 -0
- package/dist/commands/skill/find.d.ts +4 -0
- package/dist/commands/skill/find.js +254 -0
- package/dist/commands/skill/read.d.ts +1 -0
- package/dist/commands/skill/read.js +89 -0
- package/dist/commands/skill/shared.d.ts +19 -0
- package/dist/commands/skill/shared.js +207 -0
- package/dist/commands/skill/state.d.ts +3 -0
- package/dist/commands/skill/state.js +69 -0
- package/dist/commands/skill.js +12 -681
- package/dist/commands/sys/config.d.ts +1 -0
- package/dist/commands/sys/config.js +186 -0
- package/dist/commands/sys/doctor.d.ts +1 -0
- package/dist/commands/sys/doctor.js +369 -0
- package/dist/commands/sys/shared.d.ts +3 -0
- package/dist/commands/sys/shared.js +24 -0
- package/dist/commands/sys/update.d.ts +2 -0
- package/dist/commands/sys/update.js +114 -0
- package/dist/commands/sys.js +9 -694
- package/dist/core/__tests__/argv-parser.test.js +19 -1
- package/dist/core/__tests__/canvas-inbox-watcher.test.js +100 -0
- package/dist/core/__tests__/canvas.test.js +154 -0
- package/dist/core/__tests__/reset.test.js +105 -0
- package/dist/core/__tests__/resolver.test.js +69 -1
- package/dist/core/__tests__/unknown-path.test.d.ts +1 -0
- package/dist/core/__tests__/unknown-path.test.js +52 -0
- package/dist/core/bootstrap.d.ts +2 -0
- package/dist/core/bootstrap.js +66 -0
- package/dist/core/canvas/attention.d.ts +24 -0
- package/dist/core/canvas/attention.js +94 -0
- package/dist/core/canvas/canvas.d.ts +40 -0
- package/dist/core/canvas/canvas.js +210 -0
- package/dist/core/canvas/db.d.ts +7 -0
- package/dist/core/canvas/db.js +61 -0
- package/dist/core/canvas/index.d.ts +4 -0
- package/dist/core/canvas/index.js +6 -0
- package/dist/core/canvas/paths.d.ts +16 -0
- package/dist/core/canvas/paths.js +62 -0
- package/dist/core/canvas/render.d.ts +30 -0
- package/dist/core/canvas/render.js +186 -0
- package/dist/core/canvas/types.d.ts +87 -0
- package/dist/core/canvas/types.js +8 -0
- package/dist/core/command.d.ts +63 -2
- package/dist/core/command.js +97 -24
- package/dist/core/feed/feed.d.ts +43 -0
- package/dist/core/feed/feed.js +116 -0
- package/dist/core/feed/inbox.d.ts +50 -0
- package/dist/core/feed/inbox.js +124 -0
- package/dist/core/frontmatter.d.ts +10 -0
- package/dist/core/frontmatter.js +24 -9
- package/dist/core/help.d.ts +39 -8
- package/dist/core/help.js +69 -35
- package/dist/core/io.d.ts +15 -1
- package/dist/core/io.js +56 -6
- package/dist/core/personas/index.d.ts +12 -0
- package/dist/core/personas/index.js +10 -0
- package/dist/core/personas/loader.d.ts +44 -0
- package/dist/core/personas/loader.js +157 -0
- package/dist/core/personas/resolve.d.ts +36 -0
- package/dist/core/personas/resolve.js +110 -0
- package/dist/core/render.d.ts +11 -0
- package/dist/core/render.js +126 -0
- package/dist/core/resolver.d.ts +10 -0
- package/dist/core/resolver.js +160 -2
- package/dist/core/runtime/front-door.d.ts +10 -0
- package/dist/core/runtime/front-door.js +97 -0
- package/dist/core/runtime/kickoff.d.ts +23 -0
- package/dist/core/runtime/kickoff.js +134 -0
- package/dist/core/runtime/launch.d.ts +34 -0
- package/dist/core/runtime/launch.js +85 -0
- package/dist/core/runtime/nodes.d.ts +38 -0
- package/dist/core/runtime/nodes.js +95 -0
- package/dist/core/runtime/presence.d.ts +38 -0
- package/dist/core/runtime/presence.js +152 -0
- package/dist/core/runtime/promote.d.ts +30 -0
- package/dist/core/runtime/promote.js +105 -0
- package/dist/core/runtime/reset.d.ts +13 -0
- package/dist/core/runtime/reset.js +97 -0
- package/dist/core/runtime/revive.d.ts +26 -0
- package/dist/core/runtime/revive.js +89 -0
- package/dist/core/runtime/roadmap.d.ts +12 -0
- package/dist/core/runtime/roadmap.js +52 -0
- package/dist/core/runtime/spawn.d.ts +33 -0
- package/dist/core/runtime/spawn.js +118 -0
- package/dist/core/runtime/stop-guard.d.ts +18 -0
- package/dist/core/runtime/stop-guard.js +33 -0
- package/dist/core/runtime/tmux.d.ts +88 -0
- package/dist/core/runtime/tmux.js +198 -0
- package/dist/core/spawn.d.ts +17 -80
- package/dist/core/spawn.js +15 -219
- package/dist/daemon/crtrd-cli.d.ts +1 -0
- package/dist/daemon/crtrd-cli.js +4 -0
- package/dist/daemon/crtrd.d.ts +20 -0
- package/dist/daemon/crtrd.js +200 -0
- package/dist/daemon/manage.d.ts +17 -0
- package/dist/daemon/manage.js +57 -0
- package/dist/pi-extensions/canvas-inbox-watcher.d.ts +16 -0
- package/dist/pi-extensions/canvas-inbox-watcher.js +229 -0
- package/dist/pi-extensions/canvas-nav.d.ts +32 -0
- package/dist/pi-extensions/canvas-nav.js +536 -0
- package/dist/pi-extensions/canvas-stophook.d.ts +17 -0
- package/dist/pi-extensions/canvas-stophook.js +373 -0
- package/dist/types.d.ts +21 -0
- package/dist/types.js +3 -0
- package/package.json +6 -5
- package/dist/commands/agent.js +0 -384
- package/dist/commands/debug.d.ts +0 -3
- package/dist/commands/debug.js +0 -179
- package/dist/commands/job.js +0 -344
- package/dist/commands/plan.d.ts +0 -4
- package/dist/commands/plan.js +0 -309
- package/dist/commands/spec.d.ts +0 -3
- package/dist/commands/spec.js +0 -286
- package/dist/core/__tests__/flow-leaves.test.js +0 -248
- package/dist/core/__tests__/job.test.js +0 -310
- package/dist/core/__tests__/jobs.test.js +0 -66
- package/dist/core/jobs.d.ts +0 -101
- package/dist/core/jobs.js +0 -462
- package/dist/prompts/agent.d.ts +0 -18
- package/dist/prompts/agent.js +0 -153
- package/dist/prompts/debug.d.ts +0 -8
- package/dist/prompts/debug.js +0 -44
- /package/dist/core/__tests__/{flow-leaves.test.d.ts → canvas-inbox-watcher.test.d.ts} +0 -0
- /package/dist/core/__tests__/{job.test.d.ts → canvas.test.d.ts} +0 -0
- /package/dist/core/__tests__/{jobs.test.d.ts → reset.test.d.ts} +0 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// render.ts — ASCII tree rendering of the canvas subscription sub-DAG.
|
|
2
|
+
//
|
|
3
|
+
// `subscriptionsOf(nodeId)` returns the nodes a node subscribes to, which in
|
|
4
|
+
// the crtr model are its *reports* / *children*: a parent auto-subscribes to
|
|
5
|
+
// each child it spawns so it wakes on the child's output. Walking subscriptionsOf
|
|
6
|
+
// recursively therefore walks DOWN the org chart.
|
|
7
|
+
//
|
|
8
|
+
// Telemetry is read directly from <crtrHome>/nodes/<id>/job/telemetry.json
|
|
9
|
+
// (the node-local job dir written by canvas-stophook on every turn_end).
|
|
10
|
+
// Missing or corrupt telemetry → ctx 0k (best-effort, never throws).
|
|
11
|
+
//
|
|
12
|
+
// Cycle guard: the subscription graph is declared acyclic (a node cannot
|
|
13
|
+
// subscribe to its own ancestor), but we track visited ids defensively because
|
|
14
|
+
// the db is mutable and bugs happen.
|
|
15
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { getNode, listNodes, subscriptionsOf, view } from './canvas.js';
|
|
18
|
+
import { jobDir } from './paths.js';
|
|
19
|
+
import { countAsks } from './attention.js';
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Glyphs
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
const STATUS_GLYPH = {
|
|
24
|
+
active: '●',
|
|
25
|
+
idle: '○',
|
|
26
|
+
done: '✓',
|
|
27
|
+
dead: '✗',
|
|
28
|
+
};
|
|
29
|
+
function readNodeTelemetry(nodeId) {
|
|
30
|
+
const path = join(jobDir(nodeId), 'telemetry.json');
|
|
31
|
+
if (!existsSync(path))
|
|
32
|
+
return {};
|
|
33
|
+
try {
|
|
34
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** Format a token count as `Nk` (rounded down to nearest 1 k). */
|
|
41
|
+
function fmtCtx(tokensIn) {
|
|
42
|
+
if (tokensIn === undefined || tokensIn === 0)
|
|
43
|
+
return '0k';
|
|
44
|
+
return `${Math.floor(tokensIn / 1000)}k`;
|
|
45
|
+
}
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Tree builder
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
/** Build one line of the ASCII tree. */
|
|
50
|
+
function nodeLine(nodeId, indent, connector) {
|
|
51
|
+
const node = getNode(nodeId);
|
|
52
|
+
if (node === null) {
|
|
53
|
+
// Node id is in the db but meta.json is gone — paranoid guard.
|
|
54
|
+
return `${indent}${connector}? <missing meta: ${nodeId}>`;
|
|
55
|
+
}
|
|
56
|
+
const glyph = STATUS_GLYPH[node.status] ?? '?';
|
|
57
|
+
const tel = readNodeTelemetry(nodeId);
|
|
58
|
+
const ctx = fmtCtx(tel.tokens_in);
|
|
59
|
+
const asks = countAsks(nodeId);
|
|
60
|
+
const askSuffix = asks > 0 ? ` ⚑${asks}` : '';
|
|
61
|
+
return `${indent}${connector}${glyph} ${node.name} [${node.kind}/${node.mode}] ctx ${ctx}${askSuffix}`;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Recursively walk the subscription sub-DAG rooted at `nodeId`, appending
|
|
65
|
+
* rendered lines to `out`. Cycle-safe via `visited`.
|
|
66
|
+
*/
|
|
67
|
+
function walkTree(nodeId, indent, isLast, visited, out) {
|
|
68
|
+
// Guard: if we have already rendered this node in this traversal, emit a
|
|
69
|
+
// back-ref marker instead of recursing (prevents infinite loops in graphs
|
|
70
|
+
// with cycles introduced by manual edge manipulation).
|
|
71
|
+
if (visited.has(nodeId)) {
|
|
72
|
+
// The line for this node was already emitted by the caller; just return.
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
visited.add(nodeId);
|
|
76
|
+
const connector = isLast ? '└─ ' : '├─ ';
|
|
77
|
+
out.push(nodeLine(nodeId, indent, connector));
|
|
78
|
+
const children = subscriptionsOf(nodeId);
|
|
79
|
+
const childIndent = indent + (isLast ? ' ' : '│ ');
|
|
80
|
+
for (let i = 0; i < children.length; i++) {
|
|
81
|
+
const child = children[i];
|
|
82
|
+
const childIsLast = i === children.length - 1;
|
|
83
|
+
if (visited.has(child.node_id)) {
|
|
84
|
+
// Cycle reference — show the back-edge without recursing.
|
|
85
|
+
const cycleConnector = childIsLast ? '└─ ' : '├─ ';
|
|
86
|
+
out.push(`${childIndent}${cycleConnector}↺ <cycle: ${child.node_id}>`);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
walkTree(child.node_id, childIndent, childIsLast, visited, out);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
// Public API
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
/**
|
|
96
|
+
* Render the subscription sub-DAG rooted at `rootId` as an ASCII tree.
|
|
97
|
+
* The root is the first line (no connector prefix); children are indented.
|
|
98
|
+
*
|
|
99
|
+
* Each line: `<glyph> <name> [<kind>/<mode>] ctx <Nk>[ ⚑<asks>]`
|
|
100
|
+
*
|
|
101
|
+
* Returns a multi-line string (no trailing newline).
|
|
102
|
+
*/
|
|
103
|
+
export function renderTree(rootId) {
|
|
104
|
+
const node = getNode(rootId);
|
|
105
|
+
if (node === null)
|
|
106
|
+
return `? <missing node: ${rootId}>`;
|
|
107
|
+
const tel = readNodeTelemetry(rootId);
|
|
108
|
+
const ctx = fmtCtx(tel.tokens_in);
|
|
109
|
+
const asks = countAsks(rootId);
|
|
110
|
+
const askSuffix = asks > 0 ? ` ⚑${asks}` : '';
|
|
111
|
+
const glyph = STATUS_GLYPH[node.status] ?? '?';
|
|
112
|
+
const out = [];
|
|
113
|
+
out.push(`${glyph} ${node.name} [${node.kind}/${node.mode}] ctx ${ctx}${askSuffix}`);
|
|
114
|
+
// visited starts with root already rendered (walkTree doesn't re-emit root).
|
|
115
|
+
const visited = new Set([rootId]);
|
|
116
|
+
const children = subscriptionsOf(rootId);
|
|
117
|
+
for (let i = 0; i < children.length; i++) {
|
|
118
|
+
const child = children[i];
|
|
119
|
+
const isLast = i === children.length - 1;
|
|
120
|
+
walkTree(child.node_id, '', isLast, visited, out);
|
|
121
|
+
}
|
|
122
|
+
return out.join('\n');
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Render all canvas roots as a forest. A root is a node with no subscribers
|
|
126
|
+
* (no one subscribes to it = it has no managers in the org chart).
|
|
127
|
+
*
|
|
128
|
+
* If there are no roots on the canvas, returns a placeholder string.
|
|
129
|
+
*/
|
|
130
|
+
export function renderForest() {
|
|
131
|
+
const all = listNodes();
|
|
132
|
+
if (all.length === 0)
|
|
133
|
+
return '(canvas is empty)';
|
|
134
|
+
// A root has no subscribers (nobody is watching it). We discover this by
|
|
135
|
+
// looking for nodes whose node_id never appears as a "to" side of a
|
|
136
|
+
// subscribes_to edge — equivalently, nodes with parent === null are the
|
|
137
|
+
// authoritative roots per the spawn contract (spawn sets parent and records
|
|
138
|
+
// a spawned_by edge + subscribe). Fall back to parent===null because querying
|
|
139
|
+
// the full edge table would require opening the db here.
|
|
140
|
+
//
|
|
141
|
+
// Fine to use parent===null: roots are created by `node session` / `node new`
|
|
142
|
+
// without a parent; non-roots always have a parent.
|
|
143
|
+
const roots = all.filter((n) => n.parent === null);
|
|
144
|
+
// If for some reason we have no parent===null nodes (unusual: e.g., all nodes
|
|
145
|
+
// were created by hand with a parent), fall back to all nodes.
|
|
146
|
+
const renderRoots = roots.length > 0 ? roots : all;
|
|
147
|
+
const parts = [];
|
|
148
|
+
for (const r of renderRoots) {
|
|
149
|
+
parts.push(renderTree(r.node_id));
|
|
150
|
+
}
|
|
151
|
+
return parts.join('\n\n');
|
|
152
|
+
}
|
|
153
|
+
/** One row per node visible in the sub-DAG of `rootId` (including root). */
|
|
154
|
+
export function dashboardRows(rootId) {
|
|
155
|
+
const ids = [rootId, ...view(rootId)];
|
|
156
|
+
return ids.flatMap((id) => {
|
|
157
|
+
const node = getNode(id);
|
|
158
|
+
if (node === null)
|
|
159
|
+
return [];
|
|
160
|
+
const tel = readNodeTelemetry(id);
|
|
161
|
+
return [{
|
|
162
|
+
node_id: id,
|
|
163
|
+
name: node.name,
|
|
164
|
+
status: node.status,
|
|
165
|
+
kind: node.kind,
|
|
166
|
+
mode: node.mode,
|
|
167
|
+
ctx_tokens: tel.tokens_in ?? 0,
|
|
168
|
+
asks: countAsks(id),
|
|
169
|
+
}];
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
/** One row per node across the entire canvas. */
|
|
173
|
+
export function dashboardRowsAll() {
|
|
174
|
+
return listNodes().flatMap((row) => {
|
|
175
|
+
const tel = readNodeTelemetry(row.node_id);
|
|
176
|
+
return [{
|
|
177
|
+
node_id: row.node_id,
|
|
178
|
+
name: row.name,
|
|
179
|
+
status: row.status,
|
|
180
|
+
kind: row.kind,
|
|
181
|
+
mode: row.mode,
|
|
182
|
+
ctx_tokens: tel.tokens_in ?? 0,
|
|
183
|
+
asks: countAsks(row.node_id),
|
|
184
|
+
}];
|
|
185
|
+
});
|
|
186
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/** What a node is doing right now. UI shows active+idle; `done` is hidden but
|
|
2
|
+
* revivable; only `dead` is a fault. */
|
|
3
|
+
export type NodeStatus = 'active' | 'idle' | 'done' | 'dead';
|
|
4
|
+
/** Does stopping finalize the node? terminal = worker (finalizes on push --final);
|
|
5
|
+
* resident = manager/orchestrator (stays dormant, woken by inbox). */
|
|
6
|
+
export type Lifecycle = 'terminal' | 'resident';
|
|
7
|
+
/** base = hands-on worker; orchestrator = delegating manager. Bespoke per kind. */
|
|
8
|
+
export type Mode = 'base' | 'orchestrator';
|
|
9
|
+
/** Why a node last stopped — drives the daemon's reap-vs-revive decision. */
|
|
10
|
+
export type ExitIntent = 'done' | 'refresh' | 'idle-release' | null;
|
|
11
|
+
/** The two structural edges. `subscribes_to` is the load-bearing spine (flow,
|
|
12
|
+
* org chart, views, completion routing). `spawned_by` is audit only. */
|
|
13
|
+
export type EdgeType = 'subscribes_to' | 'spawned_by';
|
|
14
|
+
/** The pi launch recipe, persisted so the daemon can faithfully revive a node
|
|
15
|
+
* as its *current* self. Rewritten on every polymorph (base→orchestrator). */
|
|
16
|
+
export interface LaunchSpec {
|
|
17
|
+
/** Model id/pattern passed to pi `--model`. */
|
|
18
|
+
model?: string;
|
|
19
|
+
/** pi `--tools` allow-list. */
|
|
20
|
+
tools?: string[];
|
|
21
|
+
/** pi `-e` extension paths, loaded once; they self-gate on live {kind,mode}. */
|
|
22
|
+
extensions: string[];
|
|
23
|
+
/** Resolved system prompt text (passed via --append-system-prompt / --system-prompt). */
|
|
24
|
+
systemPrompt?: string;
|
|
25
|
+
/** Extra env injected into the pi process. */
|
|
26
|
+
env: Record<string, string>;
|
|
27
|
+
}
|
|
28
|
+
/** A node's `meta.json` — source of truth for its canvas row. Files for flesh,
|
|
29
|
+
* sqlite for skeleton: the db indexes the queryable subset of these fields. */
|
|
30
|
+
export interface NodeMeta {
|
|
31
|
+
node_id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
created: string;
|
|
34
|
+
/** The dir this node is pinned to — its cwd (where pi runs, bash executes). */
|
|
35
|
+
cwd: string;
|
|
36
|
+
/** Role the node was born as: explore | developer | plan | review | general… */
|
|
37
|
+
kind: string;
|
|
38
|
+
mode: Mode;
|
|
39
|
+
lifecycle: Lifecycle;
|
|
40
|
+
status: NodeStatus;
|
|
41
|
+
/** spawned_by target — who created me. Audit only; null for user-opened roots. */
|
|
42
|
+
parent?: string | null;
|
|
43
|
+
/** New subscriptions this node opens default to passive when true. */
|
|
44
|
+
passive_default?: boolean;
|
|
45
|
+
/** Why the node last stopped (done | refresh). Drives reap-vs-revive. */
|
|
46
|
+
intent?: ExitIntent;
|
|
47
|
+
/** The pi session id for `--resume`. */
|
|
48
|
+
pi_session_id?: string | null;
|
|
49
|
+
/** Full pi launch recipe; rewritten on every polymorph. */
|
|
50
|
+
launch?: LaunchSpec;
|
|
51
|
+
/** Presence: the tmux session (its root's home) and window this node renders
|
|
52
|
+
* in while active. Cleared when the node goes done/dead and its window closes.
|
|
53
|
+
* (Phase 5 promotes this to a dedicated presence registry.) */
|
|
54
|
+
tmux_session?: string | null;
|
|
55
|
+
window?: string | null;
|
|
56
|
+
}
|
|
57
|
+
/** The queryable projection of a NodeMeta stored as a canvas.db row. */
|
|
58
|
+
export interface NodeRow {
|
|
59
|
+
node_id: string;
|
|
60
|
+
name: string;
|
|
61
|
+
kind: string;
|
|
62
|
+
mode: Mode;
|
|
63
|
+
lifecycle: Lifecycle;
|
|
64
|
+
status: NodeStatus;
|
|
65
|
+
cwd: string;
|
|
66
|
+
parent: string | null;
|
|
67
|
+
created: string;
|
|
68
|
+
}
|
|
69
|
+
/** An edge as stored. For `subscribes_to`, `from` is the subscriber and `to`
|
|
70
|
+
* is the publisher (A subscribes_to B ⇒ A receives B's output). For
|
|
71
|
+
* `spawned_by`, `from` is the child and `to` is the parent. */
|
|
72
|
+
export interface Edge {
|
|
73
|
+
type: EdgeType;
|
|
74
|
+
from: string;
|
|
75
|
+
to: string;
|
|
76
|
+
/** Only meaningful for subscribes_to: active = wake the subscriber on emit;
|
|
77
|
+
* passive = accumulate pointers, no wake. */
|
|
78
|
+
active: boolean;
|
|
79
|
+
created: string;
|
|
80
|
+
}
|
|
81
|
+
/** A subscription as seen from one endpoint. */
|
|
82
|
+
export interface SubscriptionRef {
|
|
83
|
+
/** The node id at the other end of the edge. */
|
|
84
|
+
node_id: string;
|
|
85
|
+
active: boolean;
|
|
86
|
+
created: string;
|
|
87
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// The canvas vocabulary — the node + edge model the whole runtime hangs on.
|
|
2
|
+
//
|
|
3
|
+
// One global canvas (`~/.crtr/canvas.db`) holds the topology (nodes + edges);
|
|
4
|
+
// each node's flesh lives on disk under `~/.crtr/nodes/<id>/`. A node's
|
|
5
|
+
// `meta.json` is the source of truth for its own row; the db is a queryable
|
|
6
|
+
// index over those metas, plus the authoritative store for the mutable
|
|
7
|
+
// `subscribes_to` edges (which no single meta owns).
|
|
8
|
+
export {};
|
package/dist/core/command.d.ts
CHANGED
|
@@ -1,14 +1,43 @@
|
|
|
1
|
-
import type { RootHelp, BranchHelp, LeafHelp, InputParam } from './help.js';
|
|
1
|
+
import type { RootHelp, RootEntry, BranchHelp, LeafHelp, InputParam } from './help.js';
|
|
2
|
+
import { CrtrError } from './errors.js';
|
|
3
|
+
/** Opt-in flag that surfaces a node as an editor slash command (a pi prompt
|
|
4
|
+
* template / Claude Code command). When set, the bootstrap auto-writes a
|
|
5
|
+
* markdown template named `<name>.md` to the host's command dirs on each crtr
|
|
6
|
+
* run, so `/name` becomes available. The body is thin — it points the agent at
|
|
7
|
+
* the live `crtr` workflow so the CLI stays the source of truth. */
|
|
8
|
+
export interface SlashSpec {
|
|
9
|
+
/** Command name → `/<name>` and the template filename. */
|
|
10
|
+
name: string;
|
|
11
|
+
/** Frontmatter description shown in the autocomplete dropdown. */
|
|
12
|
+
description: string;
|
|
13
|
+
/** Optional autocomplete hint, e.g. `<url>` or `[topic]`. */
|
|
14
|
+
argumentHint?: string;
|
|
15
|
+
/** Markdown body (no frontmatter). Bootstrap wraps it with frontmatter + a
|
|
16
|
+
* version marker. Use `$ARGUMENTS` for the invocation's free text. */
|
|
17
|
+
body: string;
|
|
18
|
+
}
|
|
2
19
|
export interface LeafDef {
|
|
3
20
|
kind: 'leaf';
|
|
4
21
|
name: string;
|
|
5
22
|
help: LeafHelp;
|
|
23
|
+
/** Opt into editor slash-command exposure (see SlashSpec). */
|
|
24
|
+
slash?: SlashSpec;
|
|
6
25
|
run: (input: Record<string, unknown>) => Promise<Record<string, unknown> | void>;
|
|
26
|
+
/** Optional bespoke renderer: turn the result into instruction-shaped
|
|
27
|
+
* XML+markdown the agent acts on. Omit to fall back to the schema-driven
|
|
28
|
+
* generic renderer. Ignored when `--json` is set. */
|
|
29
|
+
render?: (result: Record<string, unknown>) => string;
|
|
7
30
|
}
|
|
8
31
|
export interface BranchDef {
|
|
9
32
|
kind: 'branch';
|
|
10
33
|
name: string;
|
|
11
34
|
help: BranchHelp;
|
|
35
|
+
/** How this subtree represents itself one level up. Present on top-level
|
|
36
|
+
* subtrees (assembled into root -h by defineRoot); omitted on nested
|
|
37
|
+
* branches, whose parent representation is the branch's own children list. */
|
|
38
|
+
rootEntry?: RootEntry;
|
|
39
|
+
/** Opt into editor slash-command exposure (see SlashSpec). */
|
|
40
|
+
slash?: SlashSpec;
|
|
12
41
|
children: (LeafDef | BranchDef)[];
|
|
13
42
|
}
|
|
14
43
|
export interface RootDef {
|
|
@@ -19,18 +48,50 @@ export interface RootDef {
|
|
|
19
48
|
export declare function defineLeaf(opts: {
|
|
20
49
|
name: string;
|
|
21
50
|
help: LeafHelp;
|
|
51
|
+
slash?: SlashSpec;
|
|
22
52
|
run: (input: Record<string, unknown>) => Promise<Record<string, unknown> | void>;
|
|
53
|
+
render?: (result: Record<string, unknown>) => string;
|
|
23
54
|
}): LeafDef;
|
|
24
55
|
export declare function defineBranch(opts: {
|
|
25
56
|
name: string;
|
|
26
57
|
help: BranchHelp;
|
|
58
|
+
rootEntry?: RootEntry;
|
|
59
|
+
slash?: SlashSpec;
|
|
27
60
|
children: (LeafDef | BranchDef)[];
|
|
28
61
|
}): BranchDef;
|
|
62
|
+
/** Walk the whole tree and collect every node's SlashSpec (depth-first). Used
|
|
63
|
+
* by the bootstrap to discover which commands opted into slash exposure. */
|
|
64
|
+
export declare function collectSlashSpecs(root: RootDef): SlashSpec[];
|
|
65
|
+
/** Assemble root -h from the subtrees themselves. Root owns only the tagline
|
|
66
|
+
* and globals; every subtree's concept line, selection rubric, and dynamic
|
|
67
|
+
* block come from its own RootEntry. A subtree without a rootEntry does not
|
|
68
|
+
* appear in root -h — declaring the parent-level representation is how a
|
|
69
|
+
* subtree opts into being listed. */
|
|
29
70
|
export declare function defineRoot(opts: {
|
|
30
|
-
|
|
71
|
+
tagline: string;
|
|
72
|
+
globals: {
|
|
73
|
+
name: string;
|
|
74
|
+
desc: string;
|
|
75
|
+
}[];
|
|
31
76
|
subtrees: BranchDef[];
|
|
32
77
|
}): RootDef;
|
|
78
|
+
type AnyNode = RootDef | BranchDef | LeafDef;
|
|
79
|
+
/** Walk argv tokens to the deepest matched node.
|
|
80
|
+
* Returns { node, path, remaining } where path is the sequence of matched node
|
|
81
|
+
* names from root (excluding root itself) and remaining are unconsumed tokens.
|
|
82
|
+
* -h / --help tokens are NOT consumed here — the caller checks for them. */
|
|
83
|
+
export declare function walk(root: RootDef, tokens: string[]): {
|
|
84
|
+
node: AnyNode;
|
|
85
|
+
path: string[];
|
|
86
|
+
remaining: string[];
|
|
87
|
+
};
|
|
88
|
+
/** Build a structured unknown-path error. Names valid children of the deepest
|
|
89
|
+
* matched node and names the entry command per the spec. The entry command is
|
|
90
|
+
* the full path to the matched node (not just its local name), so the recovery
|
|
91
|
+
* hint is a command that actually exists. No fuzzy matching. */
|
|
92
|
+
export declare function unknownPathError(node: AnyNode, path: string[], bad: string): CrtrError;
|
|
33
93
|
/** Parse remaining argv tokens against the leaf's InputParam schema.
|
|
34
94
|
* Returns a plain object whose keys are camelCase parameter names. */
|
|
35
95
|
export declare function parseArgv(params: InputParam[], tokens: string[]): Promise<Record<string, unknown>>;
|
|
36
96
|
export declare function runCli(root: RootDef, argv: string[]): Promise<void>;
|
|
97
|
+
export {};
|
package/dist/core/command.js
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
// A plain array walk + a small flag parser is ~120 lines and has no surprising
|
|
6
6
|
// edge cases.
|
|
7
7
|
import { renderRoot, renderBranch, renderLeafArgv } from './help.js';
|
|
8
|
-
import { readStdinRaw, emit, handle } from './io.js';
|
|
8
|
+
import { readStdinRaw, emit, handle, setJsonOutput, isJsonOutput } from './io.js';
|
|
9
|
+
import { renderResult } from './render.js';
|
|
9
10
|
import { CrtrError } from './errors.js';
|
|
10
11
|
import { ExitCode } from '../types.js';
|
|
11
12
|
import { readFileSync } from 'node:fs';
|
|
@@ -17,14 +18,61 @@ export function defineLeaf(opts) {
|
|
|
17
18
|
kind: 'leaf',
|
|
18
19
|
name: opts.name,
|
|
19
20
|
help: opts.help,
|
|
21
|
+
slash: opts.slash,
|
|
20
22
|
run: opts.run,
|
|
23
|
+
render: opts.render,
|
|
21
24
|
};
|
|
22
25
|
}
|
|
23
26
|
export function defineBranch(opts) {
|
|
24
|
-
return {
|
|
27
|
+
return {
|
|
28
|
+
kind: 'branch',
|
|
29
|
+
name: opts.name,
|
|
30
|
+
help: opts.help,
|
|
31
|
+
rootEntry: opts.rootEntry,
|
|
32
|
+
slash: opts.slash,
|
|
33
|
+
children: opts.children,
|
|
34
|
+
};
|
|
25
35
|
}
|
|
36
|
+
/** Walk the whole tree and collect every node's SlashSpec (depth-first). Used
|
|
37
|
+
* by the bootstrap to discover which commands opted into slash exposure. */
|
|
38
|
+
export function collectSlashSpecs(root) {
|
|
39
|
+
const out = [];
|
|
40
|
+
const visit = (node) => {
|
|
41
|
+
if (node.slash !== undefined)
|
|
42
|
+
out.push(node.slash);
|
|
43
|
+
if (node.kind === 'branch')
|
|
44
|
+
for (const c of node.children)
|
|
45
|
+
visit(c);
|
|
46
|
+
};
|
|
47
|
+
for (const s of root.subtrees)
|
|
48
|
+
visit(s);
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
/** Assemble root -h from the subtrees themselves. Root owns only the tagline
|
|
52
|
+
* and globals; every subtree's concept line, selection rubric, and dynamic
|
|
53
|
+
* block come from its own RootEntry. A subtree without a rootEntry does not
|
|
54
|
+
* appear in root -h — declaring the parent-level representation is how a
|
|
55
|
+
* subtree opts into being listed. */
|
|
26
56
|
export function defineRoot(opts) {
|
|
27
|
-
|
|
57
|
+
// Each listed subtree becomes one <name> block at root, assembled straight
|
|
58
|
+
// from its RootEntry. Root composes nothing and hardcodes nothing: add a
|
|
59
|
+
// subtree with a rootEntry and it surfaces; its concept, rubric, state tag,
|
|
60
|
+
// and dynamic block all travel with it.
|
|
61
|
+
const commands = opts.subtrees
|
|
62
|
+
.filter((s) => s.rootEntry !== undefined)
|
|
63
|
+
.map((s) => ({
|
|
64
|
+
name: s.name,
|
|
65
|
+
concept: s.rootEntry.concept,
|
|
66
|
+
desc: s.rootEntry.desc,
|
|
67
|
+
useWhen: s.rootEntry.useWhen,
|
|
68
|
+
dynamicState: s.rootEntry.dynamicState,
|
|
69
|
+
}));
|
|
70
|
+
const help = {
|
|
71
|
+
tagline: opts.tagline,
|
|
72
|
+
commands,
|
|
73
|
+
globals: opts.globals,
|
|
74
|
+
};
|
|
75
|
+
return { kind: 'root', help, subtrees: opts.subtrees };
|
|
28
76
|
}
|
|
29
77
|
/** Validate and return child names for an unknown-path error. */
|
|
30
78
|
function childNames(node) {
|
|
@@ -35,10 +83,12 @@ function childNames(node) {
|
|
|
35
83
|
return [];
|
|
36
84
|
}
|
|
37
85
|
/** Walk argv tokens to the deepest matched node.
|
|
38
|
-
* Returns { node, remaining } where
|
|
86
|
+
* Returns { node, path, remaining } where path is the sequence of matched node
|
|
87
|
+
* names from root (excluding root itself) and remaining are unconsumed tokens.
|
|
39
88
|
* -h / --help tokens are NOT consumed here — the caller checks for them. */
|
|
40
|
-
function walk(root, tokens) {
|
|
89
|
+
export function walk(root, tokens) {
|
|
41
90
|
let current = root;
|
|
91
|
+
const path = [];
|
|
42
92
|
let i = 0;
|
|
43
93
|
while (i < tokens.length) {
|
|
44
94
|
const token = tokens[i];
|
|
@@ -50,6 +100,7 @@ function walk(root, tokens) {
|
|
|
50
100
|
if (nextNode === undefined)
|
|
51
101
|
break;
|
|
52
102
|
current = nextNode;
|
|
103
|
+
path.push(nextNode.name);
|
|
53
104
|
i++;
|
|
54
105
|
}
|
|
55
106
|
else if (current.kind === 'branch') {
|
|
@@ -57,6 +108,7 @@ function walk(root, tokens) {
|
|
|
57
108
|
if (nextNode === undefined)
|
|
58
109
|
break;
|
|
59
110
|
current = nextNode;
|
|
111
|
+
path.push(nextNode.name);
|
|
60
112
|
i++;
|
|
61
113
|
}
|
|
62
114
|
else {
|
|
@@ -64,7 +116,7 @@ function walk(root, tokens) {
|
|
|
64
116
|
break;
|
|
65
117
|
}
|
|
66
118
|
}
|
|
67
|
-
return { node: current, remaining: tokens.slice(i) };
|
|
119
|
+
return { node: current, path, remaining: tokens.slice(i) };
|
|
68
120
|
}
|
|
69
121
|
function renderNode(node) {
|
|
70
122
|
if (node.kind === 'root')
|
|
@@ -77,15 +129,13 @@ function helpRequested(remaining) {
|
|
|
77
129
|
return remaining.some((t) => t === '-h' || t === '--help');
|
|
78
130
|
}
|
|
79
131
|
/** Build a structured unknown-path error. Names valid children of the deepest
|
|
80
|
-
* matched node and names the entry command per the spec.
|
|
81
|
-
|
|
132
|
+
* matched node and names the entry command per the spec. The entry command is
|
|
133
|
+
* the full path to the matched node (not just its local name), so the recovery
|
|
134
|
+
* hint is a command that actually exists. No fuzzy matching. */
|
|
135
|
+
export function unknownPathError(node, path, bad) {
|
|
82
136
|
const valid = childNames(node);
|
|
83
137
|
const validStr = valid.length > 0 ? valid.join(', ') : '(none)';
|
|
84
|
-
const entryCmd =
|
|
85
|
-
? 'crtr -h'
|
|
86
|
-
: node.kind === 'branch'
|
|
87
|
-
? `crtr ${node.name} -h`
|
|
88
|
-
: 'crtr -h';
|
|
138
|
+
const entryCmd = path.length > 0 ? `crtr ${path.join(' ')} -h` : 'crtr -h';
|
|
89
139
|
return new CrtrError('unknown_path', `unknown subcommand: ${bad}`, ExitCode.USAGE, {
|
|
90
140
|
received: bad,
|
|
91
141
|
next: `Valid children: ${validStr}. Run \`${entryCmd}\` for the full list.`,
|
|
@@ -215,7 +265,10 @@ export async function parseArgv(params, tokens) {
|
|
|
215
265
|
if (positionalValue !== undefined) {
|
|
216
266
|
throw parseArgvError('bad_invocation', `unexpected extra positional argument: ${token}`, tokens.join(' '), undefined, 'Use --flag for parameters; only one positional allowed.');
|
|
217
267
|
}
|
|
218
|
-
|
|
268
|
+
// A bare positional is accepted when the leaf declares a positional param,
|
|
269
|
+
// OR when it declares a stdin param (the positional supplies the stdin
|
|
270
|
+
// value as an ergonomic alternative to piping). Otherwise it's an error.
|
|
271
|
+
if (positionalParam === undefined && stdinParam === undefined) {
|
|
219
272
|
throw parseArgvError('bad_invocation', `this leaf takes no positional arguments: ${token}`, token, undefined, 'Use --flag for parameters. Run -h for the schema.');
|
|
220
273
|
}
|
|
221
274
|
positionalValue = token;
|
|
@@ -225,13 +278,20 @@ export async function parseArgv(params, tokens) {
|
|
|
225
278
|
if (positionalValue !== undefined && positionalParam !== undefined) {
|
|
226
279
|
result[flagNameToKey(positionalParam.name)] = positionalValue;
|
|
227
280
|
}
|
|
228
|
-
//
|
|
281
|
+
// Resolve stdin if declared. A positional token (when there's no dedicated
|
|
282
|
+
// positional param to claim it) satisfies the stdin param directly, so
|
|
283
|
+
// `crtr node new "Say hi"` works as well as piping on stdin.
|
|
229
284
|
if (stdinParam !== undefined) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
285
|
+
if (positionalValue !== undefined && positionalParam === undefined) {
|
|
286
|
+
result[flagNameToKey(stdinParam.name)] = positionalValue;
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
const raw = await readStdinRaw();
|
|
290
|
+
if (raw.trim() === '' && stdinParam.required) {
|
|
291
|
+
throw parseArgvError('missing_parameter', `stdin is required for this leaf`, '', stdinParam.name, 'Pipe the required content on stdin, or pass it as a positional argument.');
|
|
292
|
+
}
|
|
293
|
+
result[flagNameToKey(stdinParam.name)] = raw;
|
|
233
294
|
}
|
|
234
|
-
result[flagNameToKey(stdinParam.name)] = raw;
|
|
235
295
|
}
|
|
236
296
|
// Validate required params
|
|
237
297
|
for (const p of params) {
|
|
@@ -250,14 +310,21 @@ export async function parseArgv(params, tokens) {
|
|
|
250
310
|
return result;
|
|
251
311
|
}
|
|
252
312
|
export async function runCli(root, argv) {
|
|
253
|
-
// argv is process.argv — strip node binary + script path
|
|
254
|
-
|
|
313
|
+
// argv is process.argv — strip node binary + script path. `--json` is a
|
|
314
|
+
// global: pull it out anywhere it appears so the rest of argv parses against
|
|
315
|
+
// the leaf schema unchanged, and switch stdout from rendered prose to raw
|
|
316
|
+
// JSON. It is intentionally undocumented — the default prose output is the
|
|
317
|
+
// agent contract; --json exists only for programmatic/tooling consumers.
|
|
318
|
+
const rawTokens = argv.slice(2);
|
|
319
|
+
const tokens = rawTokens.filter((t) => t !== '--json');
|
|
320
|
+
if (tokens.length !== rawTokens.length)
|
|
321
|
+
setJsonOutput(true);
|
|
255
322
|
// Bare root invocation or -h at root
|
|
256
323
|
if (tokens.length === 0 || (tokens.length === 1 && (tokens[0] === '-h' || tokens[0] === '--help'))) {
|
|
257
324
|
process.stdout.write(renderRoot(root.help) + '\n');
|
|
258
325
|
process.exit(ExitCode.SUCCESS);
|
|
259
326
|
}
|
|
260
|
-
const { node, remaining } = walk(root, tokens);
|
|
327
|
+
const { node, path, remaining } = walk(root, tokens);
|
|
261
328
|
try {
|
|
262
329
|
// Help anywhere in remaining tokens → print node help and exit
|
|
263
330
|
if (helpRequested(remaining)) {
|
|
@@ -267,7 +334,7 @@ export async function runCli(root, argv) {
|
|
|
267
334
|
// Bare branch or bare root (no -h, but no leaf selected) → help surface
|
|
268
335
|
if (node.kind === 'root' || node.kind === 'branch') {
|
|
269
336
|
if (remaining.length > 0) {
|
|
270
|
-
throw unknownPathError(node, remaining[0]);
|
|
337
|
+
throw unknownPathError(node, path, remaining[0]);
|
|
271
338
|
}
|
|
272
339
|
process.stdout.write(renderNode(node) + '\n');
|
|
273
340
|
process.exit(ExitCode.SUCCESS);
|
|
@@ -277,7 +344,13 @@ export async function runCli(root, argv) {
|
|
|
277
344
|
const input = await parseArgv(params, remaining);
|
|
278
345
|
const result = await node.run(input);
|
|
279
346
|
if (result !== undefined && result !== null) {
|
|
280
|
-
|
|
347
|
+
if (isJsonOutput()) {
|
|
348
|
+
emit(result);
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
const text = node.render !== undefined ? node.render(result) : renderResult(result, node.help);
|
|
352
|
+
process.stdout.write(text + '\n');
|
|
353
|
+
}
|
|
281
354
|
}
|
|
282
355
|
// JSONL leaves call emitLine themselves and return void
|
|
283
356
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export type PushKind = 'update' | 'urgent' | 'final';
|
|
2
|
+
export interface PushOpts {
|
|
3
|
+
/** Semantic kind of this push. `final` also finalises the node. */
|
|
4
|
+
kind: PushKind;
|
|
5
|
+
/** Report body (markdown). Written verbatim after the YAML frontmatter. */
|
|
6
|
+
body: string;
|
|
7
|
+
/**
|
|
8
|
+
* Node id of the sender — recorded as `from` on each inbox entry.
|
|
9
|
+
* Defaults to `nodeId` (the publisher) when omitted.
|
|
10
|
+
*/
|
|
11
|
+
from?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface PushResult {
|
|
14
|
+
/** Absolute path of the written report file. */
|
|
15
|
+
reportPath: string;
|
|
16
|
+
/** Node ids that received an inbox pointer. */
|
|
17
|
+
deliveredTo: string[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Push a report from `nodeId` and fan it out as inbox pointers to all
|
|
21
|
+
* current subscribers.
|
|
22
|
+
*
|
|
23
|
+
* Steps:
|
|
24
|
+
* (a) Write nodes/<nodeId>/reports/<ts>-<kind>.md (YAML front + body).
|
|
25
|
+
* (b) For each active/passive subscriber, append a pointer to their inbox.
|
|
26
|
+
* (c) If kind === 'final', mark the node done.
|
|
27
|
+
*/
|
|
28
|
+
export declare function push(nodeId: string, opts: PushOpts): Promise<PushResult>;
|
|
29
|
+
/** Emit a routine progress update from `nodeId`. */
|
|
30
|
+
export declare function pushUpdate(nodeId: string, body: string, opts?: {
|
|
31
|
+
from?: string;
|
|
32
|
+
}): Promise<PushResult>;
|
|
33
|
+
/** Emit an urgent alert from `nodeId` (inbox tier: urgent). */
|
|
34
|
+
export declare function pushUrgent(nodeId: string, body: string, opts?: {
|
|
35
|
+
from?: string;
|
|
36
|
+
}): Promise<PushResult>;
|
|
37
|
+
/**
|
|
38
|
+
* Emit the final report from `nodeId` (inbox tier: normal, kind: final).
|
|
39
|
+
* Also transitions the node to status=done / intent=done.
|
|
40
|
+
*/
|
|
41
|
+
export declare function pushFinal(nodeId: string, body: string, opts?: {
|
|
42
|
+
from?: string;
|
|
43
|
+
}): Promise<PushResult>;
|