@synap-core/cli 1.12.2 → 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/LICENSE +21 -0
- package/README.md +38 -11
- package/dist/build-info.json +2 -3
- package/dist/commands/agents.d.ts +0 -1
- package/dist/commands/agents.js +13 -3
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/app.js +2 -1
- package/dist/commands/app.js.map +1 -1
- package/dist/commands/automation.js +46 -41
- package/dist/commands/automation.js.map +1 -1
- package/dist/commands/box.d.ts +42 -0
- package/dist/commands/box.js +89 -0
- package/dist/commands/box.js.map +1 -0
- package/dist/commands/bridge-setup.js +3 -1
- package/dist/commands/bridge-setup.js.map +1 -1
- package/dist/commands/capability.d.ts +87 -1
- package/dist/commands/capability.js +174 -64
- package/dist/commands/capability.js.map +1 -1
- package/dist/commands/close-questions.d.ts +27 -0
- package/dist/commands/close-questions.js +49 -0
- package/dist/commands/close-questions.js.map +1 -0
- package/dist/commands/close-questions.test.d.ts +1 -0
- package/dist/commands/close-questions.test.js +52 -0
- package/dist/commands/close-questions.test.js.map +1 -0
- package/dist/commands/connect.d.ts +1 -3
- package/dist/commands/connect.js +2 -2
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/context.js +74 -44
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/data-extra.d.ts +13 -0
- package/dist/commands/data-extra.js +35 -6
- package/dist/commands/data-extra.js.map +1 -1
- package/dist/commands/data.js +2 -1
- package/dist/commands/data.js.map +1 -1
- package/dist/commands/doctor.d.ts +39 -0
- package/dist/commands/doctor.js +136 -14
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/hooks.d.ts +19 -0
- package/dist/commands/hooks.js +70 -0
- package/dist/commands/hooks.js.map +1 -0
- package/dist/commands/import.d.ts +25 -0
- package/dist/commands/import.js +98 -12
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/init.d.ts +14 -0
- package/dist/commands/init.js +63 -21
- 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 +94 -29
- package/dist/commands/knowledge.js.map +1 -1
- package/dist/commands/lens.js +2 -1
- package/dist/commands/lens.js.map +1 -1
- package/dist/commands/market-authoring.js +1 -1
- package/dist/commands/market-authoring.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/observe.category.test.d.ts +1 -0
- package/dist/commands/observe.category.test.js +59 -0
- package/dist/commands/observe.category.test.js.map +1 -0
- package/dist/commands/observe.d.ts +3 -0
- package/dist/commands/observe.js +7 -5
- package/dist/commands/observe.js.map +1 -1
- package/dist/commands/observe.title.test.d.ts +1 -0
- package/dist/commands/observe.title.test.js +37 -0
- package/dist/commands/observe.title.test.js.map +1 -0
- package/dist/commands/playbook.js +9 -0
- package/dist/commands/playbook.js.map +1 -1
- package/dist/commands/pods.d.ts +6 -1
- package/dist/commands/pods.js +38 -17
- 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/proton-setup.js +3 -1
- package/dist/commands/proton-setup.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/session-feed-wait.d.ts +48 -0
- package/dist/commands/session-feed-wait.js +261 -0
- package/dist/commands/session-feed-wait.js.map +1 -0
- package/dist/commands/session-post.d.ts +40 -0
- package/dist/commands/session-post.js +96 -0
- package/dist/commands/session-post.js.map +1 -0
- package/dist/commands/session-wait.d.ts +9 -4
- package/dist/commands/session-wait.js +20 -5
- package/dist/commands/session-wait.js.map +1 -1
- package/dist/commands/sessions.d.ts +2 -0
- package/dist/commands/sessions.js +23 -0
- package/dist/commands/sessions.js.map +1 -1
- package/dist/commands/skills-commands.d.ts +2 -0
- package/dist/commands/skills-commands.js +9 -1
- package/dist/commands/skills-commands.js.map +1 -1
- package/dist/commands/skills-commands.test.d.ts +1 -0
- package/dist/commands/skills-commands.test.js +57 -0
- package/dist/commands/skills-commands.test.js.map +1 -0
- package/dist/commands/status.js +13 -7
- 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 +68 -6
- package/dist/commands/tracks.js +272 -16
- 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/whoami.d.ts +7 -0
- package/dist/commands/whoami.js +17 -0
- package/dist/commands/whoami.js.map +1 -1
- package/dist/commands/worker-accounts.d.ts +149 -0
- package/dist/commands/worker-accounts.js +339 -0
- package/dist/commands/worker-accounts.js.map +1 -0
- package/dist/commands/worker-accounts.test.d.ts +1 -0
- package/dist/commands/worker-accounts.test.js +224 -0
- package/dist/commands/worker-accounts.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 +406 -10
- package/dist/commands/worker.js +1180 -126
- package/dist/commands/worker.js.map +1 -1
- package/dist/commands/worker.test.js +588 -11
- package/dist/commands/worker.test.js.map +1 -1
- package/dist/index.js +220 -28
- package/dist/index.js.map +1 -1
- package/dist/lib/agent-door.d.ts +1 -3
- package/dist/lib/agent-door.js +10 -7
- package/dist/lib/agent-door.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 +28 -2
- package/dist/lib/auth.js +52 -12
- package/dist/lib/auth.js.map +1 -1
- package/dist/lib/box/egress.d.ts +82 -0
- package/dist/lib/box/egress.js +264 -0
- package/dist/lib/box/egress.js.map +1 -0
- package/dist/lib/box/evidence.d.ts +86 -0
- package/dist/lib/box/evidence.js +121 -0
- package/dist/lib/box/evidence.js.map +1 -0
- package/dist/lib/box/job-server.d.ts +72 -0
- package/dist/lib/box/job-server.js +250 -0
- package/dist/lib/box/job-server.js.map +1 -0
- package/dist/lib/box/provisioner.d.ts +83 -0
- package/dist/lib/box/provisioner.js +312 -0
- package/dist/lib/box/provisioner.js.map +1 -0
- package/dist/lib/box/proxmox.d.ts +112 -0
- package/dist/lib/box/proxmox.js +211 -0
- package/dist/lib/box/proxmox.js.map +1 -0
- package/dist/lib/box/wire.d.ts +98 -0
- package/dist/lib/box/wire.js +70 -0
- package/dist/lib/box/wire.js.map +1 -0
- 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.d.ts +1 -0
- package/dist/lib/client-header.test.js +103 -0
- package/dist/lib/client-header.test.js.map +1 -0
- package/dist/lib/conversation-headers.d.ts +89 -9
- package/dist/lib/conversation-headers.js +156 -3
- 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 +40 -2
- package/dist/lib/credential-class.js +48 -6
- 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/exporter-coverage.d.ts +1 -1
- package/dist/lib/exporter-coverage.js +1 -1
- package/dist/lib/goal-words.d.ts +38 -0
- package/dist/lib/goal-words.js +115 -0
- package/dist/lib/goal-words.js.map +1 -0
- package/dist/lib/hub-client.d.ts +87 -8
- package/dist/lib/hub-client.js +205 -70
- package/dist/lib/hub-client.js.map +1 -1
- package/dist/lib/init-handoff.d.ts +8 -0
- package/dist/lib/init-handoff.js +9 -2
- package/dist/lib/init-handoff.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/key-source.d.ts +3 -1
- package/dist/lib/key-source.js +7 -4
- package/dist/lib/key-source.js.map +1 -1
- package/dist/lib/knowledge-title.d.ts +3 -2
- package/dist/lib/knowledge-title.js +3 -2
- package/dist/lib/knowledge-title.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 +50 -0
- package/dist/lib/mcp-bridge.js +232 -11
- package/dist/lib/mcp-bridge.js.map +1 -1
- package/dist/lib/mcp-bridge.test.js +224 -4
- 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-moved.d.ts +70 -0
- package/dist/lib/pod-moved.js +146 -0
- package/dist/lib/pod-moved.js.map +1 -0
- package/dist/lib/pod-reach.d.ts +39 -0
- package/dist/lib/pod-reach.js +117 -0
- package/dist/lib/pod-reach.js.map +1 -0
- package/dist/lib/pod-reach.test.d.ts +1 -0
- package/dist/lib/pod-reach.test.js +67 -0
- package/dist/lib/pod-reach.test.js.map +1 -0
- package/dist/lib/pod.d.ts +8 -4
- package/dist/lib/pod.js +12 -10
- package/dist/lib/pod.js.map +1 -1
- package/dist/lib/register-claude-code-mcp.test.d.ts +1 -0
- package/dist/lib/register-claude-code-mcp.test.js +97 -0
- package/dist/lib/register-claude-code-mcp.test.js.map +1 -0
- package/dist/lib/review-credential.js +3 -1
- package/dist/lib/review-credential.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/session-feed.d.ts +185 -0
- package/dist/lib/session-feed.js +257 -0
- package/dist/lib/session-feed.js.map +1 -0
- package/dist/lib/targets.d.ts +52 -3
- package/dist/lib/targets.js +150 -209
- 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/dist/lib/wake-hook-settings.d.ts +57 -0
- package/dist/lib/wake-hook-settings.js +153 -0
- package/dist/lib/wake-hook-settings.js.map +1 -0
- package/dist/lib/wake-state.d.ts +65 -0
- package/dist/lib/wake-state.js +122 -0
- package/dist/lib/wake-state.js.map +1 -0
- package/package.json +5 -2
- package/skills/README.md +1 -1
- package/skills/agent-os/SKILL.md +8 -8
- package/skills/synap/SKILL.md +188 -94
- package/skills/synap/code-skills.md +84 -0
- package/skills/synap/concepts.md +38 -20
- package/skills/synap/creative-loop.md +1 -1
- package/skills/synap/document-embeds.md +17 -1
- package/skills/synap/focus-sessions.md +17 -8
- package/skills/synap/from-intent.md +33 -17
- package/skills/synap/governance-rules.md +3 -1
- package/skills/synap/hand-off.md +10 -6
- package/skills/synap/lenses.md +4 -4
- package/skills/synap/linking.md +2 -2
- package/skills/synap/mental-model.md +1 -1
- package/skills/synap/operating-mode-rules.md +1 -1
- package/skills/synap/quick-reference.md +2 -1
- package/skills/synap/reflexes.md +8 -8
- package/skills/synap/viewframe-cells.md +1 -1
- package/skills/synap/work-model.md +24 -10
- package/skills/synap/workspace-design.md +5 -5
- package/skills/synap/writes.md +16 -9
- package/skills/synap/writing-titles.md +7 -0
- package/skills/synap-app/SKILL.md +148 -66
- package/skills/synap-app/creating-the-base-app.md +12 -7
- package/skills/synap-app/governance.md +27 -16
- package/skills/synap-app/install-commands.md +18 -9
- package/skills/synap-app/overview.md +16 -9
- package/skills/synap-app/using-the-sdk.md +75 -25
- package/skills/synap-market/SKILL.md +1 -1
- package/skills/synap-market/governance-and-catalog.md +1 -1
- package/skills/synap-schema/SKILL.md +24 -21
- package/skills/synap-schema/intent-vocabulary-ssot.md +24 -21
- package/templates/box/runner.py +206 -0
package/dist/lib/mcp-bridge.js
CHANGED
|
@@ -16,11 +16,112 @@
|
|
|
16
16
|
* reply hangs the client). The pod is stateless, so there is no server→client
|
|
17
17
|
* GET stream to hold open.
|
|
18
18
|
*
|
|
19
|
+
* ONE method is not transparent: `tools/list`. Claude Code asks for it once per
|
|
20
|
+
* spawn and never retries a failure, so a bridge restarted in a herd (an account
|
|
21
|
+
* switch moves every session at once) that meets a slow pod leaves the synap
|
|
22
|
+
* tools absent for the process's whole life. The bridge keeps the last good list
|
|
23
|
+
* on disk per (pod URL, key) and, when the pod fails or has not answered within
|
|
24
|
+
* `toolsListWaitMs`, answers from it; the real call carries on in the background
|
|
25
|
+
* and a changed list is announced with `notifications/tools/list_changed` (the
|
|
26
|
+
* pod declares `tools.listChanged`). No cache yet = today's behaviour exactly.
|
|
27
|
+
*
|
|
19
28
|
* Known edge: the id is fixed at spawn. After `/clear` Claude Code starts a new
|
|
20
29
|
* session id, but MCP writes keep the pre-clear one (its transcript up to the
|
|
21
30
|
* clear stays resumable). CLI calls send the live id (`hub-client.ts`).
|
|
31
|
+
*
|
|
32
|
+
* THE MODEL: Claude Code gives a stdio server no model, and a plugin can set no
|
|
33
|
+
* header on another MCP server's requests. The synap-bridge plugin (0.14.0)
|
|
34
|
+
* writes what the conversation runs on to `~/.synap/run/claude-code-<id>.json`
|
|
35
|
+
* (`{"model", "provider", "at"}`) whenever it changes; the bridge reads that
|
|
36
|
+
* file for its spawn-time id and adds `X-Synap-Model` / `X-Synap-Model-
|
|
37
|
+
* Provider` (re-read only when the file changed). Missing, unreadable or
|
|
38
|
+
* invalid = no model headers, never an error, never a guess. Same edge as the
|
|
39
|
+
* conversation: after `/clear` the plugin writes under the new id, which this
|
|
40
|
+
* bridge does not read, so those calls carry no model.
|
|
22
41
|
*/
|
|
42
|
+
import { createHash } from "node:crypto";
|
|
43
|
+
import fs from "node:fs";
|
|
44
|
+
import os from "node:os";
|
|
45
|
+
import path from "node:path";
|
|
23
46
|
import readline from "node:readline";
|
|
47
|
+
import { writeFileAtomic } from "./atomic-file.js";
|
|
48
|
+
import { CONVERSATION_CWD_HEADER, CONVERSATION_CWD_META_KEY, CONVERSATION_HEADER, CONVERSATION_META_KEY, modelHeaders, } from "./conversation-headers.js";
|
|
49
|
+
/** A Claude Code session id as a file name, as synap-bridge names it: nothing that walks a path. */
|
|
50
|
+
const SESSION_FILE_NAME = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;
|
|
51
|
+
/**
|
|
52
|
+
* `~/.synap/run/claude-code-<session id>.json`, the file the synap-bridge
|
|
53
|
+
* plugin writes the conversation's model and provider to (its
|
|
54
|
+
* `modelHandoffPath`). Undefined without an id, or with one that could name
|
|
55
|
+
* another file.
|
|
56
|
+
*/
|
|
57
|
+
export function modelHandoffPath(sessionId, home = os.homedir()) {
|
|
58
|
+
const id = sessionId?.trim();
|
|
59
|
+
if (!id || !SESSION_FILE_NAME.test(id) || !path.isAbsolute(home))
|
|
60
|
+
return undefined;
|
|
61
|
+
return path.join(home, ".synap", "run", `claude-code-${id}.json`);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The model headers from the hand-off file, read again only when the file
|
|
65
|
+
* changed (stat: mtime, size, inode). A missing, unreadable or invalid file is
|
|
66
|
+
* no headers; the values go through `modelHeaders` (a model the pod would
|
|
67
|
+
* drop, or a provider that is not a slug, is left out).
|
|
68
|
+
*/
|
|
69
|
+
export function modelHandoffReader(file) {
|
|
70
|
+
let seen;
|
|
71
|
+
let headers = {};
|
|
72
|
+
return () => {
|
|
73
|
+
if (!file)
|
|
74
|
+
return {};
|
|
75
|
+
let stamp;
|
|
76
|
+
try {
|
|
77
|
+
const st = fs.statSync(file);
|
|
78
|
+
stamp = `${st.mtimeMs}:${st.size}:${st.ino}`;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
seen = undefined;
|
|
82
|
+
headers = {};
|
|
83
|
+
return headers; // no file: this conversation's model is unknown
|
|
84
|
+
}
|
|
85
|
+
if (stamp === seen)
|
|
86
|
+
return headers;
|
|
87
|
+
seen = stamp;
|
|
88
|
+
try {
|
|
89
|
+
const said = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
90
|
+
headers = modelHeaders(typeof said?.model === "string" ? said.model : undefined, typeof said?.provider === "string" ? said.provider : undefined);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
headers = {}; // unreadable or not JSON: unknown, never a guess
|
|
94
|
+
}
|
|
95
|
+
return headers;
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
const TOOLS_LIST_WAIT_MS = 5_000;
|
|
99
|
+
const TOOLS_LIST_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
100
|
+
/** The background refresh after a cache answer must not hold the process open for ever. */
|
|
101
|
+
const TOOLS_LIST_REFRESH_LIMIT_MS = 60_000;
|
|
102
|
+
/** One file per (MCP URL, key): the list depends on the key's profile and the URL's lens. */
|
|
103
|
+
export function toolsListCachePath(dir, url, apiKey) {
|
|
104
|
+
const h = createHash("sha256").update(`${url}\n${apiKey}`).digest("hex").slice(0, 24);
|
|
105
|
+
return path.join(dir, `mcp-tools-${h}.json`);
|
|
106
|
+
}
|
|
107
|
+
/** A plain first-page tools/list REQUEST: the only shape the cache can stand in for. */
|
|
108
|
+
function isPlainToolsList(msg) {
|
|
109
|
+
if (!msg || Array.isArray(msg) || typeof msg !== "object")
|
|
110
|
+
return false;
|
|
111
|
+
const m = msg;
|
|
112
|
+
return (m.method === "tools/list" &&
|
|
113
|
+
(typeof m.id === "string" || typeof m.id === "number") &&
|
|
114
|
+
(m.params?.cursor === undefined || m.params.cursor === null));
|
|
115
|
+
}
|
|
116
|
+
/** The reply's `result.tools` when it is a complete (single-page) list, else undefined. */
|
|
117
|
+
function completeToolsResult(payload) {
|
|
118
|
+
if (!payload || Array.isArray(payload) || typeof payload !== "object")
|
|
119
|
+
return undefined;
|
|
120
|
+
const r = payload.result;
|
|
121
|
+
if (!r || !Array.isArray(r.tools) || r.nextCursor)
|
|
122
|
+
return undefined;
|
|
123
|
+
return r;
|
|
124
|
+
}
|
|
24
125
|
/** Ids of the REQUESTS in a message (a batch may carry several). */
|
|
25
126
|
function requestIds(msg) {
|
|
26
127
|
const items = Array.isArray(msg) ? msg : [msg];
|
|
@@ -29,6 +130,38 @@ function requestIds(msg) {
|
|
|
29
130
|
.map((m) => m.id)
|
|
30
131
|
.filter((id) => typeof id === "string" || typeof id === "number");
|
|
31
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* The line with `params._meta["live.synap/conversation"]` (and the cwd) set on
|
|
135
|
+
* every request, from the same conversation the headers carry, for a path that
|
|
136
|
+
* cannot set headers. The header still wins at the pod. Responses, and a line
|
|
137
|
+
* that is not an object, pass through untouched.
|
|
138
|
+
*/
|
|
139
|
+
export function withConversationMeta(msg, headers) {
|
|
140
|
+
const conversation = headers?.[CONVERSATION_HEADER];
|
|
141
|
+
if (!conversation)
|
|
142
|
+
return msg;
|
|
143
|
+
const cwd = headers?.[CONVERSATION_CWD_HEADER];
|
|
144
|
+
const stamp = (m) => {
|
|
145
|
+
if (!m || typeof m !== "object" || typeof m.method !== "string")
|
|
146
|
+
return m;
|
|
147
|
+
const params = (m.params ?? {});
|
|
148
|
+
if (typeof params !== "object" || Array.isArray(params))
|
|
149
|
+
return m;
|
|
150
|
+
const meta = (params._meta ?? {});
|
|
151
|
+
return {
|
|
152
|
+
...m,
|
|
153
|
+
params: {
|
|
154
|
+
...params,
|
|
155
|
+
_meta: {
|
|
156
|
+
...meta,
|
|
157
|
+
[CONVERSATION_META_KEY]: conversation,
|
|
158
|
+
...(cwd ? { [CONVERSATION_CWD_META_KEY]: cwd } : {}),
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
return Array.isArray(msg) ? msg.map(stamp) : stamp(msg);
|
|
164
|
+
}
|
|
32
165
|
/** Parse SSE text into the `data:` payload of each complete event. */
|
|
33
166
|
export function sseEvents(buffer) {
|
|
34
167
|
const events = [];
|
|
@@ -57,6 +190,7 @@ export async function runMcpBridge(opts) {
|
|
|
57
190
|
let sessionId;
|
|
58
191
|
let protocolVersion;
|
|
59
192
|
const inFlight = new Set();
|
|
193
|
+
const ranOn = modelHandoffReader(opts.modelFile);
|
|
60
194
|
const write = (msg) => {
|
|
61
195
|
if (msg && typeof msg === "object") {
|
|
62
196
|
const m = msg;
|
|
@@ -72,18 +206,32 @@ export async function runMcpBridge(opts) {
|
|
|
72
206
|
for (const id of ids)
|
|
73
207
|
write({ jsonrpc: "2.0", id, error: { code: -32000, message } });
|
|
74
208
|
};
|
|
75
|
-
const
|
|
76
|
-
|
|
209
|
+
const cacheDir = opts.cacheDir ?? path.join(os.homedir(), ".synap", "cache");
|
|
210
|
+
const cacheFile = toolsListCachePath(cacheDir, opts.url, opts.apiKey);
|
|
211
|
+
const readCache = () => {
|
|
77
212
|
try {
|
|
78
|
-
|
|
213
|
+
const c = JSON.parse(fs.readFileSync(cacheFile, "utf8"));
|
|
214
|
+
const fresh = Date.now() - c.savedAt <= (opts.toolsListMaxAgeMs ?? TOOLS_LIST_MAX_AGE_MS);
|
|
215
|
+
return fresh && completeToolsResult({ result: c.result }) ? c : undefined;
|
|
79
216
|
}
|
|
80
217
|
catch {
|
|
81
|
-
|
|
82
|
-
|
|
218
|
+
return undefined; // absent or unreadable: no cache, the pod answers alone
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
const writeCache = (result) => {
|
|
222
|
+
try {
|
|
223
|
+
writeFileAtomic(cacheFile, JSON.stringify({ savedAt: Date.now(), result }));
|
|
224
|
+
}
|
|
225
|
+
catch (err) {
|
|
226
|
+
log(`could not write the tools cache: ${err instanceof Error ? err.message : String(err)}`);
|
|
83
227
|
}
|
|
84
|
-
|
|
228
|
+
};
|
|
229
|
+
/** POST one message to the pod; what comes back goes to `emit`, a failure to `onFail`. */
|
|
230
|
+
const send = async (msg, ids, emit, onFail, signal) => {
|
|
231
|
+
const emitAll = (payload) => (Array.isArray(payload) ? payload.forEach(emit) : emit(payload));
|
|
85
232
|
try {
|
|
86
233
|
const res = await doFetch(opts.url, {
|
|
234
|
+
...(signal ? { signal } : {}),
|
|
87
235
|
method: "POST",
|
|
88
236
|
headers: {
|
|
89
237
|
Authorization: `Bearer ${opts.apiKey}`,
|
|
@@ -92,15 +240,16 @@ export async function runMcpBridge(opts) {
|
|
|
92
240
|
...(sessionId ? { "Mcp-Session-Id": sessionId } : {}),
|
|
93
241
|
...(protocolVersion ? { "Mcp-Protocol-Version": protocolVersion } : {}),
|
|
94
242
|
...opts.headers,
|
|
243
|
+
...ranOn(),
|
|
95
244
|
},
|
|
96
|
-
body:
|
|
245
|
+
body: JSON.stringify(withConversationMeta(msg, opts.headers)),
|
|
97
246
|
});
|
|
98
247
|
const minted = res.headers.get("mcp-session-id");
|
|
99
248
|
if (minted)
|
|
100
249
|
sessionId = minted;
|
|
101
250
|
if (!res.ok) {
|
|
102
251
|
const text = await res.text().catch(() => "");
|
|
103
|
-
|
|
252
|
+
onFail(ids, `Synap pod answered HTTP ${res.status}${text ? `: ${text.slice(0, 300)}` : ""}`);
|
|
104
253
|
return;
|
|
105
254
|
}
|
|
106
255
|
const type = res.headers.get("content-type") ?? "";
|
|
@@ -115,7 +264,7 @@ export async function runMcpBridge(opts) {
|
|
|
115
264
|
buffer = rest;
|
|
116
265
|
for (const data of events) {
|
|
117
266
|
try {
|
|
118
|
-
|
|
267
|
+
emitAll(JSON.parse(data));
|
|
119
268
|
}
|
|
120
269
|
catch {
|
|
121
270
|
log("dropped an event that is not JSON");
|
|
@@ -128,10 +277,82 @@ export async function runMcpBridge(opts) {
|
|
|
128
277
|
}
|
|
129
278
|
const text = await res.text();
|
|
130
279
|
if (text.trim())
|
|
131
|
-
|
|
280
|
+
emitAll(JSON.parse(text));
|
|
132
281
|
}
|
|
133
282
|
catch (err) {
|
|
134
|
-
|
|
283
|
+
onFail(ids, `Synap pod unreachable at ${opts.url}: ${err instanceof Error ? err.message : String(err)}`);
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
const forward = async (line) => {
|
|
287
|
+
let msg;
|
|
288
|
+
try {
|
|
289
|
+
msg = JSON.parse(line);
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
log("dropped a line that is not JSON");
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (isPlainToolsList(msg))
|
|
296
|
+
return forwardToolsList(msg);
|
|
297
|
+
await send(msg, requestIds(msg), write, fail);
|
|
298
|
+
};
|
|
299
|
+
const sleep = (ms) => new Promise((r) => setTimeout(() => r("slow"), ms).unref?.());
|
|
300
|
+
/**
|
|
301
|
+
* tools/list through the cache. The pod's reply is collected rather than
|
|
302
|
+
* written, so that exactly ONE reply per id reaches Claude Code: the pod's
|
|
303
|
+
* when it is good and in time, else the cache's (and the pod's late reply
|
|
304
|
+
* then only refreshes the cache).
|
|
305
|
+
*/
|
|
306
|
+
const forwardToolsList = async (msg) => {
|
|
307
|
+
const collected = [];
|
|
308
|
+
let failure;
|
|
309
|
+
const settled = send(msg, [msg.id], (p) => collected.push(p), (ids, message) => {
|
|
310
|
+
failure = { ids, message };
|
|
311
|
+
}, AbortSignal.timeout(TOOLS_LIST_REFRESH_LIMIT_MS)).then(() => "done");
|
|
312
|
+
const first = await Promise.race([settled, sleep(opts.toolsListWaitMs ?? TOOLS_LIST_WAIT_MS)]);
|
|
313
|
+
const cached = readCache();
|
|
314
|
+
const goodResult = () => failure ? undefined : completeToolsResult(collected.find((c) => c?.id === msg.id));
|
|
315
|
+
const serveCache = (why) => {
|
|
316
|
+
log(`${why}: answering tools/list from the cache saved ${Math.round((Date.now() - cached.savedAt) / 1000)}s ago`);
|
|
317
|
+
write({ jsonrpc: "2.0", id: msg.id, result: cached.result });
|
|
318
|
+
};
|
|
319
|
+
if (first === "done") {
|
|
320
|
+
const fresh = goodResult();
|
|
321
|
+
if (fresh) {
|
|
322
|
+
writeCache(fresh);
|
|
323
|
+
collected.forEach(write);
|
|
324
|
+
}
|
|
325
|
+
else if (cached) {
|
|
326
|
+
serveCache(failure ? failure.message : "the pod's tools/list was not a list");
|
|
327
|
+
}
|
|
328
|
+
else if (failure) {
|
|
329
|
+
fail(failure.ids, failure.message);
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
collected.forEach(write);
|
|
333
|
+
}
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
// The pod is slow. No cache: wait for it, exactly as without one.
|
|
337
|
+
if (!cached) {
|
|
338
|
+
await settled;
|
|
339
|
+
const fresh = goodResult();
|
|
340
|
+
if (fresh)
|
|
341
|
+
writeCache(fresh);
|
|
342
|
+
if (failure)
|
|
343
|
+
fail(failure.ids, failure.message);
|
|
344
|
+
else
|
|
345
|
+
collected.forEach(write);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
serveCache(`the pod has not answered tools/list in ${opts.toolsListWaitMs ?? TOOLS_LIST_WAIT_MS}ms`);
|
|
349
|
+
await settled;
|
|
350
|
+
const fresh = goodResult();
|
|
351
|
+
if (!fresh)
|
|
352
|
+
return; // still failing: the cached list stays what Claude Code has
|
|
353
|
+
writeCache(fresh);
|
|
354
|
+
if (JSON.stringify(fresh.tools) !== JSON.stringify(cached.result.tools)) {
|
|
355
|
+
write({ jsonrpc: "2.0", method: "notifications/tools/list_changed" });
|
|
135
356
|
}
|
|
136
357
|
};
|
|
137
358
|
const rl = readline.createInterface({ input, crlfDelay: Infinity });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-bridge.js","sourceRoot":"","sources":["../../src/lib/mcp-bridge.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mcp-bridge.js","sourceRoot":"","sources":["../../src/lib/mcp-bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,10 +6,13 @@
|
|
|
6
6
|
* silent drop hangs Claude Code); SSE and JSON bodies both come through; a
|
|
7
7
|
* notification (no id) gets no invented reply.
|
|
8
8
|
*/
|
|
9
|
+
import fs from "node:fs";
|
|
10
|
+
import os from "node:os";
|
|
11
|
+
import path from "node:path";
|
|
9
12
|
import { PassThrough } from "node:stream";
|
|
10
|
-
import { describe, expect, it } from "vitest";
|
|
11
|
-
import { runMcpBridge, sseEvents } from "./mcp-bridge.js";
|
|
12
|
-
import { conversationHeaders, claudeCodeConversation } from "./conversation-headers.js";
|
|
13
|
+
import { describe, expect, it, vi } from "vitest";
|
|
14
|
+
import { modelHandoffPath, modelHandoffReader, runMcpBridge, sseEvents, toolsListCachePath, withConversationMeta, } from "./mcp-bridge.js";
|
|
15
|
+
import { conversationHeaders, claudeCodeConversation, cliConversation } from "./conversation-headers.js";
|
|
13
16
|
const SID = "8c34a72b-b1c1-4c37-9c1f-02920b69ccb2";
|
|
14
17
|
async function drive(lines, respond, headers = {}) {
|
|
15
18
|
const input = new PassThrough();
|
|
@@ -78,7 +81,14 @@ describe("conversation headers", () => {
|
|
|
78
81
|
it("only a real Claude Code UUID names a conversation", () => {
|
|
79
82
|
expect(claudeCodeConversation(SID.toUpperCase())).toBe(`claude-code:${SID}`);
|
|
80
83
|
expect(claudeCodeConversation("not-a-uuid")).toBeUndefined();
|
|
81
|
-
|
|
84
|
+
});
|
|
85
|
+
it("outside Claude Code the run names itself: one id per process, SYNAP_RUN_ID wins", () => {
|
|
86
|
+
const a = conversationHeaders(undefined, "/x")["X-Synap-Conversation"];
|
|
87
|
+
expect(a).toMatch(/^synap-cli:[0-9a-f-]{36}$/);
|
|
88
|
+
expect(conversationHeaders(undefined, "/y")["X-Synap-Conversation"]).toBe(a);
|
|
89
|
+
expect(conversationHeaders(undefined, "/x")).not.toHaveProperty("X-Synap-Conversation-Cwd");
|
|
90
|
+
expect(cliConversation({ SYNAP_RUN_ID: "nightly-42" })).toBe("synap-cli:nightly-42");
|
|
91
|
+
expect(cliConversation({ SYNAP_RUN_ID: "bad id" })).toBe(a);
|
|
82
92
|
});
|
|
83
93
|
it("a non-ASCII cwd is dropped, the conversation still sent", () => {
|
|
84
94
|
expect(conversationHeaders(SID, "/Users/é")).toEqual({ "X-Synap-Conversation": `claude-code:${SID}` });
|
|
@@ -87,4 +97,214 @@ describe("conversation headers", () => {
|
|
|
87
97
|
expect(sseEvents("data: a\n\ndata: b")).toEqual({ events: ["a"], rest: "data: b" });
|
|
88
98
|
});
|
|
89
99
|
});
|
|
100
|
+
describe("conversation in _meta", () => {
|
|
101
|
+
const conv = conversationHeaders(SID, "/Users/a/code");
|
|
102
|
+
it("every forwarded request carries live.synap/conversation (and the cwd) in params._meta", async () => {
|
|
103
|
+
const { seen } = await drive([
|
|
104
|
+
{ jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "t", _meta: { keep: 1 } } },
|
|
105
|
+
{ jsonrpc: "2.0", id: 2, method: "tools/list" },
|
|
106
|
+
], (b) => json({ jsonrpc: "2.0", id: b.id, result: {} }), conv);
|
|
107
|
+
const bodies = seen.map((s) => JSON.parse(s.body));
|
|
108
|
+
for (const b of bodies) {
|
|
109
|
+
expect(b.params._meta["live.synap/conversation"]).toBe(`claude-code:${SID}`);
|
|
110
|
+
expect(b.params._meta["live.synap/conversationCwd"]).toBe("/Users/a/code");
|
|
111
|
+
}
|
|
112
|
+
expect(bodies.find((b) => b.id === 1).params).toMatchObject({ name: "t", _meta: { keep: 1 } });
|
|
113
|
+
});
|
|
114
|
+
it("leaves responses and header-less bridges untouched", () => {
|
|
115
|
+
const resp = { jsonrpc: "2.0", id: 1, result: {} };
|
|
116
|
+
expect(withConversationMeta(resp, conv)).toEqual(resp);
|
|
117
|
+
const req = { jsonrpc: "2.0", id: 1, method: "ping" };
|
|
118
|
+
expect(withConversationMeta(req, {})).toBe(req);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
describe("tools/list cache (an account switch must not drop the tools)", () => {
|
|
122
|
+
const list = (names, id = 1) => ({
|
|
123
|
+
jsonrpc: "2.0",
|
|
124
|
+
id,
|
|
125
|
+
result: { tools: names.map((name) => ({ name })) },
|
|
126
|
+
});
|
|
127
|
+
const tmp = () => fs.mkdtempSync(path.join(os.tmpdir(), "synap-bridge-cache-"));
|
|
128
|
+
/** One bridge process: sends `tools/list`, the pod answers with `pod`. */
|
|
129
|
+
async function run(cacheDir, pod, extra = {}) {
|
|
130
|
+
const input = new PassThrough();
|
|
131
|
+
const output = new PassThrough();
|
|
132
|
+
const chunks = [];
|
|
133
|
+
output.on("data", (c) => chunks.push(String(c)));
|
|
134
|
+
const fetchImpl = ((_u, init) => pod(init));
|
|
135
|
+
const done = runMcpBridge({
|
|
136
|
+
url: "https://pod/mcp",
|
|
137
|
+
apiKey: extra.apiKey ?? "k",
|
|
138
|
+
input,
|
|
139
|
+
output,
|
|
140
|
+
fetchImpl,
|
|
141
|
+
log: () => { },
|
|
142
|
+
cacheDir,
|
|
143
|
+
toolsListWaitMs: extra.waitMs ?? 30,
|
|
144
|
+
toolsListMaxAgeMs: extra.maxAgeMs,
|
|
145
|
+
});
|
|
146
|
+
input.write(JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/list", params: extra.params }) + "\n");
|
|
147
|
+
input.end();
|
|
148
|
+
await done;
|
|
149
|
+
return chunks.join("").split("\n").filter(Boolean).map((l) => JSON.parse(l));
|
|
150
|
+
}
|
|
151
|
+
const ok = (names) => async () => json(list(names));
|
|
152
|
+
const down = async () => new Response("bad gateway", { status: 502 });
|
|
153
|
+
it("a good list is cached, then answers when the pod fails", async () => {
|
|
154
|
+
const dir = tmp();
|
|
155
|
+
expect(await run(dir, ok(["a", "b"]))).toEqual([list(["a", "b"])]);
|
|
156
|
+
expect(await run(dir, down)).toEqual([list(["a", "b"])]);
|
|
157
|
+
});
|
|
158
|
+
it("a pod that does not answer in time is answered from the cache, once; a changed list is announced", async () => {
|
|
159
|
+
const dir = tmp();
|
|
160
|
+
await run(dir, ok(["a"]));
|
|
161
|
+
const late = async () => {
|
|
162
|
+
await new Promise((r) => setTimeout(r, 120));
|
|
163
|
+
return json(list(["a", "c"]));
|
|
164
|
+
};
|
|
165
|
+
const out = await run(dir, late);
|
|
166
|
+
expect(out).toEqual([list(["a"]), { jsonrpc: "2.0", method: "notifications/tools/list_changed" }]);
|
|
167
|
+
// the refresh was kept: the next outage serves the NEW list
|
|
168
|
+
expect(await run(dir, down)).toEqual([list(["a", "c"])]);
|
|
169
|
+
});
|
|
170
|
+
it("a late, identical list sends no notification", async () => {
|
|
171
|
+
const dir = tmp();
|
|
172
|
+
await run(dir, ok(["a"]));
|
|
173
|
+
const late = async () => {
|
|
174
|
+
await new Promise((r) => setTimeout(r, 120));
|
|
175
|
+
return json(list(["a"]));
|
|
176
|
+
};
|
|
177
|
+
expect(await run(dir, late)).toEqual([list(["a"])]);
|
|
178
|
+
});
|
|
179
|
+
it("no cache yet: the failure and the slow pod behave as before", async () => {
|
|
180
|
+
const out = await run(tmp(), down);
|
|
181
|
+
expect(out).toHaveLength(1);
|
|
182
|
+
expect(out[0]).toMatchObject({ id: 1, error: { code: -32000 } });
|
|
183
|
+
const slow = async () => {
|
|
184
|
+
await new Promise((r) => setTimeout(r, 120));
|
|
185
|
+
return json(list(["a"]));
|
|
186
|
+
};
|
|
187
|
+
expect(await run(tmp(), slow)).toEqual([list(["a"])]);
|
|
188
|
+
});
|
|
189
|
+
it("a JSON-RPC error from the pod is not cached and the cache answers it", async () => {
|
|
190
|
+
const dir = tmp();
|
|
191
|
+
const err = async () => json({ jsonrpc: "2.0", id: 1, error: { code: -32603, message: "boom" } });
|
|
192
|
+
expect((await run(dir, err))[0]).toMatchObject({ error: { message: "boom" } });
|
|
193
|
+
await run(dir, ok(["a"]));
|
|
194
|
+
expect(await run(dir, err)).toEqual([list(["a"])]);
|
|
195
|
+
});
|
|
196
|
+
it("a paged list is neither cached nor intercepted", async () => {
|
|
197
|
+
const dir = tmp();
|
|
198
|
+
const paged = async () => json({ jsonrpc: "2.0", id: 1, result: { tools: [{ name: "a" }], nextCursor: "p2" } });
|
|
199
|
+
await run(dir, paged);
|
|
200
|
+
expect(fs.readdirSync(dir)).toEqual([]);
|
|
201
|
+
await run(dir, ok(["a"]));
|
|
202
|
+
// a request carrying a cursor goes straight to the pod, even when it fails
|
|
203
|
+
const out = await run(dir, down, { params: { cursor: "p2" } });
|
|
204
|
+
expect(out[0]).toMatchObject({ id: 1, error: { code: -32000 } });
|
|
205
|
+
});
|
|
206
|
+
it("the cache belongs to a key: another key never gets this key's tools", async () => {
|
|
207
|
+
const dir = tmp();
|
|
208
|
+
await run(dir, ok(["a"]), { apiKey: "key-1" });
|
|
209
|
+
expect(toolsListCachePath(dir, "https://pod/mcp", "key-1")).not.toBe(toolsListCachePath(dir, "https://pod/mcp", "key-2"));
|
|
210
|
+
expect((await run(dir, down, { apiKey: "key-2" }))[0]).toMatchObject({ error: { code: -32000 } });
|
|
211
|
+
expect(fs.readFileSync(toolsListCachePath(dir, "https://pod/mcp", "key-1"), "utf8")).not.toContain("key-1");
|
|
212
|
+
});
|
|
213
|
+
it("a cache older than the max age is not served", async () => {
|
|
214
|
+
const dir = tmp();
|
|
215
|
+
await run(dir, ok(["a"]));
|
|
216
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
217
|
+
expect((await run(dir, down, { maxAgeMs: 5 }))[0]).toMatchObject({ error: { code: -32000 } });
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
describe("model hand-off (synap-bridge writes what the conversation runs on)", () => {
|
|
221
|
+
const tmp = () => fs.mkdtempSync(path.join(os.tmpdir(), "synap-bridge-model-"));
|
|
222
|
+
let tick = 1_700_000_000;
|
|
223
|
+
/** Writes the file as the plugin does, with a fresh mtime so a rewrite is always seen. */
|
|
224
|
+
const handOff = (file, text) => {
|
|
225
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
226
|
+
fs.writeFileSync(file, text);
|
|
227
|
+
tick += 10;
|
|
228
|
+
fs.utimesSync(file, tick, tick);
|
|
229
|
+
};
|
|
230
|
+
it("the path is the plugin's: ~/.synap/run/claude-code-<id>.json; an id that walks a path names none", () => {
|
|
231
|
+
expect(modelHandoffPath(SID, "/home/a")).toBe(`/home/a/.synap/run/claude-code-${SID}.json`);
|
|
232
|
+
for (const bad of [undefined, "", "../x", "a/b", "..", ".hidden", "a b", "a".repeat(129)]) {
|
|
233
|
+
expect(modelHandoffPath(bad, "/home/a")).toBeUndefined();
|
|
234
|
+
}
|
|
235
|
+
expect(modelHandoffPath(SID, "relative/home")).toBeUndefined();
|
|
236
|
+
});
|
|
237
|
+
it("present, changed, missing, invalid: the headers follow the file, never a guess", () => {
|
|
238
|
+
const file = modelHandoffPath(SID, tmp());
|
|
239
|
+
const read = modelHandoffReader(file);
|
|
240
|
+
// Missing: no headers, no error.
|
|
241
|
+
expect(read()).toEqual({});
|
|
242
|
+
handOff(file, JSON.stringify({ model: "claude-opus-5-5", provider: "anthropic", at: 1 }));
|
|
243
|
+
expect(read()).toEqual({ "X-Synap-Model": "claude-opus-5-5", "X-Synap-Model-Provider": "anthropic" });
|
|
244
|
+
// A /model switch rewrites the file: the next request names the new model.
|
|
245
|
+
handOff(file, JSON.stringify({ model: "claude-sonnet-5-5", provider: "zai", at: 2 }));
|
|
246
|
+
expect(read()).toEqual({ "X-Synap-Model": "claude-sonnet-5-5", "X-Synap-Model-Provider": "zai" });
|
|
247
|
+
// Unknown model (null) or one the pod would drop: the provider alone.
|
|
248
|
+
handOff(file, JSON.stringify({ model: null, provider: "anthropic", at: 3 }));
|
|
249
|
+
expect(read()).toEqual({ "X-Synap-Model-Provider": "anthropic" });
|
|
250
|
+
handOff(file, JSON.stringify({ model: "https://api.z.ai/v1", provider: "https://api.z.ai", at: 4 }));
|
|
251
|
+
expect(read()).toEqual({});
|
|
252
|
+
// Not JSON, or not an object: nothing.
|
|
253
|
+
handOff(file, "{\"model\": \"claude-op");
|
|
254
|
+
expect(read()).toEqual({});
|
|
255
|
+
handOff(file, "null");
|
|
256
|
+
expect(read()).toEqual({});
|
|
257
|
+
// The file goes (the session ended): nothing, not the last value.
|
|
258
|
+
handOff(file, JSON.stringify({ model: "claude-opus-5-5", provider: "anthropic", at: 5 }));
|
|
259
|
+
expect(read()["X-Synap-Model"]).toBe("claude-opus-5-5");
|
|
260
|
+
fs.rmSync(file);
|
|
261
|
+
expect(read()).toEqual({});
|
|
262
|
+
// No file named (no Claude Code id): nothing.
|
|
263
|
+
expect(modelHandoffReader(undefined)()).toEqual({});
|
|
264
|
+
});
|
|
265
|
+
it("an unchanged file is not read again", () => {
|
|
266
|
+
const file = modelHandoffPath(SID, tmp());
|
|
267
|
+
handOff(file, JSON.stringify({ model: "claude-opus-5-5", provider: "anthropic", at: 1 }));
|
|
268
|
+
const read = modelHandoffReader(file);
|
|
269
|
+
const spy = vi.spyOn(fs, "readFileSync");
|
|
270
|
+
try {
|
|
271
|
+
read();
|
|
272
|
+
read();
|
|
273
|
+
read();
|
|
274
|
+
expect(spy.mock.calls.filter((c) => c[0] === file).length).toBe(1);
|
|
275
|
+
}
|
|
276
|
+
finally {
|
|
277
|
+
spy.mockRestore();
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
it("every forwarded request carries the model the file names at that moment", async () => {
|
|
281
|
+
const file = modelHandoffPath(SID, tmp());
|
|
282
|
+
handOff(file, JSON.stringify({ model: "claude-opus-5-5", provider: "anthropic", at: 1 }));
|
|
283
|
+
const input = new PassThrough();
|
|
284
|
+
const output = new PassThrough();
|
|
285
|
+
const sent = [];
|
|
286
|
+
const fetchImpl = (async (_u, init) => {
|
|
287
|
+
sent.push(init.headers);
|
|
288
|
+
const id = JSON.parse(String(init.body)).id;
|
|
289
|
+
return json({ jsonrpc: "2.0", id, result: {} });
|
|
290
|
+
});
|
|
291
|
+
const conv = conversationHeaders(SID, "/Users/a/code");
|
|
292
|
+
const done = runMcpBridge({ url: "u", apiKey: "k", headers: conv, input, output, fetchImpl, log: () => { }, modelFile: file });
|
|
293
|
+
const call = async (id) => {
|
|
294
|
+
input.write(JSON.stringify({ jsonrpc: "2.0", id, method: "tools/call", params: { name: "synap_ask" } }) + "\n");
|
|
295
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
296
|
+
};
|
|
297
|
+
await call(1);
|
|
298
|
+
handOff(file, JSON.stringify({ model: "claude-sonnet-5-5", provider: "anthropic", at: 2 }));
|
|
299
|
+
await call(2);
|
|
300
|
+
fs.rmSync(file);
|
|
301
|
+
await call(3);
|
|
302
|
+
input.end();
|
|
303
|
+
await done;
|
|
304
|
+
expect(sent.map((h) => h["X-Synap-Model"])).toEqual(["claude-opus-5-5", "claude-sonnet-5-5", undefined]);
|
|
305
|
+
expect(sent.map((h) => h["X-Synap-Model-Provider"])).toEqual(["anthropic", "anthropic", undefined]);
|
|
306
|
+
// The conversation headers are untouched.
|
|
307
|
+
expect(sent.every((h) => h["X-Synap-Conversation"] === `claude-code:${SID}`)).toBe(true);
|
|
308
|
+
});
|
|
309
|
+
});
|
|
90
310
|
//# sourceMappingURL=mcp-bridge.test.js.map
|