@synap-core/cli 1.10.0 → 1.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -198
- package/dist/build-info.json +6 -0
- package/dist/commands/agent-run.js +3 -2
- package/dist/commands/agent-run.js.map +1 -1
- package/dist/commands/agents.d.ts +33 -17
- package/dist/commands/agents.js +203 -237
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/app.d.ts +143 -0
- package/dist/commands/app.js +523 -0
- package/dist/commands/app.js.map +1 -0
- package/dist/commands/automate.d.ts +21 -0
- package/dist/commands/automate.js +42 -0
- package/dist/commands/automate.js.map +1 -0
- package/dist/commands/automation-schema.test.d.ts +16 -0
- package/dist/commands/automation-schema.test.js +77 -0
- package/dist/commands/automation-schema.test.js.map +1 -0
- package/dist/commands/automation.d.ts +12 -0
- package/dist/commands/automation.js +80 -35
- package/dist/commands/automation.js.map +1 -1
- package/dist/commands/bridge-setup.d.ts +14 -1
- package/dist/commands/bridge-setup.js +392 -128
- package/dist/commands/bridge-setup.js.map +1 -1
- package/dist/commands/cap-connections.d.ts +4 -4
- package/dist/commands/cap-connections.js +14 -14
- package/dist/commands/cap-connections.js.map +1 -1
- package/dist/commands/capability.d.ts +2 -0
- package/dist/commands/capability.js +297 -176
- package/dist/commands/capability.js.map +1 -1
- package/dist/commands/catalog.d.ts +275 -0
- package/dist/commands/catalog.js +481 -0
- package/dist/commands/catalog.js.map +1 -0
- package/dist/commands/cell.d.ts +155 -8
- package/dist/commands/cell.js +416 -40
- package/dist/commands/cell.js.map +1 -1
- package/dist/commands/centrality.js +3 -14
- package/dist/commands/centrality.js.map +1 -1
- package/dist/commands/connect.d.ts +38 -13
- package/dist/commands/connect.js +68 -167
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/connections.d.ts +3 -2
- package/dist/commands/connections.js +95 -25
- package/dist/commands/connections.js.map +1 -1
- package/dist/commands/context.js +9 -2
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/data-extra.d.ts +34 -0
- package/dist/commands/data-extra.js +198 -25
- package/dist/commands/data-extra.js.map +1 -1
- package/dist/commands/data.d.ts +120 -2
- package/dist/commands/data.js +307 -24
- package/dist/commands/data.js.map +1 -1
- package/dist/commands/diagnose.d.ts +66 -4
- package/dist/commands/diagnose.js +329 -29
- package/dist/commands/diagnose.js.map +1 -1
- package/dist/commands/digest.js +2 -1
- package/dist/commands/digest.js.map +1 -1
- package/dist/commands/doc.d.ts +25 -4
- package/dist/commands/doc.js +100 -18
- package/dist/commands/doc.js.map +1 -1
- package/dist/commands/doctor.d.ts +130 -0
- package/dist/commands/doctor.js +814 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/events.js +3 -1
- package/dist/commands/events.js.map +1 -1
- package/dist/commands/explain.js +5 -5
- package/dist/commands/explain.js.map +1 -1
- package/dist/commands/graph.js +5 -3
- package/dist/commands/graph.js.map +1 -1
- package/dist/commands/import.d.ts +368 -13
- package/dist/commands/import.js +1096 -55
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/init.d.ts +20 -9
- package/dist/commands/init.js +433 -973
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/keys.d.ts +1 -0
- package/dist/commands/keys.js +15 -2
- package/dist/commands/keys.js.map +1 -1
- package/dist/commands/knowledge.d.ts +28 -6
- package/dist/commands/knowledge.js +358 -73
- package/dist/commands/knowledge.js.map +1 -1
- package/dist/commands/launch.js +2 -2
- package/dist/commands/launch.js.map +1 -1
- package/dist/commands/lens.d.ts +37 -1
- package/dist/commands/lens.js +193 -72
- package/dist/commands/lens.js.map +1 -1
- package/dist/commands/market-authoring.d.ts +72 -0
- package/dist/commands/market-authoring.js +566 -0
- package/dist/commands/market-authoring.js.map +1 -0
- package/dist/commands/market-official.d.ts +58 -0
- package/dist/commands/market-official.js +84 -0
- package/dist/commands/market-official.js.map +1 -0
- package/dist/commands/market.d.ts +419 -20
- package/dist/commands/market.js +2124 -193
- package/dist/commands/market.js.map +1 -1
- package/dist/commands/mcp.d.ts +70 -13
- package/dist/commands/mcp.js +308 -40
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/open.d.ts +39 -9
- package/dist/commands/open.js +80 -25
- package/dist/commands/open.js.map +1 -1
- package/dist/commands/playbook.d.ts +42 -0
- package/dist/commands/playbook.js +162 -0
- package/dist/commands/playbook.js.map +1 -0
- package/dist/commands/pod.js +1 -1
- package/dist/commands/pod.js.map +1 -1
- package/dist/commands/pods.d.ts +19 -0
- package/dist/commands/pods.js +220 -85
- package/dist/commands/pods.js.map +1 -1
- package/dist/commands/proposals-actions.d.ts +62 -0
- package/dist/commands/proposals-actions.js +146 -8
- package/dist/commands/proposals-actions.js.map +1 -1
- package/dist/commands/proposals-await.d.ts +68 -0
- package/dist/commands/proposals-await.js +248 -0
- package/dist/commands/proposals-await.js.map +1 -0
- package/dist/commands/proton-setup.d.ts +73 -0
- package/dist/commands/proton-setup.js +577 -0
- package/dist/commands/proton-setup.js.map +1 -0
- package/dist/commands/providers.d.ts +23 -0
- package/dist/commands/providers.js +126 -1
- package/dist/commands/providers.js.map +1 -1
- package/dist/commands/raycast.js +6 -2
- package/dist/commands/raycast.js.map +1 -1
- package/dist/commands/rule.d.ts +150 -0
- package/dist/commands/rule.js +447 -0
- package/dist/commands/rule.js.map +1 -0
- package/dist/commands/session-wait.d.ts +111 -0
- package/dist/commands/session-wait.js +209 -0
- package/dist/commands/session-wait.js.map +1 -0
- package/dist/commands/sessions.d.ts +52 -0
- package/dist/commands/sessions.js +479 -58
- package/dist/commands/sessions.js.map +1 -1
- package/dist/commands/skill-manage.d.ts +21 -0
- package/dist/commands/skill-manage.js +136 -19
- package/dist/commands/skill-manage.js.map +1 -1
- package/dist/commands/skills.js +19 -1
- package/dist/commands/skills.js.map +1 -1
- package/dist/commands/status.d.ts +1 -4
- package/dist/commands/status.js +5 -34
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/statusline.backoff.test.d.ts +13 -0
- package/dist/commands/statusline.backoff.test.js +100 -0
- package/dist/commands/statusline.backoff.test.js.map +1 -0
- package/dist/commands/statusline.js +16 -9
- package/dist/commands/statusline.js.map +1 -1
- package/dist/commands/sync-status.d.ts +12 -0
- package/dist/commands/sync-status.js +125 -0
- package/dist/commands/sync-status.js.map +1 -0
- package/dist/commands/templates-retire.d.ts +34 -0
- package/dist/commands/templates-retire.js +192 -0
- package/dist/commands/templates-retire.js.map +1 -0
- package/dist/commands/tools.js +6 -21
- package/dist/commands/tools.js.map +1 -1
- package/dist/commands/tracks.d.ts +51 -0
- package/dist/commands/tracks.js +291 -0
- package/dist/commands/tracks.js.map +1 -0
- package/dist/commands/update.d.ts +3 -11
- package/dist/commands/update.js +5 -134
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/upload.d.ts +68 -0
- package/dist/commands/upload.js +257 -0
- package/dist/commands/upload.js.map +1 -0
- package/dist/commands/vendor.d.ts +28 -0
- package/dist/commands/vendor.js +110 -0
- package/dist/commands/vendor.js.map +1 -0
- package/dist/commands/versions.js +6 -0
- package/dist/commands/versions.js.map +1 -1
- package/dist/commands/view.js +10 -5
- package/dist/commands/view.js.map +1 -1
- package/dist/commands/whoami.d.ts +23 -3
- package/dist/commands/whoami.js +169 -23
- package/dist/commands/whoami.js.map +1 -1
- package/dist/commands/worker.d.ts +122 -0
- package/dist/commands/worker.js +435 -0
- package/dist/commands/worker.js.map +1 -0
- package/dist/commands/worker.test.d.ts +1 -0
- package/dist/commands/worker.test.js +116 -0
- package/dist/commands/worker.test.js.map +1 -0
- package/dist/commands/workspace-ops.d.ts +35 -0
- package/dist/commands/workspace-ops.js +153 -0
- package/dist/commands/workspace-ops.js.map +1 -0
- package/dist/index.d.ts +6 -6
- package/dist/index.js +1009 -171
- package/dist/index.js.map +1 -1
- package/dist/lib/agent-connections.d.ts +31 -0
- package/dist/lib/agent-connections.js +33 -0
- package/dist/lib/agent-connections.js.map +1 -0
- package/dist/lib/agent-detect.d.ts +27 -0
- package/dist/lib/agent-detect.js +71 -0
- package/dist/lib/agent-detect.js.map +1 -0
- package/dist/lib/agent-door.d.ts +138 -0
- package/dist/lib/agent-door.js +426 -0
- package/dist/lib/agent-door.js.map +1 -0
- package/dist/lib/agents-config.d.ts +2 -0
- package/dist/lib/agents-config.js +4 -9
- package/dist/lib/agents-config.js.map +1 -1
- package/dist/lib/approval-poll.d.ts +31 -2
- package/dist/lib/approval-poll.js +20 -7
- package/dist/lib/approval-poll.js.map +1 -1
- package/dist/lib/atomic-file.d.ts +48 -0
- package/dist/lib/atomic-file.js +129 -0
- package/dist/lib/atomic-file.js.map +1 -0
- package/dist/lib/auth.d.ts +14 -9
- package/dist/lib/auth.js +44 -61
- package/dist/lib/auth.js.map +1 -1
- package/dist/lib/batch-approval.d.ts +85 -0
- package/dist/lib/batch-approval.js +170 -0
- package/dist/lib/batch-approval.js.map +1 -0
- package/dist/lib/capture-lane.d.ts +6 -5
- package/dist/lib/capture-lane.js +11 -8
- package/dist/lib/capture-lane.js.map +1 -1
- package/dist/lib/capture-structure.d.ts +342 -0
- package/dist/lib/capture-structure.js +295 -0
- package/dist/lib/capture-structure.js.map +1 -0
- package/dist/lib/catalog-templates.d.ts +59 -0
- package/dist/lib/catalog-templates.js +126 -0
- package/dist/lib/catalog-templates.js.map +1 -0
- package/dist/lib/cli-build.d.ts +19 -0
- package/dist/lib/cli-build.js +37 -0
- package/dist/lib/cli-build.js.map +1 -0
- package/dist/lib/conversation-headers.d.ts +18 -0
- package/dist/lib/conversation-headers.js +32 -0
- package/dist/lib/conversation-headers.js.map +1 -0
- package/dist/lib/cp-packages.d.ts +216 -4
- package/dist/lib/cp-packages.js +414 -11
- package/dist/lib/cp-packages.js.map +1 -1
- package/dist/lib/credential-class.d.ts +117 -0
- package/dist/lib/credential-class.js +116 -0
- package/dist/lib/credential-class.js.map +1 -0
- package/dist/lib/describe-lens.d.ts +35 -0
- package/dist/lib/describe-lens.js +40 -0
- package/dist/lib/describe-lens.js.map +1 -1
- package/dist/lib/device-connect.d.ts +125 -0
- package/dist/lib/device-connect.js +256 -0
- package/dist/lib/device-connect.js.map +1 -0
- package/dist/lib/directory-lens.d.ts +99 -0
- package/dist/lib/directory-lens.js +187 -0
- package/dist/lib/directory-lens.js.map +1 -0
- package/dist/lib/exporter-coverage.d.ts +71 -0
- package/dist/lib/exporter-coverage.js +97 -0
- package/dist/lib/exporter-coverage.js.map +1 -0
- package/dist/lib/hub-client.d.ts +43 -9
- package/dist/lib/hub-client.js +429 -65
- package/dist/lib/hub-client.js.map +1 -1
- package/dist/lib/id.d.ts +23 -0
- package/dist/lib/id.js +35 -0
- package/dist/lib/id.js.map +1 -0
- package/dist/lib/init-connect.d.ts +41 -0
- package/dist/lib/init-connect.js +75 -0
- package/dist/lib/init-connect.js.map +1 -0
- package/dist/lib/init-exit.d.ts +24 -0
- package/dist/lib/init-exit.js +41 -0
- package/dist/lib/init-exit.js.map +1 -0
- package/dist/lib/init-flow.d.ts +25 -0
- package/dist/lib/init-flow.js +40 -0
- package/dist/lib/init-flow.js.map +1 -0
- package/dist/lib/init-handoff.d.ts +16 -0
- package/dist/lib/init-handoff.js +19 -0
- package/dist/lib/init-handoff.js.map +1 -0
- package/dist/lib/init-output.d.ts +18 -0
- package/dist/lib/init-output.js +43 -0
- package/dist/lib/init-output.js.map +1 -0
- package/dist/lib/init-pod.d.ts +45 -0
- package/dist/lib/init-pod.js +36 -0
- package/dist/lib/init-pod.js.map +1 -0
- package/dist/lib/installed.d.ts +210 -11
- package/dist/lib/installed.js +234 -36
- package/dist/lib/installed.js.map +1 -1
- package/dist/lib/key-approval.d.ts +57 -0
- package/dist/lib/key-approval.js +117 -0
- package/dist/lib/key-approval.js.map +1 -0
- package/dist/lib/key-diagnosis.d.ts +56 -0
- package/dist/lib/key-diagnosis.js +227 -0
- package/dist/lib/key-diagnosis.js.map +1 -0
- package/dist/lib/key-doctor.d.ts +136 -0
- package/dist/lib/key-doctor.js +286 -0
- package/dist/lib/key-doctor.js.map +1 -0
- package/dist/lib/key-locations.d.ts +120 -0
- package/dist/lib/key-locations.js +326 -0
- package/dist/lib/key-locations.js.map +1 -0
- package/dist/lib/key-source.d.ts +66 -0
- package/dist/lib/key-source.js +106 -0
- package/dist/lib/key-source.js.map +1 -0
- package/dist/lib/key-state.d.ts +70 -0
- package/dist/lib/key-state.js +115 -0
- package/dist/lib/key-state.js.map +1 -0
- package/dist/lib/kind-package.d.ts +181 -0
- package/dist/lib/kind-package.js +679 -0
- package/dist/lib/kind-package.js.map +1 -0
- package/dist/lib/knowledge-title.d.ts +24 -0
- package/dist/lib/knowledge-title.js +46 -0
- package/dist/lib/knowledge-title.js.map +1 -0
- package/dist/lib/knowledge-title.test.d.ts +1 -0
- package/dist/lib/knowledge-title.test.js +34 -0
- package/dist/lib/knowledge-title.test.js.map +1 -0
- package/dist/lib/machine-instance.d.ts +64 -0
- package/dist/lib/machine-instance.js +142 -0
- package/dist/lib/machine-instance.js.map +1 -0
- package/dist/lib/mcp-bridge.d.ts +42 -0
- package/dist/lib/mcp-bridge.js +147 -0
- package/dist/lib/mcp-bridge.js.map +1 -0
- package/dist/lib/mcp-bridge.test.d.ts +1 -0
- package/dist/lib/mcp-bridge.test.js +90 -0
- package/dist/lib/mcp-bridge.test.js.map +1 -0
- package/dist/lib/mcp-call.d.ts +30 -0
- package/dist/lib/mcp-call.js +107 -0
- package/dist/lib/mcp-call.js.map +1 -0
- package/dist/lib/mcp-snippets.d.ts +53 -0
- package/dist/lib/mcp-snippets.js +82 -0
- package/dist/lib/mcp-snippets.js.map +1 -0
- package/dist/lib/next-steps.d.ts +8 -0
- package/dist/lib/next-steps.js +31 -1
- package/dist/lib/next-steps.js.map +1 -1
- package/dist/lib/package-wire.d.ts +179 -0
- package/dist/lib/package-wire.js +68 -0
- package/dist/lib/package-wire.js.map +1 -0
- package/dist/lib/pair-link.d.ts +38 -0
- package/dist/lib/pair-link.js +68 -0
- package/dist/lib/pair-link.js.map +1 -0
- package/dist/lib/pod-wide.d.ts +40 -0
- package/dist/lib/pod-wide.js +46 -0
- package/dist/lib/pod-wide.js.map +1 -0
- package/dist/lib/pod.d.ts +119 -115
- package/dist/lib/pod.js +206 -412
- package/dist/lib/pod.js.map +1 -1
- package/dist/lib/project-ref.d.ts +9 -3
- package/dist/lib/project-ref.js +10 -4
- package/dist/lib/project-ref.js.map +1 -1
- package/dist/lib/review-credential.d.ts +119 -0
- package/dist/lib/review-credential.js +147 -0
- package/dist/lib/review-credential.js.map +1 -0
- package/dist/lib/safe-open.d.ts +49 -0
- package/dist/lib/safe-open.js +119 -0
- package/dist/lib/safe-open.js.map +1 -0
- package/dist/lib/session-lens.d.ts +40 -1
- package/dist/lib/session-lens.js +54 -4
- package/dist/lib/session-lens.js.map +1 -1
- package/dist/lib/superwhisper-import.d.ts +8 -0
- package/dist/lib/superwhisper-import.js +14 -7
- package/dist/lib/superwhisper-import.js.map +1 -1
- package/dist/lib/superwhisper-title.test.d.ts +1 -0
- package/dist/lib/superwhisper-title.test.js +18 -0
- package/dist/lib/superwhisper-title.test.js.map +1 -0
- package/dist/lib/targets.d.ts +134 -10
- package/dist/lib/targets.js +962 -258
- package/dist/lib/targets.js.map +1 -1
- package/dist/lib/template-file.d.ts +78 -0
- package/dist/lib/template-file.js +140 -0
- package/dist/lib/template-file.js.map +1 -0
- package/dist/lib/template-lifecycle.d.ts +217 -0
- package/dist/lib/template-lifecycle.js +433 -0
- package/dist/lib/template-lifecycle.js.map +1 -0
- package/dist/lib/template-steps.d.ts +88 -0
- package/dist/lib/template-steps.js +118 -0
- package/dist/lib/template-steps.js.map +1 -0
- package/dist/lib/time-ago.d.ts +2 -0
- package/dist/lib/time-ago.js +15 -0
- package/dist/lib/time-ago.js.map +1 -0
- package/dist/utils/logger.js +5 -2
- package/dist/utils/logger.js.map +1 -1
- package/package.json +25 -18
- package/skills/README.md +12 -10
- package/skills/agent-os/SKILL.md +220 -99
- package/skills/manifest.json +2 -2
- package/skills/onboard/SKILL.md +61 -25
- package/skills/synap/README.md +5 -8
- package/skills/synap/SKILL.md +1185 -301
- package/skills/synap/_order.txt +7 -0
- package/skills/synap/_skill.yaml +1 -1
- package/skills/synap/automations.md +15 -0
- package/skills/synap/capture.md +54 -151
- package/skills/synap/cli-operations.md +24 -18
- package/skills/synap/common-mistakes.md +1 -0
- package/skills/synap/concepts.md +28 -0
- package/skills/synap/content-os.md +92 -0
- package/skills/synap/creative-loop.md +15 -10
- package/skills/synap/crm.md +21 -52
- package/skills/synap/digesting-a-channel.md +25 -0
- package/skills/synap/document-embeds.md +141 -47
- package/skills/synap/escalation-ladder.md +20 -10
- package/skills/synap/focus-sessions.md +47 -21
- package/skills/synap/from-intent.md +142 -0
- package/skills/synap/governance-rules.md +4 -4
- package/skills/synap/governance.md +10 -7
- package/skills/synap/hand-off.md +33 -0
- package/skills/synap/inline-patterns.md +15 -7
- package/skills/synap/lenses.md +33 -15
- package/skills/synap/linking.md +41 -40
- package/skills/synap/mcp-remote-servers.md +64 -0
- package/skills/synap/mental-model.md +24 -22
- package/skills/synap/multi-entity-capture.md +6 -4
- package/skills/synap/operating-mode-rules.md +1 -1
- package/skills/synap/processes.md +118 -0
- package/skills/synap/quick-reference.md +5 -1
- package/skills/synap/reading.md +3 -1
- package/skills/synap/reflexes.md +12 -42
- package/skills/synap/viewframe-cells.md +38 -5
- package/skills/synap/work-flow.md +1 -1
- package/skills/synap/work-model.md +96 -0
- package/skills/synap/worked-examples.md +21 -1
- package/skills/synap/workspace-design.md +14 -10
- package/skills/synap/writes.md +165 -35
- package/skills/synap/writing-titles.md +63 -0
- package/skills/synap-app/SKILL.md +319 -0
- package/skills/synap-app/_order.txt +5 -0
- package/skills/synap-app/_skill.yaml +20 -0
- package/skills/synap-app/creating-the-base-app.md +64 -0
- package/skills/synap-app/governance.md +42 -0
- package/skills/synap-app/install-commands.md +55 -0
- package/skills/synap-app/overview.md +50 -0
- package/skills/synap-app/using-the-sdk.md +73 -0
- package/skills/synap-design/SKILL.md +474 -0
- package/skills/synap-design/_order.txt +4 -0
- package/skills/synap-design/_skill.yaml +21 -0
- package/skills/synap-design/loop.md +134 -0
- package/skills/synap-design/overview.md +44 -0
- package/skills/synap-design/principles.md +130 -0
- package/skills/synap-design/system.md +133 -0
- package/skills/synap-market/SKILL.md +625 -0
- package/skills/synap-market/_order.txt +5 -0
- package/skills/synap-market/_skill.yaml +24 -0
- package/skills/synap-market/author-and-publish.md +212 -0
- package/skills/synap-market/discover-and-fetch.md +59 -0
- package/skills/synap-market/governance-and-catalog.md +63 -0
- package/skills/synap-market/install.md +133 -0
- package/skills/synap-market/overview.md +119 -0
- package/skills/synap-schema/SKILL.md +172 -10
- package/skills/synap-schema/_order.txt +2 -0
- package/skills/synap-schema/_skill.yaml +4 -2
- package/skills/synap-schema/common-mistakes.md +4 -2
- package/skills/synap-schema/discover-profiles.md +1 -1
- package/skills/synap-schema/entity-scope.md +15 -3
- package/skills/synap-schema/extend-first.md +29 -0
- package/skills/synap-schema/extend-vs-create.md +1 -1
- package/skills/synap-schema/intent-vocabulary-ssot.md +111 -0
- package/skills/synap-schema/property-types.md +1 -1
- package/skills/synap-schema/relations.md +1 -1
- package/skills/synap-ui/SKILL.md +19 -12
- package/skills/synap-ui/_skill.yaml +1 -1
- package/skills/synap-ui/ai-companion.md +1 -1
- package/skills/synap-ui/arranging-bento.md +2 -0
- package/skills/synap-ui/bento-layouts.md +10 -10
- package/skills/synap-ui/bento-recipes.md +313 -543
- package/skills/synap-ui/four-builds.md +5 -0
- package/skills/synap-ui/view-types.md +2 -2
- package/skills/synap-ui/widget-catalog.md +22 -278
- package/templates/automations/bookmark-enrichment.automation.json +290 -13
- package/templates/automations/url-bookmark-capture.automation.json +73 -11
- package/templates/skills/authoring-playbooks/SKILL.md +6 -3
- package/templates/skills/authoring-playbooks/reference/stages-contract.md +15 -0
- package/templates/skills/authoring-playbooks/reference/worked-example.md +3 -0
- package/dist/commands/infra.d.ts +0 -21
- package/dist/commands/infra.js +0 -262
- package/dist/commands/infra.js.map +0 -1
- package/dist/lib/browser-auth.d.ts +0 -35
- package/dist/lib/browser-auth.js +0 -170
- package/dist/lib/browser-auth.js.map +0 -1
- package/dist/lib/openclaw.d.ts +0 -32
- package/dist/lib/openclaw.js +0 -164
- package/dist/lib/openclaw.js.map +0 -1
- package/dist/lib/seed.d.ts +0 -13
- package/dist/lib/seed.js +0 -135
- package/dist/lib/seed.js.map +0 -1
package/dist/lib/hub-client.js
CHANGED
|
@@ -1,10 +1,51 @@
|
|
|
1
|
-
import { existsSync, readFileSync,
|
|
1
|
+
import { existsSync, readFileSync, unlinkSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { getActivePodConfig, getActiveWorkspaceId, getActiveProjectId, listPodProfiles, getPodOverride, getSurfaceAgentKey } from "./pod.js";
|
|
3
|
+
import { getActivePodConfig, getActiveWorkspaceId, getActiveWorkspaceIdForPod, findPodNameByUrl, getActiveProjectBinding, getActiveProjectId, listPodProfiles, getPodOverride, getSurfaceAgentKey, } from "./pod.js";
|
|
4
4
|
import { resolveAgentOverride } from "./agents-config.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { resolveActiveLensForPod, getClaudeSessionId, writeLens, clearLensField, } from "./session-lens.js";
|
|
6
|
+
import { conversationHeaders } from "./conversation-headers.js";
|
|
7
|
+
import { effectiveDirectoryLens, resolveDirectoryLensForPod, writeDirectoryLens, clearDirectoryLensField, } from "./directory-lens.js";
|
|
8
|
+
import { preferClaudeCodeSurfaceKey } from "./key-source.js";
|
|
9
|
+
import { samePodOrigin } from "./project-ref.js";
|
|
10
|
+
import { HubRestClient } from "@synap-core/hub-rest-client";
|
|
7
11
|
import { log } from "../utils/logger.js";
|
|
12
|
+
import chalk from "chalk";
|
|
13
|
+
/**
|
|
14
|
+
* Resolve a projectId safe to send to `podUrl`.
|
|
15
|
+
*
|
|
16
|
+
* Order: pod-qualified lens → directory lens → env project when co-located with
|
|
17
|
+
* this pod (trustEnvProject — same model as SYNAP_WORKSPACE_ID under env rung) →
|
|
18
|
+
* ActiveProjectBinding for this pod → legacy activeProjectId only when the
|
|
19
|
+
* active profile is this pod.
|
|
20
|
+
*
|
|
21
|
+
* Write paths should consume `resolveHubConfig().projectId` rather than bare
|
|
22
|
+
* `getActiveProjectId()` / env.
|
|
23
|
+
*/
|
|
24
|
+
function resolveProjectIdForPod(podUrl, opts) {
|
|
25
|
+
if (opts.lensProjectId)
|
|
26
|
+
return opts.lensProjectId;
|
|
27
|
+
if (opts.dirLensProjectId)
|
|
28
|
+
return opts.dirLensProjectId;
|
|
29
|
+
// Env pin co-located with env pod (connect --pin-project, Claude settings.env).
|
|
30
|
+
if (opts.trustEnvProject && opts.envProject)
|
|
31
|
+
return opts.envProject;
|
|
32
|
+
const profiles = listPodProfiles();
|
|
33
|
+
const binding = getActiveProjectBinding();
|
|
34
|
+
if (binding) {
|
|
35
|
+
const boundPod = profiles.find((x) => x.name === binding.podName);
|
|
36
|
+
if (boundPod && samePodOrigin(boundPod.config.podUrl, podUrl)) {
|
|
37
|
+
return binding.projectId;
|
|
38
|
+
}
|
|
39
|
+
// Binding is for another pod — do not send it.
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
// Legacy: activeProjectId with no binding — only when active profile is this pod.
|
|
43
|
+
const active = getActivePodConfig();
|
|
44
|
+
if (active && samePodOrigin(active.podUrl, podUrl)) {
|
|
45
|
+
return getActiveProjectId() || undefined;
|
|
46
|
+
}
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
8
49
|
/**
|
|
9
50
|
* A failed Hub call, carrying the structured facts callers need to branch on.
|
|
10
51
|
*
|
|
@@ -22,6 +63,10 @@ export class HubError extends Error {
|
|
|
22
63
|
rawBody;
|
|
23
64
|
path;
|
|
24
65
|
method;
|
|
66
|
+
/** Origin of the pod this call actually hit — for cross-pod diagnostics. */
|
|
67
|
+
podUrl;
|
|
68
|
+
/** Workspace this call actually sent — for cross-pod 403 diagnostics. */
|
|
69
|
+
workspaceId;
|
|
25
70
|
constructor(init) {
|
|
26
71
|
super(init.message);
|
|
27
72
|
this.name = "HubError";
|
|
@@ -32,6 +77,8 @@ export class HubError extends Error {
|
|
|
32
77
|
this.rawBody = init.rawBody;
|
|
33
78
|
this.path = init.path;
|
|
34
79
|
this.method = init.method;
|
|
80
|
+
this.podUrl = init.podUrl;
|
|
81
|
+
this.workspaceId = init.workspaceId;
|
|
35
82
|
}
|
|
36
83
|
}
|
|
37
84
|
/**
|
|
@@ -166,21 +213,56 @@ function readErrorBody(status, rawBody) {
|
|
|
166
213
|
const oneLine = trimmed.replace(/\s+/g, " ");
|
|
167
214
|
return { detail: oneLine.length > 200 ? `${oneLine.slice(0, 200)}…` : oneLine };
|
|
168
215
|
}
|
|
216
|
+
// tRPC batch errors are array-shaped: `[{ error: { message, code, data: { httpStatus, ... } } }]`
|
|
217
|
+
// (superjson may additionally nest the real payload one level deeper under
|
|
218
|
+
// `error.json`). Unwrap to the first entry's error object before falling
|
|
219
|
+
// through to the normal object-shaped handling below, so a tRPC failure
|
|
220
|
+
// surfaces its real `message` instead of the generic status phrase.
|
|
221
|
+
if (Array.isArray(parsed)) {
|
|
222
|
+
const first = parsed[0];
|
|
223
|
+
const err = first?.error;
|
|
224
|
+
if (err && typeof err === "object") {
|
|
225
|
+
const json = err.json;
|
|
226
|
+
parsed = json ?? err;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
else if (parsed &&
|
|
230
|
+
typeof parsed === "object" &&
|
|
231
|
+
"error" in parsed) {
|
|
232
|
+
// Single tRPC-shaped error body: `{ error: { message, ... } }` or
|
|
233
|
+
// `{ error: { json: { message, ... } } }`. Only unwrap when `error` is an
|
|
234
|
+
// OBJECT — a Hub-REST body's `error` is a plain string category
|
|
235
|
+
// (`{ error: "...", detail: "..." }`) and must fall through unchanged.
|
|
236
|
+
const errVal = parsed.error;
|
|
237
|
+
if (errVal && typeof errVal === "object") {
|
|
238
|
+
const errObj = errVal;
|
|
239
|
+
const json = errObj.json;
|
|
240
|
+
parsed = json ?? errObj;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
169
243
|
if (parsed && typeof parsed === "object") {
|
|
170
244
|
const o = parsed;
|
|
171
245
|
const code = typeof o.code === "string" ? o.code : undefined;
|
|
172
246
|
const reason = typeof o.reason === "string" ? o.reason : undefined;
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
247
|
+
const str = (v) => typeof v === "string" && v ? v : undefined;
|
|
248
|
+
// Backend error contract for the Hub package/workspace routes is
|
|
249
|
+
// `{ error: <category>, detail: <the real underlying cause> }`. The specific
|
|
250
|
+
// cause lives in `detail` — surfacing only `error` throws away the one thing
|
|
251
|
+
// that makes a 500 actionable (e.g. "profile X targets unknown profile Y").
|
|
252
|
+
// Precedence: message (tRPC-shaped) → error+detail combined → error → detail.
|
|
253
|
+
const message = str(o.message);
|
|
254
|
+
const errorField = str(o.error);
|
|
255
|
+
const detailField = str(o.detail);
|
|
256
|
+
const detail = message ??
|
|
257
|
+
(errorField && detailField
|
|
258
|
+
? `${errorField}: ${detailField}`
|
|
259
|
+
: errorField ?? detailField ?? statusPhrase(status));
|
|
178
260
|
return { detail, code, reason, parsed };
|
|
179
261
|
}
|
|
180
262
|
return { detail: statusPhrase(status), parsed };
|
|
181
263
|
}
|
|
182
264
|
/** Build the HubError for a non-ok response body. */
|
|
183
|
-
function hubError(method, path, status, rawBody, suffix) {
|
|
265
|
+
function hubError(method, path, status, rawBody, suffix, ctx) {
|
|
184
266
|
const { detail, code, reason, parsed } = readErrorBody(status, rawBody);
|
|
185
267
|
return new HubError({
|
|
186
268
|
message: `Hub ${method} ${path} failed (HTTP ${status})${suffix ? ` ${suffix}` : ""}: ${detail}`,
|
|
@@ -191,15 +273,50 @@ function hubError(method, path, status, rawBody, suffix) {
|
|
|
191
273
|
rawBody,
|
|
192
274
|
path,
|
|
193
275
|
method,
|
|
276
|
+
podUrl: ctx?.podUrl ? originOf(ctx.podUrl) : undefined,
|
|
277
|
+
workspaceId: ctx?.workspaceId,
|
|
194
278
|
});
|
|
195
279
|
}
|
|
280
|
+
/** Where to send users who hit a wall. */
|
|
281
|
+
const DISCORD_INVITE_URL = "https://discord.gg/xhRdQ7hG5h";
|
|
282
|
+
/** Wrap a URL in an OSC-8 terminal hyperlink so `label` is clickable. */
|
|
283
|
+
function osc8Link(url, label) {
|
|
284
|
+
return `\x1b]8;;${url}\x1b\\${label}\x1b]8;;\x1b\\`;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Once-per-process guard for the Discord help footer. A single CLI invocation
|
|
288
|
+
* runs one command; printing the footer at most once keeps it a footer, not a
|
|
289
|
+
* per-error/per-line spam.
|
|
290
|
+
*/
|
|
291
|
+
let _discordHelpShown = false;
|
|
292
|
+
/**
|
|
293
|
+
* Append a one-line "need help?" footer with a clickable Discord invite. Prints
|
|
294
|
+
* to stderr (same stream as the error it follows, so `2>/dev/null` hides both)
|
|
295
|
+
* and only once per process. The invite is an OSC-8 hyperlink on an interactive
|
|
296
|
+
* terminal, a plain URL when the stream is redirected/piped.
|
|
297
|
+
*/
|
|
298
|
+
export function discordHelpLine() {
|
|
299
|
+
if (_discordHelpShown)
|
|
300
|
+
return;
|
|
301
|
+
_discordHelpShown = true;
|
|
302
|
+
const link = process.stderr.isTTY
|
|
303
|
+
? osc8Link(DISCORD_INVITE_URL, DISCORD_INVITE_URL)
|
|
304
|
+
: DISCORD_INVITE_URL;
|
|
305
|
+
console.error(chalk.dim(` Need help? Join our Discord: ${link}`));
|
|
306
|
+
}
|
|
196
307
|
/**
|
|
197
308
|
* Print a failed Hub call: its humanized message plus a hint for the classes
|
|
198
309
|
* where the cause is not obvious from the message alone. Lives here rather than
|
|
199
310
|
* in utils/logger so the logger stays domain-agnostic and the taxonomy and its
|
|
200
|
-
* presentation sit in one file.
|
|
311
|
+
* presentation sit in one file. Always closes with the one-time Discord footer.
|
|
201
312
|
*/
|
|
202
313
|
export function renderHubError(err) {
|
|
314
|
+
renderHubErrorBody(err);
|
|
315
|
+
// ONE central place every failed Hub command flows through — append the help
|
|
316
|
+
// footer here so it prints once per failed command (guarded), not per line.
|
|
317
|
+
discordHelpLine();
|
|
318
|
+
}
|
|
319
|
+
function renderHubErrorBody(err) {
|
|
203
320
|
// Hints use log.hint (stderr), not log.dim (stdout): a hint belongs to the
|
|
204
321
|
// error it explains, and splitting one message across two streams means
|
|
205
322
|
// `2>/dev/null` shows a bare hint with no error above it.
|
|
@@ -212,15 +329,112 @@ export function renderHubError(err) {
|
|
|
212
329
|
log.error(err instanceof Error ? err.message : String(err));
|
|
213
330
|
return;
|
|
214
331
|
}
|
|
332
|
+
// Connection-class failure — the execute door returns 424 with a machine
|
|
333
|
+
// `errorClass` (+ `providerRef`) precisely so it SURVIVES the pod's 5xx egress
|
|
334
|
+
// sanitizer. This is client-ACTIONABLE (reconnect), not a server fault, so name
|
|
335
|
+
// the cause + the exact fix instead of the generic status hint. Mirrors the
|
|
336
|
+
// browser's P1 self-heal reconnect chip.
|
|
337
|
+
const errBody = err.body;
|
|
338
|
+
const errorClass = errBody?.errorClass;
|
|
339
|
+
if (errorClass === "auth" || errorClass === "no_connection") {
|
|
340
|
+
const provider = errBody?.providerRef
|
|
341
|
+
? chalk.bold(errBody.providerRef)
|
|
342
|
+
: "this capability's";
|
|
343
|
+
log.error(`The ${provider} connection needs reconnecting — its access has expired or was revoked.`);
|
|
344
|
+
log.hint('Reconnect it: synap cap connect "<capability>" --reconnect');
|
|
345
|
+
log.hint("Find the capability name with: synap cap list");
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
if (errorClass === "target_missing") {
|
|
349
|
+
log.error(err.message);
|
|
350
|
+
log.hint("The workspace or target this run needs no longer exists on this pod. Run: synap orient");
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
// SETUP-REQUIRED failure — the APPLY door (`POST /capabilities/apply`) returns
|
|
354
|
+
// a DUCK-TYPED body (412 for `no_connection`, 400 for `missing_field`) with
|
|
355
|
+
// `failureClass` (+ `missingFields` / `connection`) so an install that needs a
|
|
356
|
+
// human before it can apply survives as an actionable message. This is a
|
|
357
|
+
// client-ACTIONABLE, NON-retryable state — name the missing step instead of the
|
|
358
|
+
// generic status phrase below (a bare 412/400 tells the user nothing to do).
|
|
359
|
+
const setupBody = err.body;
|
|
360
|
+
const failureClass = setupBody?.failureClass;
|
|
361
|
+
if (failureClass === "no_connection") {
|
|
362
|
+
const provider = setupBody?.connection?.provider;
|
|
363
|
+
log.error(provider
|
|
364
|
+
? `The "${provider}" provider isn't connected yet — connect it first (synap cap connect "${provider}").`
|
|
365
|
+
: 'A required provider isn\'t connected yet — connect it first (synap cap connect "<provider>").');
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
if (failureClass === "missing_field") {
|
|
369
|
+
const missingFields = Array.isArray(setupBody?.missingFields)
|
|
370
|
+
? setupBody.missingFields.filter((field) => typeof field === "string")
|
|
371
|
+
: [];
|
|
372
|
+
log.error(missingFields.length > 0
|
|
373
|
+
? `Missing required value(s): ${missingFields.join(", ")}`
|
|
374
|
+
: "Missing required value(s).");
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
215
377
|
log.error(err.message);
|
|
378
|
+
// A 500 carries an `errorId` (the egress sanitizer logs the real cause under
|
|
379
|
+
// it server-side) — surface it so "quote errorId when reporting it" is
|
|
380
|
+
// actionable rather than a dead end.
|
|
381
|
+
const errorId = typeof err.body?.errorId === "string"
|
|
382
|
+
? err.body.errorId
|
|
383
|
+
: undefined;
|
|
384
|
+
if (err.status >= 500 && errorId) {
|
|
385
|
+
log.hint(`errorId: ${errorId}`);
|
|
386
|
+
}
|
|
387
|
+
// A 500 whose body carried a specific cause (detail/message) is already in
|
|
388
|
+
// `err.message` above — do NOT then claim "internal error, not in this
|
|
389
|
+
// command": a workspace/template creation failure is often an actionable
|
|
390
|
+
// template or data problem. Only add the generic pod-fault hint when the body
|
|
391
|
+
// gave nothing better than the status phrase.
|
|
392
|
+
const hasSpecificCause = !!err.rawBody && !err.message.endsWith("the pod hit an internal error");
|
|
393
|
+
// A 403/message naming a workspace: the real cause is almost always "that
|
|
394
|
+
// workspace doesn't exist or isn't accessible ON THIS POD" (a stale saved
|
|
395
|
+
// workspace id after a pod switch), not a scopes problem — name it + the
|
|
396
|
+
// exact next command instead of the generic "credentials" hint.
|
|
397
|
+
const namesWorkspace = /workspace/i.test(err.message);
|
|
216
398
|
if (err.status >= 500) {
|
|
217
|
-
|
|
399
|
+
if (!hasSpecificCause) {
|
|
400
|
+
log.hint("The pod hit an internal error — check the pod logs for the stack trace.");
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
else if (err.status === 403 && namesWorkspace) {
|
|
404
|
+
// Name the exact workspace + pod this call used: the usual cause is a global
|
|
405
|
+
// active workspace left over from ANOTHER pod (a stale `synap use` after a
|
|
406
|
+
// pod switch) being sent to a pod it doesn't belong to.
|
|
407
|
+
if (err.workspaceId && err.podUrl) {
|
|
408
|
+
// State the FACT (what was sent where), then the two causes WITHOUT
|
|
409
|
+
// picking one — this code cannot tell them apart, and asserting the
|
|
410
|
+
// cross-pod story is wrong whenever the saved workspace simply no longer
|
|
411
|
+
// exists on its own pod (dogfood: `pods.team.workspaceId` pointed at a
|
|
412
|
+
// deleted workspace; the hint blamed a pod mix-up that had not happened).
|
|
413
|
+
// `synap doctor` is the only thing here that actually CHECKS, so it leads.
|
|
414
|
+
log.hint(`Sent workspace ${err.workspaceId} to ${err.podUrl}.`);
|
|
415
|
+
log.hint("That workspace is not accessible on this pod — either it lives on a different pod, or it no longer exists here.");
|
|
416
|
+
log.hint("Run: synap doctor (checks which of the two it is), then: synap orient (see valid workspaces) and synap use <id>.");
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
log.hint("Workspace not found or not accessible on this pod — run: synap orient (see valid workspaces), or: synap doctor");
|
|
420
|
+
}
|
|
218
421
|
}
|
|
219
422
|
else if (err.status === 401 || err.status === 403) {
|
|
220
|
-
log.hint("The pod rejected this key's credentials or its scopes for this operation.");
|
|
423
|
+
log.hint("The pod rejected this key's credentials or its scopes for this operation. Run: synap doctor");
|
|
221
424
|
}
|
|
222
425
|
else if (err.status === 404) {
|
|
223
|
-
|
|
426
|
+
// A structured JSON body (err.body set) means THIS app answered — the
|
|
427
|
+
// endpoint just doesn't exist here (older build / wrong record). A body
|
|
428
|
+
// that failed to parse as JSON (readErrorBody's plain-text fallback — a
|
|
429
|
+
// proxy default page, an HTML error, a completely different service)
|
|
430
|
+
// means the request never reached this app at all: the classic
|
|
431
|
+
// stale/migrated-domain failure, not an old build.
|
|
432
|
+
if (err.body === undefined && err.rawBody.trim()) {
|
|
433
|
+
log.hint("This host doesn't look like a Synap pod — your saved pod URL may be stale. Run: synap doctor");
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
log.hint("The pod has no such endpoint or record — it may be running an older build, or the id doesn't exist. Run: synap doctor");
|
|
437
|
+
}
|
|
224
438
|
}
|
|
225
439
|
}
|
|
226
440
|
const userIdCache = new Map();
|
|
@@ -250,14 +464,21 @@ export async function resolveHubConfig(opts) {
|
|
|
250
464
|
// Beats the env vars deliberately — that is the whole point of the flag.
|
|
251
465
|
const podOverride = getPodOverride();
|
|
252
466
|
if (podOverride) {
|
|
467
|
+
// Prefer cross-pod-safe resolution (binding + "not another pod's default")
|
|
468
|
+
// over the profile's baked `workspaceId`, which can drift to a foreign UUID
|
|
469
|
+
// after dual-pod work (dogfood: perso profile held team CRM ws → 403).
|
|
470
|
+
const overrideName = findPodNameByUrl(podOverride.podUrl);
|
|
471
|
+
const safeWs = getActiveWorkspaceIdForPod(overrideName) ||
|
|
472
|
+
// Only fall back to profile default when it isn't known to belong elsewhere
|
|
473
|
+
// (getActiveWorkspaceIdForPod already applied that rule against the profile).
|
|
474
|
+
undefined;
|
|
253
475
|
return {
|
|
254
476
|
podUrl: podOverride.podUrl,
|
|
255
477
|
apiKey: podOverride.hubApiKey,
|
|
256
478
|
// A profile's agentUserId may be empty; "cli" makes resolveUserId fetch /users/me.
|
|
257
479
|
userId: podOverride.agentUserId || "cli",
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
workspaceId: podOverride.workspaceId || undefined,
|
|
480
|
+
workspaceId: safeWs,
|
|
481
|
+
projectId: resolveProjectIdForPod(podOverride.podUrl, {}),
|
|
261
482
|
};
|
|
262
483
|
}
|
|
263
484
|
// 1. Explicit flags (escape hatch)
|
|
@@ -278,7 +499,10 @@ export async function resolveHubConfig(opts) {
|
|
|
278
499
|
return {
|
|
279
500
|
podUrl,
|
|
280
501
|
apiKey: agentOverride.apiKey,
|
|
281
|
-
|
|
502
|
+
// The AGENT's own id, recorded with its key. Never the pod profile name
|
|
503
|
+
// (it was, so `synap worker` never recognised its own room posts and
|
|
504
|
+
// resumed itself on them). Unrecorded → "cli": resolveUserId asks the pod.
|
|
505
|
+
userId: agentOverride.agentUserId || "cli",
|
|
282
506
|
scopes: envScopes ? envScopes.split(",") : undefined,
|
|
283
507
|
};
|
|
284
508
|
}
|
|
@@ -297,24 +521,51 @@ export async function resolveHubConfig(opts) {
|
|
|
297
521
|
// the pinned key so a leaked/inherited env var can't silently redefine
|
|
298
522
|
// which agent this session acts as. Conservative: only applies here, in
|
|
299
523
|
// step 3 — steps 0-2 (--pod, explicit flags, SYNAP_AGENT) still win
|
|
300
|
-
// outright, untouched.
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
524
|
+
// outright, untouched. Preference logic lives in key-source.ts so whoami
|
|
525
|
+
// reports the same rule (no drift).
|
|
526
|
+
const preferred = preferClaudeCodeSurfaceKey({
|
|
527
|
+
envPod,
|
|
528
|
+
envKey,
|
|
529
|
+
envUser,
|
|
530
|
+
surface: getSurfaceAgentKey("claude-code", envPod),
|
|
531
|
+
});
|
|
532
|
+
if (preferred.usedSurface) {
|
|
305
533
|
process.stderr.write(`[synap] warning: ambient SYNAP_HUB_API_KEY does not match the claude-code surface key pinned to ${envPod} — using the pinned key instead of the inherited one.\n`);
|
|
306
|
-
apiKey = surfaceKey.hubApiKey;
|
|
307
|
-
userId = surfaceKey.agentUserId || envUser;
|
|
308
534
|
}
|
|
535
|
+
const apiKey = preferred.apiKey;
|
|
536
|
+
const userId = preferred.userId;
|
|
537
|
+
// Per-Claude-session lens wins over the global env var, so concurrent
|
|
538
|
+
// sessions can each be scoped to a different workspace — but ONLY when the
|
|
539
|
+
// lens belongs to THIS pod. A lens stamped with another pod is ignored so
|
|
540
|
+
// the pod-qualified fallback below applies (the "Access denied to
|
|
541
|
+
// workspace" 403 after `synap pods use`).
|
|
542
|
+
const lens = resolveActiveLensForPod(envPod);
|
|
543
|
+
// …and the per-DIRECTORY lens sits directly beneath it: the durable default
|
|
544
|
+
// for this working tree, overridden by the session lens when one is set.
|
|
545
|
+
// Pod-qualified through the same door, for the same reason — a directory
|
|
546
|
+
// lens outlives many `synap pods use` switches, so it is MORE exposed to
|
|
547
|
+
// the cross-pod 403 than the session lens is.
|
|
548
|
+
const dirLens = resolveDirectoryLensForPod(envPod)?.lens;
|
|
309
549
|
return {
|
|
310
550
|
podUrl: envPod,
|
|
311
551
|
apiKey,
|
|
312
552
|
userId,
|
|
313
|
-
//
|
|
314
|
-
//
|
|
315
|
-
workspaceId:
|
|
316
|
-
|
|
317
|
-
|
|
553
|
+
// The global-config fallback is resolved AGAINST THIS POD (envPod) —
|
|
554
|
+
// never send a workspace that belongs to a different pod.
|
|
555
|
+
workspaceId: lens?.workspaceId ||
|
|
556
|
+
dirLens?.workspaceId ||
|
|
557
|
+
envWorkspace ||
|
|
558
|
+
getActiveWorkspaceIdForPod(findPodNameByUrl(envPod)),
|
|
559
|
+
// Project is a peer lens. Lens/dir-lens are already pod-qualified;
|
|
560
|
+
// env/global active project only when ActiveProjectBinding matches this pod.
|
|
561
|
+
// Write paths must use this resolveHubConfig output — never bare
|
|
562
|
+
// getActiveProjectId() / SYNAP_PROJECT_ID without the pod check.
|
|
563
|
+
projectId: resolveProjectIdForPod(envPod, {
|
|
564
|
+
lensProjectId: lens?.projectId,
|
|
565
|
+
dirLensProjectId: dirLens?.projectId,
|
|
566
|
+
envProject,
|
|
567
|
+
trustEnvProject: true,
|
|
568
|
+
}),
|
|
318
569
|
scopes: envScopes ? envScopes.split(",") : undefined,
|
|
319
570
|
};
|
|
320
571
|
}
|
|
@@ -322,40 +573,136 @@ export async function resolveHubConfig(opts) {
|
|
|
322
573
|
const config = getActivePodConfig();
|
|
323
574
|
if (!config)
|
|
324
575
|
throw new Error("No pod configured. Run: synap pods add");
|
|
576
|
+
// Same pod-qualification as step 3: a lens stamped with another pod must not
|
|
577
|
+
// short-circuit `getActiveWorkspaceId()`, which IS cross-pod-safe.
|
|
578
|
+
const activeLens = resolveActiveLensForPod(config.podUrl);
|
|
579
|
+
const activeDirLens = resolveDirectoryLensForPod(config.podUrl)?.lens;
|
|
325
580
|
return {
|
|
326
581
|
podUrl: config.podUrl,
|
|
327
582
|
apiKey: config.hubApiKey,
|
|
328
583
|
userId: config.agentUserId,
|
|
329
|
-
|
|
330
|
-
|
|
584
|
+
// session lens › directory lens › global config. (No env rung here: this
|
|
585
|
+
// branch is only reached when SYNAP_POD_URL is unset.)
|
|
586
|
+
workspaceId: activeLens?.workspaceId || activeDirLens?.workspaceId || getActiveWorkspaceId(),
|
|
587
|
+
// Project: same pod-qualification as step 3 (no bare getActiveProjectId).
|
|
588
|
+
projectId: resolveProjectIdForPod(config.podUrl, {
|
|
589
|
+
lensProjectId: activeLens?.projectId,
|
|
590
|
+
dirLensProjectId: activeDirLens?.projectId,
|
|
591
|
+
}),
|
|
331
592
|
};
|
|
332
593
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
594
|
+
// ─── Active focus session ─────────────────────────────────────────────────────
|
|
595
|
+
//
|
|
596
|
+
// ONE store, two rungs of the SAME lens mechanism:
|
|
597
|
+
//
|
|
598
|
+
// env SYNAP_SESSION_ID › session lens (per Claude tab) › directory lens
|
|
599
|
+
//
|
|
600
|
+
// The directory rung supplies a session only OUTSIDE a Claude Code conversation
|
|
601
|
+
// (`effectiveDirectoryLens`): inside one, the session belongs to the
|
|
602
|
+
// conversation, never to the working tree.
|
|
603
|
+
//
|
|
604
|
+
// The retired legacy store was a per-CWD `.synap-session` file that the session
|
|
605
|
+
// lens already silently shadowed — two concurrent "current session" stores for
|
|
606
|
+
// one concept. It is now read exactly once, to migrate (below).
|
|
607
|
+
/** Legacy per-CWD session file — RETIRED as a store; read once to migrate. */
|
|
608
|
+
const LEGACY_SESSION_FILE = ".synap-session";
|
|
609
|
+
let legacySessionMigrationChecked = false;
|
|
610
|
+
/**
|
|
611
|
+
* One-time migration of a pre-existing `.synap-session` into the DIRECTORY lens.
|
|
612
|
+
*
|
|
613
|
+
* COMPAT DECISION — read-and-migrate, not a permanent read-only fallback. A
|
|
614
|
+
* fallback would keep two stores alive indefinitely, which is exactly the
|
|
615
|
+
* divergence being removed. Migrating is faithful: `.synap-session` was
|
|
616
|
+
* per-working-tree and durable, and the directory lens has precisely that
|
|
617
|
+
* lifetime and scope — so an already-attached session survives the upgrade for
|
|
618
|
+
* every terminal in that tree. The file is deleted afterwards so it can never
|
|
619
|
+
* shadow (or be shadowed by) the lens again.
|
|
620
|
+
*
|
|
621
|
+
* No `podUrl` is stamped: the legacy file carried no pod, and stamping one here
|
|
622
|
+
* would falsely pod-qualify whatever workspace/project the lens already holds.
|
|
623
|
+
*/
|
|
624
|
+
function migrateLegacySessionFile() {
|
|
625
|
+
if (legacySessionMigrationChecked)
|
|
626
|
+
return;
|
|
627
|
+
legacySessionMigrationChecked = true;
|
|
628
|
+
const filePath = join(process.cwd(), LEGACY_SESSION_FILE);
|
|
629
|
+
if (!existsSync(filePath))
|
|
630
|
+
return;
|
|
631
|
+
let id;
|
|
632
|
+
try {
|
|
633
|
+
id = readFileSync(filePath, "utf8").trim() || undefined;
|
|
634
|
+
}
|
|
635
|
+
catch {
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
try {
|
|
639
|
+
if (id)
|
|
640
|
+
writeDirectoryLens({ focusSessionId: id });
|
|
641
|
+
unlinkSync(filePath);
|
|
642
|
+
}
|
|
643
|
+
catch {
|
|
644
|
+
return; // best-effort — a read-only tree simply keeps the stale file
|
|
645
|
+
}
|
|
646
|
+
process.stderr.write(`[synap] .synap-session is deprecated${id ? ` — session ${id.slice(0, 8)}… migrated into .synap/lens.json` : " — removed (empty)"}.\n`);
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* The focus session every hub call should be tagged with.
|
|
650
|
+
*
|
|
651
|
+
* Both lens rungs are pod-qualified: a focus-session id is POD-LOCAL, so a lens
|
|
652
|
+
* stamped with another pod must not leak into `X-Session-Id`. (Lenses written
|
|
653
|
+
* before the stamp existed have no `podUrl` and match leniently — see
|
|
654
|
+
* `lensMatchesPod`.)
|
|
655
|
+
*/
|
|
656
|
+
export function resolveActiveSessionId(podUrl) {
|
|
336
657
|
const fromEnv = process.env.SYNAP_SESSION_ID;
|
|
337
658
|
if (fromEnv)
|
|
338
659
|
return fromEnv;
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
660
|
+
migrateLegacySessionFile();
|
|
661
|
+
const fromSessionLens = resolveActiveLensForPod(podUrl)?.focusSessionId;
|
|
662
|
+
if (fromSessionLens)
|
|
663
|
+
return fromSessionLens;
|
|
664
|
+
const dir = resolveDirectoryLensForPod(podUrl);
|
|
665
|
+
return dir ? effectiveDirectoryLens(dir.lens).focusSessionId : undefined;
|
|
344
666
|
}
|
|
345
|
-
/**
|
|
346
|
-
|
|
347
|
-
|
|
667
|
+
/**
|
|
668
|
+
* Attach a focus session to this terminal.
|
|
669
|
+
*
|
|
670
|
+
* Inside Claude Code the SESSION lens wins — per-tab isolation is the point
|
|
671
|
+
* (tab A on session X, tab B on session Y, same directory). Outside it there is
|
|
672
|
+
* no `CLAUDE_CODE_SESSION_ID` to key on, so the DIRECTORY lens is the
|
|
673
|
+
* legitimate fallback: the same per-working-tree scope the retired
|
|
674
|
+
* `.synap-session` had. `attach` therefore never silently no-ops.
|
|
675
|
+
*/
|
|
676
|
+
export function attachActiveSessionId(sessionId) {
|
|
677
|
+
const claudeSession = getClaudeSessionId();
|
|
678
|
+
if (claudeSession) {
|
|
679
|
+
writeLens(claudeSession, { focusSessionId: sessionId });
|
|
680
|
+
return "session-lens";
|
|
681
|
+
}
|
|
682
|
+
writeDirectoryLens({ focusSessionId: sessionId });
|
|
683
|
+
return "directory-lens";
|
|
348
684
|
}
|
|
349
|
-
/**
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
685
|
+
/**
|
|
686
|
+
* Detach — clear the focus session from BOTH lens rungs, and return whatever
|
|
687
|
+
* was active. Clearing only the top rung would "detach" into the lower one,
|
|
688
|
+
* which reads as the command failing.
|
|
689
|
+
*/
|
|
690
|
+
export function detachActiveSessionId(podUrl) {
|
|
691
|
+
const previous = resolveActiveSessionId(podUrl);
|
|
692
|
+
const claudeSession = getClaudeSessionId();
|
|
693
|
+
if (claudeSession)
|
|
694
|
+
clearLensField(claudeSession, "focusSessionId");
|
|
695
|
+
clearDirectoryLensField("focusSessionId");
|
|
696
|
+
return previous;
|
|
354
697
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
698
|
+
/**
|
|
699
|
+
* Per-request headers: the focus session this terminal is attached to, and the
|
|
700
|
+
* Claude Code conversation the call comes from (`conversation-headers.ts`) — so
|
|
701
|
+
* the work it lands can be traced back to, and reopened in, that conversation.
|
|
702
|
+
*/
|
|
703
|
+
function sessionHeaders(cfg) {
|
|
704
|
+
const id = resolveActiveSessionId(cfg?.podUrl);
|
|
705
|
+
return { ...conversationHeaders(getClaudeSessionId()), ...(id ? { "X-Session-Id": id } : {}) };
|
|
359
706
|
}
|
|
360
707
|
/** Max 429 retries for bulk import (each waits up to the rate-limit window). */
|
|
361
708
|
const HUB_429_MAX_RETRIES = 12;
|
|
@@ -464,15 +811,26 @@ export async function hubGet(path, params, cfg) {
|
|
|
464
811
|
url.searchParams.set(k, String(v));
|
|
465
812
|
}
|
|
466
813
|
const res = await hubFetchWithRetry(url.toString(), {
|
|
467
|
-
headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders() },
|
|
814
|
+
headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders(cfg) },
|
|
468
815
|
signal: AbortSignal.timeout(15_000),
|
|
469
816
|
}, `GET ${path}`, 15_000);
|
|
470
817
|
if (!res.ok) {
|
|
471
818
|
const body = await res.text().catch(() => "");
|
|
472
|
-
throw hubError("GET", path, res.status, body);
|
|
819
|
+
throw hubError("GET", path, res.status, body, undefined, cfg);
|
|
473
820
|
}
|
|
474
821
|
return res.json();
|
|
475
822
|
}
|
|
823
|
+
/**
|
|
824
|
+
* The workspace a failing request ACTUALLY carried. A caller may override the
|
|
825
|
+
* configured lens per-command (e.g. `--workspace <id>`), and naming the config's
|
|
826
|
+
* workspace in that error sends the reader chasing the wrong id.
|
|
827
|
+
*/
|
|
828
|
+
function errorWorkspace(body, cfg) {
|
|
829
|
+
const sent = body && typeof body === "object" && "workspaceId" in body
|
|
830
|
+
? body.workspaceId
|
|
831
|
+
: undefined;
|
|
832
|
+
return typeof sent === "string" && sent.length > 0 ? sent : cfg.workspaceId;
|
|
833
|
+
}
|
|
476
834
|
export async function hubPost(path, body, cfg,
|
|
477
835
|
// Default 15s suits CRUD doors. Capability runs invoke external provider APIs
|
|
478
836
|
// (often several sequential calls) and legitimately take longer — callers
|
|
@@ -483,14 +841,17 @@ timeoutMs = 15_000) {
|
|
|
483
841
|
headers: {
|
|
484
842
|
Authorization: `Bearer ${cfg.apiKey}`,
|
|
485
843
|
"Content-Type": "application/json",
|
|
486
|
-
...sessionHeaders(),
|
|
844
|
+
...sessionHeaders(cfg),
|
|
487
845
|
},
|
|
488
846
|
body: JSON.stringify(body),
|
|
489
847
|
signal: AbortSignal.timeout(timeoutMs),
|
|
490
848
|
}, `POST ${path}`, timeoutMs);
|
|
491
849
|
if (!res.ok) {
|
|
492
850
|
const bodyText = await res.text().catch(() => "");
|
|
493
|
-
throw hubError("POST", path, res.status, bodyText
|
|
851
|
+
throw hubError("POST", path, res.status, bodyText, undefined, {
|
|
852
|
+
podUrl: cfg.podUrl,
|
|
853
|
+
workspaceId: errorWorkspace(body, cfg),
|
|
854
|
+
});
|
|
494
855
|
}
|
|
495
856
|
return res.json();
|
|
496
857
|
}
|
|
@@ -504,13 +865,13 @@ timeoutMs = 15_000) {
|
|
|
504
865
|
export async function hubPostMultipart(path, form, cfg, timeoutMs = 120_000) {
|
|
505
866
|
const res = await hubFetchWithRetry(`${cfg.podUrl}/api/hub${path}`, {
|
|
506
867
|
method: "POST",
|
|
507
|
-
headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders() },
|
|
868
|
+
headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders(cfg) },
|
|
508
869
|
body: form,
|
|
509
870
|
signal: AbortSignal.timeout(timeoutMs),
|
|
510
871
|
}, `POST multipart ${path}`, timeoutMs);
|
|
511
872
|
if (!res.ok) {
|
|
512
873
|
const bodyText = await res.text().catch(() => "");
|
|
513
|
-
throw hubError("POST multipart", path, res.status, bodyText);
|
|
874
|
+
throw hubError("POST multipart", path, res.status, bodyText, undefined, cfg);
|
|
514
875
|
}
|
|
515
876
|
return res.json();
|
|
516
877
|
}
|
|
@@ -528,7 +889,7 @@ export async function hubPostMultipartRetryable(path, buildForm, cfg, timeoutMs
|
|
|
528
889
|
try {
|
|
529
890
|
res = await fetch(url, {
|
|
530
891
|
method: "POST",
|
|
531
|
-
headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders() },
|
|
892
|
+
headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders(cfg) },
|
|
532
893
|
body: form,
|
|
533
894
|
signal: AbortSignal.timeout(timeoutMs),
|
|
534
895
|
});
|
|
@@ -539,7 +900,7 @@ export async function hubPostMultipartRetryable(path, buildForm, cfg, timeoutMs
|
|
|
539
900
|
if (res.status !== 429) {
|
|
540
901
|
if (!res.ok) {
|
|
541
902
|
const bodyText = await res.text().catch(() => "");
|
|
542
|
-
throw hubError("POST multipart", path, res.status, bodyText);
|
|
903
|
+
throw hubError("POST multipart", path, res.status, bodyText, undefined, cfg);
|
|
543
904
|
}
|
|
544
905
|
return res.json();
|
|
545
906
|
}
|
|
@@ -560,7 +921,7 @@ export async function hubPatch(path, body, cfg) {
|
|
|
560
921
|
try {
|
|
561
922
|
res = await fetch(url, {
|
|
562
923
|
method: "PATCH",
|
|
563
|
-
headers: { Authorization: `Bearer ${cfg.apiKey}`, "Content-Type": "application/json", ...sessionHeaders() },
|
|
924
|
+
headers: { Authorization: `Bearer ${cfg.apiKey}`, "Content-Type": "application/json", ...sessionHeaders(cfg) },
|
|
564
925
|
body: JSON.stringify(body),
|
|
565
926
|
signal: AbortSignal.timeout(15_000),
|
|
566
927
|
});
|
|
@@ -570,7 +931,10 @@ export async function hubPatch(path, body, cfg) {
|
|
|
570
931
|
}
|
|
571
932
|
if (!res.ok) {
|
|
572
933
|
const bodyText = await res.text().catch(() => "");
|
|
573
|
-
throw hubError("PATCH", path, res.status, bodyText
|
|
934
|
+
throw hubError("PATCH", path, res.status, bodyText, undefined, {
|
|
935
|
+
podUrl: cfg.podUrl,
|
|
936
|
+
workspaceId: errorWorkspace(body, cfg),
|
|
937
|
+
});
|
|
574
938
|
}
|
|
575
939
|
return res.json();
|
|
576
940
|
}
|
|
@@ -580,7 +944,7 @@ export async function hubDelete(path, cfg) {
|
|
|
580
944
|
try {
|
|
581
945
|
res = await fetch(url, {
|
|
582
946
|
method: "DELETE",
|
|
583
|
-
headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders() },
|
|
947
|
+
headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders(cfg) },
|
|
584
948
|
signal: AbortSignal.timeout(15_000),
|
|
585
949
|
});
|
|
586
950
|
}
|
|
@@ -589,7 +953,7 @@ export async function hubDelete(path, cfg) {
|
|
|
589
953
|
}
|
|
590
954
|
if (!res.ok) {
|
|
591
955
|
const bodyText = await res.text().catch(() => "");
|
|
592
|
-
throw hubError("DELETE", path, res.status, bodyText);
|
|
956
|
+
throw hubError("DELETE", path, res.status, bodyText, undefined, cfg);
|
|
593
957
|
}
|
|
594
958
|
return res.json().catch(() => ({}));
|
|
595
959
|
}
|
|
@@ -605,5 +969,5 @@ export function makeHubClient(cfg) {
|
|
|
605
969
|
workspaceId: cfg.workspaceId,
|
|
606
970
|
});
|
|
607
971
|
}
|
|
608
|
-
export { HubRestClient } from "@synap/hub-rest-client";
|
|
972
|
+
export { HubRestClient } from "@synap-core/hub-rest-client";
|
|
609
973
|
//# sourceMappingURL=hub-client.js.map
|