@synap-core/cli 1.13.0 → 1.13.1-next.20261102.246dc73
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 +13 -3
- package/dist/build-info.json +2 -3
- package/dist/commands/agents.js +12 -2
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/capability.d.ts +86 -0
- package/dist/commands/capability.js +117 -30
- package/dist/commands/capability.js.map +1 -1
- package/dist/commands/context.js +2 -1
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/data-extra.d.ts +13 -0
- package/dist/commands/data-extra.js +23 -2
- package/dist/commands/data-extra.js.map +1 -1
- package/dist/commands/doctor.d.ts +18 -0
- package/dist/commands/doctor.js +28 -1
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init.js +4 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/keys-request.d.ts +48 -0
- package/dist/commands/keys-request.js +236 -0
- package/dist/commands/keys-request.js.map +1 -0
- package/dist/commands/keys-request.test.d.ts +1 -0
- package/dist/commands/keys-request.test.js +176 -0
- package/dist/commands/keys-request.test.js.map +1 -0
- package/dist/commands/knowledge.d.ts +42 -5
- package/dist/commands/knowledge.js +90 -27
- package/dist/commands/knowledge.js.map +1 -1
- package/dist/commands/market.d.ts +89 -31
- package/dist/commands/market.js +215 -222
- package/dist/commands/market.js.map +1 -1
- package/dist/commands/mcp.d.ts +1 -1
- package/dist/commands/mcp.js +14 -9
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/pods.js +6 -2
- package/dist/commands/pods.js.map +1 -1
- package/dist/commands/proposals-actions.js +7 -1
- package/dist/commands/proposals-actions.js.map +1 -1
- package/dist/commands/providers.d.ts +22 -6
- package/dist/commands/providers.js +39 -32
- package/dist/commands/providers.js.map +1 -1
- package/dist/commands/raycast.d.ts +15 -0
- package/dist/commands/raycast.js +36 -10
- package/dist/commands/raycast.js.map +1 -1
- package/dist/commands/status.js +12 -6
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/statusline.backoff.test.js +13 -3
- package/dist/commands/statusline.backoff.test.js.map +1 -1
- package/dist/commands/statusline.d.ts +104 -9
- package/dist/commands/statusline.js +392 -148
- package/dist/commands/statusline.js.map +1 -1
- package/dist/commands/statusline.line2.test.d.ts +15 -0
- package/dist/commands/statusline.line2.test.js +226 -0
- package/dist/commands/statusline.line2.test.js.map +1 -0
- package/dist/commands/statusline.needs-you.test.d.ts +1 -0
- package/dist/commands/statusline.needs-you.test.js +17 -0
- package/dist/commands/statusline.needs-you.test.js.map +1 -0
- package/dist/commands/statusline.now.test.d.ts +10 -0
- package/dist/commands/statusline.now.test.js +250 -0
- package/dist/commands/statusline.now.test.js.map +1 -0
- package/dist/commands/tracks.d.ts +1 -0
- package/dist/commands/tracks.js +4 -0
- package/dist/commands/tracks.js.map +1 -1
- package/dist/commands/upload.js +12 -0
- package/dist/commands/upload.js.map +1 -1
- package/dist/commands/wait.d.ts +124 -0
- package/dist/commands/wait.js +384 -0
- package/dist/commands/wait.js.map +1 -0
- package/dist/commands/wait.test.d.ts +8 -0
- package/dist/commands/wait.test.js +308 -0
- package/dist/commands/wait.test.js.map +1 -0
- package/dist/commands/worker-github-app.d.ts +63 -0
- package/dist/commands/worker-github-app.js +161 -0
- package/dist/commands/worker-github-app.js.map +1 -0
- package/dist/commands/worker-github-app.test.d.ts +1 -0
- package/dist/commands/worker-github-app.test.js +102 -0
- package/dist/commands/worker-github-app.test.js.map +1 -0
- package/dist/commands/worker-github-broker.d.ts +39 -0
- package/dist/commands/worker-github-broker.js +106 -0
- package/dist/commands/worker-github-broker.js.map +1 -0
- package/dist/commands/worker-github-broker.test.d.ts +1 -0
- package/dist/commands/worker-github-broker.test.js +90 -0
- package/dist/commands/worker-github-broker.test.js.map +1 -0
- package/dist/commands/worker-runs.test.d.ts +1 -0
- package/dist/commands/worker-runs.test.js +150 -0
- package/dist/commands/worker-runs.test.js.map +1 -0
- package/dist/commands/worker-worktrees.d.ts +34 -0
- package/dist/commands/worker-worktrees.js +87 -0
- package/dist/commands/worker-worktrees.js.map +1 -0
- package/dist/commands/worker.d.ts +192 -5
- package/dist/commands/worker.js +467 -45
- package/dist/commands/worker.js.map +1 -1
- package/dist/commands/worker.test.js +269 -3
- package/dist/commands/worker.test.js.map +1 -1
- package/dist/index.js +59 -4
- package/dist/index.js.map +1 -1
- package/dist/lib/agents-config.d.ts +9 -0
- package/dist/lib/agents-config.js +20 -0
- package/dist/lib/agents-config.js.map +1 -1
- package/dist/lib/atomic-file.d.ts +20 -1
- package/dist/lib/atomic-file.js +100 -2
- package/dist/lib/atomic-file.js.map +1 -1
- package/dist/lib/auth.d.ts +25 -0
- package/dist/lib/auth.js +49 -9
- package/dist/lib/auth.js.map +1 -1
- package/dist/lib/box/job-server.js +4 -0
- package/dist/lib/box/job-server.js.map +1 -1
- package/dist/lib/bridge-invocation.test.d.ts +1 -0
- package/dist/lib/bridge-invocation.test.js +75 -0
- package/dist/lib/bridge-invocation.test.js.map +1 -0
- package/dist/lib/bridge-status.d.ts +144 -0
- package/dist/lib/bridge-status.js +227 -0
- package/dist/lib/bridge-status.js.map +1 -0
- package/dist/lib/bridge-status.test.d.ts +6 -0
- package/dist/lib/bridge-status.test.js +197 -0
- package/dist/lib/bridge-status.test.js.map +1 -0
- package/dist/lib/client-header.test.js +19 -1
- package/dist/lib/client-header.test.js.map +1 -1
- package/dist/lib/conversation-headers.d.ts +67 -2
- package/dist/lib/conversation-headers.js +122 -6
- package/dist/lib/conversation-headers.js.map +1 -1
- package/dist/lib/conversation-headers.test.d.ts +1 -0
- package/dist/lib/conversation-headers.test.js +78 -0
- package/dist/lib/conversation-headers.test.js.map +1 -0
- package/dist/lib/credential-class.d.ts +12 -1
- package/dist/lib/credential-class.js +20 -0
- package/dist/lib/credential-class.js.map +1 -1
- package/dist/lib/device-connect.d.ts +45 -0
- package/dist/lib/device-connect.js +60 -0
- package/dist/lib/device-connect.js.map +1 -1
- package/dist/lib/goal-words.js +6 -3
- package/dist/lib/goal-words.js.map +1 -1
- package/dist/lib/hub-client.d.ts +59 -1
- package/dist/lib/hub-client.js +130 -36
- package/dist/lib/hub-client.js.map +1 -1
- package/dist/lib/key-locations.d.ts +3 -3
- package/dist/lib/key-locations.js +3 -39
- package/dist/lib/key-locations.js.map +1 -1
- package/dist/lib/machine-instance.d.ts +7 -5
- package/dist/lib/machine-instance.js +7 -5
- package/dist/lib/machine-instance.js.map +1 -1
- package/dist/lib/mcp-bridge.d.ts +43 -0
- package/dist/lib/mcp-bridge.js +199 -11
- package/dist/lib/mcp-bridge.js.map +1 -1
- package/dist/lib/mcp-bridge.test.js +194 -2
- package/dist/lib/mcp-bridge.test.js.map +1 -1
- package/dist/lib/mcp-call.d.ts +5 -1
- package/dist/lib/mcp-call.js +7 -6
- package/dist/lib/mcp-call.js.map +1 -1
- package/dist/lib/mcp-connector-url.test.d.ts +1 -0
- package/dist/lib/mcp-connector-url.test.js +13 -0
- package/dist/lib/mcp-connector-url.test.js.map +1 -0
- package/dist/lib/mcp-snippets.d.ts +16 -0
- package/dist/lib/mcp-snippets.js +20 -2
- package/dist/lib/mcp-snippets.js.map +1 -1
- package/dist/lib/now-line.d.ts +66 -0
- package/dist/lib/now-line.js +193 -0
- package/dist/lib/now-line.js.map +1 -0
- package/dist/lib/now-line.test.d.ts +5 -0
- package/dist/lib/now-line.test.js +177 -0
- package/dist/lib/now-line.test.js.map +1 -0
- package/dist/lib/package-updates.d.ts +66 -0
- package/dist/lib/package-updates.js +76 -0
- package/dist/lib/package-updates.js.map +1 -0
- package/dist/lib/pod.d.ts +0 -4
- package/dist/lib/pod.js +0 -7
- package/dist/lib/pod.js.map +1 -1
- package/dist/lib/register-claude-code-mcp.test.js +19 -0
- package/dist/lib/register-claude-code-mcp.test.js.map +1 -1
- package/dist/lib/safe-open.d.ts +5 -3
- package/dist/lib/safe-open.js +9 -8
- package/dist/lib/safe-open.js.map +1 -1
- package/dist/lib/self-host.d.ts +17 -0
- package/dist/lib/self-host.js +24 -0
- package/dist/lib/self-host.js.map +1 -0
- package/dist/lib/targets.d.ts +31 -0
- package/dist/lib/targets.js +100 -104
- package/dist/lib/targets.js.map +1 -1
- package/dist/lib/template-lifecycle.d.ts +0 -31
- package/dist/lib/template-lifecycle.js +0 -74
- package/dist/lib/template-lifecycle.js.map +1 -1
- package/dist/lib/term.d.ts +24 -0
- package/dist/lib/term.js +52 -0
- package/dist/lib/term.js.map +1 -0
- package/dist/lib/term.test.d.ts +1 -0
- package/dist/lib/term.test.js +29 -0
- package/dist/lib/term.test.js.map +1 -0
- package/dist/lib/transcript-index.d.ts +94 -0
- package/dist/lib/transcript-index.js +321 -0
- package/dist/lib/transcript-index.js.map +1 -0
- package/dist/lib/transcript-index.test.d.ts +10 -0
- package/dist/lib/transcript-index.test.js +200 -0
- package/dist/lib/transcript-index.test.js.map +1 -0
- package/dist/lib/wait-help.d.ts +11 -0
- package/dist/lib/wait-help.js +25 -0
- package/dist/lib/wait-help.js.map +1 -0
- package/dist/lib/wait-state.d.ts +57 -0
- package/dist/lib/wait-state.js +114 -0
- package/dist/lib/wait-state.js.map +1 -0
- package/package.json +2 -2
- package/skills/synap/SKILL.md +81 -44
- package/skills/synap/code-skills.md +15 -1
- package/skills/synap/concepts.md +36 -26
- package/skills/synap/document-embeds.md +15 -1
- package/skills/synap/focus-sessions.md +4 -4
- package/skills/synap/from-intent.md +2 -2
- package/skills/synap/governance-rules.md +2 -0
- package/skills/synap/hand-off.md +10 -6
- package/skills/synap/reflexes.md +4 -4
- package/skills/synap/work-model.md +1 -1
- package/skills/synap/writing-titles.md +7 -0
- package/skills/synap-app/SKILL.md +2 -2
- package/skills/synap-app/overview.md +2 -2
- package/skills/synap-market/SKILL.md +1 -1
- package/skills/synap-market/governance-and-catalog.md +1 -1
- package/skills/synap-schema/SKILL.md +4 -1
- package/skills/synap-schema/intent-vocabulary-ssot.md +4 -1
- package/dist/commands/template.d.ts +0 -42
- package/dist/commands/template.js +0 -191
- package/dist/commands/template.js.map +0 -1
- package/dist/lib/browser-auth.d.ts +0 -35
- package/dist/lib/browser-auth.js +0 -170
- package/dist/lib/browser-auth.js.map +0 -1
- package/dist/lib/openclaw.d.ts +0 -32
- package/dist/lib/openclaw.js +0 -165
- package/dist/lib/openclaw.js.map +0 -1
- package/dist/lib/seed.d.ts +0 -13
- package/dist/lib/seed.js +0 -135
- package/dist/lib/seed.js.map +0 -1
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The status line's 4th line from the pod, through the synap-bridge plugin's
|
|
3
|
+
* per-conversation status file (bridge 0.10, plan P3.3).
|
|
4
|
+
*
|
|
5
|
+
* After each read of the bound session the bridge writes
|
|
6
|
+
* `~/.synap/bridge/status/<Claude Code session id>.json`, whole (tmp + `mv -f`,
|
|
7
|
+
* so a reader never sees half a file): `{ v: 1, writtenAt, sessionId, title,
|
|
8
|
+
* live, nextMove, waitingOn, workUnits }`, each from the session's
|
|
9
|
+
* continuation packet. A section the pod could not read is `null`, never
|
|
10
|
+
* empty; `sessionId: null` = nothing is bound. The writer is the bridge's
|
|
11
|
+
* `statusFile` (synap-bridge `hooks/lib.ts`).
|
|
12
|
+
*
|
|
13
|
+
* The file is the FIRST source of what the conversation waits on; the
|
|
14
|
+
* transcript index (lib/now-line.ts) stays the fallback whenever the file is
|
|
15
|
+
* absent, unbound, stale or unreadable. The turn state itself (working /
|
|
16
|
+
* needs you / stopped) stays Claude Code's registry when it is readable: the
|
|
17
|
+
* pod's `live` is the bridge's relay of that same registry, smoothed by the
|
|
18
|
+
* pod's 5-minute rule, so it is used only when the registry is not there.
|
|
19
|
+
*
|
|
20
|
+
* ▶ working · 4m · ⏳ 2 · other work (2) · synap-cli#52 open ✗
|
|
21
|
+
* ⏳ waiting on 3 · other work (2) · approval · synap-cli#52 open ✓
|
|
22
|
+
* ■ stopped · next: Produce "Wave 2 report" · pod 40m ago
|
|
23
|
+
*
|
|
24
|
+
* Local files only, no network. `readBridgeStatus` never throws;
|
|
25
|
+
* `buildBridgeLine` is pure.
|
|
26
|
+
*/
|
|
27
|
+
import fs from "node:fs";
|
|
28
|
+
import os from "node:os";
|
|
29
|
+
import path from "node:path";
|
|
30
|
+
import { WAITING_ON_REASON_LABELS, goalWord } from "./goal-words.js";
|
|
31
|
+
import { age, capped, clip, DEFAULT_WIDTH } from "./now-line.js";
|
|
32
|
+
import { AMBER as A, BOLD_YELLOW as Y, CYAN as C, DIM as D, GREEN as G, RED as R, RESET as X, osc8, scrubStrings } from "./term.js";
|
|
33
|
+
/** The pod's working window (`SESSION_WORKING_WINDOW_MS`), for a file whose `live` carries none. */
|
|
34
|
+
export const POD_WORKING_WINDOW_MS = 5 * 60_000;
|
|
35
|
+
/**
|
|
36
|
+
* A file older than this is stale even when no registry says when this
|
|
37
|
+
* Claude Code process started: beyond half a day nothing shows its writer
|
|
38
|
+
* still runs. A quiet file younger than this is still true (the bridge
|
|
39
|
+
* rewrites it on every change the pod's feed sends), and its age is shown.
|
|
40
|
+
*/
|
|
41
|
+
export const BRIDGE_STATUS_MAX_AGE_MS = 12 * 60 * 60_000;
|
|
42
|
+
export function bridgeStatusPath(claudeSessionId, home = os.homedir()) {
|
|
43
|
+
return path.join(home, ".synap", "bridge", "status", `${claudeSessionId}.json`);
|
|
44
|
+
}
|
|
45
|
+
const isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
46
|
+
const isTime = (v) => typeof v === "string" && Number.isFinite(Date.parse(v));
|
|
47
|
+
const isStrOrNull = (v) => v === null || typeof v === "string";
|
|
48
|
+
/**
|
|
49
|
+
* The file's `live`, normalised. One malformed field costs that field (null),
|
|
50
|
+
* and a `live` that is no object at all reads as "not measured" (null): the
|
|
51
|
+
* rest of the file is still true, so it is never a failed read.
|
|
52
|
+
*/
|
|
53
|
+
function readLive(v) {
|
|
54
|
+
if (!isObj(v))
|
|
55
|
+
return null;
|
|
56
|
+
const windowMs = typeof v.windowMs === "number" && Number.isFinite(v.windowMs) && v.windowMs > 0 ? v.windowMs : null;
|
|
57
|
+
return {
|
|
58
|
+
state: v.state === "working" ? "working" : "idle",
|
|
59
|
+
turnInFlight: v.turnInFlight === true,
|
|
60
|
+
since: isTime(v.since) ? v.since : null,
|
|
61
|
+
lastActivityAt: isTime(v.lastActivityAt) ? v.lastActivityAt : null,
|
|
62
|
+
measuredAt: isTime(v.measuredAt) ? v.measuredAt : null,
|
|
63
|
+
windowMs,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The parsed JSON as a version-1 file, or why it is not one. Every string in
|
|
68
|
+
* it is scrubbed of control characters first (term.ts): the file carries pod
|
|
69
|
+
* words (titles, labels, PR names) that are printed as they are.
|
|
70
|
+
*/
|
|
71
|
+
export function parseBridgeStatus(raw) {
|
|
72
|
+
const json = scrubStrings(raw);
|
|
73
|
+
if (!isObj(json))
|
|
74
|
+
return "not an object";
|
|
75
|
+
if (json.v !== 1)
|
|
76
|
+
return `version ${JSON.stringify(json.v)} is not 1`;
|
|
77
|
+
if (!isTime(json.writtenAt))
|
|
78
|
+
return "no writtenAt time";
|
|
79
|
+
if (!isStrOrNull(json.sessionId))
|
|
80
|
+
return "sessionId is not a string";
|
|
81
|
+
if (!isStrOrNull(json.title ?? null))
|
|
82
|
+
return "title is not a string";
|
|
83
|
+
const live = readLive(json.live);
|
|
84
|
+
const next = json.nextMove ?? null;
|
|
85
|
+
if (next !== null && (!isObj(next) || typeof next.label !== "string"))
|
|
86
|
+
return "nextMove is malformed";
|
|
87
|
+
const waits = json.waitingOn ?? null;
|
|
88
|
+
if (waits !== null && (!Array.isArray(waits) || !waits.every((m) => isObj(m) && typeof m.reason === "string" && typeof m.count === "number")))
|
|
89
|
+
return "waitingOn is malformed";
|
|
90
|
+
const units = json.workUnits ?? null;
|
|
91
|
+
if (units !== null && (!Array.isArray(units) || !units.every((u) => isObj(u) && typeof u.label === "string" && typeof u.state === "string")))
|
|
92
|
+
return "workUnits is malformed";
|
|
93
|
+
return {
|
|
94
|
+
v: 1,
|
|
95
|
+
writtenAt: json.writtenAt,
|
|
96
|
+
sessionId: json.sessionId,
|
|
97
|
+
title: json.title ?? null,
|
|
98
|
+
live,
|
|
99
|
+
nextMove: next,
|
|
100
|
+
waitingOn: waits,
|
|
101
|
+
workUnits: units,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* This conversation's status file, judged. Stale when it was written before
|
|
106
|
+
* this Claude Code process started (`startedAt`, from the registry: the
|
|
107
|
+
* bridge of an earlier process wrote it, and this one has not confirmed the
|
|
108
|
+
* binding yet), or is older than BRIDGE_STATUS_MAX_AGE_MS. Never throws.
|
|
109
|
+
*/
|
|
110
|
+
export function readBridgeStatus(claudeSessionId, opts) {
|
|
111
|
+
// A session id becomes a file name: anything that could leave the directory is no file.
|
|
112
|
+
if (!/^[A-Za-z0-9_-]+$/.test(claudeSessionId))
|
|
113
|
+
return { status: "absent" };
|
|
114
|
+
let raw;
|
|
115
|
+
try {
|
|
116
|
+
raw = fs.readFileSync(bridgeStatusPath(claudeSessionId, opts.home), "utf-8");
|
|
117
|
+
}
|
|
118
|
+
catch (e) {
|
|
119
|
+
if (e.code === "ENOENT")
|
|
120
|
+
return { status: "absent" };
|
|
121
|
+
return { status: "failed", why: `unreadable (${e.code ?? "error"})` };
|
|
122
|
+
}
|
|
123
|
+
let json;
|
|
124
|
+
try {
|
|
125
|
+
json = JSON.parse(raw);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return { status: "failed", why: "not JSON" };
|
|
129
|
+
}
|
|
130
|
+
const file = parseBridgeStatus(json);
|
|
131
|
+
if (typeof file === "string")
|
|
132
|
+
return { status: "failed", why: file };
|
|
133
|
+
const writtenAt = Date.parse(file.writtenAt);
|
|
134
|
+
if (opts.startedAt && writtenAt < opts.startedAt)
|
|
135
|
+
return { status: "stale", why: "written before this Claude Code process started" };
|
|
136
|
+
if (opts.now - writtenAt > BRIDGE_STATUS_MAX_AGE_MS)
|
|
137
|
+
return { status: "stale", why: `written ${age(opts.now - writtenAt)} ago` };
|
|
138
|
+
if (file.sessionId === null)
|
|
139
|
+
return { status: "unbound" };
|
|
140
|
+
return { status: "fresh", file, writtenAt };
|
|
141
|
+
}
|
|
142
|
+
// ── The line ───────────────────────────────────────────────────────────
|
|
143
|
+
/** One mark: `other work (2)`, words through the vocabulary's table (goal-words.ts). */
|
|
144
|
+
function markText(m) {
|
|
145
|
+
const reason = goalWord(WAITING_ON_REASON_LABELS, m.reason).toLowerCase();
|
|
146
|
+
return m.count > 1 ? `${reason} (${m.count})` : reason;
|
|
147
|
+
}
|
|
148
|
+
/** A pull request still under way: `synap-cli#52 open ✗`, its name a link to it. */
|
|
149
|
+
function unitText(u) {
|
|
150
|
+
const isPr = u.kind === "pull_request" || /\/pull\/\d+/.test(u.url ?? "");
|
|
151
|
+
if (!isPr || (u.state !== "open" && u.state !== "claimed"))
|
|
152
|
+
return null;
|
|
153
|
+
const name = osc8(u.url, /^\S+#\d+/.exec(u.label)?.[0] ?? clip(u.label, 30));
|
|
154
|
+
const p = u.prState;
|
|
155
|
+
if (!p)
|
|
156
|
+
return name; // the pod recorded nothing about it: draw nothing, never a guess
|
|
157
|
+
if (p.status !== "ok")
|
|
158
|
+
return `${name} ${D}⚑ not read${X}`;
|
|
159
|
+
const word = (p.label ?? p.state ?? "").toLowerCase();
|
|
160
|
+
const checks = p.checks === "passed" ? ` ${G}✓${X}` : p.checks === "failed" ? ` ${R}✗${X}` : "";
|
|
161
|
+
return `${name}${word ? ` ${D}${word}${X}` : ""}${checks}`;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* The pod's rule (`isSessionWorkingNow`) on the file's `live`, judged now.
|
|
165
|
+
*
|
|
166
|
+
* `turnInFlight` is the pod's word AT `measuredAt` (else when the file was
|
|
167
|
+
* written, `writtenAt`), not now: it holds for one working window after that
|
|
168
|
+
* and no longer. Read as "now", a file the bridge last wrote mid-turn said
|
|
169
|
+
* "working" for as long as the file stayed fresh (12 h).
|
|
170
|
+
*/
|
|
171
|
+
export function podWorking(live, now, writtenAt) {
|
|
172
|
+
const window = live.windowMs ?? POD_WORKING_WINDOW_MS;
|
|
173
|
+
const last = live.lastActivityAt ? Date.parse(live.lastActivityAt) : NaN;
|
|
174
|
+
if (Number.isFinite(last) && now - last <= window)
|
|
175
|
+
return true;
|
|
176
|
+
if (!live.turnInFlight)
|
|
177
|
+
return false;
|
|
178
|
+
const measured = live.measuredAt ? Date.parse(live.measuredAt) : writtenAt ?? NaN;
|
|
179
|
+
return Number.isFinite(measured) && now - measured <= window;
|
|
180
|
+
}
|
|
181
|
+
/** The 4th line from a fresh status file. */
|
|
182
|
+
export function buildBridgeLine(input) {
|
|
183
|
+
const { now, file, registry } = input;
|
|
184
|
+
const width = input.width && input.width > 20 ? input.width : DEFAULT_WIDTH;
|
|
185
|
+
if (registry?.status === "waiting") {
|
|
186
|
+
return `${Y}✋ needs you${X} ${D}·${X} ${clip(registry.waitingFor || "your answer", width - 16)}`;
|
|
187
|
+
}
|
|
188
|
+
const marks = (file.waitingOn ?? []).filter((m) => m.count > 0);
|
|
189
|
+
const waited = marks.reduce((n, m) => n + m.count, 0);
|
|
190
|
+
const units = (file.workUnits ?? []).map(unitText).filter((t) => t !== null);
|
|
191
|
+
const unread = [file.waitingOn === null && "waits", file.workUnits === null && "PRs"].filter(Boolean);
|
|
192
|
+
// The registry's turn state when there is one; the pod's relay of it otherwise.
|
|
193
|
+
let working = false;
|
|
194
|
+
let since;
|
|
195
|
+
if (registry) {
|
|
196
|
+
working = registry.status === "busy";
|
|
197
|
+
since = registry.statusUpdatedAt;
|
|
198
|
+
}
|
|
199
|
+
else if (file.live) {
|
|
200
|
+
working = podWorking(file.live, now, input.writtenAt);
|
|
201
|
+
const at = Date.parse((working ? file.live.since : file.live.lastActivityAt) ?? "");
|
|
202
|
+
since = Number.isFinite(at) ? at : undefined;
|
|
203
|
+
}
|
|
204
|
+
const items = [...marks.map(markText), ...units];
|
|
205
|
+
if (!working && file.nextMove?.label)
|
|
206
|
+
items.push(`${D}next:${X} ${clip(file.nextMove.label, 60)}`);
|
|
207
|
+
if (unread.length)
|
|
208
|
+
items.push(`${D}⚑ not read: ${unread.join(", ")}${X}`);
|
|
209
|
+
// A quiet file is still true, but say how old the pod's word is.
|
|
210
|
+
if (now - input.writtenAt > POD_WORKING_WINDOW_MS)
|
|
211
|
+
items.push(`${D}pod ${age(now - input.writtenAt)} ago${X}`);
|
|
212
|
+
let head;
|
|
213
|
+
if (working) {
|
|
214
|
+
head = `${C}▶ working${X}${since ? ` ${D}· ${age(now - since)}${X}` : ""}${waited ? ` ${D}·${X} ⏳ ${waited}` : ""}`;
|
|
215
|
+
}
|
|
216
|
+
else if (waited) {
|
|
217
|
+
head = `${Y}⏳ waiting on ${waited}${X}`;
|
|
218
|
+
}
|
|
219
|
+
else if (!registry && !file.live) {
|
|
220
|
+
head = `${D}○ no session status${X}`;
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
head = `${A}■ stopped${X}${since ? ` ${D}· ${age(now - since)}${X}` : ""}`;
|
|
224
|
+
}
|
|
225
|
+
return capped(head, items, width);
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=bridge-status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-status.js","sourceRoot":"","sources":["../../src/lib/bridge-status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bridge's status file as the status line reads it: judged (fresh /
|
|
3
|
+
* absent / unbound / stale / failed) and drawn. Files go under a temp HOME
|
|
4
|
+
* passed as `home`; time, registry and width are arguments.
|
|
5
|
+
*/
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import os from "node:os";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
10
|
+
import { BRIDGE_STATUS_MAX_AGE_MS, bridgeStatusPath, buildBridgeLine, readBridgeStatus, } from "./bridge-status.js";
|
|
11
|
+
const NOW = Date.parse("2026-10-30T22:00:00Z");
|
|
12
|
+
const min = (m) => NOW - m * 60_000;
|
|
13
|
+
const iso = (ms) => new Date(ms).toISOString();
|
|
14
|
+
// eslint-disable-next-line no-control-regex
|
|
15
|
+
const plain = (s) => s.replace(/\x1b\[[0-9;]*m|\x1b\]8;;[^\x1b]*\x1b\\/g, "");
|
|
16
|
+
const CONV = "11111111-2222-3333-4444-555555555555";
|
|
17
|
+
/** A file as the bridge's `statusFile` writes it (synap-bridge hooks/lib.ts). */
|
|
18
|
+
const file = (over = {}) => ({
|
|
19
|
+
v: 1,
|
|
20
|
+
writtenAt: iso(min(1)),
|
|
21
|
+
sessionId: "54436c32-783f-4a9d-9322-16ff18f448f7",
|
|
22
|
+
title: "Lane cli-statusfile",
|
|
23
|
+
live: { state: "idle", turnInFlight: false, since: iso(min(20)), lastActivityAt: iso(min(20)), measuredAt: iso(min(1)), windowMs: 300_000 },
|
|
24
|
+
nextMove: { kind: "agent_slot", actor: "ai", label: 'Produce "Wave 2 report"', reason: "Declared deliverable with no output yet" },
|
|
25
|
+
waitingOn: [],
|
|
26
|
+
workUnits: [],
|
|
27
|
+
...over,
|
|
28
|
+
});
|
|
29
|
+
const reg = (status, extra = {}) => ({
|
|
30
|
+
sessionId: CONV,
|
|
31
|
+
pid: 1,
|
|
32
|
+
status,
|
|
33
|
+
statusUpdatedAt: min(4),
|
|
34
|
+
startedAt: min(600),
|
|
35
|
+
...extra,
|
|
36
|
+
});
|
|
37
|
+
let home;
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
home = fs.mkdtempSync(path.join(os.tmpdir(), "bridge-status-"));
|
|
40
|
+
});
|
|
41
|
+
const write = (content) => {
|
|
42
|
+
const p = bridgeStatusPath(CONV, home);
|
|
43
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
44
|
+
fs.writeFileSync(p, content);
|
|
45
|
+
};
|
|
46
|
+
const read = (opts = {}) => readBridgeStatus(CONV, { now: NOW, home, ...opts });
|
|
47
|
+
describe("reading the file", () => {
|
|
48
|
+
it("is where the bridge writes it", () => {
|
|
49
|
+
expect(bridgeStatusPath(CONV, "/home/x")).toBe(`/home/x/.synap/bridge/status/${CONV}.json`);
|
|
50
|
+
});
|
|
51
|
+
it("a fresh file of a bound session is drawn", () => {
|
|
52
|
+
write(JSON.stringify(file()));
|
|
53
|
+
const r = read({ startedAt: min(600) });
|
|
54
|
+
expect(r.status).toBe("fresh");
|
|
55
|
+
if (r.status === "fresh")
|
|
56
|
+
expect(r.writtenAt).toBe(min(1));
|
|
57
|
+
});
|
|
58
|
+
it("no file is absent (no bridge, or it has not written yet)", () => {
|
|
59
|
+
expect(read()).toEqual({ status: "absent" });
|
|
60
|
+
});
|
|
61
|
+
it("nothing bound (`sessionId: null`, /bridge unbind) is unbound, not drawn", () => {
|
|
62
|
+
write(JSON.stringify(file({ sessionId: null, title: null })));
|
|
63
|
+
expect(read()).toEqual({ status: "unbound" });
|
|
64
|
+
});
|
|
65
|
+
it("a file written before this Claude Code process started is stale", () => {
|
|
66
|
+
write(JSON.stringify(file({ writtenAt: iso(min(90)) })));
|
|
67
|
+
expect(read({ startedAt: min(60) })).toEqual({ status: "stale", why: "written before this Claude Code process started" });
|
|
68
|
+
});
|
|
69
|
+
it("a file older than the max age is stale, even with no registry", () => {
|
|
70
|
+
write(JSON.stringify(file({ writtenAt: iso(NOW - BRIDGE_STATUS_MAX_AGE_MS - 60_000) })));
|
|
71
|
+
expect(read()).toMatchObject({ status: "stale" });
|
|
72
|
+
});
|
|
73
|
+
it("a quiet file younger than the max age is still fresh", () => {
|
|
74
|
+
write(JSON.stringify(file({ writtenAt: iso(min(45)) })));
|
|
75
|
+
expect(read({ startedAt: min(600) }).status).toBe("fresh");
|
|
76
|
+
});
|
|
77
|
+
it("malformed JSON is a failed read, never absent, and never throws", () => {
|
|
78
|
+
write('{"v":1,"writtenAt":');
|
|
79
|
+
expect(read()).toEqual({ status: "failed", why: "not JSON" });
|
|
80
|
+
});
|
|
81
|
+
it("another version, a missing time or a malformed section is a failed read", () => {
|
|
82
|
+
write(JSON.stringify({ ...file(), v: 2 }));
|
|
83
|
+
expect(read()).toMatchObject({ status: "failed", why: "version 2 is not 1" });
|
|
84
|
+
write(JSON.stringify({ ...file(), writtenAt: "soon" }));
|
|
85
|
+
expect(read()).toMatchObject({ status: "failed", why: "no writtenAt time" });
|
|
86
|
+
write(JSON.stringify({ ...file(), waitingOn: { status: "ok", marks: [] } }));
|
|
87
|
+
expect(read()).toMatchObject({ status: "failed", why: "waitingOn is malformed" });
|
|
88
|
+
write("[]");
|
|
89
|
+
expect(read()).toMatchObject({ status: "failed", why: "not an object" });
|
|
90
|
+
});
|
|
91
|
+
it("a malformed `live` costs only its own fields, never the file (bridge ≤ 0.11 wrote NaN and \"undefined\")", () => {
|
|
92
|
+
// What `String(undefined)` and `Number(undefined)` (NaN → null in JSON) leave in the file.
|
|
93
|
+
write(JSON.stringify({ ...file(), live: { state: "idle", turnInFlight: false, since: null, lastActivityAt: null, measuredAt: "undefined", windowMs: null } }));
|
|
94
|
+
const r = read({ startedAt: min(600) });
|
|
95
|
+
expect(r.status).toBe("fresh");
|
|
96
|
+
if (r.status === "fresh")
|
|
97
|
+
expect(r.file.live).toEqual({ state: "idle", turnInFlight: false, since: null, lastActivityAt: null, measuredAt: null, windowMs: null });
|
|
98
|
+
write(JSON.stringify({ ...file(), live: { state: "working" } }));
|
|
99
|
+
expect(read().status).toBe("fresh");
|
|
100
|
+
write(JSON.stringify({ ...file(), live: "undefined" }));
|
|
101
|
+
const s = read();
|
|
102
|
+
expect(s.status).toBe("fresh");
|
|
103
|
+
if (s.status === "fresh")
|
|
104
|
+
expect(s.file.live).toBeNull();
|
|
105
|
+
});
|
|
106
|
+
it("every string in the file is scrubbed of control characters", () => {
|
|
107
|
+
write(JSON.stringify(file({
|
|
108
|
+
nextMove: { kind: "agent_slot", actor: "ai", label: "Open\x1b]8;;https://evil.test\x1b\\ the PR\x07", reason: "" },
|
|
109
|
+
workUnits: [{ key: "a", label: "backend#7\x1b[2J x", kind: "pull_request", url: "https://github.com/Synap-core/backend/pull/7\x1b\\", state: "open", prState: null }],
|
|
110
|
+
})));
|
|
111
|
+
const r = read({ startedAt: min(600) });
|
|
112
|
+
expect(r.status).toBe("fresh");
|
|
113
|
+
if (r.status !== "fresh")
|
|
114
|
+
return;
|
|
115
|
+
const raw = buildBridgeLine({ now: NOW, file: r.file, writtenAt: r.writtenAt, registry: reg("idle") });
|
|
116
|
+
expect(raw).not.toContain("https://evil.test\x1b");
|
|
117
|
+
expect(raw).not.toContain("\x1b[2J");
|
|
118
|
+
expect(raw).not.toContain("\x07");
|
|
119
|
+
// The URL's own ESC is gone (its backslash is left, inert); our link around the PR name is intact.
|
|
120
|
+
expect(raw).toContain("\x1b]8;;https://github.com/Synap-core/backend/pull/7\\\x1b\\backend#7\x1b]8;;\x1b\\");
|
|
121
|
+
expect(plain(raw)).toContain("next: Open]8;;https://evil.test\\ the PR");
|
|
122
|
+
});
|
|
123
|
+
it("an unreadable file (here a directory) is a failed read", () => {
|
|
124
|
+
fs.mkdirSync(bridgeStatusPath(CONV, home), { recursive: true });
|
|
125
|
+
expect(read()).toMatchObject({ status: "failed", why: expect.stringContaining("unreadable") });
|
|
126
|
+
});
|
|
127
|
+
it("a session id that could leave the directory reads no file", () => {
|
|
128
|
+
expect(readBridgeStatus("../../etc/passwd", { now: NOW, home })).toEqual({ status: "absent" });
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
const line = (f, registry = reg("idle"), width) => plain(buildBridgeLine({ now: NOW, file: f, writtenAt: Date.parse(f.writtenAt), registry, width }));
|
|
132
|
+
describe("drawing the line", () => {
|
|
133
|
+
it("idle with nothing waited on: stopped, with the pod's next move", () => {
|
|
134
|
+
expect(line(file())).toBe('■ stopped · 4m · next: Produce "Wave 2 report"');
|
|
135
|
+
});
|
|
136
|
+
it("idle and waiting: the pod's marks, in the vocabulary's words, and the open PRs", () => {
|
|
137
|
+
const f = file({
|
|
138
|
+
waitingOn: [
|
|
139
|
+
{ reason: "another_work", count: 2, refs: [{ kind: "session", id: "s2" }] },
|
|
140
|
+
{ reason: "approval", count: 1 },
|
|
141
|
+
],
|
|
142
|
+
workUnits: [
|
|
143
|
+
{ key: "pr-synap-cli-52", label: "synap-cli#52 feat(statusline): read the bridge file", kind: "pull_request", url: "https://github.com/Synap-core/synap-cli/pull/52", state: "open", prState: { status: "ok", label: "Open", state: "open", checks: "failed" } },
|
|
144
|
+
{ key: "pr-backend-9", label: "backend#9 done already", kind: "pull_request", url: "https://github.com/Synap-core/backend/pull/9", state: "met", prState: { status: "ok", label: "Merged", state: "merged" } },
|
|
145
|
+
],
|
|
146
|
+
});
|
|
147
|
+
expect(line(f)).toBe('⏳ waiting on 3 · other work (2) · approval · synap-cli#52 open ✗ · next: Produce "Wave 2 report"');
|
|
148
|
+
});
|
|
149
|
+
it("working (the registry says busy): the marks follow, the next move does not", () => {
|
|
150
|
+
const f = file({ waitingOn: [{ reason: "input", count: 1 }] });
|
|
151
|
+
expect(line(f, reg("busy", { statusUpdatedAt: min(3) }))).toBe("▶ working · 3m · ⏳ 1 · input");
|
|
152
|
+
});
|
|
153
|
+
it("the registry's permission prompt beats everything", () => {
|
|
154
|
+
expect(line(file(), reg("waiting", { waitingFor: "permission prompt" }))).toBe("✋ needs you · permission prompt");
|
|
155
|
+
});
|
|
156
|
+
it("no registry: the pod's live, re-judged now by its own window", () => {
|
|
157
|
+
const working = file({ live: { state: "working", turnInFlight: false, since: iso(min(2)), lastActivityAt: iso(min(2)), measuredAt: iso(min(1)), windowMs: 300_000 } });
|
|
158
|
+
expect(line(working, null)).toBe("▶ working · 2m");
|
|
159
|
+
// Measured as working, but its last activity is now past the window: idle.
|
|
160
|
+
const lapsed = file({ live: { state: "working", turnInFlight: false, since: iso(min(8)), lastActivityAt: iso(min(8)), measuredAt: iso(min(1)), windowMs: 300_000 } });
|
|
161
|
+
expect(line(lapsed, null)).toBe('■ stopped · 8m · next: Produce "Wave 2 report"');
|
|
162
|
+
expect(line(file({ live: null }), null)).toBe('○ no session status · next: Produce "Wave 2 report"');
|
|
163
|
+
});
|
|
164
|
+
it("a turn in flight is the pod's word at `measuredAt`: it lapses one window later, never 12 h later", () => {
|
|
165
|
+
const inFlight = (measured, windowMs = 300_000) => file({ live: { state: "working", turnInFlight: true, since: iso(min(700)), lastActivityAt: iso(min(700)), measuredAt: iso(measured), windowMs } });
|
|
166
|
+
expect(line(inFlight(min(1)), null)).toBe("▶ working · 11h40");
|
|
167
|
+
// Written mid-turn 6 h ago and never rewritten: not "working".
|
|
168
|
+
expect(line(inFlight(min(360)), null)).toBe('■ stopped · 11h40 · next: Produce "Wave 2 report"');
|
|
169
|
+
// No usable measurement time: the time the file was written bounds it, and the pod's default window applies.
|
|
170
|
+
const unmeasured = file({ writtenAt: iso(min(30)), live: { ...inFlight(min(1)).live, measuredAt: null, windowMs: null } });
|
|
171
|
+
expect(line(unmeasured, null)).toBe('■ stopped · 11h40 · next: Produce "Wave 2 report" · pod 30m ago');
|
|
172
|
+
expect(line({ ...unmeasured, writtenAt: iso(min(2)) }, null)).toBe("▶ working · 11h40");
|
|
173
|
+
});
|
|
174
|
+
it("a section the pod could not read is said, never drawn as nothing", () => {
|
|
175
|
+
expect(line(file({ waitingOn: null, workUnits: null, nextMove: null }))).toBe("■ stopped · 4m · ⚑ not read: waits, PRs");
|
|
176
|
+
});
|
|
177
|
+
it("a PR whose state the pod could not read is marked; one with nothing recorded is just named", () => {
|
|
178
|
+
const f = file({
|
|
179
|
+
nextMove: null,
|
|
180
|
+
workUnits: [
|
|
181
|
+
{ key: "a", label: "backend#7 x", kind: "pull_request", url: "https://github.com/Synap-core/backend/pull/7", state: "open", prState: { status: "unavailable" } },
|
|
182
|
+
{ key: "b", label: "relay-app#3 y", kind: "pull_request", url: null, state: "claimed", prState: null },
|
|
183
|
+
],
|
|
184
|
+
});
|
|
185
|
+
expect(line(f)).toBe("■ stopped · 4m · backend#7 ⚑ not read · relay-app#3");
|
|
186
|
+
});
|
|
187
|
+
it("a quiet file says how old the pod's word is", () => {
|
|
188
|
+
expect(line(file({ writtenAt: iso(min(40)), nextMove: null }))).toBe("■ stopped · 4m · pod 40m ago");
|
|
189
|
+
});
|
|
190
|
+
it("caps itself to the width", () => {
|
|
191
|
+
const f = file({ waitingOn: Array.from({ length: 6 }, (_, i) => ({ reason: `reason_${i}`, count: 1 })) });
|
|
192
|
+
const out = line(f, reg("idle"), 50);
|
|
193
|
+
expect(out.length).toBeLessThanOrEqual(50);
|
|
194
|
+
expect(out).toMatch(/\+\d+ more$/);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
//# sourceMappingURL=bridge-status.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-status.test.js","sourceRoot":"","sources":["../../src/lib/bridge-status.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,7 +20,7 @@ vi.mock("./mcp-bridge.js", async (orig) => {
|
|
|
20
20
|
};
|
|
21
21
|
});
|
|
22
22
|
import { hubGet } from "./hub-client.js";
|
|
23
|
-
import { mcpBridgeHeaders, CLIENT_HEADER, clientHeader } from "./conversation-headers.js";
|
|
23
|
+
import { mcpBridgeHeaders, CLIENT_HEADER, RUNNER_HEADER, claudeCodeProcess, clientHeader } from "./conversation-headers.js";
|
|
24
24
|
import { mcpBridge } from "../commands/mcp.js";
|
|
25
25
|
const VERSION = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf8")).version;
|
|
26
26
|
afterEach(() => {
|
|
@@ -45,6 +45,8 @@ describe("X-Synap-Client", () => {
|
|
|
45
45
|
await hubGet("/workspaces", {}, { podUrl: "https://pod.example", apiKey: "k" });
|
|
46
46
|
expect(seen).toHaveLength(1);
|
|
47
47
|
expect(seen[0][CLIENT_HEADER]).toBe(`synap-cli/${VERSION}`);
|
|
48
|
+
// The worker's served stamp is the worker poll's alone: an ordinary command never claims it.
|
|
49
|
+
expect(seen[0][RUNNER_HEADER]).toBeUndefined();
|
|
48
50
|
});
|
|
49
51
|
it("the stdio MCP bridge sends synap-cli-mcp/<version> on every request", async () => {
|
|
50
52
|
const { runMcpBridge } = await vi.importActual("./mcp-bridge.js");
|
|
@@ -74,6 +76,8 @@ describe("X-Synap-Client", () => {
|
|
|
74
76
|
expect(sent.length).toBe(2);
|
|
75
77
|
for (const h of sent)
|
|
76
78
|
expect(h[CLIENT_HEADER]).toBe(`synap-cli-mcp/${VERSION}`);
|
|
79
|
+
for (const h of sent)
|
|
80
|
+
expect(h[RUNNER_HEADER]).toBeUndefined();
|
|
77
81
|
});
|
|
78
82
|
it("`synap mcp bridge` hands the bridge those headers", async () => {
|
|
79
83
|
vi.stubEnv("SYNAP_HUB_API_KEY", "k");
|
|
@@ -81,5 +85,19 @@ describe("X-Synap-Client", () => {
|
|
|
81
85
|
expect(captured.opts?.headers?.[CLIENT_HEADER]).toBe(`synap-cli-mcp/${VERSION}`);
|
|
82
86
|
expect(captured.opts?.headers?.["X-Synap-Conversation"]).toBeTruthy();
|
|
83
87
|
});
|
|
88
|
+
it("`synap mcp bridge` inside Claude Code names its parent process (plan C2); outside it names none", async () => {
|
|
89
|
+
vi.stubEnv("SYNAP_HUB_API_KEY", "k");
|
|
90
|
+
vi.stubEnv("CLAUDE_CODE_SESSION_ID", "8c34a72b-b1c1-4c37-9c1f-02920b69ccb2");
|
|
91
|
+
await mcpBridge({ url: "https://pod/mcp" });
|
|
92
|
+
const sent = captured.opts?.headers?.["X-Synap-Process"];
|
|
93
|
+
// This runner's own parent stands for Claude Code; a host without ps sends none.
|
|
94
|
+
if (sent !== undefined)
|
|
95
|
+
expect(sent).toMatch(new RegExp(`^[A-Za-z0-9._-]+:${process.ppid}:\\d{13}$`));
|
|
96
|
+
else
|
|
97
|
+
expect(claudeCodeProcess()).toBeUndefined();
|
|
98
|
+
vi.stubEnv("CLAUDE_CODE_SESSION_ID", "");
|
|
99
|
+
await mcpBridge({ url: "https://pod/mcp" });
|
|
100
|
+
expect(captured.opts?.headers?.["X-Synap-Process"]).toBeUndefined();
|
|
101
|
+
});
|
|
84
102
|
});
|
|
85
103
|
//# sourceMappingURL=client-header.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-header.test.js","sourceRoot":"","sources":["../../src/lib/client-header.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE7D,MAAM,QAAQ,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,SAA6D,EAAE,CAAC,CAAC,CAAC;AAC7G,EAAE,CAAC,IAAI,CAAC,iBAAiB,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;IACxC,MAAM,IAAI,GAAG,MAAM,IAAI,EAAoC,CAAC;IAC5D,OAAO;QACL,GAAG,IAAI;QACP,YAAY,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,IAA0C,EAAE,EAAE;YACvE,QAAQ,CAAC,IAAI,GAAG,IAAI,CAAC;QACvB,CAAC,CAAC;KACH,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"client-header.test.js","sourceRoot":"","sources":["../../src/lib/client-header.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,6 +11,66 @@ export declare const CLIENT_HEADER = "X-Synap-Client";
|
|
|
11
11
|
export type SynapClientName = "synap-cli" | "synap-cli-mcp";
|
|
12
12
|
/** `{ "X-Synap-Client": "<name>/<version>" }`; the version is the package's. */
|
|
13
13
|
export declare function clientHeader(name: SynapClientName, version?: string): Record<string, string>;
|
|
14
|
+
/**
|
|
15
|
+
* The worker's stamp, `synap-worker/<version>`: sent ONLY by `synap worker` on
|
|
16
|
+
* its own orient poll. The pod records, per key, the last time a request
|
|
17
|
+
* carried it (`servedAt`): proof that a process serves that agent, which a key
|
|
18
|
+
* merely being used is not. Interactive commands and the MCP bridge never send
|
|
19
|
+
* it, or every use of a key would read as "a worker runs".
|
|
20
|
+
*/
|
|
21
|
+
export declare const RUNNER_HEADER = "X-Synap-Runner";
|
|
22
|
+
/** `{ "X-Synap-Runner": "synap-worker/<version>" }`; the version is the package's. */
|
|
23
|
+
export declare function workerRunnerHeader(version?: string): Record<string, string>;
|
|
24
|
+
/**
|
|
25
|
+
* Which OS process of a conversation calls (`<host>:<pid>:<startedAtMs>`), and
|
|
26
|
+
* what that runner is doing (`busy` | `idle` | `needs_permission` |
|
|
27
|
+
* `needs_answer` (a question or a plan dialog) | `ended`).
|
|
28
|
+
* The pod records both on the conversation's `session_runners` rows; a pod
|
|
29
|
+
* that reads neither ignores them. synap-bridge sends the state the same way.
|
|
30
|
+
*/
|
|
31
|
+
export declare const PROCESS_HEADER = "X-Synap-Process";
|
|
32
|
+
export declare const RUNNER_STATE_HEADER = "X-Synap-Runner-State";
|
|
33
|
+
/**
|
|
34
|
+
* Which MODEL a run uses and which PROVIDER serves it: facts the client
|
|
35
|
+
* reports, never identity (pod decision 5e7c77dc). The pod keeps a provider
|
|
36
|
+
* slug it knows (anthropic, bedrock, vertex, foundry, openai, openrouter, zai,
|
|
37
|
+
* deepseek) and reads any other word as `other`; it drops a model id that is
|
|
38
|
+
* not an id. Send nothing rather than a guess: absent reads unknown, never
|
|
39
|
+
* "anthropic". Never send a URL. A pod that reads neither ignores them.
|
|
40
|
+
*/
|
|
41
|
+
export declare const MODEL_HEADER = "X-Synap-Model";
|
|
42
|
+
export declare const MODEL_PROVIDER_HEADER = "X-Synap-Model-Provider";
|
|
43
|
+
/** A model id the pod will keep, else undefined (`<synthetic>`, a URL, junk). */
|
|
44
|
+
export declare function reportableModel(model: string | undefined): string | undefined;
|
|
45
|
+
/** `{ X-Synap-Model?, X-Synap-Model-Provider? }`: each only when known. */
|
|
46
|
+
export declare function modelHeaders(model: string | undefined, provider: string | undefined): Record<string, string>;
|
|
47
|
+
/**
|
|
48
|
+
* THE process identity format, `<host>:<pid>:<startedAtMs>` (the pod's
|
|
49
|
+
* `parseProcessIdentity`): the host with every character outside
|
|
50
|
+
* `[A-Za-z0-9._-]` made `-`, at most 255, `computer` when empty. `synap worker`
|
|
51
|
+
* (`runProcessIdentity`), the stdio MCP bridge (`claudeCodeProcess`) and the
|
|
52
|
+
* synap-bridge mod (`processIdentity`, `hooks/lib.ts`, byte for byte, tested on
|
|
53
|
+
* the same fixture) all format through it, so one Claude Code process is one
|
|
54
|
+
* runner row on the pod.
|
|
55
|
+
*/
|
|
56
|
+
export declare function processIdentity(hostname: string, pid: number, startedAtMs: number): string;
|
|
57
|
+
/**
|
|
58
|
+
* `ps -o lstart=` read in UTC with the C locale (`Mon Nov 2 13:09:07 2026`,
|
|
59
|
+
* BSD and procps alike) -> epoch ms; undefined for anything else. Parsed field
|
|
60
|
+
* by field, never by `Date.parse`, as the synap-bridge mod parses it.
|
|
61
|
+
*/
|
|
62
|
+
export declare function lstartMs(text: string): number | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* The Claude Code process this stdio MCP bridge serves: Claude Code spawns it
|
|
65
|
+
* directly, so it is the parent (`process.ppid`), started when `ps` says. Read
|
|
66
|
+
* once, at the bridge's start. Undefined when it cannot be read (no `ps`, an
|
|
67
|
+
* orphan): the header is then not sent, never guessed.
|
|
68
|
+
*/
|
|
69
|
+
export declare function claudeCodeProcess(read?: {
|
|
70
|
+
ppid?: number;
|
|
71
|
+
hostname?: string;
|
|
72
|
+
startMs?: (pid: number) => number | undefined;
|
|
73
|
+
}): string | undefined;
|
|
14
74
|
/** The same two values in a request's `params._meta` (reverse-DNS vendor keys; `io.modelcontextprotocol/` is reserved). */
|
|
15
75
|
export declare const CONVERSATION_META_KEY = "live.synap/conversation";
|
|
16
76
|
export declare const CONVERSATION_CWD_META_KEY = "live.synap/conversationCwd";
|
|
@@ -29,5 +89,10 @@ export declare function cliConversation(env?: NodeJS.ProcessEnv): string;
|
|
|
29
89
|
* only with a Claude Code conversation (the one a person can reopen).
|
|
30
90
|
*/
|
|
31
91
|
export declare function conversationHeaders(sessionId: string | undefined, cwd?: string): Record<string, string>;
|
|
32
|
-
/**
|
|
33
|
-
|
|
92
|
+
/**
|
|
93
|
+
* Every header the stdio MCP bridge sends: its client name, the conversation,
|
|
94
|
+
* and, with a Claude Code conversation, the Claude Code process it runs in
|
|
95
|
+
* (`claudeCodeProcess`, the same value the synap-bridge mod sends), so the
|
|
96
|
+
* pod's runner rows of that process are one, whichever door called.
|
|
97
|
+
*/
|
|
98
|
+
export declare function mcpBridgeHeaders(sessionId: string | undefined, cwd?: string, processId?: string): Record<string, string>;
|