@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,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-click approval for the agent keys `synap init` mints (V1 D5).
|
|
3
|
+
*
|
|
4
|
+
* Codex and Cursor each mint their agent key with `requireApproval`, and
|
|
5
|
+
* `provisionAgentKey` used to open one review page per key and block on it.
|
|
6
|
+
* Connecting both meant two pages and two clicks, one after the other.
|
|
7
|
+
*
|
|
8
|
+
* Now `init` mints every key that needs approval FIRST (deferred), opens ONE
|
|
9
|
+
* page (`/approve-agents?keys=…` on the pod admin, backed by
|
|
10
|
+
* `POST /api/hub/setup/agent/pending/approve-batch`), and polls every key
|
|
11
|
+
* until the person approves them together. The approved keys are then handed
|
|
12
|
+
* to the installers through `takePreApprovedKey`, so each installer's own
|
|
13
|
+
* `provisionAgentKey` call returns the key the person already approved instead
|
|
14
|
+
* of minting a second one.
|
|
15
|
+
*
|
|
16
|
+
* A pod older than the batch door has no `/approve-agents` page: `init`
|
|
17
|
+
* probes the batch lookup door first and, when it is missing, opens each
|
|
18
|
+
* key's own review page instead (every pod version serves those).
|
|
19
|
+
*/
|
|
20
|
+
import type { TargetName } from "./targets.js";
|
|
21
|
+
/** How a target's installer mints its agent key — ONE table, read by both. */
|
|
22
|
+
export interface KeyMintSpec {
|
|
23
|
+
agentType: string;
|
|
24
|
+
idempotent: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The targets whose installer mints an agent key that needs the person's
|
|
28
|
+
* approval. Claude Code mints without approval, so it is not here. The installers read their spec from this table
|
|
29
|
+
* (`installCodex` / `installCursor`), so the pre-mint cannot ask for a key the
|
|
30
|
+
* installer would not have asked for.
|
|
31
|
+
*/
|
|
32
|
+
export declare const TARGET_KEY_MINT: Partial<Record<TargetName, KeyMintSpec>>;
|
|
33
|
+
export interface ReadyAgentKey {
|
|
34
|
+
hubApiKey: string;
|
|
35
|
+
agentUserId: string;
|
|
36
|
+
}
|
|
37
|
+
/** Take (once) the key already minted — and approved — for `agentType`. */
|
|
38
|
+
export declare function takePreApprovedKey(agentType: string): ReadyAgentKey | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Hand an installer a key that is already approved — the device flow returns
|
|
41
|
+
* one per agent the person approved with the computer (`device-connect.ts`).
|
|
42
|
+
* The installer's own `provisionAgentKey` call then takes it instead of minting,
|
|
43
|
+
* and `approveAgentKeysAtOnce` asks for nothing more for that agent.
|
|
44
|
+
*/
|
|
45
|
+
export declare function presetApprovedKey(agentType: string, key: ReadyAgentKey): void;
|
|
46
|
+
/** The ONE review page for several pending keys, on the same origin as a single key's review URL. */
|
|
47
|
+
export declare function batchReviewUrl(reviewUrl: string, keyIds: readonly string[]): string;
|
|
48
|
+
/**
|
|
49
|
+
* Does this pod serve the batch approval page? Its lookup door answers 401/400
|
|
50
|
+
* without a signed-in session; a pod older than it answers 404. Unreachable
|
|
51
|
+
* reads as "no": the per-key pages work on every pod.
|
|
52
|
+
*/
|
|
53
|
+
export declare function podSupportsBatchApproval(podUrl: string): Promise<boolean>;
|
|
54
|
+
/** One pending key, as the wait sees it. */
|
|
55
|
+
export interface PendingKeyWait {
|
|
56
|
+
podUrl: string;
|
|
57
|
+
pendingKey: string;
|
|
58
|
+
pendingToken: string;
|
|
59
|
+
humanApiKey: string;
|
|
60
|
+
}
|
|
61
|
+
export interface BatchApprovalDeps {
|
|
62
|
+
provision?: typeof import("./targets.js").provisionAgentKey;
|
|
63
|
+
/**
|
|
64
|
+
* Wait for one pending key to go live. Default `waitForKeyLive`: it asks
|
|
65
|
+
* about the pending key WITH the pending key (`/auth/key-state`), so the
|
|
66
|
+
* wait still sees the approval when the person's own key is the one that
|
|
67
|
+
* approval just replaced (2026-10-12). Older pods: the per-key pending door.
|
|
68
|
+
*/
|
|
69
|
+
wait?: (p: PendingKeyWait) => Promise<import("./key-approval.js").KeyWaitOutcome>;
|
|
70
|
+
openUrl?: (url: string) => void;
|
|
71
|
+
supportsBatch?: (podUrl: string) => Promise<boolean>;
|
|
72
|
+
}
|
|
73
|
+
export interface BatchApprovalResult {
|
|
74
|
+
/** Targets the person did not approve (rejected, timed out, or the mint failed). */
|
|
75
|
+
notApproved: TargetName[];
|
|
76
|
+
/** Why, per target, for the one warning line `init` prints. */
|
|
77
|
+
reasons: Partial<Record<TargetName, string>>;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Mint every approval-gated key for `targets`, open one page, wait for all.
|
|
81
|
+
* Returns the targets the person did NOT approve (rejected, timed out, or
|
|
82
|
+
* failed to mint): `init` skips those instead of asking again, and prints
|
|
83
|
+
* one line for each.
|
|
84
|
+
*/
|
|
85
|
+
export declare function approveAgentKeysAtOnce(podUrl: string, humanApiKey: string, targets: readonly TargetName[], deps?: BatchApprovalDeps): Promise<BatchApprovalResult>;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-click approval for the agent keys `synap init` mints (V1 D5).
|
|
3
|
+
*
|
|
4
|
+
* Codex and Cursor each mint their agent key with `requireApproval`, and
|
|
5
|
+
* `provisionAgentKey` used to open one review page per key and block on it.
|
|
6
|
+
* Connecting both meant two pages and two clicks, one after the other.
|
|
7
|
+
*
|
|
8
|
+
* Now `init` mints every key that needs approval FIRST (deferred), opens ONE
|
|
9
|
+
* page (`/approve-agents?keys=…` on the pod admin, backed by
|
|
10
|
+
* `POST /api/hub/setup/agent/pending/approve-batch`), and polls every key
|
|
11
|
+
* until the person approves them together. The approved keys are then handed
|
|
12
|
+
* to the installers through `takePreApprovedKey`, so each installer's own
|
|
13
|
+
* `provisionAgentKey` call returns the key the person already approved instead
|
|
14
|
+
* of minting a second one.
|
|
15
|
+
*
|
|
16
|
+
* A pod older than the batch door has no `/approve-agents` page: `init`
|
|
17
|
+
* probes the batch lookup door first and, when it is missing, opens each
|
|
18
|
+
* key's own review page instead (every pod version serves those).
|
|
19
|
+
*/
|
|
20
|
+
import chalk from "chalk";
|
|
21
|
+
import { log } from "../utils/logger.js";
|
|
22
|
+
import { openPodPage } from "./safe-open.js";
|
|
23
|
+
/**
|
|
24
|
+
* The targets whose installer mints an agent key that needs the person's
|
|
25
|
+
* approval. Claude Code mints without approval, so it is not here. The installers read their spec from this table
|
|
26
|
+
* (`installCodex` / `installCursor`), so the pre-mint cannot ask for a key the
|
|
27
|
+
* installer would not have asked for.
|
|
28
|
+
*/
|
|
29
|
+
export const TARGET_KEY_MINT = {
|
|
30
|
+
codex: { agentType: "codex", idempotent: true },
|
|
31
|
+
cursor: { agentType: "cursor", idempotent: true },
|
|
32
|
+
};
|
|
33
|
+
const ready = new Map();
|
|
34
|
+
/** Take (once) the key already minted — and approved — for `agentType`. */
|
|
35
|
+
export function takePreApprovedKey(agentType) {
|
|
36
|
+
const key = ready.get(agentType);
|
|
37
|
+
ready.delete(agentType);
|
|
38
|
+
return key;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Hand an installer a key that is already approved — the device flow returns
|
|
42
|
+
* one per agent the person approved with the computer (`device-connect.ts`).
|
|
43
|
+
* The installer's own `provisionAgentKey` call then takes it instead of minting,
|
|
44
|
+
* and `approveAgentKeysAtOnce` asks for nothing more for that agent.
|
|
45
|
+
*/
|
|
46
|
+
export function presetApprovedKey(agentType, key) {
|
|
47
|
+
ready.set(agentType, key);
|
|
48
|
+
}
|
|
49
|
+
/** The ONE review page for several pending keys, on the same origin as a single key's review URL. */
|
|
50
|
+
export function batchReviewUrl(reviewUrl, keyIds) {
|
|
51
|
+
const origin = new URL(reviewUrl).origin;
|
|
52
|
+
return `${origin}/approve-agents?keys=${keyIds.map(encodeURIComponent).join(",")}`;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Does this pod serve the batch approval page? Its lookup door answers 401/400
|
|
56
|
+
* without a signed-in session; a pod older than it answers 404. Unreachable
|
|
57
|
+
* reads as "no": the per-key pages work on every pod.
|
|
58
|
+
*/
|
|
59
|
+
export async function podSupportsBatchApproval(podUrl) {
|
|
60
|
+
const res = await fetch(`${podUrl.replace(/\/$/, "")}/api/hub/setup/agent/pending/lookup`, {
|
|
61
|
+
method: "POST",
|
|
62
|
+
headers: { "Content-Type": "application/json" },
|
|
63
|
+
body: JSON.stringify({ keyIds: [] }),
|
|
64
|
+
signal: AbortSignal.timeout(10_000),
|
|
65
|
+
}).catch(() => null);
|
|
66
|
+
return res !== null && res.status !== 404;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Mint every approval-gated key for `targets`, open one page, wait for all.
|
|
70
|
+
* Returns the targets the person did NOT approve (rejected, timed out, or
|
|
71
|
+
* failed to mint): `init` skips those instead of asking again, and prints
|
|
72
|
+
* one line for each.
|
|
73
|
+
*/
|
|
74
|
+
export async function approveAgentKeysAtOnce(podUrl, humanApiKey, targets, deps = {}) {
|
|
75
|
+
const provision = deps.provision ?? (await import("./targets.js")).provisionAgentKey;
|
|
76
|
+
const wait = deps.wait ?? (await import("./key-approval.js")).waitForKeyLive;
|
|
77
|
+
const openUrl = deps.openUrl ?? (await import("./targets.js")).openBrowserUrl;
|
|
78
|
+
const supportsBatch = deps.supportsBatch ?? podSupportsBatchApproval;
|
|
79
|
+
const podBase = podUrl.replace(/\/$/, "");
|
|
80
|
+
const pending = [];
|
|
81
|
+
const notApproved = [];
|
|
82
|
+
const reasons = {};
|
|
83
|
+
for (const target of targets) {
|
|
84
|
+
const spec = TARGET_KEY_MINT[target];
|
|
85
|
+
if (!spec)
|
|
86
|
+
continue;
|
|
87
|
+
// Approved already (with the computer, in the device flow): nothing to ask.
|
|
88
|
+
if (ready.has(spec.agentType))
|
|
89
|
+
continue;
|
|
90
|
+
try {
|
|
91
|
+
const r = await provision(podUrl, humanApiKey, spec.agentType, {
|
|
92
|
+
idempotent: spec.idempotent,
|
|
93
|
+
deferApproval: true,
|
|
94
|
+
});
|
|
95
|
+
if (r.pending) {
|
|
96
|
+
pending.push({
|
|
97
|
+
target,
|
|
98
|
+
agentType: spec.agentType,
|
|
99
|
+
hubApiKey: r.hubApiKey,
|
|
100
|
+
agentUserId: r.agentUserId,
|
|
101
|
+
...r.pending,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
// Already valid (reused) or active on mint: nothing to approve.
|
|
106
|
+
ready.set(spec.agentType, { hubApiKey: r.hubApiKey, agentUserId: r.agentUserId });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
notApproved.push(target);
|
|
111
|
+
reasons[target] = err instanceof Error ? err.message : String(err);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (pending.length === 0)
|
|
115
|
+
return { notApproved, reasons };
|
|
116
|
+
// One key keeps its own review page (which every pod version serves); so do
|
|
117
|
+
// several on a pod that predates the batch page.
|
|
118
|
+
const batch = pending.length > 1 && (await supportsBatch(podUrl));
|
|
119
|
+
const urls = batch
|
|
120
|
+
? [batchReviewUrl(pending[0].reviewUrl, pending.map((p) => p.pendingToken))]
|
|
121
|
+
: pending.map((p) => p.reviewUrl);
|
|
122
|
+
log.blank();
|
|
123
|
+
log.info(batch
|
|
124
|
+
? `Approve your ${pending.length} agents in one step:`
|
|
125
|
+
: pending.length > 1
|
|
126
|
+
? `Approve each of your ${pending.length} agents:`
|
|
127
|
+
: "Approve your agent:");
|
|
128
|
+
for (const url of urls) {
|
|
129
|
+
log.dim(url);
|
|
130
|
+
// A pod-supplied link: opened only on this pod's own / pod-admin origin.
|
|
131
|
+
openPodPage(url, podUrl, openUrl);
|
|
132
|
+
}
|
|
133
|
+
process.stdout.write(chalk.dim(" Waiting for approval"));
|
|
134
|
+
const ticker = setInterval(() => process.stdout.write(chalk.dim(".")), 2000);
|
|
135
|
+
try {
|
|
136
|
+
const outcomes = await Promise.allSettled(pending.map((p) => wait({ podUrl: podBase, pendingKey: p.hubApiKey, pendingToken: p.pendingToken, humanApiKey })));
|
|
137
|
+
process.stdout.write("\n");
|
|
138
|
+
const { clearPendingAgentKey } = await import("./pod.js");
|
|
139
|
+
outcomes.forEach((o, i) => {
|
|
140
|
+
const p = pending[i];
|
|
141
|
+
const outcome = o.status === "fulfilled" ? o.value : null;
|
|
142
|
+
if (outcome === "approved") {
|
|
143
|
+
// Live now: the recorded request is done; the installer writes the key.
|
|
144
|
+
clearPendingAgentKey(p.agentType, podUrl);
|
|
145
|
+
ready.set(p.agentType, { hubApiKey: p.hubApiKey, agentUserId: p.agentUserId });
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
notApproved.push(p.target);
|
|
149
|
+
if (outcome === "rejected") {
|
|
150
|
+
clearPendingAgentKey(p.agentType, podUrl);
|
|
151
|
+
reasons[p.target] = "It was declined on the pod.";
|
|
152
|
+
}
|
|
153
|
+
else if (outcome === "timeout") {
|
|
154
|
+
// Still filed and recorded: the next connect resumes this request.
|
|
155
|
+
reasons[p.target] = `Nobody approved it in time — approve it at ${p.reviewUrl}, then run: synap doctor --fix`;
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
reasons[p.target] = o.status === "rejected" && o.reason instanceof Error ? o.reason.message : String(o.status === "rejected" ? o.reason : "unknown");
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
finally {
|
|
163
|
+
clearInterval(ticker);
|
|
164
|
+
}
|
|
165
|
+
const approved = pending.length - pending.filter((p) => notApproved.includes(p.target)).length;
|
|
166
|
+
if (approved > 0)
|
|
167
|
+
log.success(`Approved ${approved} agent${approved > 1 ? "s" : ""}.`);
|
|
168
|
+
return { notApproved, reasons };
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=batch-approval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch-approval.js","sourceRoot":"","sources":["../../src/lib/batch-approval.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The run box's ONE way out: an HTTP(S) proxy on the provisioner, bound to the
|
|
3
|
+
* box bridge. A box has no gateway and no resolver; everything it fetches goes
|
|
4
|
+
* through here, and here decides (runbox design C4 "Egress"):
|
|
5
|
+
*
|
|
6
|
+
* setup phase → the package registries only (PyPI, npm)
|
|
7
|
+
* run phase → the hosts the script declared, nothing else
|
|
8
|
+
*
|
|
9
|
+
* A name that passes is resolved HERE, every address it resolves to is checked
|
|
10
|
+
* against internal ranges (RFC1918, loopback, link-local and cloud metadata,
|
|
11
|
+
* CGNAT = the tailnet), and the connection goes to the address that was
|
|
12
|
+
* checked, never to a second lookup — so a declared host that resolves to the
|
|
13
|
+
* pod (DNS rebinding) is refused. Every refusal is recorded for the evidence
|
|
14
|
+
* bundle. The bridge firewall is the wall; this is the policy.
|
|
15
|
+
*/
|
|
16
|
+
import http from "node:http";
|
|
17
|
+
import net from "node:net";
|
|
18
|
+
export type BoxPhase = "setup" | "run";
|
|
19
|
+
/** Hosts a box may reach while it installs declared packages. */
|
|
20
|
+
export declare const REGISTRY_HOSTS: readonly string[];
|
|
21
|
+
/** Ports a bare allow entry opens: the web only. */
|
|
22
|
+
export declare const ALLOWED_PORTS: readonly number[];
|
|
23
|
+
export interface BlockedEgress {
|
|
24
|
+
host: string;
|
|
25
|
+
port: number;
|
|
26
|
+
phase: BoxPhase;
|
|
27
|
+
reason: string;
|
|
28
|
+
at: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* True when a box must not connect to this address. An unparsable address is
|
|
32
|
+
* forbidden: the check fails closed.
|
|
33
|
+
*/
|
|
34
|
+
export declare function isForbiddenAddress(ip: string): boolean;
|
|
35
|
+
/** Lower-cased, trailing dot dropped, brackets of an IPv6 literal removed. */
|
|
36
|
+
export declare function normalizeHost(host: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* `host` matches an allow entry exactly, or a `*.example.com` entry as a
|
|
39
|
+
* strict subdomain (never the apex, never a suffix like `evilexample.com`).
|
|
40
|
+
*/
|
|
41
|
+
export declare function hostMatches(host: string, allow: readonly string[]): boolean;
|
|
42
|
+
export interface EgressPolicy {
|
|
43
|
+
phase: BoxPhase;
|
|
44
|
+
/** The script's declared hosts (the run-phase allowlist). */
|
|
45
|
+
runHosts: readonly string[];
|
|
46
|
+
}
|
|
47
|
+
export type EgressDecision = {
|
|
48
|
+
allow: true;
|
|
49
|
+
} | {
|
|
50
|
+
allow: false;
|
|
51
|
+
reason: string;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* The name-level decision, before any lookup. A bare entry opens the web
|
|
55
|
+
* ports (80, 443); a `host:port` entry opens that port on that host only.
|
|
56
|
+
*/
|
|
57
|
+
export declare function decideByName(host: string, port: number, policy: EgressPolicy): EgressDecision;
|
|
58
|
+
export type Resolver = (host: string) => Promise<string[]>;
|
|
59
|
+
/**
|
|
60
|
+
* The full decision: by name, then by every resolved address. Returns the ONE
|
|
61
|
+
* address to connect to, so the connection never re-resolves.
|
|
62
|
+
*/
|
|
63
|
+
export declare function decideEgress(host: string, port: number, policy: EgressPolicy, resolve?: Resolver): Promise<{
|
|
64
|
+
allow: true;
|
|
65
|
+
address: string;
|
|
66
|
+
} | {
|
|
67
|
+
allow: false;
|
|
68
|
+
reason: string;
|
|
69
|
+
}>;
|
|
70
|
+
export interface EgressProxyOptions {
|
|
71
|
+
/** The policy for a box, by its address on the bridge; null = not a live box. */
|
|
72
|
+
policyFor: (sourceIp: string) => EgressPolicy | null;
|
|
73
|
+
onBlock: (sourceIp: string, block: BlockedEgress) => void;
|
|
74
|
+
resolve?: Resolver;
|
|
75
|
+
/** Connect to the checked address (tests replace it). */
|
|
76
|
+
connect?: (address: string, port: number) => net.Socket;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* An HTTP proxy: CONNECT for HTTPS (a tunnel to the checked address), and
|
|
80
|
+
* absolute-form requests for plain HTTP. Anything else is refused.
|
|
81
|
+
*/
|
|
82
|
+
export declare function createEgressProxy(opts: EgressProxyOptions): http.Server;
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The run box's ONE way out: an HTTP(S) proxy on the provisioner, bound to the
|
|
3
|
+
* box bridge. A box has no gateway and no resolver; everything it fetches goes
|
|
4
|
+
* through here, and here decides (runbox design C4 "Egress"):
|
|
5
|
+
*
|
|
6
|
+
* setup phase → the package registries only (PyPI, npm)
|
|
7
|
+
* run phase → the hosts the script declared, nothing else
|
|
8
|
+
*
|
|
9
|
+
* A name that passes is resolved HERE, every address it resolves to is checked
|
|
10
|
+
* against internal ranges (RFC1918, loopback, link-local and cloud metadata,
|
|
11
|
+
* CGNAT = the tailnet), and the connection goes to the address that was
|
|
12
|
+
* checked, never to a second lookup — so a declared host that resolves to the
|
|
13
|
+
* pod (DNS rebinding) is refused. Every refusal is recorded for the evidence
|
|
14
|
+
* bundle. The bridge firewall is the wall; this is the policy.
|
|
15
|
+
*/
|
|
16
|
+
import dns from "node:dns/promises";
|
|
17
|
+
import http from "node:http";
|
|
18
|
+
import net from "node:net";
|
|
19
|
+
/** Hosts a box may reach while it installs declared packages. */
|
|
20
|
+
export const REGISTRY_HOSTS = [
|
|
21
|
+
"pypi.org",
|
|
22
|
+
"files.pythonhosted.org",
|
|
23
|
+
"registry.npmjs.org",
|
|
24
|
+
];
|
|
25
|
+
/** Ports a bare allow entry opens: the web only. */
|
|
26
|
+
export const ALLOWED_PORTS = [80, 443];
|
|
27
|
+
// ─── addresses ───────────────────────────────────────────────────────────────
|
|
28
|
+
function v4ToInt(ip) {
|
|
29
|
+
return ip.split(".").reduce((acc, o) => (acc << 8) + Number(o), 0) >>> 0;
|
|
30
|
+
}
|
|
31
|
+
function inV4(ip, cidr) {
|
|
32
|
+
const [base, bits] = cidr.split("/");
|
|
33
|
+
const mask = Number(bits) === 0 ? 0 : (~0 << (32 - Number(bits))) >>> 0;
|
|
34
|
+
return (ip & mask) === (v4ToInt(base) & mask);
|
|
35
|
+
}
|
|
36
|
+
/** IPv4 ranges a box must never reach, whatever name led there. */
|
|
37
|
+
const FORBIDDEN_V4 = [
|
|
38
|
+
"0.0.0.0/8", // "this network"
|
|
39
|
+
"10.0.0.0/8", // RFC1918: vmbr1 (the pod at 10.10.0.10) and the box bridge
|
|
40
|
+
"100.64.0.0/10", // CGNAT: the tailnet
|
|
41
|
+
"127.0.0.0/8", // loopback
|
|
42
|
+
"169.254.0.0/16", // link-local, incl. cloud metadata 169.254.169.254
|
|
43
|
+
"172.16.0.0/12", // RFC1918
|
|
44
|
+
"192.0.0.0/24", // IETF protocol assignments
|
|
45
|
+
"192.168.0.0/16", // RFC1918: the LAN, the Proxmox host
|
|
46
|
+
"198.18.0.0/15", // benchmarking
|
|
47
|
+
"224.0.0.0/4", // multicast
|
|
48
|
+
"240.0.0.0/4", // reserved, incl. broadcast
|
|
49
|
+
];
|
|
50
|
+
/** Expands a valid IPv6 literal to 8 hextets (zone id dropped, embedded v4 folded). */
|
|
51
|
+
function v6Hextets(ip) {
|
|
52
|
+
let addr = ip.split("%")[0].toLowerCase();
|
|
53
|
+
const embedded = /^(.*:)(\d+\.\d+\.\d+\.\d+)$/.exec(addr);
|
|
54
|
+
if (embedded) {
|
|
55
|
+
if (!net.isIPv4(embedded[2]))
|
|
56
|
+
return null;
|
|
57
|
+
const n = v4ToInt(embedded[2]);
|
|
58
|
+
addr = `${embedded[1]}${(n >>> 16).toString(16)}:${(n & 0xffff).toString(16)}`;
|
|
59
|
+
}
|
|
60
|
+
const parts = addr.split("::");
|
|
61
|
+
if (parts.length > 2)
|
|
62
|
+
return null;
|
|
63
|
+
const left = parts[0] ? parts[0].split(":") : [];
|
|
64
|
+
const right = parts.length === 2 && parts[1] ? parts[1].split(":") : [];
|
|
65
|
+
const missing = 8 - left.length - right.length;
|
|
66
|
+
if (parts.length === 1 ? missing !== 0 : missing < 0)
|
|
67
|
+
return null;
|
|
68
|
+
const out = [...left, ...Array(missing).fill("0"), ...right].map((h) => parseInt(h, 16));
|
|
69
|
+
return out.every((h) => Number.isInteger(h) && h >= 0 && h <= 0xffff) ? out : null;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* True when a box must not connect to this address. An unparsable address is
|
|
73
|
+
* forbidden: the check fails closed.
|
|
74
|
+
*/
|
|
75
|
+
export function isForbiddenAddress(ip) {
|
|
76
|
+
if (net.isIPv4(ip)) {
|
|
77
|
+
const n = v4ToInt(ip);
|
|
78
|
+
return FORBIDDEN_V4.some((c) => inV4(n, c));
|
|
79
|
+
}
|
|
80
|
+
if (!net.isIPv6(ip))
|
|
81
|
+
return true;
|
|
82
|
+
const h = v6Hextets(ip);
|
|
83
|
+
if (!h)
|
|
84
|
+
return true;
|
|
85
|
+
// IPv4-mapped (::ffff:a.b.c.d) and NAT64 (64:ff9b::a.b.c.d) carry a v4 address.
|
|
86
|
+
const isMapped = h.slice(0, 5).every((x) => x === 0) && h[5] === 0xffff;
|
|
87
|
+
const isNat64 = h[0] === 0x64 && h[1] === 0xff9b && h.slice(2, 6).every((x) => x === 0);
|
|
88
|
+
if (isMapped || isNat64) {
|
|
89
|
+
const v4 = `${h[6] >>> 8}.${h[6] & 0xff}.${h[7] >>> 8}.${h[7] & 0xff}`;
|
|
90
|
+
return isForbiddenAddress(v4);
|
|
91
|
+
}
|
|
92
|
+
// ::/96 holds ::, ::1 and the deprecated IPv4-compatible ::a.b.c.d: none is a public peer.
|
|
93
|
+
if (h.slice(0, 6).every((x) => x === 0))
|
|
94
|
+
return true;
|
|
95
|
+
// 6to4 (2002::/16) carries a v4 address in hextets 1-2.
|
|
96
|
+
if (h[0] === 0x2002)
|
|
97
|
+
return isForbiddenAddress(`${h[1] >>> 8}.${h[1] & 0xff}.${h[2] >>> 8}.${h[2] & 0xff}`);
|
|
98
|
+
if (h[0] === 0x2001 && h[1] === 0)
|
|
99
|
+
return true; // Teredo 2001::/32 (an obfuscated v4 inside)
|
|
100
|
+
if (h[0] === 0x2001 && h[1] === 0x0db8)
|
|
101
|
+
return true; // documentation 2001:db8::/32
|
|
102
|
+
if ((h[0] & 0xfe00) === 0xfc00)
|
|
103
|
+
return true; // fc00::/7 unique local
|
|
104
|
+
if ((h[0] & 0xffc0) === 0xfe80)
|
|
105
|
+
return true; // fe80::/10 link-local
|
|
106
|
+
if ((h[0] & 0xff00) === 0xff00)
|
|
107
|
+
return true; // multicast
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
// ─── names ───────────────────────────────────────────────────────────────────
|
|
111
|
+
/** Lower-cased, trailing dot dropped, brackets of an IPv6 literal removed. */
|
|
112
|
+
export function normalizeHost(host) {
|
|
113
|
+
return host.trim().toLowerCase().replace(/^\[(.*)\]$/, "$1").replace(/\.$/, "");
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* `host` matches an allow entry exactly, or a `*.example.com` entry as a
|
|
117
|
+
* strict subdomain (never the apex, never a suffix like `evilexample.com`).
|
|
118
|
+
*/
|
|
119
|
+
export function hostMatches(host, allow) {
|
|
120
|
+
const h = normalizeHost(host);
|
|
121
|
+
return allow.some((raw) => {
|
|
122
|
+
const a = normalizeHost(raw);
|
|
123
|
+
if (a.startsWith("*."))
|
|
124
|
+
return h.endsWith(a.slice(1)) && h.length > a.length - 1;
|
|
125
|
+
return h === a;
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/** An allow entry split into its host and, when it names one, its port. */
|
|
129
|
+
function splitEntry(entry) {
|
|
130
|
+
const m = /^(.*):(\d{1,5})$/.exec(entry.trim());
|
|
131
|
+
return m && !m[1].includes(":") ? { host: m[1], port: Number(m[2]) } : { host: entry, port: null };
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The name-level decision, before any lookup. A bare entry opens the web
|
|
135
|
+
* ports (80, 443); a `host:port` entry opens that port on that host only.
|
|
136
|
+
*/
|
|
137
|
+
export function decideByName(host, port, policy) {
|
|
138
|
+
const h = normalizeHost(host);
|
|
139
|
+
if (net.isIP(h))
|
|
140
|
+
return { allow: false, reason: "an address literal is never allowed; declare a host name" };
|
|
141
|
+
const entries = (policy.phase === "setup" ? REGISTRY_HOSTS : policy.runHosts).map(splitEntry);
|
|
142
|
+
const named = entries.filter((e) => hostMatches(h, [e.host]));
|
|
143
|
+
if (named.length === 0) {
|
|
144
|
+
return {
|
|
145
|
+
allow: false,
|
|
146
|
+
reason: policy.phase === "setup"
|
|
147
|
+
? "only the package registries are reachable while packages install"
|
|
148
|
+
: "not a host this script declared",
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const portOk = named.some((e) => (e.port === null ? ALLOWED_PORTS.includes(port) : e.port === port));
|
|
152
|
+
if (!portOk)
|
|
153
|
+
return { allow: false, reason: `port ${port} is not declared for this host` };
|
|
154
|
+
return { allow: true };
|
|
155
|
+
}
|
|
156
|
+
const systemResolver = async (host) => (await dns.lookup(host, { all: true, verbatim: true })).map((a) => a.address);
|
|
157
|
+
/**
|
|
158
|
+
* The full decision: by name, then by every resolved address. Returns the ONE
|
|
159
|
+
* address to connect to, so the connection never re-resolves.
|
|
160
|
+
*/
|
|
161
|
+
export async function decideEgress(host, port, policy, resolve = systemResolver) {
|
|
162
|
+
const byName = decideByName(host, port, policy);
|
|
163
|
+
if (!byName.allow)
|
|
164
|
+
return byName;
|
|
165
|
+
let addresses;
|
|
166
|
+
try {
|
|
167
|
+
addresses = await resolve(normalizeHost(host));
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
return { allow: false, reason: `could not resolve: ${err.message}` };
|
|
171
|
+
}
|
|
172
|
+
if (addresses.length === 0)
|
|
173
|
+
return { allow: false, reason: "resolved to no address" };
|
|
174
|
+
const bad = addresses.find(isForbiddenAddress);
|
|
175
|
+
if (bad)
|
|
176
|
+
return { allow: false, reason: `resolves to an internal address (${bad})` };
|
|
177
|
+
return { allow: true, address: addresses[0] };
|
|
178
|
+
}
|
|
179
|
+
function sourceIpOf(sock) {
|
|
180
|
+
return (sock.remoteAddress ?? "").replace(/^::ffff:/, "");
|
|
181
|
+
}
|
|
182
|
+
function splitHostPort(authority, defaultPort) {
|
|
183
|
+
const m = /^(\[[^\]]+\]|[^:]+)(?::(\d+))?$/.exec(authority);
|
|
184
|
+
if (!m)
|
|
185
|
+
return null;
|
|
186
|
+
return { host: m[1], port: m[2] ? Number(m[2]) : defaultPort };
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* An HTTP proxy: CONNECT for HTTPS (a tunnel to the checked address), and
|
|
190
|
+
* absolute-form requests for plain HTTP. Anything else is refused.
|
|
191
|
+
*/
|
|
192
|
+
export function createEgressProxy(opts) {
|
|
193
|
+
const resolve = opts.resolve ?? systemResolver;
|
|
194
|
+
const connect = opts.connect ?? ((address, port) => net.connect({ host: address, port }));
|
|
195
|
+
async function gate(sock, host, port) {
|
|
196
|
+
const src = sourceIpOf(sock);
|
|
197
|
+
const policy = opts.policyFor(src);
|
|
198
|
+
if (!policy)
|
|
199
|
+
return { status: 403, reason: "not a running box" };
|
|
200
|
+
const d = await decideEgress(host, port, policy, resolve);
|
|
201
|
+
if (!d.allow) {
|
|
202
|
+
opts.onBlock(src, { host: normalizeHost(host), port, phase: policy.phase, reason: d.reason, at: new Date().toISOString() });
|
|
203
|
+
return { status: 403, reason: d.reason };
|
|
204
|
+
}
|
|
205
|
+
return { address: d.address };
|
|
206
|
+
}
|
|
207
|
+
const server = http.createServer(async (req, res) => {
|
|
208
|
+
let target;
|
|
209
|
+
try {
|
|
210
|
+
target = new URL(req.url ?? "");
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
res.writeHead(400).end("absolute-form request required\n");
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (target.protocol !== "http:") {
|
|
217
|
+
res.writeHead(400).end("only http:// is proxied in absolute form; use CONNECT for https\n");
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const port = target.port ? Number(target.port) : 80;
|
|
221
|
+
const g = await gate(req.socket, target.hostname, port);
|
|
222
|
+
if (!("address" in g)) {
|
|
223
|
+
res.writeHead(g.status, { "content-type": "text/plain" }).end(`synap box egress refused: ${g.reason}\n`);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const upstream = http.request({
|
|
227
|
+
host: g.address,
|
|
228
|
+
port,
|
|
229
|
+
method: req.method,
|
|
230
|
+
path: target.pathname + target.search,
|
|
231
|
+
headers: { ...req.headers, host: target.host },
|
|
232
|
+
createConnection: () => connect(g.address, port),
|
|
233
|
+
}, (up) => {
|
|
234
|
+
res.writeHead(up.statusCode ?? 502, up.headers);
|
|
235
|
+
up.pipe(res);
|
|
236
|
+
});
|
|
237
|
+
upstream.on("error", () => res.headersSent ? res.destroy() : res.writeHead(502).end());
|
|
238
|
+
req.pipe(upstream);
|
|
239
|
+
});
|
|
240
|
+
server.on("connect", async (req, client, head) => {
|
|
241
|
+
const hp = splitHostPort(req.url ?? "", 443);
|
|
242
|
+
if (!hp) {
|
|
243
|
+
client.end("HTTP/1.1 400 Bad Request\r\n\r\n");
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const g = await gate(client, hp.host, hp.port);
|
|
247
|
+
if (!("address" in g)) {
|
|
248
|
+
client.end(`HTTP/1.1 ${g.status} Forbidden\r\ncontent-type: text/plain\r\n\r\nsynap box egress refused: ${g.reason}\n`);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const upstream = connect(g.address, hp.port);
|
|
252
|
+
upstream.once("connect", () => {
|
|
253
|
+
client.write("HTTP/1.1 200 Connection Established\r\n\r\n");
|
|
254
|
+
if (head.length)
|
|
255
|
+
upstream.write(head);
|
|
256
|
+
upstream.pipe(client);
|
|
257
|
+
client.pipe(upstream);
|
|
258
|
+
});
|
|
259
|
+
upstream.on("error", () => client.destroy());
|
|
260
|
+
client.on("error", () => upstream.destroy());
|
|
261
|
+
});
|
|
262
|
+
return server;
|
|
263
|
+
}
|
|
264
|
+
//# sourceMappingURL=egress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"egress.js","sourceRoot":"","sources":["../../../src/lib/box/egress.ts"],"names":[],"mappings":"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"}
|