@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
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential class — "what is this key allowed to DO?"
|
|
3
|
+
*
|
|
4
|
+
* The ONE door for introspecting the calling key (`GET /api/hub/auth/status`)
|
|
5
|
+
* and for the single question the proposals surface needs to answer honestly:
|
|
6
|
+
* *can this credential perform a human review?*
|
|
7
|
+
*
|
|
8
|
+
* Why this exists: `synap proposals approve <id>` advertised a capability the
|
|
9
|
+
* agent key running the CLI does not have. The pod hard-rejects it with
|
|
10
|
+
* HTTP 403 "An agent credential cannot approve proposals" — see
|
|
11
|
+
* `synap-backend/.../hub-protocol/rest/_shared.ts:144-161` (`rejectAgentReviewer`).
|
|
12
|
+
* A surface that offers a verb it can never execute is the same defect class as
|
|
13
|
+
* a renderer that fails open into a plausible-looking label.
|
|
14
|
+
*
|
|
15
|
+
* ── What the server actually gates on ────────────────────────────────────────
|
|
16
|
+
* The server's predicate is `c.get("agentUserId")`, set iff the api-key row
|
|
17
|
+
* carries `linkedUserId` (hub-protocol-rest.ts:403-408) — i.e. an AGENT key
|
|
18
|
+
* whose writes are attributed to the human who created the agent.
|
|
19
|
+
* `GET /auth/status` does NOT expose `linkedUserId` or `agentUserId`
|
|
20
|
+
* (auth.ts:140-157 returns keyId/userId/userEmail/keyType/scopes/…), so the CLI
|
|
21
|
+
* CANNOT reproduce the server's predicate exactly. What it CAN see is the
|
|
22
|
+
* identity the key resolves to: an agent user is provisioned with a synthetic
|
|
23
|
+
* `agent-<type>-<short>@synap.agent` email (agent-users.ts:222,
|
|
24
|
+
* provision-agent.ts:472, intelligence.ts:1313). That domain is the proxy
|
|
25
|
+
* signal used here.
|
|
26
|
+
*
|
|
27
|
+
* Because the proxy can be wrong in the FUTURE (a human key must never be
|
|
28
|
+
* blocked), classification is deliberately three-valued and the guard is
|
|
29
|
+
* belt-and-braces:
|
|
30
|
+
* • `agent` — positive agent signal → refuse locally, before the network call.
|
|
31
|
+
* • `human` — positive human signal (`user_pat` NOT owned by an agent user) → never blocked.
|
|
32
|
+
* • `unknown` — no signal → PROCEED, and translate the server's 403 after the
|
|
33
|
+
* fact (`isAgentReviewRejection`). The server stays authoritative.
|
|
34
|
+
*
|
|
35
|
+
* ── Scope: approve, not reject ───────────────────────────────────────────────
|
|
36
|
+
* `rejectAgentReviewer` is called for "approve" and "revert" ONLY. `POST
|
|
37
|
+
* /proposals/:id/reject` is INTENTIONALLY ungated (proposals.ts:437-441:
|
|
38
|
+
* "rejection only prevents a pending change from landing, so it carries no
|
|
39
|
+
* self-approval / undo risk"). So `synap proposals reject` genuinely WORKS for
|
|
40
|
+
* an agent credential and must NOT be guarded here.
|
|
41
|
+
*/
|
|
42
|
+
import chalk from "chalk";
|
|
43
|
+
import { openAppUrl, openUrl } from "@synap-core/hub-rest-client";
|
|
44
|
+
import { log } from "../utils/logger.js";
|
|
45
|
+
import { hubGet, HubError } from "./hub-client.js";
|
|
46
|
+
/** Introspect a key. `apiKey` overrides `cfg.apiKey` (used by `whoami` to probe both sides of a divergence). */
|
|
47
|
+
export async function fetchAuthStatus(cfg, apiKey) {
|
|
48
|
+
return (await hubGet("/auth/status", {}, apiKey ? { ...cfg, apiKey } : cfg));
|
|
49
|
+
}
|
|
50
|
+
const agentUserIdCache = new Map();
|
|
51
|
+
/**
|
|
52
|
+
* WHO this key is — the agent principal, for recognising the agent's OWN
|
|
53
|
+
* posts (a room message carries it as `routedTeammateId`). Never the acting
|
|
54
|
+
* user sent to the pod: that is `resolveUserId` (/users/me), which for an
|
|
55
|
+
* agent key answers the linked HUMAN. The id recorded with the key
|
|
56
|
+
* (`cfg.agentUserId`) wins; else the pod's key-owner answer (/auth/status),
|
|
57
|
+
* cached per key. For a person's own key both questions answer the person.
|
|
58
|
+
*/
|
|
59
|
+
export async function resolveAgentUserId(cfg) {
|
|
60
|
+
if (cfg.agentUserId)
|
|
61
|
+
return cfg.agentUserId;
|
|
62
|
+
const cached = agentUserIdCache.get(cfg.apiKey);
|
|
63
|
+
if (cached)
|
|
64
|
+
return cached;
|
|
65
|
+
const owner = (await fetchAuthStatus(cfg)).userId;
|
|
66
|
+
if (!owner)
|
|
67
|
+
throw new Error("The pod did not say who owns this key (GET /auth/status has no userId).");
|
|
68
|
+
agentUserIdCache.set(cfg.apiKey, owner);
|
|
69
|
+
return owner;
|
|
70
|
+
}
|
|
71
|
+
/** Resolve the effective identity. `apiKey` overrides `cfg.apiKey`, mirroring `fetchAuthStatus`. */
|
|
72
|
+
export async function fetchEffectiveIdentity(cfg, apiKey) {
|
|
73
|
+
return (await hubGet("/users/me", {}, apiKey ? { ...cfg, apiKey } : cfg));
|
|
74
|
+
}
|
|
75
|
+
/** The synthetic email domain every provisioned agent user carries. */
|
|
76
|
+
const AGENT_EMAIL_DOMAIN = "@synap.agent";
|
|
77
|
+
export function classifyReviewer(status) {
|
|
78
|
+
if (!status)
|
|
79
|
+
return "unknown";
|
|
80
|
+
// The agent domain FIRST: the server derives the agent from the key owner's
|
|
81
|
+
// `userType === 'agent'` (access/key-identity.ts), whatever the key type, and
|
|
82
|
+
// only agent users carry the synthetic domain. The device flow mints
|
|
83
|
+
// `user_pat` keys owned by a machine agent, so keyType alone is not human.
|
|
84
|
+
const email = (status.userEmail ?? "").toLowerCase();
|
|
85
|
+
if (email.endsWith(AGENT_EMAIL_DOMAIN))
|
|
86
|
+
return "agent";
|
|
87
|
+
if (status.keyType === "user_pat")
|
|
88
|
+
return "human";
|
|
89
|
+
return "unknown";
|
|
90
|
+
}
|
|
91
|
+
/** Best-effort classification. A pod that is down/older must not block the command — it degrades to `unknown`. */
|
|
92
|
+
export async function classifyActiveCredential(cfg) {
|
|
93
|
+
try {
|
|
94
|
+
return classifyReviewer(await fetchAuthStatus(cfg));
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return "unknown";
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Did the pod reject this call with the agent-reviewer guard?
|
|
102
|
+
*
|
|
103
|
+
* Matches `rejectAgentReviewer`'s 403 envelope (`_shared.ts:155-159`):
|
|
104
|
+
* "An agent credential cannot approve proposals — approve is human review. …"
|
|
105
|
+
* Kept narrow (403 + the distinctive phrase) so an unrelated 403 still renders
|
|
106
|
+
* as its own error.
|
|
107
|
+
*/
|
|
108
|
+
export function isAgentReviewRejection(err) {
|
|
109
|
+
if (!(err instanceof HubError) || err.status !== 403)
|
|
110
|
+
return false;
|
|
111
|
+
const haystack = `${err.message} ${err.rawBody}`.toLowerCase();
|
|
112
|
+
return haystack.includes("agent credential") && haystack.includes("proposal");
|
|
113
|
+
}
|
|
114
|
+
/** The `synap://` deep link the desktop app registers — `synap open proposal <id>` sends exactly this (open.ts:36-38). */
|
|
115
|
+
export function proposalDeepLink(id) {
|
|
116
|
+
return openAppUrl("proposal", id);
|
|
117
|
+
}
|
|
118
|
+
/** The pod's web review page for a bare id — the same route `bridge-setup.ts:894` and `statusline.ts:425` print. */
|
|
119
|
+
export function proposalWebLink(podUrl, id) {
|
|
120
|
+
return openUrl(podUrl, id);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Explain why this credential cannot approve, and point at the door that works.
|
|
124
|
+
* Used both by the pre-flight guard and by the post-hoc 403 translation, so the
|
|
125
|
+
* operator sees ONE message regardless of which path caught it.
|
|
126
|
+
*/
|
|
127
|
+
export function renderCannotReview(id, cfg, action = "approve") {
|
|
128
|
+
log.error(`This credential cannot ${action} proposals — ${action} is human review.`);
|
|
129
|
+
log.dim(" The active key is an agent credential. The pod blocks agent self-review so an\n" +
|
|
130
|
+
" agent cannot create-then-approve its own writes (including destructive ones).");
|
|
131
|
+
log.blank();
|
|
132
|
+
log.info(" Review it as a human instead:");
|
|
133
|
+
log.dim(` synap open proposal ${id} ${chalk.dim(`(${proposalDeepLink(id)})`)}`);
|
|
134
|
+
log.dim(` ${proposalWebLink(cfg.podUrl, id)}`);
|
|
135
|
+
log.blank();
|
|
136
|
+
log.dim(" Run `synap whoami` to see which key is active.");
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=credential-class.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-class.js","sourceRoot":"","sources":["../../src/lib/credential-class.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,6 +17,41 @@ export interface LensEntry {
|
|
|
17
17
|
id: string;
|
|
18
18
|
name?: string;
|
|
19
19
|
}
|
|
20
|
+
/** The rungs of the resolution ladder, highest precedence first. */
|
|
21
|
+
export type LensSource = "flag" | "session" | "directory" | "env" | "global";
|
|
22
|
+
/** Human wording for each rung — used verbatim in `synap lens` output. */
|
|
23
|
+
export declare const LENS_SOURCE_LABEL: Record<LensSource, string>;
|
|
24
|
+
/** One candidate rung. `detail` is the concrete origin (a file path, an env var name). */
|
|
25
|
+
export interface LensRung {
|
|
26
|
+
source: LensSource;
|
|
27
|
+
workspaceId?: string;
|
|
28
|
+
projectId?: string;
|
|
29
|
+
focusSessionId?: string;
|
|
30
|
+
detail?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface LensFieldProvenance {
|
|
33
|
+
id: string;
|
|
34
|
+
source: LensSource;
|
|
35
|
+
/** e.g. "/Users/x/repo/.synap/lens.json" or "SYNAP_WORKSPACE_ID". */
|
|
36
|
+
detail?: string;
|
|
37
|
+
/** "directory lens: /Users/x/repo/.synap/lens.json" — ready to print in parentheses. */
|
|
38
|
+
origin: string;
|
|
39
|
+
}
|
|
40
|
+
export interface LensProvenance {
|
|
41
|
+
workspace?: LensFieldProvenance;
|
|
42
|
+
project?: LensFieldProvenance;
|
|
43
|
+
session?: LensFieldProvenance;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Resolve each lens field to the FIRST rung that supplies it.
|
|
47
|
+
*
|
|
48
|
+
* Pure and order-driven: the caller passes rungs highest-precedence first, so
|
|
49
|
+
* this function can never disagree with the ladder in `hub-client.ts` about
|
|
50
|
+
* priority — it just reports which entry won. Rungs the caller could not
|
|
51
|
+
* evaluate (e.g. a pod-mismatched lens) are simply omitted by the caller,
|
|
52
|
+
* which is exactly how they behave in resolution.
|
|
53
|
+
*/
|
|
54
|
+
export declare function resolveLensProvenance(rungs: LensRung[]): LensProvenance;
|
|
20
55
|
export interface ResolvedLens {
|
|
21
56
|
workspace?: LensEntry;
|
|
22
57
|
project?: LensEntry;
|
|
@@ -13,6 +13,46 @@
|
|
|
13
13
|
* consumer will use neither. Callers wrap the plain names/ids this module
|
|
14
14
|
* resolves in whatever presentation their surface needs.
|
|
15
15
|
*/
|
|
16
|
+
/** Human wording for each rung — used verbatim in `synap lens` output. */
|
|
17
|
+
export const LENS_SOURCE_LABEL = {
|
|
18
|
+
flag: "explicit flag",
|
|
19
|
+
session: "session lens",
|
|
20
|
+
directory: "directory lens",
|
|
21
|
+
env: "environment variable",
|
|
22
|
+
global: "global config",
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Resolve each lens field to the FIRST rung that supplies it.
|
|
26
|
+
*
|
|
27
|
+
* Pure and order-driven: the caller passes rungs highest-precedence first, so
|
|
28
|
+
* this function can never disagree with the ladder in `hub-client.ts` about
|
|
29
|
+
* priority — it just reports which entry won. Rungs the caller could not
|
|
30
|
+
* evaluate (e.g. a pod-mismatched lens) are simply omitted by the caller,
|
|
31
|
+
* which is exactly how they behave in resolution.
|
|
32
|
+
*/
|
|
33
|
+
export function resolveLensProvenance(rungs) {
|
|
34
|
+
const pick = (field) => {
|
|
35
|
+
for (const rung of rungs) {
|
|
36
|
+
const id = rung[field];
|
|
37
|
+
if (!id)
|
|
38
|
+
continue;
|
|
39
|
+
return {
|
|
40
|
+
id,
|
|
41
|
+
source: rung.source,
|
|
42
|
+
detail: rung.detail,
|
|
43
|
+
origin: rung.detail
|
|
44
|
+
? `${LENS_SOURCE_LABEL[rung.source]}: ${rung.detail}`
|
|
45
|
+
: LENS_SOURCE_LABEL[rung.source],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
};
|
|
50
|
+
return {
|
|
51
|
+
workspace: pick("workspaceId"),
|
|
52
|
+
project: pick("projectId"),
|
|
53
|
+
session: pick("focusSessionId"),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
16
56
|
function describeField(entry) {
|
|
17
57
|
if (!entry?.id)
|
|
18
58
|
return undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"describe-lens.js","sourceRoot":"","sources":["../../src/lib/describe-lens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;
|
|
1
|
+
{"version":3,"file":"describe-lens.js","sourceRoot":"","sources":["../../src/lib/describe-lens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAgBH,0EAA0E;AAC1E,MAAM,CAAC,MAAM,iBAAiB,GAA+B;IAC3D,IAAI,EAAE,eAAe;IACrB,OAAO,EAAE,cAAc;IACvB,SAAS,EAAE,gBAAgB;IAC3B,GAAG,EAAE,sBAAsB;IAC3B,MAAM,EAAE,eAAe;CACxB,CAAC;AA0BF;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAiB;IACrD,MAAM,IAAI,GAAG,CAAC,KAAqD,EAAmC,EAAE;QACtG,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;YACvB,IAAI,CAAC,EAAE;gBAAE,SAAS;YAClB,OAAO;gBACL,EAAE;gBACF,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,MAAM,EAAE,IAAI,CAAC,MAAM;oBACjB,CAAC,CAAC,GAAG,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,MAAM,EAAE;oBACrD,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC;aACnC,CAAC;QACJ,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC;IACF,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,aAAa,CAAC;QAC9B,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC;QAC1B,OAAO,EAAE,IAAI,CAAC,gBAAgB,CAAC;KAChC,CAAC;AACJ,CAAC;AA2BD,SAAS,aAAa,CAAC,KAAiB;IACtC,IAAI,CAAC,KAAK,EAAE,EAAE;QAAE,OAAO,SAAS,CAAC;IACjC,OAAO,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,KAAK,CAAC,EAAE,EAAE,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAkB;IAC7C,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC;IAEvC,MAAM,KAAK,GAA2B;QACpC,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,IAAI,cAAc,EAAE,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC,EAAE;QAC3F,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE;QAC/E,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE;KAChF,CAAC;IAEF,OAAO,EAAE,UAAU,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC;AACzE,CAAC"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connect THIS computer to a pod — the pod-side device flow (RFC 8628,
|
|
3
|
+
* MACHINE-IDENTITY-PLAN §4). The only way `synap init` and `synap login` get a
|
|
4
|
+
* credential for a pod, on self-hosted and managed pods alike.
|
|
5
|
+
*
|
|
6
|
+
* 1. POST {pod}/api/hub/setup/device → a user code + where to enter it
|
|
7
|
+
* 2. the person opens the link on any device, signs in to the pod, approves
|
|
8
|
+
* the computer and the agents it asked for (one decision)
|
|
9
|
+
* 3. POST {pod}/api/hub/setup/device/token → 400 RFC 8628 errors until then,
|
|
10
|
+
* then ONCE: the computer's key + one key per approved agent
|
|
11
|
+
*
|
|
12
|
+
* The computer gets its OWN governed principal ("devbox"); each agent is a
|
|
13
|
+
* distinct agent on that computer ("Claude Code on devbox"). Nothing here needs
|
|
14
|
+
* a Synap account session, a browser on this machine, or a loopback port.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* The agent types the device door accepts (`SURFACE_AGENT_TYPES`,
|
|
18
|
+
* synap-backend `routers/hub-protocol/rest/setup.ts`). A mirror: the CLI does
|
|
19
|
+
* not depend on the backend package. A type outside it would 400 the whole
|
|
20
|
+
* request, so it is filtered out before asking.
|
|
21
|
+
*/
|
|
22
|
+
export declare const DEVICE_AGENT_TYPES: readonly ["claude-code", "claude-desktop", "cursor", "raycast", "codex", "openwebui", "discord", "proton", "generic"];
|
|
23
|
+
export type DeviceAgentType = (typeof DEVICE_AGENT_TYPES)[number];
|
|
24
|
+
/** The pod's cap on agents per request (`DEVICE_MAX_AGENTS`). */
|
|
25
|
+
export declare const DEVICE_MAX_AGENTS = 8;
|
|
26
|
+
/** The agents of `targets` the device door can approve, deduped, capped. */
|
|
27
|
+
export declare function deviceAgentsFor(targets: readonly string[]): DeviceAgentType[];
|
|
28
|
+
export interface DeviceKey {
|
|
29
|
+
apiKey: string;
|
|
30
|
+
keyId: string;
|
|
31
|
+
/** ISO time the key stops working, or null when it never expires. */
|
|
32
|
+
expiresAt: string | null;
|
|
33
|
+
}
|
|
34
|
+
export interface DeviceGrant {
|
|
35
|
+
computer: DeviceKey & {
|
|
36
|
+
id: string;
|
|
37
|
+
name: string;
|
|
38
|
+
};
|
|
39
|
+
agents: Array<DeviceKey & {
|
|
40
|
+
agentType: string;
|
|
41
|
+
agentUserId: string;
|
|
42
|
+
name: string;
|
|
43
|
+
displayName: string;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
export type DeviceConnectOutcome = {
|
|
47
|
+
status: "approved";
|
|
48
|
+
grant: DeviceGrant;
|
|
49
|
+
} | {
|
|
50
|
+
status: "denied";
|
|
51
|
+
message: string;
|
|
52
|
+
} | {
|
|
53
|
+
status: "expired";
|
|
54
|
+
message: string;
|
|
55
|
+
};
|
|
56
|
+
/** The pod could not start a request (an older pod has no device door: 404). */
|
|
57
|
+
export declare class DeviceStartError extends Error {
|
|
58
|
+
readonly status: number | null;
|
|
59
|
+
constructor(status: number | null, message: string);
|
|
60
|
+
}
|
|
61
|
+
export interface DeviceConnectDeps {
|
|
62
|
+
/** Terminal QR renderer; default the one Relay pairing uses. */
|
|
63
|
+
renderQr?: (text: string) => Promise<string>;
|
|
64
|
+
/** Shared poll loop; injectable so a test can shrink the clock. */
|
|
65
|
+
poll?: typeof import("./approval-poll.js").pollForApproval;
|
|
66
|
+
/** Multiply every server interval/expiry (tests only). Default 1. */
|
|
67
|
+
timeScale?: number;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Ask the pod to connect this computer, show the person where to approve it,
|
|
71
|
+
* and wait — honouring the pod's `interval` and `slow_down` — until it is
|
|
72
|
+
* approved, denied, or the code expires. Throws `DeviceStartError` only when
|
|
73
|
+
* the request could not be started at all.
|
|
74
|
+
*/
|
|
75
|
+
export declare function deviceConnect(podUrl: string, opts: {
|
|
76
|
+
agents: readonly string[];
|
|
77
|
+
clientName?: string;
|
|
78
|
+
hostname?: string;
|
|
79
|
+
platform?: string;
|
|
80
|
+
}, deps?: DeviceConnectDeps): Promise<DeviceConnectOutcome>;
|
|
81
|
+
/** Renew a computer key this close to (or past) its expiry. */
|
|
82
|
+
export declare const COMPUTER_KEY_RENEW_DAYS = 7;
|
|
83
|
+
/**
|
|
84
|
+
* True when a saved computer key expires within `COMPUTER_KEY_RENEW_DAYS`, or
|
|
85
|
+
* already has. A credential with no recorded expiry (a pasted key, an old
|
|
86
|
+
* operator key) is never "due": nothing says it expires.
|
|
87
|
+
*/
|
|
88
|
+
export declare function computerKeyNeedsRenewal(config: {
|
|
89
|
+
keyExpiresAt?: string | null;
|
|
90
|
+
}, now?: number): boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Keep what the device flow returned, once, where each reader already looks:
|
|
93
|
+
* - the computer's key → the pod profile `name` (it IS the CLI's identity
|
|
94
|
+
* for this pod now), with its expiry so it can be renewed in time;
|
|
95
|
+
* - each agent's key → the surface key store (`agentKeys`) AND the
|
|
96
|
+
* pre-approved slot its installer takes from, so `synap connect`'s own
|
|
97
|
+
* installer writes it into Claude Code / Codex / Cursor instead of minting.
|
|
98
|
+
* Fields of an existing profile (label, podId) are kept.
|
|
99
|
+
*/
|
|
100
|
+
export declare function saveDeviceGrant(podUrl: string, grant: DeviceGrant, profile: {
|
|
101
|
+
name: string;
|
|
102
|
+
podId?: string;
|
|
103
|
+
}): Promise<import("./pod.js").LocalPodConfig>;
|
|
104
|
+
/** The approved agents this CLI has an installer for, as `synap connect` targets. */
|
|
105
|
+
export declare function approvedTargets(grant: DeviceGrant): Promise<import("./targets.js").TargetName[]>;
|
|
106
|
+
/**
|
|
107
|
+
* Write each approved agent's own key into its client config — the same
|
|
108
|
+
* installers (`connectAgentsBatch` → `installForTarget`) `synap init` uses —
|
|
109
|
+
* for the callers that have no agents step of their own (`synap login`,
|
|
110
|
+
* reconnect, renewal).
|
|
111
|
+
*/
|
|
112
|
+
export declare function wireApprovedAgents(podUrl: string, computerKey: string, grant: DeviceGrant): Promise<void>;
|
|
113
|
+
/**
|
|
114
|
+
* Connect this computer to `podUrl` and save the result under profile `name`:
|
|
115
|
+
* the device flow, then `saveDeviceGrant`, then the agents' configs. The agents
|
|
116
|
+
* asked for are the ones found on this machine. Returns the saved profile, or
|
|
117
|
+
* null after printing why (denied, expired, or the pod can't do it).
|
|
118
|
+
*/
|
|
119
|
+
export declare function connectThisComputer(podUrl: string, profile: {
|
|
120
|
+
name: string;
|
|
121
|
+
podId?: string;
|
|
122
|
+
}, opts?: {
|
|
123
|
+
agents?: readonly string[];
|
|
124
|
+
wireAgents?: boolean;
|
|
125
|
+
}): Promise<import("./pod.js").LocalPodConfig | null>;
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connect THIS computer to a pod — the pod-side device flow (RFC 8628,
|
|
3
|
+
* MACHINE-IDENTITY-PLAN §4). The only way `synap init` and `synap login` get a
|
|
4
|
+
* credential for a pod, on self-hosted and managed pods alike.
|
|
5
|
+
*
|
|
6
|
+
* 1. POST {pod}/api/hub/setup/device → a user code + where to enter it
|
|
7
|
+
* 2. the person opens the link on any device, signs in to the pod, approves
|
|
8
|
+
* the computer and the agents it asked for (one decision)
|
|
9
|
+
* 3. POST {pod}/api/hub/setup/device/token → 400 RFC 8628 errors until then,
|
|
10
|
+
* then ONCE: the computer's key + one key per approved agent
|
|
11
|
+
*
|
|
12
|
+
* The computer gets its OWN governed principal ("devbox"); each agent is a
|
|
13
|
+
* distinct agent on that computer ("Claude Code on devbox"). Nothing here needs
|
|
14
|
+
* a Synap account session, a browser on this machine, or a loopback port.
|
|
15
|
+
*/
|
|
16
|
+
import os from "os";
|
|
17
|
+
import chalk from "chalk";
|
|
18
|
+
import { log } from "../utils/logger.js";
|
|
19
|
+
/**
|
|
20
|
+
* The agent types the device door accepts (`SURFACE_AGENT_TYPES`,
|
|
21
|
+
* synap-backend `routers/hub-protocol/rest/setup.ts`). A mirror: the CLI does
|
|
22
|
+
* not depend on the backend package. A type outside it would 400 the whole
|
|
23
|
+
* request, so it is filtered out before asking.
|
|
24
|
+
*/
|
|
25
|
+
export const DEVICE_AGENT_TYPES = [
|
|
26
|
+
"claude-code",
|
|
27
|
+
"claude-desktop",
|
|
28
|
+
"cursor",
|
|
29
|
+
"raycast",
|
|
30
|
+
"codex",
|
|
31
|
+
"openwebui",
|
|
32
|
+
"discord",
|
|
33
|
+
"proton",
|
|
34
|
+
"generic",
|
|
35
|
+
];
|
|
36
|
+
/** The pod's cap on agents per request (`DEVICE_MAX_AGENTS`). */
|
|
37
|
+
export const DEVICE_MAX_AGENTS = 8;
|
|
38
|
+
/** The agents of `targets` the device door can approve, deduped, capped. */
|
|
39
|
+
export function deviceAgentsFor(targets) {
|
|
40
|
+
const ok = targets.filter((t) => DEVICE_AGENT_TYPES.includes(t));
|
|
41
|
+
return [...new Set(ok)].slice(0, DEVICE_MAX_AGENTS);
|
|
42
|
+
}
|
|
43
|
+
/** The pod could not start a request (an older pod has no device door: 404). */
|
|
44
|
+
export class DeviceStartError extends Error {
|
|
45
|
+
status;
|
|
46
|
+
constructor(status, message) {
|
|
47
|
+
super(message);
|
|
48
|
+
this.status = status;
|
|
49
|
+
this.name = "DeviceStartError";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const podHost = (podUrl) => {
|
|
53
|
+
try {
|
|
54
|
+
return new URL(podUrl).host;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return podUrl;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Ask the pod to connect this computer, show the person where to approve it,
|
|
62
|
+
* and wait — honouring the pod's `interval` and `slow_down` — until it is
|
|
63
|
+
* approved, denied, or the code expires. Throws `DeviceStartError` only when
|
|
64
|
+
* the request could not be started at all.
|
|
65
|
+
*/
|
|
66
|
+
export async function deviceConnect(podUrl, opts, deps = {}) {
|
|
67
|
+
const base = podUrl.replace(/\/+$/, "");
|
|
68
|
+
const scale = deps.timeScale ?? 1;
|
|
69
|
+
const host = podHost(base);
|
|
70
|
+
let res;
|
|
71
|
+
try {
|
|
72
|
+
res = await fetch(`${base}/api/hub/setup/device`, {
|
|
73
|
+
method: "POST",
|
|
74
|
+
headers: { "Content-Type": "application/json" },
|
|
75
|
+
body: JSON.stringify({
|
|
76
|
+
clientName: opts.clientName ?? "Synap CLI",
|
|
77
|
+
hostname: (opts.hostname ?? os.hostname()).slice(0, 253) || "computer",
|
|
78
|
+
platform: opts.platform ?? process.platform,
|
|
79
|
+
agents: deviceAgentsFor(opts.agents),
|
|
80
|
+
}),
|
|
81
|
+
signal: AbortSignal.timeout(15_000),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
throw new DeviceStartError(null, `Could not reach ${host}: ${err.message}`);
|
|
86
|
+
}
|
|
87
|
+
if (!res.ok) {
|
|
88
|
+
const body = (await res.json().catch(() => null));
|
|
89
|
+
throw new DeviceStartError(res.status, res.status === 404
|
|
90
|
+
? `${host} cannot connect a computer yet (it needs an update).`
|
|
91
|
+
: `${host} could not start the request (HTTP ${res.status})${body?.error_description ? `: ${body.error_description}` : "."}`);
|
|
92
|
+
}
|
|
93
|
+
const auth = (await res.json());
|
|
94
|
+
// The instruction comes FIRST and stays on screen: on a headless box (SSH)
|
|
95
|
+
// there is no browser to open — the person uses their phone or laptop.
|
|
96
|
+
log.blank();
|
|
97
|
+
console.log(` Open ${chalk.cyan.bold(auth.verification_uri)} on any device and enter ${chalk.bold(auth.user_code)}`);
|
|
98
|
+
if (auth.verification_uri_complete) {
|
|
99
|
+
log.dim(`Or open: ${auth.verification_uri_complete}`);
|
|
100
|
+
try {
|
|
101
|
+
const qr = await (deps.renderQr ?? (await import("./pair-link.js")).renderTerminalQr)(auth.verification_uri_complete);
|
|
102
|
+
console.log("\n" + qr);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// The link above is enough; a QR is a convenience.
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const minutes = Math.max(1, Math.round(auth.expires_in / 60));
|
|
109
|
+
log.dim(`Waiting for you to approve this computer on ${host} (the code lasts ${minutes} min)…`);
|
|
110
|
+
const poll = deps.poll ?? (await import("./approval-poll.js")).pollForApproval;
|
|
111
|
+
let lastError;
|
|
112
|
+
try {
|
|
113
|
+
const grant = await poll({
|
|
114
|
+
url: `${base}/api/hub/setup/device/token`,
|
|
115
|
+
method: "POST",
|
|
116
|
+
body: JSON.stringify({ device_code: auth.device_code }),
|
|
117
|
+
readErrorBodies: true,
|
|
118
|
+
intervalMs: (auth.interval ?? 5) * 1000 * scale,
|
|
119
|
+
timeoutMs: auth.expires_in * 1000 * scale,
|
|
120
|
+
isApproved: (d) => typeof d.access_token === "string",
|
|
121
|
+
isRejected: (d) => {
|
|
122
|
+
const e = d.error;
|
|
123
|
+
if (e === "access_denied" || e === "expired_token" || e === "invalid_grant") {
|
|
124
|
+
lastError = e;
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
return false;
|
|
128
|
+
},
|
|
129
|
+
// RFC 8628 §3.5: slow_down adds to the interval for the rest of the wait.
|
|
130
|
+
nextIntervalMs: (d, current) => {
|
|
131
|
+
const body = d;
|
|
132
|
+
if (body.error !== "slow_down")
|
|
133
|
+
return current;
|
|
134
|
+
return typeof body.interval === "number" ? body.interval * 1000 * scale : current + 5000 * scale;
|
|
135
|
+
},
|
|
136
|
+
onApproved: (d) => {
|
|
137
|
+
const b = d;
|
|
138
|
+
return { computer: b.computer, agents: b.agents ?? [] };
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
return { status: "approved", grant };
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
if (lastError === "access_denied")
|
|
145
|
+
return { status: "denied", message: `Denied on ${host}.` };
|
|
146
|
+
return { status: "expired", message: "Code expired — run again." };
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/** Renew a computer key this close to (or past) its expiry. */
|
|
150
|
+
export const COMPUTER_KEY_RENEW_DAYS = 7;
|
|
151
|
+
/**
|
|
152
|
+
* True when a saved computer key expires within `COMPUTER_KEY_RENEW_DAYS`, or
|
|
153
|
+
* already has. A credential with no recorded expiry (a pasted key, an old
|
|
154
|
+
* operator key) is never "due": nothing says it expires.
|
|
155
|
+
*/
|
|
156
|
+
export function computerKeyNeedsRenewal(config, now = Date.now()) {
|
|
157
|
+
if (!config.keyExpiresAt)
|
|
158
|
+
return false;
|
|
159
|
+
const at = Date.parse(config.keyExpiresAt);
|
|
160
|
+
if (Number.isNaN(at))
|
|
161
|
+
return false;
|
|
162
|
+
return at - now < COMPUTER_KEY_RENEW_DAYS * 24 * 60 * 60 * 1000;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Keep what the device flow returned, once, where each reader already looks:
|
|
166
|
+
* - the computer's key → the pod profile `name` (it IS the CLI's identity
|
|
167
|
+
* for this pod now), with its expiry so it can be renewed in time;
|
|
168
|
+
* - each agent's key → the surface key store (`agentKeys`) AND the
|
|
169
|
+
* pre-approved slot its installer takes from, so `synap connect`'s own
|
|
170
|
+
* installer writes it into Claude Code / Codex / Cursor instead of minting.
|
|
171
|
+
* Fields of an existing profile (label, podId) are kept.
|
|
172
|
+
*/
|
|
173
|
+
export async function saveDeviceGrant(podUrl, grant, profile) {
|
|
174
|
+
const pod = await import("./pod.js");
|
|
175
|
+
const { presetApprovedKey } = await import("./batch-approval.js");
|
|
176
|
+
const prior = pod.listPodProfiles().find((p) => p.name === profile.name)?.config;
|
|
177
|
+
const podId = profile.podId ?? prior?.podId;
|
|
178
|
+
const config = {
|
|
179
|
+
...prior,
|
|
180
|
+
podUrl,
|
|
181
|
+
...(podId ? { podId } : {}),
|
|
182
|
+
workspaceId: prior?.workspaceId ?? "",
|
|
183
|
+
agentUserId: grant.computer.id,
|
|
184
|
+
hubApiKey: grant.computer.apiKey,
|
|
185
|
+
credential: "computer",
|
|
186
|
+
computerName: grant.computer.name,
|
|
187
|
+
savedAt: new Date().toISOString(),
|
|
188
|
+
};
|
|
189
|
+
if (grant.computer.expiresAt)
|
|
190
|
+
config.keyExpiresAt = grant.computer.expiresAt;
|
|
191
|
+
else
|
|
192
|
+
delete config.keyExpiresAt;
|
|
193
|
+
pod.addPodProfile(profile.name, config);
|
|
194
|
+
for (const a of grant.agents) {
|
|
195
|
+
if (pod.SURFACE_NAMES.includes(a.agentType)) {
|
|
196
|
+
pod.setSurfaceAgentKey(a.agentType, {
|
|
197
|
+
hubApiKey: a.apiKey,
|
|
198
|
+
agentUserId: a.agentUserId,
|
|
199
|
+
podUrl,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
presetApprovedKey(a.agentType, { hubApiKey: a.apiKey, agentUserId: a.agentUserId });
|
|
203
|
+
}
|
|
204
|
+
return config;
|
|
205
|
+
}
|
|
206
|
+
/** The approved agents this CLI has an installer for, as `synap connect` targets. */
|
|
207
|
+
export async function approvedTargets(grant) {
|
|
208
|
+
const { isTargetName } = await import("./targets.js");
|
|
209
|
+
return grant.agents.map((a) => a.agentType).filter(isTargetName);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Write each approved agent's own key into its client config — the same
|
|
213
|
+
* installers (`connectAgentsBatch` → `installForTarget`) `synap init` uses —
|
|
214
|
+
* for the callers that have no agents step of their own (`synap login`,
|
|
215
|
+
* reconnect, renewal).
|
|
216
|
+
*/
|
|
217
|
+
export async function wireApprovedAgents(podUrl, computerKey, grant) {
|
|
218
|
+
const targets = await approvedTargets(grant);
|
|
219
|
+
if (targets.length === 0)
|
|
220
|
+
return;
|
|
221
|
+
const { connectAgentsBatch } = await import("./agent-door.js");
|
|
222
|
+
await connectAgentsBatch({ podUrl, apiKey: computerKey }, targets, {});
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Connect this computer to `podUrl` and save the result under profile `name`:
|
|
226
|
+
* the device flow, then `saveDeviceGrant`, then the agents' configs. The agents
|
|
227
|
+
* asked for are the ones found on this machine. Returns the saved profile, or
|
|
228
|
+
* null after printing why (denied, expired, or the pod can't do it).
|
|
229
|
+
*/
|
|
230
|
+
export async function connectThisComputer(podUrl, profile, opts = {}) {
|
|
231
|
+
let agents = opts.agents;
|
|
232
|
+
if (!agents) {
|
|
233
|
+
const { detectAgents } = await import("./agent-detect.js");
|
|
234
|
+
agents = detectAgents()
|
|
235
|
+
.filter((r) => r.found)
|
|
236
|
+
.map((r) => r.target);
|
|
237
|
+
}
|
|
238
|
+
let outcome;
|
|
239
|
+
try {
|
|
240
|
+
outcome = await deviceConnect(podUrl, { agents });
|
|
241
|
+
}
|
|
242
|
+
catch (err) {
|
|
243
|
+
log.error(err instanceof Error ? err.message : String(err));
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
if (outcome.status !== "approved") {
|
|
247
|
+
log.error(outcome.message);
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
const config = await saveDeviceGrant(podUrl, outcome.grant, profile);
|
|
251
|
+
log.success(`${outcome.grant.computer.name} connected to ${podHost(podUrl)}`);
|
|
252
|
+
if (opts.wireAgents !== false)
|
|
253
|
+
await wireApprovedAgents(podUrl, config.hubApiKey, outcome.grant);
|
|
254
|
+
return config;
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=device-connect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-connect.js","sourceRoot":"","sources":["../../src/lib/device-connect.ts"],"names":[],"mappings":"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"}
|