@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
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The status line's 4th line, for each state, from fixtures. Pure: time,
|
|
3
|
+
* liveness and width are arguments.
|
|
4
|
+
*/
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import os from "node:os";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { describe, it, expect } from "vitest";
|
|
9
|
+
import { buildNowLine, ordinal, readRegistry, visibleLength } from "./now-line.js";
|
|
10
|
+
const NOW = Date.parse("2026-10-29T12:00:00Z");
|
|
11
|
+
const min = (m) => NOW - m * 60_000;
|
|
12
|
+
// eslint-disable-next-line no-control-regex
|
|
13
|
+
const plain = (s) => s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
14
|
+
const reg = (status, extra = {}) => ({
|
|
15
|
+
sessionId: "s1",
|
|
16
|
+
pid: 1,
|
|
17
|
+
status,
|
|
18
|
+
statusUpdatedAt: min(4),
|
|
19
|
+
startedAt: min(600),
|
|
20
|
+
...extra,
|
|
21
|
+
});
|
|
22
|
+
const task = (description, startedMin, extra = {}) => ({
|
|
23
|
+
kind: "bash",
|
|
24
|
+
description,
|
|
25
|
+
toolUseId: `t-${description}`,
|
|
26
|
+
startedAt: min(startedMin),
|
|
27
|
+
...extra,
|
|
28
|
+
});
|
|
29
|
+
const wait = (extra) => ({
|
|
30
|
+
id: "w1",
|
|
31
|
+
kind: "devbox",
|
|
32
|
+
ref: "w7-regen",
|
|
33
|
+
label: "",
|
|
34
|
+
pid: 4242,
|
|
35
|
+
startedAt: min(6),
|
|
36
|
+
probedAt: min(0.5),
|
|
37
|
+
changedAt: min(2),
|
|
38
|
+
state: "running",
|
|
39
|
+
detail: "be-file · 2/4 done",
|
|
40
|
+
attempt: 1,
|
|
41
|
+
probeErrors: 0,
|
|
42
|
+
...extra,
|
|
43
|
+
});
|
|
44
|
+
const alive = () => true;
|
|
45
|
+
const dead = () => false;
|
|
46
|
+
const line = (over) => plain(buildNowLine({ now: NOW, registry: reg("idle"), open: [], waits: [], alive, ...over }));
|
|
47
|
+
describe("the four states", () => {
|
|
48
|
+
it("needs you: the registry says waiting, with what for", () => {
|
|
49
|
+
expect(line({ registry: reg("waiting", { waitingFor: "permission prompt" }) })).toBe("✋ needs you · permission prompt");
|
|
50
|
+
});
|
|
51
|
+
it("working: busy, with its age, then the open waits", () => {
|
|
52
|
+
expect(line({ registry: reg("busy") })).toBe("▶ working · 4m");
|
|
53
|
+
expect(line({ registry: reg("busy"), open: [task("Run the suite", 3)] })).toBe("▶ working · 4m · ⏳ 1 · Run the suite 3m");
|
|
54
|
+
});
|
|
55
|
+
it("waiting on n: idle (turn over) with open waits, typed waits first", () => {
|
|
56
|
+
const out = line({ registry: reg("shell"), open: [task("Serve the build", 10)], waits: [wait({})] });
|
|
57
|
+
expect(out).toBe("⏳ waiting on 2 · devbox w7-regen running (be-file · 2/4 done) 6m · Serve the build 10m");
|
|
58
|
+
});
|
|
59
|
+
it("stopped: idle with nothing open, and how long since", () => {
|
|
60
|
+
expect(line({ registry: reg("idle") })).toBe("■ stopped · nothing running · 4m");
|
|
61
|
+
});
|
|
62
|
+
it("no registry entry at all says so instead of guessing", () => {
|
|
63
|
+
expect(line({ registry: null })).toBe("○ no session status");
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe("which background tasks count", () => {
|
|
67
|
+
it("a task from before this Claude Code process started is not shown (it died with that process)", () => {
|
|
68
|
+
expect(line({ registry: reg("busy", { startedAt: min(5) }), open: [task("old", 30)] })).toBe("▶ working · 4m");
|
|
69
|
+
});
|
|
70
|
+
it("idle means no live background shell, so open shells are dropped; agents and teammates stay", () => {
|
|
71
|
+
const open = [task("Serve the build", 10), task("Audit", 10, { kind: "agent" }), task("@lane-a", 9, { kind: "teammate" })];
|
|
72
|
+
expect(line({ registry: reg("idle"), open })).toBe("⏳ waiting on 2 · Audit 10m · @lane-a 9m");
|
|
73
|
+
expect(line({ registry: reg("shell"), open })).toContain("Serve the build");
|
|
74
|
+
});
|
|
75
|
+
it("a background task that runs `synap wait` for a typed wait is shown once, as the typed wait", () => {
|
|
76
|
+
const t = task("Wait for w7", 7, { command: "synap wait devbox w7-regen --label w7" });
|
|
77
|
+
const out = line({ registry: reg("shell"), open: [t], waits: [wait({})] });
|
|
78
|
+
expect(out).toBe("⏳ waiting on 1 · devbox w7-regen running (be-file · 2/4 done) 6m");
|
|
79
|
+
});
|
|
80
|
+
it("a `synap wait` task for ANOTHER target is still its own item", () => {
|
|
81
|
+
const t = task("Wait for PR", 7, { command: "synap wait pr backend#412" });
|
|
82
|
+
expect(line({ registry: reg("shell"), open: [t], waits: [wait({})] })).toContain("Wait for PR");
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
describe("typed wait states", () => {
|
|
86
|
+
const one = (w, alive_ = alive) => line({ registry: reg("shell"), waits: [wait(w)], alive: alive_ });
|
|
87
|
+
const raw = (w) => buildNowLine({ now: NOW, registry: reg("shell"), open: [], waits: [wait(w)], alive });
|
|
88
|
+
it("queued is dim, running cyan", () => {
|
|
89
|
+
expect(one({ state: "queued", detail: "queued 2nd (light)" })).toBe("⏳ waiting on 1 · devbox w7-regen queued 2nd (light) 6m");
|
|
90
|
+
expect(raw({ state: "queued", detail: "queued 2nd (light)" })).toContain("\x1b[2mdevbox w7-regen queued");
|
|
91
|
+
expect(raw({ state: "running" })).toContain("\x1b[36mdevbox w7-regen running");
|
|
92
|
+
});
|
|
93
|
+
it("passed shows green ✓ for 10 min after it finished, then goes", () => {
|
|
94
|
+
const passed = { state: "passed", detail: "4 gates green", exitCode: 0 };
|
|
95
|
+
expect(one({ ...passed, finishedAt: min(3) })).toBe("■ stopped · nothing running · 4m · ✓ devbox w7-regen passed 3m ago");
|
|
96
|
+
expect(raw({ ...passed, finishedAt: min(3) })).toContain("\x1b[32m✓");
|
|
97
|
+
expect(one({ ...passed, finishedAt: min(11) })).toBe("■ stopped · nothing running · 4m");
|
|
98
|
+
});
|
|
99
|
+
it("failed shows red ✗ with its detail", () => {
|
|
100
|
+
const out = one({ state: "failed", detail: "red: be-file (3 tests)", exitCode: 1, finishedAt: min(1) });
|
|
101
|
+
expect(out).toBe("■ stopped · nothing running · 4m · ✗ devbox w7-regen failed (red: be-file (3 tests))");
|
|
102
|
+
expect(raw({ state: "failed", detail: "x", exitCode: 1, finishedAt: min(1) })).toContain("\x1b[31m✗");
|
|
103
|
+
});
|
|
104
|
+
it("infra, error and timeout are red", () => {
|
|
105
|
+
for (const state of ["infra", "error", "timeout"]) {
|
|
106
|
+
expect(raw({ state, detail: "d", exitCode: state === "timeout" ? 2 : 3, finishedAt: min(1) })).toContain(`\x1b[31m✗ devbox w7-regen ${state}`);
|
|
107
|
+
}
|
|
108
|
+
expect(raw({ state: "error", detail: "probe failed 3× in a row" })).toContain("\x1b[31mdevbox w7-regen error");
|
|
109
|
+
});
|
|
110
|
+
it("a result is not something waited on: an open wait is counted, a finished one only shown", () => {
|
|
111
|
+
const out = line({ registry: reg("shell"), waits: [wait({ id: "a" }), wait({ id: "b", ref: "w8", state: "failed", detail: "red: x", exitCode: 1, finishedAt: min(1) })] });
|
|
112
|
+
expect(out).toBe("⏳ waiting on 1 · devbox w7-regen running (be-file · 2/4 done) 6m · ✗ devbox w8 failed (red: x)");
|
|
113
|
+
expect(line({ registry: reg("busy"), waits: [wait({ state: "passed", exitCode: 0, finishedAt: min(1) })] })).toBe("▶ working · 4m · ✓ devbox w7-regen passed 1m ago");
|
|
114
|
+
});
|
|
115
|
+
it("stale: a non-final state that has not changed for 30 min is amber, with how long", () => {
|
|
116
|
+
const out = raw({ state: "queued", detail: "queued 1st (heavy)", changedAt: min(34) });
|
|
117
|
+
expect(out).toContain("\x1b[33mdevbox w7-regen queued");
|
|
118
|
+
expect(plain(out)).toContain("no change 34m");
|
|
119
|
+
});
|
|
120
|
+
it("died: a non-final state whose process is gone is red", () => {
|
|
121
|
+
expect(one({ state: "running" }, dead)).toBe("■ stopped · nothing running · 4m · ✗ devbox w7-regen died (the wait stopped at running)");
|
|
122
|
+
});
|
|
123
|
+
it("the 3rd try on one target is called out in amber", () => {
|
|
124
|
+
expect(one({ attempt: 3 })).toContain("· 3rd try");
|
|
125
|
+
expect(raw({ attempt: 3 })).toContain("\x1b[33m· 3rd try");
|
|
126
|
+
expect(one({ attempt: 2 })).not.toContain("try");
|
|
127
|
+
});
|
|
128
|
+
it("a PR wait drops the Synap-core owner from its name", () => {
|
|
129
|
+
expect(one({ kind: "pr", ref: "Synap-core/backend#412", state: "running", detail: "Release gate running · 2/4 done" })).toContain("pr backend#412 Release gate running · 2/4 done");
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
describe("width", () => {
|
|
133
|
+
it("caps the line to the width, saying how many more there are", () => {
|
|
134
|
+
const open = Array.from({ length: 8 }, (_, i) => task(`Background job number ${i}`, 20 - i, { kind: "agent" }));
|
|
135
|
+
const out = line({ registry: reg("idle"), open, width: 100 });
|
|
136
|
+
expect(visibleLength(out)).toBeLessThanOrEqual(100);
|
|
137
|
+
expect(out).toMatch(/· \+\d more$/);
|
|
138
|
+
expect(out.startsWith("⏳ waiting on 8 · Background job number 0")).toBe(true);
|
|
139
|
+
});
|
|
140
|
+
it("defaults to 120 columns", () => {
|
|
141
|
+
const open = Array.from({ length: 20 }, (_, i) => task(`Job ${i} with a long description`, 20, { kind: "agent" }));
|
|
142
|
+
expect(visibleLength(line({ registry: reg("idle"), open }))).toBeLessThanOrEqual(120);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
describe("helpers", () => {
|
|
146
|
+
it("ordinal", () => {
|
|
147
|
+
expect([1, 2, 3, 4, 11, 12, 13, 21, 22, 23, 101].map(ordinal)).toEqual(["1st", "2nd", "3rd", "4th", "11th", "12th", "13th", "21st", "22nd", "23rd", "101st"]);
|
|
148
|
+
});
|
|
149
|
+
it("readRegistry picks the file for this conversation, newest status when several", () => {
|
|
150
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "synap-reg-"));
|
|
151
|
+
fs.writeFileSync(path.join(dir, "1.json"), JSON.stringify({ pid: 1, sessionId: "a", status: "idle", statusUpdatedAt: 5 }));
|
|
152
|
+
fs.writeFileSync(path.join(dir, "2.json"), JSON.stringify({ pid: 2, sessionId: "a", status: "busy", statusUpdatedAt: 9 }));
|
|
153
|
+
fs.writeFileSync(path.join(dir, "3.json"), JSON.stringify({ pid: 3, sessionId: "b", status: "waiting", statusUpdatedAt: 99 }));
|
|
154
|
+
fs.writeFileSync(path.join(dir, "3.abc.key"), "not json");
|
|
155
|
+
expect(readRegistry("a", dir)?.status).toBe("busy");
|
|
156
|
+
expect(readRegistry("z", dir)).toBeNull();
|
|
157
|
+
expect(readRegistry("a", path.join(dir, "missing"))).toBeNull();
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
describe("no wait, task or prompt text reaches the terminal with its control characters", () => {
|
|
161
|
+
it("a wait label, a task description and a waitingFor are printed inert", () => {
|
|
162
|
+
const raw = buildNowLine({
|
|
163
|
+
now: NOW,
|
|
164
|
+
registry: reg("busy"),
|
|
165
|
+
open: [task("run\x1b]0;pwned\x07 tests", 3)],
|
|
166
|
+
waits: [wait({ label: "PR \x1b[2J#5" })],
|
|
167
|
+
alive,
|
|
168
|
+
});
|
|
169
|
+
expect(raw).not.toContain("\x1b]0;");
|
|
170
|
+
expect(raw).not.toContain("\x1b[2J");
|
|
171
|
+
expect(raw).not.toContain("\x07");
|
|
172
|
+
expect(plain(raw)).toContain("PR [2J#5");
|
|
173
|
+
const asked = buildNowLine({ now: NOW, registry: reg("waiting", { waitingFor: "allow\x1b[8m rm" }), open: [], waits: [], alive });
|
|
174
|
+
expect(asked).not.toContain("\x1b[8m");
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
//# sourceMappingURL=now-line.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"now-line.test.js","sourceRoot":"","sources":["../../src/lib/now-line.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Does this installed package have an update?" — the POD's answer, read.
|
|
3
|
+
*
|
|
4
|
+
* The pod derives it ONCE for every installed kind (a space's template, the
|
|
5
|
+
* overlay packs on it, capabilities, views, skills, automations) with one
|
|
6
|
+
* rule: the catalog's content-hash version against the one the pod last
|
|
7
|
+
* converged to (`packageHasUpdate`, `@synap-core/types/package-updates`,
|
|
8
|
+
* served by Hub `GET /package-updates` → `installed[]`). Pod admin's Updates
|
|
9
|
+
* tab reads the same list, so the CLI and pod admin can no longer disagree
|
|
10
|
+
* (2 Nov 2026: the CLI said "brand-library: content changed" while pod admin
|
|
11
|
+
* said nothing, because each derived it its own way).
|
|
12
|
+
*
|
|
13
|
+
* This module never compares versions. A pod that cannot answer (an older
|
|
14
|
+
* pod, a key that is not a pod admin's) is said out loud by the caller:
|
|
15
|
+
* never a verdict the CLI made up.
|
|
16
|
+
*/
|
|
17
|
+
import { type HubConfig } from "./hub-client.js";
|
|
18
|
+
import type { InstalledKind } from "./installed.js";
|
|
19
|
+
/** One installed package and the pod's verdict (`PackageStanding` on the pod). */
|
|
20
|
+
export interface PackageStanding {
|
|
21
|
+
slug: string;
|
|
22
|
+
kind: InstalledKind;
|
|
23
|
+
name: string;
|
|
24
|
+
installedVersion: string | null;
|
|
25
|
+
latestVersion: string | null;
|
|
26
|
+
/** true / false = the pod compared it; null = it could not (`reason`). */
|
|
27
|
+
hasUpdate: boolean | null;
|
|
28
|
+
reason?: string;
|
|
29
|
+
}
|
|
30
|
+
/** Why the pod's answer could not be read. */
|
|
31
|
+
export type StandingsUnavailable =
|
|
32
|
+
/** The key's person is not a pod admin: the pod answers updates to admins only. */
|
|
33
|
+
"not-pod-admin"
|
|
34
|
+
/** The pod predates the one rule (no `GET /package-updates`, or no `installed[]`). */
|
|
35
|
+
| "pod-too-old";
|
|
36
|
+
export declare class StandingsUnavailableError extends Error {
|
|
37
|
+
readonly reason: StandingsUnavailable;
|
|
38
|
+
constructor(reason: StandingsUnavailable, message: string);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Every installed package with the pod's verdict. Throws
|
|
42
|
+
* {@link StandingsUnavailableError} when the pod cannot answer, and the hub
|
|
43
|
+
* error itself for anything else (unreachable, 5xx): never an empty list for
|
|
44
|
+
* a read that failed.
|
|
45
|
+
*/
|
|
46
|
+
export declare function fetchPackageStandings(cfg: HubConfig): Promise<PackageStanding[]>;
|
|
47
|
+
/** The shape `market update` prints and returns in `checks[]`, from the pod's verdicts. */
|
|
48
|
+
export interface StandingCheck {
|
|
49
|
+
slug: string;
|
|
50
|
+
kind: InstalledKind;
|
|
51
|
+
installedVersion: string;
|
|
52
|
+
latestVersion: string | undefined;
|
|
53
|
+
updateAvailable: boolean;
|
|
54
|
+
/** No version stamp on the pod: it could not compare. */
|
|
55
|
+
noVersionInfo: boolean;
|
|
56
|
+
/** The pod's reason when it could not compare. */
|
|
57
|
+
reason?: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The pod's verdicts as `market update` checks. `updateAvailable` is the
|
|
61
|
+
* pod's `hasUpdate === true`, nothing else: a package the pod could not
|
|
62
|
+
* compare is never "up to date" (no latest version → "can't check"; no stamp
|
|
63
|
+
* → "version unknown"; a space whose last update did not finish has an update
|
|
64
|
+
* and no stamp → "not finished").
|
|
65
|
+
*/
|
|
66
|
+
export declare function checksFromStandings(standings: PackageStanding[]): StandingCheck[];
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Does this installed package have an update?" — the POD's answer, read.
|
|
3
|
+
*
|
|
4
|
+
* The pod derives it ONCE for every installed kind (a space's template, the
|
|
5
|
+
* overlay packs on it, capabilities, views, skills, automations) with one
|
|
6
|
+
* rule: the catalog's content-hash version against the one the pod last
|
|
7
|
+
* converged to (`packageHasUpdate`, `@synap-core/types/package-updates`,
|
|
8
|
+
* served by Hub `GET /package-updates` → `installed[]`). Pod admin's Updates
|
|
9
|
+
* tab reads the same list, so the CLI and pod admin can no longer disagree
|
|
10
|
+
* (2 Nov 2026: the CLI said "brand-library: content changed" while pod admin
|
|
11
|
+
* said nothing, because each derived it its own way).
|
|
12
|
+
*
|
|
13
|
+
* This module never compares versions. A pod that cannot answer (an older
|
|
14
|
+
* pod, a key that is not a pod admin's) is said out loud by the caller:
|
|
15
|
+
* never a verdict the CLI made up.
|
|
16
|
+
*/
|
|
17
|
+
import { hubGet, HubError } from "./hub-client.js";
|
|
18
|
+
export class StandingsUnavailableError extends Error {
|
|
19
|
+
reason;
|
|
20
|
+
constructor(reason, message) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.reason = reason;
|
|
23
|
+
this.name = "StandingsUnavailableError";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const SENTENCES = {
|
|
27
|
+
"not-pod-admin": "Only a pod admin can read the pod's package updates, and this key's person is not one. Nothing was checked.",
|
|
28
|
+
"pod-too-old": "This pod is too old to answer which packages have an update (no installed[] on GET /package-updates). Update the pod, then run this again. Nothing was checked.",
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Every installed package with the pod's verdict. Throws
|
|
32
|
+
* {@link StandingsUnavailableError} when the pod cannot answer, and the hub
|
|
33
|
+
* error itself for anything else (unreachable, 5xx): never an empty list for
|
|
34
|
+
* a read that failed.
|
|
35
|
+
*/
|
|
36
|
+
export async function fetchPackageStandings(cfg) {
|
|
37
|
+
let body;
|
|
38
|
+
try {
|
|
39
|
+
// One definition fetch per package with an update, on the pod: a cold
|
|
40
|
+
// catalog can take a while.
|
|
41
|
+
body = await hubGet("/package-updates", {}, cfg, 60_000);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
if (err instanceof HubError && err.status === 403 && !/scope/i.test(`${err.message} ${err.detail ?? ""}`)) {
|
|
45
|
+
throw new StandingsUnavailableError("not-pod-admin", SENTENCES["not-pod-admin"]);
|
|
46
|
+
}
|
|
47
|
+
if (err instanceof HubError && err.status === 404) {
|
|
48
|
+
throw new StandingsUnavailableError("pod-too-old", SENTENCES["pod-too-old"]);
|
|
49
|
+
}
|
|
50
|
+
throw err;
|
|
51
|
+
}
|
|
52
|
+
const installed = body?.installed;
|
|
53
|
+
if (!Array.isArray(installed)) {
|
|
54
|
+
throw new StandingsUnavailableError("pod-too-old", SENTENCES["pod-too-old"]);
|
|
55
|
+
}
|
|
56
|
+
return installed;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The pod's verdicts as `market update` checks. `updateAvailable` is the
|
|
60
|
+
* pod's `hasUpdate === true`, nothing else: a package the pod could not
|
|
61
|
+
* compare is never "up to date" (no latest version → "can't check"; no stamp
|
|
62
|
+
* → "version unknown"; a space whose last update did not finish has an update
|
|
63
|
+
* and no stamp → "not finished").
|
|
64
|
+
*/
|
|
65
|
+
export function checksFromStandings(standings) {
|
|
66
|
+
return standings.map((s) => ({
|
|
67
|
+
slug: s.slug,
|
|
68
|
+
kind: s.kind,
|
|
69
|
+
installedVersion: s.installedVersion ?? "",
|
|
70
|
+
latestVersion: s.hasUpdate === null && s.installedVersion ? undefined : (s.latestVersion ?? undefined),
|
|
71
|
+
updateAvailable: s.hasUpdate === true,
|
|
72
|
+
noVersionInfo: !s.installedVersion,
|
|
73
|
+
...(s.reason ? { reason: s.reason } : {}),
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=package-updates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-updates.js","sourceRoot":"","sources":["../../src/lib/package-updates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAkB,MAAM,iBAAiB,CAAC;AAsBnE,MAAM,OAAO,yBAA0B,SAAQ,KAAK;IAEvC;IADX,YACW,MAA4B,EACrC,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAHN,WAAM,GAAN,MAAM,CAAsB;QAIrC,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;IAC1C,CAAC;CACF;AAED,MAAM,SAAS,GAAyC;IACtD,eAAe,EACb,6GAA6G;IAC/G,aAAa,EACX,iKAAiK;CACpK,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,GAAc;IACxD,IAAI,IAAa,CAAC;IAClB,IAAI,CAAC;QACH,sEAAsE;QACtE,4BAA4B;QAC5B,IAAI,GAAG,MAAM,MAAM,CAAC,kBAAkB,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;IAC3D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC;YAC1G,MAAM,IAAI,yBAAyB,CAAC,eAAe,EAAE,SAAS,CAAC,eAAe,CAAC,CAAC,CAAC;QACnF,CAAC;QACD,IAAI,GAAG,YAAY,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAClD,MAAM,IAAI,yBAAyB,CAAC,aAAa,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC;QAC/E,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,MAAM,SAAS,GAAI,IAAuC,EAAE,SAAS,CAAC;IACtE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,yBAAyB,CAAC,aAAa,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO,SAA8B,CAAC;AACxC,CAAC;AAeD;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,SAA4B;IAC9D,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC3B,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,gBAAgB,EAAE,CAAC,CAAC,gBAAgB,IAAI,EAAE;QAC1C,aAAa,EAAE,CAAC,CAAC,SAAS,KAAK,IAAI,IAAI,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,IAAI,SAAS,CAAC;QACtG,eAAe,EAAE,CAAC,CAAC,SAAS,KAAK,IAAI;QACrC,aAAa,EAAE,CAAC,CAAC,CAAC,gBAAgB;QAClC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC1C,CAAC,CAAC,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A pod that moved to a new address.
|
|
3
|
+
*
|
|
4
|
+
* Hosted pods move from `{name}.synap.live` to `{name}.pod.synap.live`
|
|
5
|
+
* (Control Plane "tenant domains v2"). For 90 days the old name answers every
|
|
6
|
+
* request with a redirect to the new one. The CLI NEVER follows a redirect to
|
|
7
|
+
* another host for an API call: the request carries the pod key, and a
|
|
8
|
+
* redirect is something any server on the old name can send. Instead:
|
|
9
|
+
*
|
|
10
|
+
* 1. every pod call goes through `podFetch`, which refuses a cross-host
|
|
11
|
+
* redirect with `PodMovedError` (one line: what happened, what to run);
|
|
12
|
+
* 2. `synap doctor` asks the Control Plane where the pod lives now —
|
|
13
|
+
* `POST {cp}/pods/handshake {targetUrl: <old url>}` answers
|
|
14
|
+
* `410 {code: "POD_MOVED", podId, podUrl, consoleUrl}` for a moved pod
|
|
15
|
+
* (CP `src/routes/pods.ts`, `src/lib/pod-handshake-problem.ts`) —
|
|
16
|
+
* and `synap doctor --fix` rewrites the saved profile through the one
|
|
17
|
+
* door for a URL change (`synap pods update`).
|
|
18
|
+
*
|
|
19
|
+
* The new address comes ONLY from the Control Plane, never from the redirect.
|
|
20
|
+
*/
|
|
21
|
+
/** What to run when a pod has moved. */
|
|
22
|
+
export declare const POD_MOVED_HINT = "Run: synap doctor --fix (it finds the new address and updates your settings)";
|
|
23
|
+
/** The one line a command prints when its pod has moved. */
|
|
24
|
+
export declare function podMovedMessage(podUrl: string): string;
|
|
25
|
+
export declare class PodMovedError extends Error {
|
|
26
|
+
/** The address the CLI holds (origin). */
|
|
27
|
+
readonly podUrl: string;
|
|
28
|
+
/** Where the old address redirected — informational only, never used. */
|
|
29
|
+
readonly redirectedTo: string;
|
|
30
|
+
constructor(podUrl: string, redirectedTo: string);
|
|
31
|
+
}
|
|
32
|
+
type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
|
|
33
|
+
/**
|
|
34
|
+
* `fetch` for calls to a pod: redirects within the same host are followed (at
|
|
35
|
+
* most 5), a redirect to another host throws `PodMovedError`. Network errors
|
|
36
|
+
* propagate unchanged, so callers keep their own transport classification.
|
|
37
|
+
*/
|
|
38
|
+
export declare function podFetch(url: string, init?: RequestInit, fetchImpl?: FetchLike): Promise<Response>;
|
|
39
|
+
/** What the Control Plane says about a pod address. */
|
|
40
|
+
export type PodMoveAnswer = {
|
|
41
|
+
kind: "moved";
|
|
42
|
+
podUrl: string;
|
|
43
|
+
consoleUrl?: string;
|
|
44
|
+
podId?: string;
|
|
45
|
+
}
|
|
46
|
+
/** The CP knows this address as the pod's own (or knows nothing to move). */
|
|
47
|
+
| {
|
|
48
|
+
kind: "not-moved";
|
|
49
|
+
code?: string;
|
|
50
|
+
}
|
|
51
|
+
/** No Control Plane sign-in on this computer, or it expired. */
|
|
52
|
+
| {
|
|
53
|
+
kind: "signed-out";
|
|
54
|
+
}
|
|
55
|
+
/** The CP could not be asked, or answered something unreadable. */
|
|
56
|
+
| {
|
|
57
|
+
kind: "unknown";
|
|
58
|
+
reason: string;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Ask the Control Plane where a pod lives now. Needs the CP sign-in from
|
|
62
|
+
* `synap login` (a bearer token); without one the answer is `signed-out`.
|
|
63
|
+
*/
|
|
64
|
+
export declare function askControlPlaneWherePodIs(params: {
|
|
65
|
+
podUrl: string;
|
|
66
|
+
cpUrl: string;
|
|
67
|
+
cpToken: string | undefined;
|
|
68
|
+
fetchImpl?: FetchLike;
|
|
69
|
+
}): Promise<PodMoveAnswer>;
|
|
70
|
+
export {};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A pod that moved to a new address.
|
|
3
|
+
*
|
|
4
|
+
* Hosted pods move from `{name}.synap.live` to `{name}.pod.synap.live`
|
|
5
|
+
* (Control Plane "tenant domains v2"). For 90 days the old name answers every
|
|
6
|
+
* request with a redirect to the new one. The CLI NEVER follows a redirect to
|
|
7
|
+
* another host for an API call: the request carries the pod key, and a
|
|
8
|
+
* redirect is something any server on the old name can send. Instead:
|
|
9
|
+
*
|
|
10
|
+
* 1. every pod call goes through `podFetch`, which refuses a cross-host
|
|
11
|
+
* redirect with `PodMovedError` (one line: what happened, what to run);
|
|
12
|
+
* 2. `synap doctor` asks the Control Plane where the pod lives now —
|
|
13
|
+
* `POST {cp}/pods/handshake {targetUrl: <old url>}` answers
|
|
14
|
+
* `410 {code: "POD_MOVED", podId, podUrl, consoleUrl}` for a moved pod
|
|
15
|
+
* (CP `src/routes/pods.ts`, `src/lib/pod-handshake-problem.ts`) —
|
|
16
|
+
* and `synap doctor --fix` rewrites the saved profile through the one
|
|
17
|
+
* door for a URL change (`synap pods update`).
|
|
18
|
+
*
|
|
19
|
+
* The new address comes ONLY from the Control Plane, never from the redirect.
|
|
20
|
+
*/
|
|
21
|
+
/** What to run when a pod has moved. */
|
|
22
|
+
export const POD_MOVED_HINT = "Run: synap doctor --fix (it finds the new address and updates your settings)";
|
|
23
|
+
/** The one line a command prints when its pod has moved. */
|
|
24
|
+
export function podMovedMessage(podUrl) {
|
|
25
|
+
return `The pod at ${podUrl} has a new address. Run: synap doctor --fix`;
|
|
26
|
+
}
|
|
27
|
+
export class PodMovedError extends Error {
|
|
28
|
+
/** The address the CLI holds (origin). */
|
|
29
|
+
podUrl;
|
|
30
|
+
/** Where the old address redirected — informational only, never used. */
|
|
31
|
+
redirectedTo;
|
|
32
|
+
constructor(podUrl, redirectedTo) {
|
|
33
|
+
super(podMovedMessage(podUrl));
|
|
34
|
+
this.name = "PodMovedError";
|
|
35
|
+
this.podUrl = podUrl;
|
|
36
|
+
this.redirectedTo = redirectedTo;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const MAX_SAME_HOST_HOPS = 5;
|
|
40
|
+
function isRedirect(status) {
|
|
41
|
+
return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A redirect the CLI may follow: the same origin, or the same host upgraded
|
|
45
|
+
* from http to https (default ports). Anything else is another host.
|
|
46
|
+
*/
|
|
47
|
+
function isSameHost(from, to) {
|
|
48
|
+
if (from.origin === to.origin)
|
|
49
|
+
return true;
|
|
50
|
+
return (from.protocol === "http:" &&
|
|
51
|
+
to.protocol === "https:" &&
|
|
52
|
+
from.hostname === to.hostname &&
|
|
53
|
+
from.port === "" &&
|
|
54
|
+
to.port === "");
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* `fetch` for calls to a pod: redirects within the same host are followed (at
|
|
58
|
+
* most 5), a redirect to another host throws `PodMovedError`. Network errors
|
|
59
|
+
* propagate unchanged, so callers keep their own transport classification.
|
|
60
|
+
*/
|
|
61
|
+
export async function podFetch(url, init = {}, fetchImpl = fetch) {
|
|
62
|
+
const start = new URL(url);
|
|
63
|
+
let current = start;
|
|
64
|
+
let currentInit = init;
|
|
65
|
+
for (let hop = 0;; hop++) {
|
|
66
|
+
const res = await fetchImpl(current.toString(), { ...currentInit, redirect: "manual" });
|
|
67
|
+
if (!isRedirect(res.status))
|
|
68
|
+
return res;
|
|
69
|
+
const location = res.headers.get("location");
|
|
70
|
+
if (!location)
|
|
71
|
+
return res;
|
|
72
|
+
let next;
|
|
73
|
+
try {
|
|
74
|
+
next = new URL(location, current);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return res;
|
|
78
|
+
}
|
|
79
|
+
if (!isSameHost(start, next)) {
|
|
80
|
+
throw new PodMovedError(start.origin, next.origin);
|
|
81
|
+
}
|
|
82
|
+
if (hop >= MAX_SAME_HOST_HOPS)
|
|
83
|
+
return res;
|
|
84
|
+
if (res.status === 303 && (currentInit.method ?? "GET").toUpperCase() !== "GET") {
|
|
85
|
+
const { body: _body, ...rest } = currentInit;
|
|
86
|
+
currentInit = { ...rest, method: "GET" };
|
|
87
|
+
}
|
|
88
|
+
current = next;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Ask the Control Plane where a pod lives now. Needs the CP sign-in from
|
|
93
|
+
* `synap login` (a bearer token); without one the answer is `signed-out`.
|
|
94
|
+
*/
|
|
95
|
+
export async function askControlPlaneWherePodIs(params) {
|
|
96
|
+
if (!params.cpToken)
|
|
97
|
+
return { kind: "signed-out" };
|
|
98
|
+
const fetchImpl = params.fetchImpl ?? fetch;
|
|
99
|
+
let res;
|
|
100
|
+
try {
|
|
101
|
+
res = await fetchImpl(`${params.cpUrl.replace(/\/+$/, "")}/pods/handshake`, {
|
|
102
|
+
method: "POST",
|
|
103
|
+
headers: {
|
|
104
|
+
Authorization: `Bearer ${params.cpToken}`,
|
|
105
|
+
"Content-Type": "application/json",
|
|
106
|
+
},
|
|
107
|
+
body: JSON.stringify({ targetUrl: params.podUrl }),
|
|
108
|
+
redirect: "manual",
|
|
109
|
+
signal: AbortSignal.timeout(10_000),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
return { kind: "unknown", reason: err instanceof Error ? err.message : String(err) };
|
|
114
|
+
}
|
|
115
|
+
if (res.status === 401)
|
|
116
|
+
return { kind: "signed-out" };
|
|
117
|
+
const body = (await res.json().catch(() => null));
|
|
118
|
+
if (res.status === 410 && body?.code === "POD_MOVED") {
|
|
119
|
+
const podUrl = typeof body.podUrl === "string" ? safeHttpsOrigin(body.podUrl) : null;
|
|
120
|
+
if (!podUrl)
|
|
121
|
+
return { kind: "unknown", reason: "the Control Plane named no usable new address" };
|
|
122
|
+
return {
|
|
123
|
+
kind: "moved",
|
|
124
|
+
podUrl,
|
|
125
|
+
...(typeof body.consoleUrl === "string" ? { consoleUrl: body.consoleUrl } : {}),
|
|
126
|
+
...(typeof body.podId === "string" ? { podId: body.podId } : {}),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
if (res.ok || res.status === 403 || res.status === 409) {
|
|
130
|
+
return { kind: "not-moved", ...(typeof body?.code === "string" ? { code: body.code } : {}) };
|
|
131
|
+
}
|
|
132
|
+
return { kind: "unknown", reason: `the Control Plane answered ${res.status}` };
|
|
133
|
+
}
|
|
134
|
+
/** An https origin, or null. A new pod address is never plain http. */
|
|
135
|
+
function safeHttpsOrigin(value) {
|
|
136
|
+
try {
|
|
137
|
+
const url = new URL(value);
|
|
138
|
+
if (url.protocol !== "https:" || url.username || url.password)
|
|
139
|
+
return null;
|
|
140
|
+
return url.origin;
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=pod-moved.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pod-moved.js","sourceRoot":"","sources":["../../src/lib/pod-moved.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Why a pod probe failed, told apart: the network between this machine and the
|
|
3
|
+
* pod (egress proxy, DNS, firewall, TLS) versus the pod itself answering badly.
|
|
4
|
+
*
|
|
5
|
+
* `checkPodHealth` used to fold every failure into `healthy: false`, so `synap
|
|
6
|
+
* init` behind an egress proxy that answers 403 to CONNECT printed a pod-side
|
|
7
|
+
* checklist ("is the container running?") for a pod that was never contacted.
|
|
8
|
+
* This is the one place that taxonomy lives for the probe.
|
|
9
|
+
*/
|
|
10
|
+
export type ReachKind =
|
|
11
|
+
/** An egress proxy refused the CONNECT tunnel — the pod was never contacted. */
|
|
12
|
+
"proxy"
|
|
13
|
+
/** DNS, refused, reset, unroutable, timeout, TLS — no HTTP answer from anything. */
|
|
14
|
+
| "network"
|
|
15
|
+
/** The pod host answered, but not with a healthy /health. */
|
|
16
|
+
| "pod";
|
|
17
|
+
export interface ReachFailure {
|
|
18
|
+
kind: ReachKind;
|
|
19
|
+
/** The underlying error, one line: status / error code / message. */
|
|
20
|
+
detail: string;
|
|
21
|
+
/** Env variable whose proxy this process used, with credentials stripped. */
|
|
22
|
+
proxy?: {
|
|
23
|
+
env: string;
|
|
24
|
+
url: string;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** The env proxy that applies to `podUrl`, credentials removed. Honors NO_PROXY. */
|
|
28
|
+
export declare function proxyInUse(podUrl: string, env?: NodeJS.ProcessEnv): {
|
|
29
|
+
env: string;
|
|
30
|
+
url: string;
|
|
31
|
+
} | undefined;
|
|
32
|
+
/** Classify a fetch that threw (no HTTP answer from the pod). */
|
|
33
|
+
export declare function classifyFetchError(err: unknown, podUrl: string, env?: NodeJS.ProcessEnv): ReachFailure;
|
|
34
|
+
/** Classify a /health that answered with a non-2xx status. */
|
|
35
|
+
export declare function classifyHttpStatus(status: number, podUrl: string, env?: NodeJS.ProcessEnv): ReachFailure;
|
|
36
|
+
/** The lines `synap init` / `synap doctor` print under "not reachable", by cause. */
|
|
37
|
+
export declare function reachChecklist(f: ReachFailure | undefined): string[];
|
|
38
|
+
/** The one-line headline: names the underlying error, not just "not reachable". */
|
|
39
|
+
export declare function unreachableMessage(podUrl: string, failure?: ReachFailure): string;
|