@synap-core/cli 1.11.0 → 1.13.0
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 +40 -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 +32 -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 +524 -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 +116 -66
- 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.d.ts +14 -1
- package/dist/commands/bridge-setup.js +395 -129
- 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 +3 -1
- package/dist/commands/capability.js +249 -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 +1 -13
- package/dist/commands/centrality.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 +36 -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 +79 -44
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/data-extra.d.ts +34 -0
- package/dist/commands/data-extra.js +203 -25
- package/dist/commands/data-extra.js.map +1 -1
- package/dist/commands/data.d.ts +120 -2
- package/dist/commands/data.js +295 -21
- package/dist/commands/data.js.map +1 -1
- package/dist/commands/diagnose.d.ts +66 -4
- package/dist/commands/diagnose.js +285 -28
- package/dist/commands/diagnose.js.map +1 -1
- package/dist/commands/doc.d.ts +11 -4
- package/dist/commands/doc.js +39 -14
- package/dist/commands/doc.js.map +1 -1
- package/dist/commands/doctor.d.ts +133 -3
- package/dist/commands/doctor.js +761 -41
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/explain.js +5 -5
- package/dist/commands/explain.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 +393 -13
- package/dist/commands/import.js +1150 -54
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/init.d.ts +34 -9
- package/dist/commands/init.js +481 -981
- 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 +22 -6
- package/dist/commands/knowledge.js +121 -57
- package/dist/commands/knowledge.js.map +1 -1
- package/dist/commands/lens.d.ts +37 -1
- package/dist/commands/lens.js +194 -72
- package/dist/commands/lens.js.map +1 -1
- package/dist/commands/market-authoring.d.ts +29 -2
- package/dist/commands/market-authoring.js +348 -7
- package/dist/commands/market-authoring.js.map +1 -1
- 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 +400 -16
- package/dist/commands/market.js +1802 -146
- package/dist/commands/market.js.map +1 -1
- package/dist/commands/mcp.d.ts +60 -25
- package/dist/commands/mcp.js +251 -64
- 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/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 +171 -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 +11 -1
- package/dist/commands/pods.js +129 -131
- package/dist/commands/pods.js.map +1 -1
- package/dist/commands/proposals-actions.d.ts +62 -0
- package/dist/commands/proposals-actions.js +143 -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 +579 -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-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 +116 -0
- package/dist/commands/session-wait.js +224 -0
- package/dist/commands/session-wait.js.map +1 -0
- package/dist/commands/sessions.d.ts +54 -0
- package/dist/commands/sessions.js +501 -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-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/skills.js +16 -0
- package/dist/commands/skills.js.map +1 -1
- package/dist/commands/status.d.ts +1 -4
- package/dist/commands/status.js +6 -35
- 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 +112 -0
- package/dist/commands/tracks.js +543 -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 +41 -3
- package/dist/commands/upload.js +120 -18
- package/dist/commands/upload.js.map +1 -1
- 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 +30 -3
- package/dist/commands/whoami.js +186 -23
- 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.d.ts +331 -0
- package/dist/commands/worker.js +1067 -0
- package/dist/commands/worker.js.map +1 -0
- package/dist/commands/worker.test.d.ts +1 -0
- package/dist/commands/worker.test.js +427 -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 +1043 -186
- 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 +136 -0
- package/dist/lib/agent-door.js +429 -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 +17 -11
- package/dist/lib/auth.js +47 -64
- 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/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 +246 -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/capture-lane.d.ts +6 -5
- package/dist/lib/capture-lane.js +6 -5
- package/dist/lib/capture-lane.js.map +1 -1
- package/dist/lib/capture-structure.d.ts +230 -11
- package/dist/lib/capture-structure.js +251 -13
- package/dist/lib/capture-structure.js.map +1 -1
- 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/client-header.test.d.ts +1 -0
- package/dist/lib/client-header.test.js +85 -0
- package/dist/lib/client-header.test.js.map +1 -0
- package/dist/lib/conversation-headers.d.ts +33 -0
- package/dist/lib/conversation-headers.js +69 -0
- package/dist/lib/conversation-headers.js.map +1 -0
- package/dist/lib/cp-packages.d.ts +167 -11
- package/dist/lib/cp-packages.js +253 -21
- package/dist/lib/cp-packages.js.map +1 -1
- package/dist/lib/credential-class.d.ts +144 -0
- package/dist/lib/credential-class.js +138 -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/goal-words.d.ts +38 -0
- package/dist/lib/goal-words.js +112 -0
- package/dist/lib/goal-words.js.map +1 -0
- package/dist/lib/hub-client.d.ts +57 -15
- package/dist/lib/hub-client.js +396 -82
- package/dist/lib/hub-client.js.map +1 -1
- 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 +24 -0
- package/dist/lib/init-handoff.js +26 -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 +68 -0
- package/dist/lib/key-source.js +109 -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 +25 -0
- package/dist/lib/knowledge-title.js +47 -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 +49 -0
- package/dist/lib/mcp-bridge.js +180 -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 +118 -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/openclaw.js +3 -2
- package/dist/lib/openclaw.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-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-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 +74 -112
- package/dist/lib/pod.js +146 -415
- 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/register-claude-code-mcp.test.d.ts +1 -0
- package/dist/lib/register-claude-code-mcp.test.js +78 -0
- package/dist/lib/register-claude-code-mcp.test.js.map +1 -0
- package/dist/lib/review-credential.d.ts +119 -0
- package/dist/lib/review-credential.js +149 -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-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/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 +152 -10
- package/dist/lib/targets.js +974 -325
- package/dist/lib/targets.js.map +1 -1
- package/dist/lib/template-file.d.ts +4 -0
- package/dist/lib/template-file.js.map +1 -1
- 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/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/dist/utils/logger.js +5 -2
- package/dist/utils/logger.js.map +1 -1
- package/package.json +26 -17
- package/skills/README.md +13 -11
- package/skills/agent-os/SKILL.md +214 -99
- package/skills/manifest.json +1 -1
- package/skills/onboard/SKILL.md +55 -26
- package/skills/synap/README.md +5 -8
- package/skills/synap/SKILL.md +1149 -289
- 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 +5 -0
- package/skills/synap/cli-operations.md +24 -18
- package/skills/synap/code-skills.md +70 -0
- package/skills/synap/concepts.md +36 -0
- package/skills/synap/content-os.md +10 -1
- package/skills/synap/creative-loop.md +15 -10
- package/skills/synap/crm.md +21 -52
- package/skills/synap/document-embeds.md +143 -47
- package/skills/synap/escalation-ladder.md +20 -10
- package/skills/synap/focus-sessions.md +56 -21
- package/skills/synap/from-intent.md +158 -0
- package/skills/synap/governance-rules.md +5 -5
- 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 +34 -16
- package/skills/synap/linking.md +42 -41
- package/skills/synap/mcp-remote-servers.md +64 -0
- package/skills/synap/mental-model.md +17 -15
- package/skills/synap/operating-mode-rules.md +2 -2
- package/skills/synap/processes.md +118 -0
- package/skills/synap/quick-reference.md +7 -2
- 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 +110 -0
- package/skills/synap/worked-examples.md +4 -5
- package/skills/synap/workspace-design.md +15 -11
- package/skills/synap/writes.md +105 -25
- package/skills/synap/writing-titles.md +63 -0
- package/skills/synap-app/SKILL.md +149 -67
- package/skills/synap-app/_skill.yaml +1 -1
- 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-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 +306 -16
- package/skills/synap-market/_skill.yaml +1 -1
- package/skills/synap-market/author-and-publish.md +145 -8
- package/skills/synap-market/governance-and-catalog.md +7 -0
- package/skills/synap-market/install.md +79 -1
- package/skills/synap-market/overview.md +74 -6
- 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/box/runner.py +206 -0
- 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/node_modules/@synap/hub-rest-client/dist/index.cjs +0 -1261
- package/node_modules/@synap/hub-rest-client/dist/index.d.cts +0 -1647
- package/node_modules/@synap/hub-rest-client/dist/index.d.ts +0 -1647
- package/node_modules/@synap/hub-rest-client/dist/index.js +0 -1231
- package/node_modules/@synap/hub-rest-client/package.json +0 -45
- package/node_modules/@synap/hub-rest-client/src/client.ts +0 -1631
- package/node_modules/@synap/hub-rest-client/src/errors.ts +0 -30
- package/node_modules/@synap/hub-rest-client/src/index.ts +0 -153
- package/node_modules/@synap/hub-rest-client/src/setup.ts +0 -133
- package/node_modules/@synap/hub-rest-client/src/types.ts +0 -1299
package/dist/lib/hub-client.js
CHANGED
|
@@ -1,11 +1,52 @@
|
|
|
1
|
-
import { existsSync, readFileSync,
|
|
1
|
+
import { existsSync, readFileSync, unlinkSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { getActivePodConfig, getActiveWorkspaceId, getActiveWorkspaceIdForPod, findPodNameByUrl, 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 { clientHeader, 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 { PodMovedError, podFetch } from "./pod-moved.js";
|
|
11
|
+
import { HubRestClient } from "@synap-core/hub-rest-client";
|
|
7
12
|
import { log } from "../utils/logger.js";
|
|
8
13
|
import chalk from "chalk";
|
|
14
|
+
/**
|
|
15
|
+
* Resolve a projectId safe to send to `podUrl`.
|
|
16
|
+
*
|
|
17
|
+
* Order: pod-qualified lens → directory lens → env project when co-located with
|
|
18
|
+
* this pod (trustEnvProject — same model as SYNAP_WORKSPACE_ID under env rung) →
|
|
19
|
+
* ActiveProjectBinding for this pod → legacy activeProjectId only when the
|
|
20
|
+
* active profile is this pod.
|
|
21
|
+
*
|
|
22
|
+
* Write paths should consume `resolveHubConfig().projectId` rather than bare
|
|
23
|
+
* `getActiveProjectId()` / env.
|
|
24
|
+
*/
|
|
25
|
+
function resolveProjectIdForPod(podUrl, opts) {
|
|
26
|
+
if (opts.lensProjectId)
|
|
27
|
+
return opts.lensProjectId;
|
|
28
|
+
if (opts.dirLensProjectId)
|
|
29
|
+
return opts.dirLensProjectId;
|
|
30
|
+
// Env pin co-located with env pod (connect --pin-project, Claude settings.env).
|
|
31
|
+
if (opts.trustEnvProject && opts.envProject)
|
|
32
|
+
return opts.envProject;
|
|
33
|
+
const profiles = listPodProfiles();
|
|
34
|
+
const binding = getActiveProjectBinding();
|
|
35
|
+
if (binding) {
|
|
36
|
+
const boundPod = profiles.find((x) => x.name === binding.podName);
|
|
37
|
+
if (boundPod && samePodOrigin(boundPod.config.podUrl, podUrl)) {
|
|
38
|
+
return binding.projectId;
|
|
39
|
+
}
|
|
40
|
+
// Binding is for another pod — do not send it.
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
// Legacy: activeProjectId with no binding — only when active profile is this pod.
|
|
44
|
+
const active = getActivePodConfig();
|
|
45
|
+
if (active && samePodOrigin(active.podUrl, podUrl)) {
|
|
46
|
+
return getActiveProjectId() || undefined;
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
9
50
|
/**
|
|
10
51
|
* A failed Hub call, carrying the structured facts callers need to branch on.
|
|
11
52
|
*
|
|
@@ -173,6 +214,33 @@ function readErrorBody(status, rawBody) {
|
|
|
173
214
|
const oneLine = trimmed.replace(/\s+/g, " ");
|
|
174
215
|
return { detail: oneLine.length > 200 ? `${oneLine.slice(0, 200)}…` : oneLine };
|
|
175
216
|
}
|
|
217
|
+
// tRPC batch errors are array-shaped: `[{ error: { message, code, data: { httpStatus, ... } } }]`
|
|
218
|
+
// (superjson may additionally nest the real payload one level deeper under
|
|
219
|
+
// `error.json`). Unwrap to the first entry's error object before falling
|
|
220
|
+
// through to the normal object-shaped handling below, so a tRPC failure
|
|
221
|
+
// surfaces its real `message` instead of the generic status phrase.
|
|
222
|
+
if (Array.isArray(parsed)) {
|
|
223
|
+
const first = parsed[0];
|
|
224
|
+
const err = first?.error;
|
|
225
|
+
if (err && typeof err === "object") {
|
|
226
|
+
const json = err.json;
|
|
227
|
+
parsed = json ?? err;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
else if (parsed &&
|
|
231
|
+
typeof parsed === "object" &&
|
|
232
|
+
"error" in parsed) {
|
|
233
|
+
// Single tRPC-shaped error body: `{ error: { message, ... } }` or
|
|
234
|
+
// `{ error: { json: { message, ... } } }`. Only unwrap when `error` is an
|
|
235
|
+
// OBJECT — a Hub-REST body's `error` is a plain string category
|
|
236
|
+
// (`{ error: "...", detail: "..." }`) and must fall through unchanged.
|
|
237
|
+
const errVal = parsed.error;
|
|
238
|
+
if (errVal && typeof errVal === "object") {
|
|
239
|
+
const errObj = errVal;
|
|
240
|
+
const json = errObj.json;
|
|
241
|
+
parsed = json ?? errObj;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
176
244
|
if (parsed && typeof parsed === "object") {
|
|
177
245
|
const o = parsed;
|
|
178
246
|
const code = typeof o.code === "string" ? o.code : undefined;
|
|
@@ -210,44 +278,48 @@ function hubError(method, path, status, rawBody, suffix, ctx) {
|
|
|
210
278
|
workspaceId: ctx?.workspaceId,
|
|
211
279
|
});
|
|
212
280
|
}
|
|
213
|
-
/**
|
|
281
|
+
/**
|
|
282
|
+
* Where to send users who hit a wall. The support door is the ONE customer
|
|
283
|
+
* support entry point shared by every Synap surface (`from=cli` tells support
|
|
284
|
+
* which surface the person came from); Discord stays as the community option.
|
|
285
|
+
*/
|
|
286
|
+
export const SUPPORT_URL = "https://synap.live/support?from=cli";
|
|
214
287
|
const DISCORD_INVITE_URL = "https://discord.gg/xhRdQ7hG5h";
|
|
215
288
|
/** Wrap a URL in an OSC-8 terminal hyperlink so `label` is clickable. */
|
|
216
289
|
function osc8Link(url, label) {
|
|
217
290
|
return `\x1b]8;;${url}\x1b\\${label}\x1b]8;;\x1b\\`;
|
|
218
291
|
}
|
|
219
292
|
/**
|
|
220
|
-
* Once-per-process guard for the
|
|
293
|
+
* Once-per-process guard for the help footer. A single CLI invocation
|
|
221
294
|
* runs one command; printing the footer at most once keeps it a footer, not a
|
|
222
295
|
* per-error/per-line spam.
|
|
223
296
|
*/
|
|
224
|
-
let
|
|
297
|
+
let _helpShown = false;
|
|
225
298
|
/**
|
|
226
|
-
* Append a one-line "need help?" footer
|
|
227
|
-
* to stderr (same stream as the error it
|
|
228
|
-
* and only once per process.
|
|
229
|
-
* terminal, a plain URL when the stream is
|
|
299
|
+
* Append a one-line "need help?" footer naming the support door (open a ticket)
|
|
300
|
+
* and the Discord invite. Prints to stderr (same stream as the error it
|
|
301
|
+
* follows, so `2>/dev/null` hides both) and only once per process. Each URL is
|
|
302
|
+
* an OSC-8 hyperlink on an interactive terminal, a plain URL when the stream is
|
|
303
|
+
* redirected/piped.
|
|
230
304
|
*/
|
|
231
|
-
export function
|
|
232
|
-
if (
|
|
305
|
+
export function helpFooterLine() {
|
|
306
|
+
if (_helpShown)
|
|
233
307
|
return;
|
|
234
|
-
|
|
235
|
-
const link = process.stderr.isTTY
|
|
236
|
-
|
|
237
|
-
: DISCORD_INVITE_URL;
|
|
238
|
-
console.error(chalk.dim(` Need help? Join our Discord: ${link}`));
|
|
308
|
+
_helpShown = true;
|
|
309
|
+
const link = (url) => (process.stderr.isTTY ? osc8Link(url, url) : url);
|
|
310
|
+
console.error(chalk.dim(` Need help? Open a ticket: ${link(SUPPORT_URL)} (or join our Discord: ${link(DISCORD_INVITE_URL)})`));
|
|
239
311
|
}
|
|
240
312
|
/**
|
|
241
313
|
* Print a failed Hub call: its humanized message plus a hint for the classes
|
|
242
314
|
* where the cause is not obvious from the message alone. Lives here rather than
|
|
243
315
|
* in utils/logger so the logger stays domain-agnostic and the taxonomy and its
|
|
244
|
-
* presentation sit in one file. Always closes with the one-time
|
|
316
|
+
* presentation sit in one file. Always closes with the one-time help footer.
|
|
245
317
|
*/
|
|
246
318
|
export function renderHubError(err) {
|
|
247
319
|
renderHubErrorBody(err);
|
|
248
320
|
// ONE central place every failed Hub command flows through — append the help
|
|
249
321
|
// footer here so it prints once per failed command (guarded), not per line.
|
|
250
|
-
|
|
322
|
+
helpFooterLine();
|
|
251
323
|
}
|
|
252
324
|
function renderHubErrorBody(err) {
|
|
253
325
|
// Hints use log.hint (stderr), not log.dim (stdout): a hint belongs to the
|
|
@@ -258,11 +330,70 @@ function renderHubErrorBody(err) {
|
|
|
258
330
|
log.hint(err.hint);
|
|
259
331
|
return;
|
|
260
332
|
}
|
|
333
|
+
if (err instanceof PodMovedError) {
|
|
334
|
+
// One line: what happened and what to run.
|
|
335
|
+
log.error(err.message);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
261
338
|
if (!(err instanceof HubError)) {
|
|
262
339
|
log.error(err instanceof Error ? err.message : String(err));
|
|
263
340
|
return;
|
|
264
341
|
}
|
|
342
|
+
// Connection-class failure — the execute door returns 424 with a machine
|
|
343
|
+
// `errorClass` (+ `providerRef`) precisely so it SURVIVES the pod's 5xx egress
|
|
344
|
+
// sanitizer. This is client-ACTIONABLE (reconnect), not a server fault, so name
|
|
345
|
+
// the cause + the exact fix instead of the generic status hint. Mirrors the
|
|
346
|
+
// browser's P1 self-heal reconnect chip.
|
|
347
|
+
const errBody = err.body;
|
|
348
|
+
const errorClass = errBody?.errorClass;
|
|
349
|
+
if (errorClass === "auth" || errorClass === "no_connection") {
|
|
350
|
+
const provider = errBody?.providerRef
|
|
351
|
+
? chalk.bold(errBody.providerRef)
|
|
352
|
+
: "this capability's";
|
|
353
|
+
log.error(`The ${provider} connection needs reconnecting — its access has expired or was revoked.`);
|
|
354
|
+
log.hint('Reconnect it: synap cap connect "<capability>" --reconnect');
|
|
355
|
+
log.hint("Find the capability name with: synap cap list");
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
if (errorClass === "target_missing") {
|
|
359
|
+
log.error(err.message);
|
|
360
|
+
log.hint("The workspace or target this run needs no longer exists on this pod. Run: synap orient");
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
// SETUP-REQUIRED failure — the APPLY door (`POST /capabilities/apply`) returns
|
|
364
|
+
// a DUCK-TYPED body (412 for `no_connection`, 400 for `missing_field`) with
|
|
365
|
+
// `failureClass` (+ `missingFields` / `connection`) so an install that needs a
|
|
366
|
+
// human before it can apply survives as an actionable message. This is a
|
|
367
|
+
// client-ACTIONABLE, NON-retryable state — name the missing step instead of the
|
|
368
|
+
// generic status phrase below (a bare 412/400 tells the user nothing to do).
|
|
369
|
+
const setupBody = err.body;
|
|
370
|
+
const failureClass = setupBody?.failureClass;
|
|
371
|
+
if (failureClass === "no_connection") {
|
|
372
|
+
const provider = setupBody?.connection?.provider;
|
|
373
|
+
log.error(provider
|
|
374
|
+
? `The "${provider}" provider isn't connected yet — connect it first (synap cap connect "${provider}").`
|
|
375
|
+
: 'A required provider isn\'t connected yet — connect it first (synap cap connect "<provider>").');
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
if (failureClass === "missing_field") {
|
|
379
|
+
const missingFields = Array.isArray(setupBody?.missingFields)
|
|
380
|
+
? setupBody.missingFields.filter((field) => typeof field === "string")
|
|
381
|
+
: [];
|
|
382
|
+
log.error(missingFields.length > 0
|
|
383
|
+
? `Missing required value(s): ${missingFields.join(", ")}`
|
|
384
|
+
: "Missing required value(s).");
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
265
387
|
log.error(err.message);
|
|
388
|
+
// A 500 carries an `errorId` (the egress sanitizer logs the real cause under
|
|
389
|
+
// it server-side) — surface it so "quote errorId when reporting it" is
|
|
390
|
+
// actionable rather than a dead end.
|
|
391
|
+
const errorId = typeof err.body?.errorId === "string"
|
|
392
|
+
? err.body.errorId
|
|
393
|
+
: undefined;
|
|
394
|
+
if (err.status >= 500 && errorId) {
|
|
395
|
+
log.hint(`errorId: ${errorId}`);
|
|
396
|
+
}
|
|
266
397
|
// A 500 whose body carried a specific cause (detail/message) is already in
|
|
267
398
|
// `err.message` above — do NOT then claim "internal error, not in this
|
|
268
399
|
// command": a workspace/template creation failure is often an actionable
|
|
@@ -284,9 +415,15 @@ function renderHubErrorBody(err) {
|
|
|
284
415
|
// active workspace left over from ANOTHER pod (a stale `synap use` after a
|
|
285
416
|
// pod switch) being sent to a pod it doesn't belong to.
|
|
286
417
|
if (err.workspaceId && err.podUrl) {
|
|
418
|
+
// State the FACT (what was sent where), then the two causes WITHOUT
|
|
419
|
+
// picking one — this code cannot tell them apart, and asserting the
|
|
420
|
+
// cross-pod story is wrong whenever the saved workspace simply no longer
|
|
421
|
+
// exists on its own pod (dogfood: `pods.team.workspaceId` pointed at a
|
|
422
|
+
// deleted workspace; the hint blamed a pod mix-up that had not happened).
|
|
423
|
+
// `synap doctor` is the only thing here that actually CHECKS, so it leads.
|
|
287
424
|
log.hint(`Sent workspace ${err.workspaceId} to ${err.podUrl}.`);
|
|
288
|
-
log.hint("That workspace
|
|
289
|
-
log.hint("
|
|
425
|
+
log.hint("That workspace is not accessible on this pod — either it lives on a different pod, or it no longer exists here.");
|
|
426
|
+
log.hint("Run: synap doctor (checks which of the two it is), then: synap orient (see valid workspaces) and synap use <id>.");
|
|
290
427
|
}
|
|
291
428
|
else {
|
|
292
429
|
log.hint("Workspace not found or not accessible on this pod — run: synap orient (see valid workspaces), or: synap doctor");
|
|
@@ -337,14 +474,22 @@ export async function resolveHubConfig(opts) {
|
|
|
337
474
|
// Beats the env vars deliberately — that is the whole point of the flag.
|
|
338
475
|
const podOverride = getPodOverride();
|
|
339
476
|
if (podOverride) {
|
|
477
|
+
// Prefer cross-pod-safe resolution (binding + "not another pod's default")
|
|
478
|
+
// over the profile's baked `workspaceId`, which can drift to a foreign UUID
|
|
479
|
+
// after dual-pod work (dogfood: perso profile held team CRM ws → 403).
|
|
480
|
+
const overrideName = findPodNameByUrl(podOverride.podUrl);
|
|
481
|
+
const safeWs = getActiveWorkspaceIdForPod(overrideName) ||
|
|
482
|
+
// Only fall back to profile default when it isn't known to belong elsewhere
|
|
483
|
+
// (getActiveWorkspaceIdForPod already applied that rule against the profile).
|
|
484
|
+
undefined;
|
|
340
485
|
return {
|
|
341
486
|
podUrl: podOverride.podUrl,
|
|
342
487
|
apiKey: podOverride.hubApiKey,
|
|
343
|
-
//
|
|
344
|
-
userId:
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
488
|
+
// "cli": resolveUserId asks the pod who this key acts as (see HubConfig.userId).
|
|
489
|
+
userId: "cli",
|
|
490
|
+
agentUserId: podOverride.agentUserId || undefined,
|
|
491
|
+
workspaceId: safeWs,
|
|
492
|
+
projectId: resolveProjectIdForPod(podOverride.podUrl, {}),
|
|
348
493
|
};
|
|
349
494
|
}
|
|
350
495
|
// 1. Explicit flags (escape hatch)
|
|
@@ -365,7 +510,13 @@ export async function resolveHubConfig(opts) {
|
|
|
365
510
|
return {
|
|
366
511
|
podUrl,
|
|
367
512
|
apiKey: agentOverride.apiKey,
|
|
368
|
-
|
|
513
|
+
// The acting user is asked of the pod ("cli" → /users/me): an agent key
|
|
514
|
+
// acts as its linked human, and the pod 403s the agent's own id.
|
|
515
|
+
userId: "cli",
|
|
516
|
+
// The AGENT's own id, recorded with its key — for `synap worker` to
|
|
517
|
+
// recognise its own room posts (resolveAgentUserId). Never the pod
|
|
518
|
+
// profile name (it once was, and the worker resumed itself on them).
|
|
519
|
+
agentUserId: agentOverride.agentUserId || undefined,
|
|
369
520
|
scopes: envScopes ? envScopes.split(",") : undefined,
|
|
370
521
|
};
|
|
371
522
|
}
|
|
@@ -376,7 +527,13 @@ export async function resolveHubConfig(opts) {
|
|
|
376
527
|
const envScopes = process.env.SYNAP_KEY_SCOPES;
|
|
377
528
|
const envWorkspace = process.env.SYNAP_WORKSPACE_ID;
|
|
378
529
|
const envProject = process.env.SYNAP_PROJECT_ID;
|
|
379
|
-
|
|
530
|
+
// Pod + key are enough. SYNAP_USER_ID used to be required too, so a shell
|
|
531
|
+
// with just the pair (`SYNAP_POD_URL=… SYNAP_HUB_API_KEY=… synap app
|
|
532
|
+
// connect`, the documented way to try another key) fell through to "No pod
|
|
533
|
+
// configured" (2026-10-20). Unset, userId is "cli" and resolveUserId asks
|
|
534
|
+
// the pod (/users/me) — the same fallback --pod and SYNAP_AGENT use. The
|
|
535
|
+
// claude-code surface-key preference below still applies.
|
|
536
|
+
if (envPod && envKey) {
|
|
380
537
|
// Surface-bound resolution guard: if this pod has a `claude-code` surface
|
|
381
538
|
// key pinned to it (via `synap connect --target=claude-code`) and the
|
|
382
539
|
// ambient SYNAP_HUB_API_KEY doesn't match it, the session inherited a
|
|
@@ -384,28 +541,53 @@ export async function resolveHubConfig(opts) {
|
|
|
384
541
|
// the pinned key so a leaked/inherited env var can't silently redefine
|
|
385
542
|
// which agent this session acts as. Conservative: only applies here, in
|
|
386
543
|
// step 3 — steps 0-2 (--pod, explicit flags, SYNAP_AGENT) still win
|
|
387
|
-
// outright, untouched.
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
544
|
+
// outright, untouched. Preference logic lives in key-source.ts so whoami
|
|
545
|
+
// reports the same rule (no drift).
|
|
546
|
+
const preferred = preferClaudeCodeSurfaceKey({
|
|
547
|
+
envPod,
|
|
548
|
+
envKey,
|
|
549
|
+
envUser,
|
|
550
|
+
surface: getSurfaceAgentKey("claude-code", envPod),
|
|
551
|
+
});
|
|
552
|
+
if (preferred.usedSurface) {
|
|
392
553
|
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`);
|
|
393
|
-
apiKey = surfaceKey.hubApiKey;
|
|
394
|
-
userId = surfaceKey.agentUserId || envUser;
|
|
395
554
|
}
|
|
555
|
+
const apiKey = preferred.apiKey;
|
|
556
|
+
const userId = preferred.userId;
|
|
557
|
+
const agentUserId = preferred.agentUserId;
|
|
558
|
+
// Per-Claude-session lens wins over the global env var, so concurrent
|
|
559
|
+
// sessions can each be scoped to a different workspace — but ONLY when the
|
|
560
|
+
// lens belongs to THIS pod. A lens stamped with another pod is ignored so
|
|
561
|
+
// the pod-qualified fallback below applies (the "Access denied to
|
|
562
|
+
// workspace" 403 after `synap pods use`).
|
|
563
|
+
const lens = resolveActiveLensForPod(envPod);
|
|
564
|
+
// …and the per-DIRECTORY lens sits directly beneath it: the durable default
|
|
565
|
+
// for this working tree, overridden by the session lens when one is set.
|
|
566
|
+
// Pod-qualified through the same door, for the same reason — a directory
|
|
567
|
+
// lens outlives many `synap pods use` switches, so it is MORE exposed to
|
|
568
|
+
// the cross-pod 403 than the session lens is.
|
|
569
|
+
const dirLens = resolveDirectoryLensForPod(envPod)?.lens;
|
|
396
570
|
return {
|
|
397
571
|
podUrl: envPod,
|
|
398
572
|
apiKey,
|
|
399
573
|
userId,
|
|
400
|
-
|
|
401
|
-
//
|
|
402
|
-
//
|
|
403
|
-
|
|
404
|
-
|
|
574
|
+
agentUserId,
|
|
575
|
+
// The global-config fallback is resolved AGAINST THIS POD (envPod) —
|
|
576
|
+
// never send a workspace that belongs to a different pod.
|
|
577
|
+
workspaceId: lens?.workspaceId ||
|
|
578
|
+
dirLens?.workspaceId ||
|
|
405
579
|
envWorkspace ||
|
|
406
580
|
getActiveWorkspaceIdForPod(findPodNameByUrl(envPod)),
|
|
407
|
-
// Project is a peer lens
|
|
408
|
-
|
|
581
|
+
// Project is a peer lens. Lens/dir-lens are already pod-qualified;
|
|
582
|
+
// env/global active project only when ActiveProjectBinding matches this pod.
|
|
583
|
+
// Write paths must use this resolveHubConfig output — never bare
|
|
584
|
+
// getActiveProjectId() / SYNAP_PROJECT_ID without the pod check.
|
|
585
|
+
projectId: resolveProjectIdForPod(envPod, {
|
|
586
|
+
lensProjectId: lens?.projectId,
|
|
587
|
+
dirLensProjectId: dirLens?.projectId,
|
|
588
|
+
envProject,
|
|
589
|
+
trustEnvProject: true,
|
|
590
|
+
}),
|
|
409
591
|
scopes: envScopes ? envScopes.split(",") : undefined,
|
|
410
592
|
};
|
|
411
593
|
}
|
|
@@ -413,40 +595,144 @@ export async function resolveHubConfig(opts) {
|
|
|
413
595
|
const config = getActivePodConfig();
|
|
414
596
|
if (!config)
|
|
415
597
|
throw new Error("No pod configured. Run: synap pods add");
|
|
598
|
+
// Same pod-qualification as step 3: a lens stamped with another pod must not
|
|
599
|
+
// short-circuit `getActiveWorkspaceId()`, which IS cross-pod-safe.
|
|
600
|
+
const activeLens = resolveActiveLensForPod(config.podUrl);
|
|
601
|
+
const activeDirLens = resolveDirectoryLensForPod(config.podUrl)?.lens;
|
|
416
602
|
return {
|
|
417
603
|
podUrl: config.podUrl,
|
|
418
604
|
apiKey: config.hubApiKey,
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
605
|
+
// "cli": resolveUserId asks the pod. A profile's agentUserId is the key's
|
|
606
|
+
// principal (a computer or agent key), not who the pod acts as.
|
|
607
|
+
userId: "cli",
|
|
608
|
+
agentUserId: config.agentUserId || undefined,
|
|
609
|
+
// session lens › directory lens › global config. (No env rung here: this
|
|
610
|
+
// branch is only reached when SYNAP_POD_URL is unset.)
|
|
611
|
+
workspaceId: activeLens?.workspaceId || activeDirLens?.workspaceId || getActiveWorkspaceId(),
|
|
612
|
+
// Project: same pod-qualification as step 3 (no bare getActiveProjectId).
|
|
613
|
+
projectId: resolveProjectIdForPod(config.podUrl, {
|
|
614
|
+
lensProjectId: activeLens?.projectId,
|
|
615
|
+
dirLensProjectId: activeDirLens?.projectId,
|
|
616
|
+
}),
|
|
422
617
|
};
|
|
423
618
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
619
|
+
// ─── Active focus session ─────────────────────────────────────────────────────
|
|
620
|
+
//
|
|
621
|
+
// ONE store, two rungs of the SAME lens mechanism:
|
|
622
|
+
//
|
|
623
|
+
// env SYNAP_SESSION_ID › session lens (per Claude tab) › directory lens
|
|
624
|
+
//
|
|
625
|
+
// The directory rung supplies a session only OUTSIDE a Claude Code conversation
|
|
626
|
+
// (`effectiveDirectoryLens`): inside one, the session belongs to the
|
|
627
|
+
// conversation, never to the working tree.
|
|
628
|
+
//
|
|
629
|
+
// The retired legacy store was a per-CWD `.synap-session` file that the session
|
|
630
|
+
// lens already silently shadowed — two concurrent "current session" stores for
|
|
631
|
+
// one concept. It is now read exactly once, to migrate (below).
|
|
632
|
+
/** Legacy per-CWD session file — RETIRED as a store; read once to migrate. */
|
|
633
|
+
const LEGACY_SESSION_FILE = ".synap-session";
|
|
634
|
+
let legacySessionMigrationChecked = false;
|
|
635
|
+
/**
|
|
636
|
+
* One-time migration of a pre-existing `.synap-session` into the DIRECTORY lens.
|
|
637
|
+
*
|
|
638
|
+
* COMPAT DECISION — read-and-migrate, not a permanent read-only fallback. A
|
|
639
|
+
* fallback would keep two stores alive indefinitely, which is exactly the
|
|
640
|
+
* divergence being removed. Migrating is faithful: `.synap-session` was
|
|
641
|
+
* per-working-tree and durable, and the directory lens has precisely that
|
|
642
|
+
* lifetime and scope — so an already-attached session survives the upgrade for
|
|
643
|
+
* every terminal in that tree. The file is deleted afterwards so it can never
|
|
644
|
+
* shadow (or be shadowed by) the lens again.
|
|
645
|
+
*
|
|
646
|
+
* No `podUrl` is stamped: the legacy file carried no pod, and stamping one here
|
|
647
|
+
* would falsely pod-qualify whatever workspace/project the lens already holds.
|
|
648
|
+
*/
|
|
649
|
+
function migrateLegacySessionFile() {
|
|
650
|
+
if (legacySessionMigrationChecked)
|
|
651
|
+
return;
|
|
652
|
+
legacySessionMigrationChecked = true;
|
|
653
|
+
const filePath = join(process.cwd(), LEGACY_SESSION_FILE);
|
|
654
|
+
if (!existsSync(filePath))
|
|
655
|
+
return;
|
|
656
|
+
let id;
|
|
657
|
+
try {
|
|
658
|
+
id = readFileSync(filePath, "utf8").trim() || undefined;
|
|
659
|
+
}
|
|
660
|
+
catch {
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
try {
|
|
664
|
+
if (id)
|
|
665
|
+
writeDirectoryLens({ focusSessionId: id });
|
|
666
|
+
unlinkSync(filePath);
|
|
667
|
+
}
|
|
668
|
+
catch {
|
|
669
|
+
return; // best-effort — a read-only tree simply keeps the stale file
|
|
670
|
+
}
|
|
671
|
+
process.stderr.write(`[synap] .synap-session is deprecated${id ? ` — session ${id.slice(0, 8)}… migrated into .synap/lens.json` : " — removed (empty)"}.\n`);
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* The focus session every hub call should be tagged with.
|
|
675
|
+
*
|
|
676
|
+
* Both lens rungs are pod-qualified: a focus-session id is POD-LOCAL, so a lens
|
|
677
|
+
* stamped with another pod must not leak into `X-Session-Id`. (Lenses written
|
|
678
|
+
* before the stamp existed have no `podUrl` and match leniently — see
|
|
679
|
+
* `lensMatchesPod`.)
|
|
680
|
+
*/
|
|
681
|
+
export function resolveActiveSessionId(podUrl) {
|
|
427
682
|
const fromEnv = process.env.SYNAP_SESSION_ID;
|
|
428
683
|
if (fromEnv)
|
|
429
684
|
return fromEnv;
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
685
|
+
migrateLegacySessionFile();
|
|
686
|
+
const fromSessionLens = resolveActiveLensForPod(podUrl)?.focusSessionId;
|
|
687
|
+
if (fromSessionLens)
|
|
688
|
+
return fromSessionLens;
|
|
689
|
+
const dir = resolveDirectoryLensForPod(podUrl);
|
|
690
|
+
return dir ? effectiveDirectoryLens(dir.lens).focusSessionId : undefined;
|
|
435
691
|
}
|
|
436
|
-
/**
|
|
437
|
-
|
|
438
|
-
|
|
692
|
+
/**
|
|
693
|
+
* Attach a focus session to this terminal.
|
|
694
|
+
*
|
|
695
|
+
* Inside Claude Code the SESSION lens wins — per-tab isolation is the point
|
|
696
|
+
* (tab A on session X, tab B on session Y, same directory). Outside it there is
|
|
697
|
+
* no `CLAUDE_CODE_SESSION_ID` to key on, so the DIRECTORY lens is the
|
|
698
|
+
* legitimate fallback: the same per-working-tree scope the retired
|
|
699
|
+
* `.synap-session` had. `attach` therefore never silently no-ops.
|
|
700
|
+
*/
|
|
701
|
+
export function attachActiveSessionId(sessionId) {
|
|
702
|
+
const claudeSession = getClaudeSessionId();
|
|
703
|
+
if (claudeSession) {
|
|
704
|
+
writeLens(claudeSession, { focusSessionId: sessionId });
|
|
705
|
+
return "session-lens";
|
|
706
|
+
}
|
|
707
|
+
writeDirectoryLens({ focusSessionId: sessionId });
|
|
708
|
+
return "directory-lens";
|
|
439
709
|
}
|
|
440
|
-
/**
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
710
|
+
/**
|
|
711
|
+
* Detach — clear the focus session from BOTH lens rungs, and return whatever
|
|
712
|
+
* was active. Clearing only the top rung would "detach" into the lower one,
|
|
713
|
+
* which reads as the command failing.
|
|
714
|
+
*/
|
|
715
|
+
export function detachActiveSessionId(podUrl) {
|
|
716
|
+
const previous = resolveActiveSessionId(podUrl);
|
|
717
|
+
const claudeSession = getClaudeSessionId();
|
|
718
|
+
if (claudeSession)
|
|
719
|
+
clearLensField(claudeSession, "focusSessionId");
|
|
720
|
+
clearDirectoryLensField("focusSessionId");
|
|
721
|
+
return previous;
|
|
445
722
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
723
|
+
/**
|
|
724
|
+
* Per-request headers: which client calls (`synap-cli/<version>`), the focus
|
|
725
|
+
* session this terminal is attached to, and the
|
|
726
|
+
* Claude Code conversation the call comes from (`conversation-headers.ts`) — so
|
|
727
|
+
* the work it lands can be traced back to, and reopened in, that conversation.
|
|
728
|
+
*/
|
|
729
|
+
function sessionHeaders(cfg) {
|
|
730
|
+
const id = resolveActiveSessionId(cfg?.podUrl);
|
|
731
|
+
return {
|
|
732
|
+
...clientHeader("synap-cli"),
|
|
733
|
+
...conversationHeaders(getClaudeSessionId()),
|
|
734
|
+
...(id ? { "X-Session-Id": id } : {}),
|
|
735
|
+
};
|
|
450
736
|
}
|
|
451
737
|
/** Max 429 retries for bulk import (each waits up to the rate-limit window). */
|
|
452
738
|
const HUB_429_MAX_RETRIES = 12;
|
|
@@ -530,9 +816,11 @@ async function hubFetchWithRetry(url, init, label, timeoutMs) {
|
|
|
530
816
|
await acquireHubSlot();
|
|
531
817
|
let res;
|
|
532
818
|
try {
|
|
533
|
-
res = await
|
|
819
|
+
res = await podFetch(url, init);
|
|
534
820
|
}
|
|
535
821
|
catch (err) {
|
|
822
|
+
if (err instanceof PodMovedError)
|
|
823
|
+
throw err;
|
|
536
824
|
throw networkError(err, url, labelMethod, labelPath, timeoutMs);
|
|
537
825
|
}
|
|
538
826
|
if (res.status !== 429)
|
|
@@ -548,22 +836,36 @@ async function hubFetchWithRetry(url, init, label, timeoutMs) {
|
|
|
548
836
|
await sleep(wait);
|
|
549
837
|
}
|
|
550
838
|
}
|
|
551
|
-
export async function hubGet(path, params, cfg
|
|
839
|
+
export async function hubGet(path, params, cfg,
|
|
840
|
+
// Default 15s suits reads. A long-poll door (`/answers/wait`) holds the
|
|
841
|
+
// request open for its own `timeoutSeconds`; its caller passes more.
|
|
842
|
+
timeoutMs = 15_000) {
|
|
552
843
|
const url = new URL(`${cfg.podUrl}/api/hub${path}`);
|
|
553
844
|
for (const [k, v] of Object.entries(params)) {
|
|
554
845
|
if (v !== undefined)
|
|
555
846
|
url.searchParams.set(k, String(v));
|
|
556
847
|
}
|
|
557
848
|
const res = await hubFetchWithRetry(url.toString(), {
|
|
558
|
-
headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders() },
|
|
559
|
-
signal: AbortSignal.timeout(
|
|
560
|
-
}, `GET ${path}`,
|
|
849
|
+
headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders(cfg) },
|
|
850
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
851
|
+
}, `GET ${path}`, timeoutMs);
|
|
561
852
|
if (!res.ok) {
|
|
562
853
|
const body = await res.text().catch(() => "");
|
|
563
854
|
throw hubError("GET", path, res.status, body, undefined, cfg);
|
|
564
855
|
}
|
|
565
856
|
return res.json();
|
|
566
857
|
}
|
|
858
|
+
/**
|
|
859
|
+
* The workspace a failing request ACTUALLY carried. A caller may override the
|
|
860
|
+
* configured lens per-command (e.g. `--workspace <id>`), and naming the config's
|
|
861
|
+
* workspace in that error sends the reader chasing the wrong id.
|
|
862
|
+
*/
|
|
863
|
+
function errorWorkspace(body, cfg) {
|
|
864
|
+
const sent = body && typeof body === "object" && "workspaceId" in body
|
|
865
|
+
? body.workspaceId
|
|
866
|
+
: undefined;
|
|
867
|
+
return typeof sent === "string" && sent.length > 0 ? sent : cfg.workspaceId;
|
|
868
|
+
}
|
|
567
869
|
export async function hubPost(path, body, cfg,
|
|
568
870
|
// Default 15s suits CRUD doors. Capability runs invoke external provider APIs
|
|
569
871
|
// (often several sequential calls) and legitimately take longer — callers
|
|
@@ -574,14 +876,17 @@ timeoutMs = 15_000) {
|
|
|
574
876
|
headers: {
|
|
575
877
|
Authorization: `Bearer ${cfg.apiKey}`,
|
|
576
878
|
"Content-Type": "application/json",
|
|
577
|
-
...sessionHeaders(),
|
|
879
|
+
...sessionHeaders(cfg),
|
|
578
880
|
},
|
|
579
881
|
body: JSON.stringify(body),
|
|
580
882
|
signal: AbortSignal.timeout(timeoutMs),
|
|
581
883
|
}, `POST ${path}`, timeoutMs);
|
|
582
884
|
if (!res.ok) {
|
|
583
885
|
const bodyText = await res.text().catch(() => "");
|
|
584
|
-
throw hubError("POST", path, res.status, bodyText, undefined,
|
|
886
|
+
throw hubError("POST", path, res.status, bodyText, undefined, {
|
|
887
|
+
podUrl: cfg.podUrl,
|
|
888
|
+
workspaceId: errorWorkspace(body, cfg),
|
|
889
|
+
});
|
|
585
890
|
}
|
|
586
891
|
return res.json();
|
|
587
892
|
}
|
|
@@ -595,7 +900,7 @@ timeoutMs = 15_000) {
|
|
|
595
900
|
export async function hubPostMultipart(path, form, cfg, timeoutMs = 120_000) {
|
|
596
901
|
const res = await hubFetchWithRetry(`${cfg.podUrl}/api/hub${path}`, {
|
|
597
902
|
method: "POST",
|
|
598
|
-
headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders() },
|
|
903
|
+
headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders(cfg) },
|
|
599
904
|
body: form,
|
|
600
905
|
signal: AbortSignal.timeout(timeoutMs),
|
|
601
906
|
}, `POST multipart ${path}`, timeoutMs);
|
|
@@ -617,14 +922,16 @@ export async function hubPostMultipartRetryable(path, buildForm, cfg, timeoutMs
|
|
|
617
922
|
const url = `${cfg.podUrl}/api/hub${path}`;
|
|
618
923
|
let res;
|
|
619
924
|
try {
|
|
620
|
-
res = await
|
|
925
|
+
res = await podFetch(url, {
|
|
621
926
|
method: "POST",
|
|
622
|
-
headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders() },
|
|
927
|
+
headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders(cfg) },
|
|
623
928
|
body: form,
|
|
624
929
|
signal: AbortSignal.timeout(timeoutMs),
|
|
625
930
|
});
|
|
626
931
|
}
|
|
627
932
|
catch (err) {
|
|
933
|
+
if (err instanceof PodMovedError)
|
|
934
|
+
throw err;
|
|
628
935
|
throw networkError(err, url, "POST multipart", path, timeoutMs);
|
|
629
936
|
}
|
|
630
937
|
if (res.status !== 429) {
|
|
@@ -649,19 +956,24 @@ export async function hubPatch(path, body, cfg) {
|
|
|
649
956
|
const url = `${cfg.podUrl}/api/hub${path}`;
|
|
650
957
|
let res;
|
|
651
958
|
try {
|
|
652
|
-
res = await
|
|
959
|
+
res = await podFetch(url, {
|
|
653
960
|
method: "PATCH",
|
|
654
|
-
headers: { Authorization: `Bearer ${cfg.apiKey}`, "Content-Type": "application/json", ...sessionHeaders() },
|
|
961
|
+
headers: { Authorization: `Bearer ${cfg.apiKey}`, "Content-Type": "application/json", ...sessionHeaders(cfg) },
|
|
655
962
|
body: JSON.stringify(body),
|
|
656
963
|
signal: AbortSignal.timeout(15_000),
|
|
657
964
|
});
|
|
658
965
|
}
|
|
659
966
|
catch (err) {
|
|
967
|
+
if (err instanceof PodMovedError)
|
|
968
|
+
throw err;
|
|
660
969
|
throw networkError(err, url, "PATCH", path, 15_000);
|
|
661
970
|
}
|
|
662
971
|
if (!res.ok) {
|
|
663
972
|
const bodyText = await res.text().catch(() => "");
|
|
664
|
-
throw hubError("PATCH", path, res.status, bodyText, undefined,
|
|
973
|
+
throw hubError("PATCH", path, res.status, bodyText, undefined, {
|
|
974
|
+
podUrl: cfg.podUrl,
|
|
975
|
+
workspaceId: errorWorkspace(body, cfg),
|
|
976
|
+
});
|
|
665
977
|
}
|
|
666
978
|
return res.json();
|
|
667
979
|
}
|
|
@@ -669,13 +981,15 @@ export async function hubDelete(path, cfg) {
|
|
|
669
981
|
const url = `${cfg.podUrl}/api/hub${path}`;
|
|
670
982
|
let res;
|
|
671
983
|
try {
|
|
672
|
-
res = await
|
|
984
|
+
res = await podFetch(url, {
|
|
673
985
|
method: "DELETE",
|
|
674
|
-
headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders() },
|
|
986
|
+
headers: { Authorization: `Bearer ${cfg.apiKey}`, ...sessionHeaders(cfg) },
|
|
675
987
|
signal: AbortSignal.timeout(15_000),
|
|
676
988
|
});
|
|
677
989
|
}
|
|
678
990
|
catch (err) {
|
|
991
|
+
if (err instanceof PodMovedError)
|
|
992
|
+
throw err;
|
|
679
993
|
throw networkError(err, url, "DELETE", path, 15_000);
|
|
680
994
|
}
|
|
681
995
|
if (!res.ok) {
|
|
@@ -696,5 +1010,5 @@ export function makeHubClient(cfg) {
|
|
|
696
1010
|
workspaceId: cfg.workspaceId,
|
|
697
1011
|
});
|
|
698
1012
|
}
|
|
699
|
-
export { HubRestClient } from "@synap/hub-rest-client";
|
|
1013
|
+
export { HubRestClient } from "@synap-core/hub-rest-client";
|
|
700
1014
|
//# sourceMappingURL=hub-client.js.map
|