@synap-core/cli 1.11.0 → 1.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -198
- package/dist/build-info.json +6 -0
- package/dist/commands/agent-run.js +3 -2
- package/dist/commands/agent-run.js.map +1 -1
- package/dist/commands/agents.d.ts +33 -17
- package/dist/commands/agents.js +203 -237
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/app.d.ts +143 -0
- package/dist/commands/app.js +523 -0
- package/dist/commands/app.js.map +1 -0
- package/dist/commands/automate.d.ts +21 -0
- package/dist/commands/automate.js +42 -0
- package/dist/commands/automate.js.map +1 -0
- package/dist/commands/automation-schema.test.d.ts +16 -0
- package/dist/commands/automation-schema.test.js +77 -0
- package/dist/commands/automation-schema.test.js.map +1 -0
- package/dist/commands/automation.d.ts +12 -0
- package/dist/commands/automation.js +80 -35
- package/dist/commands/automation.js.map +1 -1
- package/dist/commands/bridge-setup.d.ts +14 -1
- package/dist/commands/bridge-setup.js +392 -128
- package/dist/commands/bridge-setup.js.map +1 -1
- package/dist/commands/cap-connections.d.ts +4 -4
- package/dist/commands/cap-connections.js +14 -14
- package/dist/commands/cap-connections.js.map +1 -1
- package/dist/commands/capability.d.ts +2 -0
- package/dist/commands/capability.js +218 -168
- 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/connect.d.ts +38 -13
- package/dist/commands/connect.js +68 -167
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/connections.d.ts +3 -2
- package/dist/commands/connections.js +95 -25
- package/dist/commands/connections.js.map +1 -1
- package/dist/commands/context.js +7 -1
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/data-extra.d.ts +34 -0
- package/dist/commands/data-extra.js +193 -23
- package/dist/commands/data-extra.js.map +1 -1
- package/dist/commands/data.d.ts +120 -2
- package/dist/commands/data.js +293 -20
- 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 +112 -3
- package/dist/commands/doctor.js +657 -32
- 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/import.d.ts +368 -13
- package/dist/commands/import.js +1064 -54
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/init.d.ts +20 -9
- package/dist/commands/init.js +433 -973
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/keys.d.ts +1 -0
- package/dist/commands/keys.js +15 -2
- package/dist/commands/keys.js.map +1 -1
- package/dist/commands/knowledge.d.ts +22 -6
- package/dist/commands/knowledge.js +117 -55
- package/dist/commands/knowledge.js.map +1 -1
- package/dist/commands/lens.d.ts +37 -1
- package/dist/commands/lens.js +192 -71
- 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/open.d.ts +39 -9
- package/dist/commands/open.js +80 -25
- package/dist/commands/open.js.map +1 -1
- package/dist/commands/playbook.d.ts +42 -0
- package/dist/commands/playbook.js +162 -0
- package/dist/commands/playbook.js.map +1 -0
- package/dist/commands/pod.js +1 -1
- package/dist/commands/pod.js.map +1 -1
- package/dist/commands/pods.d.ts +5 -0
- package/dist/commands/pods.js +97 -116
- 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 +577 -0
- package/dist/commands/proton-setup.js.map +1 -0
- package/dist/commands/providers.d.ts +23 -0
- package/dist/commands/providers.js +126 -1
- package/dist/commands/providers.js.map +1 -1
- package/dist/commands/raycast.js +6 -2
- package/dist/commands/raycast.js.map +1 -1
- package/dist/commands/rule.d.ts +150 -0
- package/dist/commands/rule.js +447 -0
- package/dist/commands/rule.js.map +1 -0
- package/dist/commands/session-wait.d.ts +111 -0
- package/dist/commands/session-wait.js +209 -0
- package/dist/commands/session-wait.js.map +1 -0
- package/dist/commands/sessions.d.ts +52 -0
- package/dist/commands/sessions.js +478 -58
- package/dist/commands/sessions.js.map +1 -1
- package/dist/commands/skill-manage.d.ts +21 -0
- package/dist/commands/skill-manage.js +136 -19
- package/dist/commands/skill-manage.js.map +1 -1
- package/dist/commands/skills.js +16 -0
- package/dist/commands/skills.js.map +1 -1
- package/dist/commands/status.d.ts +1 -4
- package/dist/commands/status.js +5 -34
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/statusline.backoff.test.d.ts +13 -0
- package/dist/commands/statusline.backoff.test.js +100 -0
- package/dist/commands/statusline.backoff.test.js.map +1 -0
- package/dist/commands/statusline.js +16 -9
- package/dist/commands/statusline.js.map +1 -1
- package/dist/commands/sync-status.d.ts +12 -0
- package/dist/commands/sync-status.js +125 -0
- package/dist/commands/sync-status.js.map +1 -0
- package/dist/commands/templates-retire.d.ts +34 -0
- package/dist/commands/templates-retire.js +192 -0
- package/dist/commands/templates-retire.js.map +1 -0
- package/dist/commands/tools.js +6 -21
- package/dist/commands/tools.js.map +1 -1
- package/dist/commands/tracks.d.ts +51 -0
- package/dist/commands/tracks.js +291 -0
- package/dist/commands/tracks.js.map +1 -0
- package/dist/commands/update.d.ts +3 -11
- package/dist/commands/update.js +5 -134
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/upload.d.ts +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 +23 -3
- package/dist/commands/whoami.js +169 -23
- package/dist/commands/whoami.js.map +1 -1
- package/dist/commands/worker.d.ts +122 -0
- package/dist/commands/worker.js +435 -0
- package/dist/commands/worker.js.map +1 -0
- package/dist/commands/worker.test.d.ts +1 -0
- package/dist/commands/worker.test.js +116 -0
- package/dist/commands/worker.test.js.map +1 -0
- package/dist/commands/workspace-ops.d.ts +35 -0
- package/dist/commands/workspace-ops.js +153 -0
- package/dist/commands/workspace-ops.js.map +1 -0
- package/dist/index.d.ts +6 -6
- package/dist/index.js +900 -180
- package/dist/index.js.map +1 -1
- package/dist/lib/agent-connections.d.ts +31 -0
- package/dist/lib/agent-connections.js +33 -0
- package/dist/lib/agent-connections.js.map +1 -0
- package/dist/lib/agent-detect.d.ts +27 -0
- package/dist/lib/agent-detect.js +71 -0
- package/dist/lib/agent-detect.js.map +1 -0
- package/dist/lib/agent-door.d.ts +138 -0
- package/dist/lib/agent-door.js +426 -0
- package/dist/lib/agent-door.js.map +1 -0
- package/dist/lib/agents-config.d.ts +2 -0
- package/dist/lib/agents-config.js +4 -9
- package/dist/lib/agents-config.js.map +1 -1
- package/dist/lib/approval-poll.d.ts +31 -2
- package/dist/lib/approval-poll.js +20 -7
- package/dist/lib/approval-poll.js.map +1 -1
- package/dist/lib/atomic-file.d.ts +48 -0
- package/dist/lib/atomic-file.js +129 -0
- package/dist/lib/atomic-file.js.map +1 -0
- package/dist/lib/auth.d.ts +14 -9
- package/dist/lib/auth.js +44 -61
- package/dist/lib/auth.js.map +1 -1
- package/dist/lib/batch-approval.d.ts +85 -0
- package/dist/lib/batch-approval.js +170 -0
- package/dist/lib/batch-approval.js.map +1 -0
- package/dist/lib/capture-lane.d.ts +6 -5
- package/dist/lib/capture-lane.js +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/conversation-headers.d.ts +18 -0
- package/dist/lib/conversation-headers.js +32 -0
- package/dist/lib/conversation-headers.js.map +1 -0
- package/dist/lib/cp-packages.d.ts +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 +117 -0
- package/dist/lib/credential-class.js +116 -0
- package/dist/lib/credential-class.js.map +1 -0
- package/dist/lib/describe-lens.d.ts +35 -0
- package/dist/lib/describe-lens.js +40 -0
- package/dist/lib/describe-lens.js.map +1 -1
- package/dist/lib/device-connect.d.ts +125 -0
- package/dist/lib/device-connect.js +256 -0
- package/dist/lib/device-connect.js.map +1 -0
- package/dist/lib/directory-lens.d.ts +99 -0
- package/dist/lib/directory-lens.js +187 -0
- package/dist/lib/directory-lens.js.map +1 -0
- package/dist/lib/exporter-coverage.d.ts +71 -0
- package/dist/lib/exporter-coverage.js +97 -0
- package/dist/lib/exporter-coverage.js.map +1 -0
- package/dist/lib/hub-client.d.ts +29 -8
- package/dist/lib/hub-client.js +328 -55
- 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 +16 -0
- package/dist/lib/init-handoff.js +19 -0
- package/dist/lib/init-handoff.js.map +1 -0
- package/dist/lib/init-output.d.ts +18 -0
- package/dist/lib/init-output.js +43 -0
- package/dist/lib/init-output.js.map +1 -0
- package/dist/lib/init-pod.d.ts +45 -0
- package/dist/lib/init-pod.js +36 -0
- package/dist/lib/init-pod.js.map +1 -0
- package/dist/lib/installed.d.ts +210 -11
- package/dist/lib/installed.js +234 -36
- package/dist/lib/installed.js.map +1 -1
- package/dist/lib/key-approval.d.ts +57 -0
- package/dist/lib/key-approval.js +117 -0
- package/dist/lib/key-approval.js.map +1 -0
- package/dist/lib/key-diagnosis.d.ts +56 -0
- package/dist/lib/key-diagnosis.js +227 -0
- package/dist/lib/key-diagnosis.js.map +1 -0
- package/dist/lib/key-doctor.d.ts +136 -0
- package/dist/lib/key-doctor.js +286 -0
- package/dist/lib/key-doctor.js.map +1 -0
- package/dist/lib/key-locations.d.ts +120 -0
- package/dist/lib/key-locations.js +326 -0
- package/dist/lib/key-locations.js.map +1 -0
- package/dist/lib/key-source.d.ts +66 -0
- package/dist/lib/key-source.js +106 -0
- package/dist/lib/key-source.js.map +1 -0
- package/dist/lib/key-state.d.ts +70 -0
- package/dist/lib/key-state.js +115 -0
- package/dist/lib/key-state.js.map +1 -0
- package/dist/lib/kind-package.d.ts +181 -0
- package/dist/lib/kind-package.js +679 -0
- package/dist/lib/kind-package.js.map +1 -0
- package/dist/lib/knowledge-title.d.ts +24 -0
- package/dist/lib/knowledge-title.js +46 -0
- package/dist/lib/knowledge-title.js.map +1 -0
- package/dist/lib/knowledge-title.test.d.ts +1 -0
- package/dist/lib/knowledge-title.test.js +34 -0
- package/dist/lib/knowledge-title.test.js.map +1 -0
- package/dist/lib/machine-instance.d.ts +64 -0
- package/dist/lib/machine-instance.js +142 -0
- package/dist/lib/machine-instance.js.map +1 -0
- package/dist/lib/mcp-bridge.d.ts +42 -0
- package/dist/lib/mcp-bridge.js +147 -0
- package/dist/lib/mcp-bridge.js.map +1 -0
- package/dist/lib/mcp-bridge.test.d.ts +1 -0
- package/dist/lib/mcp-bridge.test.js +90 -0
- package/dist/lib/mcp-bridge.test.js.map +1 -0
- package/dist/lib/mcp-call.d.ts +30 -0
- package/dist/lib/mcp-call.js +107 -0
- package/dist/lib/mcp-call.js.map +1 -0
- package/dist/lib/mcp-snippets.d.ts +53 -0
- package/dist/lib/mcp-snippets.js +82 -0
- package/dist/lib/mcp-snippets.js.map +1 -0
- package/dist/lib/next-steps.d.ts +8 -0
- package/dist/lib/next-steps.js +31 -1
- package/dist/lib/next-steps.js.map +1 -1
- package/dist/lib/package-wire.d.ts +179 -0
- package/dist/lib/package-wire.js +68 -0
- package/dist/lib/package-wire.js.map +1 -0
- package/dist/lib/pair-link.d.ts +38 -0
- package/dist/lib/pair-link.js +68 -0
- package/dist/lib/pair-link.js.map +1 -0
- package/dist/lib/pod-wide.d.ts +40 -0
- package/dist/lib/pod-wide.js +46 -0
- package/dist/lib/pod-wide.js.map +1 -0
- package/dist/lib/pod.d.ts +66 -112
- package/dist/lib/pod.js +134 -412
- package/dist/lib/pod.js.map +1 -1
- package/dist/lib/project-ref.d.ts +9 -3
- package/dist/lib/project-ref.js +10 -4
- package/dist/lib/project-ref.js.map +1 -1
- package/dist/lib/review-credential.d.ts +119 -0
- package/dist/lib/review-credential.js +147 -0
- package/dist/lib/review-credential.js.map +1 -0
- package/dist/lib/safe-open.d.ts +49 -0
- package/dist/lib/safe-open.js +119 -0
- package/dist/lib/safe-open.js.map +1 -0
- package/dist/lib/session-lens.d.ts +40 -1
- package/dist/lib/session-lens.js +54 -4
- package/dist/lib/session-lens.js.map +1 -1
- package/dist/lib/superwhisper-import.d.ts +8 -0
- package/dist/lib/superwhisper-import.js +14 -7
- package/dist/lib/superwhisper-import.js.map +1 -1
- package/dist/lib/superwhisper-title.test.d.ts +1 -0
- package/dist/lib/superwhisper-title.test.js +18 -0
- package/dist/lib/superwhisper-title.test.js.map +1 -0
- package/dist/lib/targets.d.ts +134 -10
- package/dist/lib/targets.js +962 -258
- package/dist/lib/targets.js.map +1 -1
- package/dist/lib/template-file.d.ts +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/utils/logger.js +5 -2
- package/dist/utils/logger.js.map +1 -1
- package/package.json +23 -17
- package/skills/README.md +12 -10
- 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 +1086 -283
- 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/concepts.md +28 -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 +141 -47
- package/skills/synap/escalation-ladder.md +20 -10
- package/skills/synap/focus-sessions.md +47 -21
- package/skills/synap/from-intent.md +142 -0
- package/skills/synap/governance-rules.md +4 -4
- package/skills/synap/governance.md +10 -7
- package/skills/synap/hand-off.md +33 -0
- package/skills/synap/inline-patterns.md +15 -7
- package/skills/synap/lenses.md +33 -15
- package/skills/synap/linking.md +41 -40
- package/skills/synap/mcp-remote-servers.md +64 -0
- package/skills/synap/mental-model.md +17 -15
- package/skills/synap/operating-mode-rules.md +1 -1
- package/skills/synap/processes.md +118 -0
- package/skills/synap/quick-reference.md +5 -1
- package/skills/synap/reading.md +3 -1
- package/skills/synap/reflexes.md +12 -42
- package/skills/synap/viewframe-cells.md +38 -5
- package/skills/synap/work-flow.md +1 -1
- package/skills/synap/work-model.md +96 -0
- package/skills/synap/worked-examples.md +4 -5
- package/skills/synap/workspace-design.md +14 -10
- package/skills/synap/writes.md +97 -24
- package/skills/synap/writing-titles.md +63 -0
- package/skills/synap-app/SKILL.md +2 -2
- package/skills/synap-app/_skill.yaml +1 -1
- package/skills/synap-app/install-commands.md +1 -1
- 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/skills/authoring-playbooks/SKILL.md +6 -3
- package/templates/skills/authoring-playbooks/reference/stages-contract.md +15 -0
- package/templates/skills/authoring-playbooks/reference/worked-example.md +3 -0
- package/dist/commands/infra.d.ts +0 -21
- package/dist/commands/infra.js +0 -262
- package/dist/commands/infra.js.map +0 -1
- package/dist/commands/template.d.ts +0 -42
- package/dist/commands/template.js +0 -191
- package/dist/commands/template.js.map +0 -1
- package/dist/lib/browser-auth.d.ts +0 -35
- package/dist/lib/browser-auth.js +0 -170
- package/dist/lib/browser-auth.js.map +0 -1
- package/dist/lib/openclaw.d.ts +0 -32
- package/dist/lib/openclaw.js +0 -164
- package/dist/lib/openclaw.js.map +0 -1
- package/dist/lib/seed.d.ts +0 -13
- package/dist/lib/seed.js +0 -135
- package/dist/lib/seed.js.map +0 -1
- package/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,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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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-DIRECTORY lens — the durable default scope for a working tree.
|
|
3
|
+
*
|
|
4
|
+
* RATIFIED MODEL: a directory's `.synap/lens.json` is where a lens normally
|
|
5
|
+
* lives; the per-Claude-session lens (`session-lens.ts`) is an explicit
|
|
6
|
+
* OVERRIDE on top of it.
|
|
7
|
+
*
|
|
8
|
+
* The session lens alone could not express "this repo is always Builder" — it
|
|
9
|
+
* dies with the tab, so every new session started pod-wide (or, worse, on
|
|
10
|
+
* whatever the global config happened to hold) and the operator re-pinned by
|
|
11
|
+
* hand. A directory lens is discovered the way git discovers `.git`: walk UP
|
|
12
|
+
* from the cwd until a `.synap/lens.json` turns up.
|
|
13
|
+
*
|
|
14
|
+
* Full precedence, highest first (assembled in `hub-client.ts`):
|
|
15
|
+
*
|
|
16
|
+
* explicit flag › session lens › DIRECTORY LENS › env var › global config
|
|
17
|
+
*
|
|
18
|
+
* ── Pod qualification ────────────────────────────────────────────────────────
|
|
19
|
+
* Workspace and project ids are POD-LOCAL: sending workspace W (pod A) to pod B
|
|
20
|
+
* is the "Access denied to workspace" 403. A directory lens is exposed to that
|
|
21
|
+
* hazard MORE than a session lens is, because it outlives many `synap pods use`
|
|
22
|
+
* switches. It therefore carries the same `podUrl` stamp and is compared
|
|
23
|
+
* through the SAME door — {@link lensMatchesPod} from `session-lens.ts`. There
|
|
24
|
+
* is deliberately no second comparison implementation here; a duplicated
|
|
25
|
+
* "is this the right pod" rule that drifts is the bug, not the fix.
|
|
26
|
+
*
|
|
27
|
+
* ── Write safety ─────────────────────────────────────────────────────────────
|
|
28
|
+
* Writes never escape the user's own tree: the target is either an EXISTING
|
|
29
|
+
* lens found on the upward walk (only when it sits at or below the home dir) or
|
|
30
|
+
* the cwd itself. A lens is never created in an ancestor, never above the home
|
|
31
|
+
* dir, and never at the filesystem root.
|
|
32
|
+
*/
|
|
33
|
+
import { type SessionLens } from "./session-lens.js";
|
|
34
|
+
/** A directory lens is the same shape as a session lens — same fields, different lifetime. */
|
|
35
|
+
export type DirectoryLens = SessionLens;
|
|
36
|
+
export interface FoundDirectoryLens {
|
|
37
|
+
lens: DirectoryLens;
|
|
38
|
+
/** Absolute path to the `.synap/lens.json` that supplied it — shown by `synap lens`. */
|
|
39
|
+
file: string;
|
|
40
|
+
/** The directory that owns it (the one containing `.synap/`). */
|
|
41
|
+
dir: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Every directory to consider, nearest first: the cwd, then each ancestor.
|
|
45
|
+
* The walk STOPS after the home dir (a lens above `~` would apply to every
|
|
46
|
+
* unrelated project on the machine) and at the filesystem root.
|
|
47
|
+
*/
|
|
48
|
+
export declare function lensSearchPath(startDir?: string): string[];
|
|
49
|
+
/**
|
|
50
|
+
* The nearest directory lens at or above `startDir`, ignoring pod qualification.
|
|
51
|
+
* Callers that are about to SEND ids must use {@link resolveDirectoryLensForPod}.
|
|
52
|
+
*/
|
|
53
|
+
export declare function findDirectoryLens(startDir?: string): FoundDirectoryLens | null;
|
|
54
|
+
/**
|
|
55
|
+
* The nearest directory lens, but ONLY if it belongs to `podUrl`.
|
|
56
|
+
*
|
|
57
|
+
* A lens stamped with a different pod returns null so the caller falls through
|
|
58
|
+
* to the next rung (env var, then the pod-qualified global config) instead of
|
|
59
|
+
* sending a foreign workspace id. Comparison is delegated to `lensMatchesPod`
|
|
60
|
+
* — including its deliberate leniency for lenses written before the `podUrl`
|
|
61
|
+
* stamp existed.
|
|
62
|
+
*/
|
|
63
|
+
export declare function resolveDirectoryLensForPod(podUrl: string | undefined, startDir?: string): FoundDirectoryLens | null;
|
|
64
|
+
/**
|
|
65
|
+
* The fields of a directory lens that are in force for THIS process.
|
|
66
|
+
*
|
|
67
|
+
* Workspace and project are the tree's durable scope and always apply. The
|
|
68
|
+
* focus SESSION does not, inside a Claude Code conversation: a session is the
|
|
69
|
+
* conversation's own, and `attachActiveSessionId` writes it to the session lens
|
|
70
|
+
* there for exactly that reason. Read through, a directory-lens session —
|
|
71
|
+
* attached from a plain terminal, or migrated from a `.synap-session` — was
|
|
72
|
+
* sent as `X-Session-Id` by every conversation opened in the tree, long after
|
|
73
|
+
* it went quiet, so each one's writes were filed into that old session (and a
|
|
74
|
+
* stale one was revived by them). Outside Claude Code there is no conversation
|
|
75
|
+
* to own a session, so the directory lens still carries it.
|
|
76
|
+
*/
|
|
77
|
+
export declare function effectiveDirectoryLens(lens: DirectoryLens): DirectoryLens;
|
|
78
|
+
/**
|
|
79
|
+
* Where would a write land? An existing lens on the upward walk wins (so
|
|
80
|
+
* `synap use` from a subdirectory re-pins the repo's lens rather than seeding a
|
|
81
|
+
* nested one), but only when that lens sits at or below the home dir. Otherwise
|
|
82
|
+
* the cwd — a lens is never CREATED in an ancestor.
|
|
83
|
+
*/
|
|
84
|
+
export declare function directoryLensWriteTarget(startDir?: string): string;
|
|
85
|
+
/**
|
|
86
|
+
* Merge-write the directory lens. Returns the file it wrote, so the caller can
|
|
87
|
+
* TELL the user which path now owns their scope — a silently-resolved lens is
|
|
88
|
+
* exactly what produced the 403 class this module exists to make legible.
|
|
89
|
+
*/
|
|
90
|
+
export declare function writeDirectoryLens(patch: Partial<DirectoryLens>, startDir?: string): {
|
|
91
|
+
lens: DirectoryLens;
|
|
92
|
+
file: string;
|
|
93
|
+
dir: string;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Clear one field (or the whole lens) from the nearest directory lens.
|
|
97
|
+
* Returns the file touched, or null when there was nothing to clear.
|
|
98
|
+
*/
|
|
99
|
+
export declare function clearDirectoryLensField(field?: keyof DirectoryLens, startDir?: string): string | null;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-DIRECTORY lens — the durable default scope for a working tree.
|
|
3
|
+
*
|
|
4
|
+
* RATIFIED MODEL: a directory's `.synap/lens.json` is where a lens normally
|
|
5
|
+
* lives; the per-Claude-session lens (`session-lens.ts`) is an explicit
|
|
6
|
+
* OVERRIDE on top of it.
|
|
7
|
+
*
|
|
8
|
+
* The session lens alone could not express "this repo is always Builder" — it
|
|
9
|
+
* dies with the tab, so every new session started pod-wide (or, worse, on
|
|
10
|
+
* whatever the global config happened to hold) and the operator re-pinned by
|
|
11
|
+
* hand. A directory lens is discovered the way git discovers `.git`: walk UP
|
|
12
|
+
* from the cwd until a `.synap/lens.json` turns up.
|
|
13
|
+
*
|
|
14
|
+
* Full precedence, highest first (assembled in `hub-client.ts`):
|
|
15
|
+
*
|
|
16
|
+
* explicit flag › session lens › DIRECTORY LENS › env var › global config
|
|
17
|
+
*
|
|
18
|
+
* ── Pod qualification ────────────────────────────────────────────────────────
|
|
19
|
+
* Workspace and project ids are POD-LOCAL: sending workspace W (pod A) to pod B
|
|
20
|
+
* is the "Access denied to workspace" 403. A directory lens is exposed to that
|
|
21
|
+
* hazard MORE than a session lens is, because it outlives many `synap pods use`
|
|
22
|
+
* switches. It therefore carries the same `podUrl` stamp and is compared
|
|
23
|
+
* through the SAME door — {@link lensMatchesPod} from `session-lens.ts`. There
|
|
24
|
+
* is deliberately no second comparison implementation here; a duplicated
|
|
25
|
+
* "is this the right pod" rule that drifts is the bug, not the fix.
|
|
26
|
+
*
|
|
27
|
+
* ── Write safety ─────────────────────────────────────────────────────────────
|
|
28
|
+
* Writes never escape the user's own tree: the target is either an EXISTING
|
|
29
|
+
* lens found on the upward walk (only when it sits at or below the home dir) or
|
|
30
|
+
* the cwd itself. A lens is never created in an ancestor, never above the home
|
|
31
|
+
* dir, and never at the filesystem root.
|
|
32
|
+
*/
|
|
33
|
+
import fs from "node:fs";
|
|
34
|
+
import os from "node:os";
|
|
35
|
+
import path from "node:path";
|
|
36
|
+
import { getClaudeSessionId, lensMatchesPod } from "./session-lens.js";
|
|
37
|
+
import { writeSecretFile } from "./atomic-file.js";
|
|
38
|
+
/** The directory name a lens lives in, and the file inside it. */
|
|
39
|
+
const LENS_DIR_NAME = ".synap";
|
|
40
|
+
const LENS_FILE_NAME = "lens.json";
|
|
41
|
+
function lensFileIn(dir) {
|
|
42
|
+
return path.join(dir, LENS_DIR_NAME, LENS_FILE_NAME);
|
|
43
|
+
}
|
|
44
|
+
/** Is `dir` the home dir or inside it? Used to bound where a lens may be WRITTEN. */
|
|
45
|
+
function isAtOrBelowHome(dir) {
|
|
46
|
+
const home = path.resolve(os.homedir());
|
|
47
|
+
const target = path.resolve(dir);
|
|
48
|
+
if (target === home)
|
|
49
|
+
return true;
|
|
50
|
+
return target.startsWith(home + path.sep);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Every directory to consider, nearest first: the cwd, then each ancestor.
|
|
54
|
+
* The walk STOPS after the home dir (a lens above `~` would apply to every
|
|
55
|
+
* unrelated project on the machine) and at the filesystem root.
|
|
56
|
+
*/
|
|
57
|
+
export function lensSearchPath(startDir = process.cwd()) {
|
|
58
|
+
const home = path.resolve(os.homedir());
|
|
59
|
+
const dirs = [];
|
|
60
|
+
let dir = path.resolve(startDir);
|
|
61
|
+
for (;;) {
|
|
62
|
+
dirs.push(dir);
|
|
63
|
+
if (dir === home)
|
|
64
|
+
break;
|
|
65
|
+
const parent = path.dirname(dir);
|
|
66
|
+
if (parent === dir)
|
|
67
|
+
break; // filesystem root
|
|
68
|
+
dir = parent;
|
|
69
|
+
}
|
|
70
|
+
return dirs;
|
|
71
|
+
}
|
|
72
|
+
/** Read + parse one candidate, or null when absent/corrupt. A malformed lens must not crash the CLI. */
|
|
73
|
+
function readLensFile(dir) {
|
|
74
|
+
const file = lensFileIn(dir);
|
|
75
|
+
try {
|
|
76
|
+
const parsed = JSON.parse(fs.readFileSync(file, "utf-8"));
|
|
77
|
+
if (!parsed || typeof parsed !== "object")
|
|
78
|
+
return null;
|
|
79
|
+
return { lens: parsed, file, dir };
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The nearest directory lens at or above `startDir`, ignoring pod qualification.
|
|
87
|
+
* Callers that are about to SEND ids must use {@link resolveDirectoryLensForPod}.
|
|
88
|
+
*/
|
|
89
|
+
export function findDirectoryLens(startDir) {
|
|
90
|
+
for (const dir of lensSearchPath(startDir)) {
|
|
91
|
+
const found = readLensFile(dir);
|
|
92
|
+
if (found)
|
|
93
|
+
return found;
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The nearest directory lens, but ONLY if it belongs to `podUrl`.
|
|
99
|
+
*
|
|
100
|
+
* A lens stamped with a different pod returns null so the caller falls through
|
|
101
|
+
* to the next rung (env var, then the pod-qualified global config) instead of
|
|
102
|
+
* sending a foreign workspace id. Comparison is delegated to `lensMatchesPod`
|
|
103
|
+
* — including its deliberate leniency for lenses written before the `podUrl`
|
|
104
|
+
* stamp existed.
|
|
105
|
+
*/
|
|
106
|
+
export function resolveDirectoryLensForPod(podUrl, startDir) {
|
|
107
|
+
const found = findDirectoryLens(startDir);
|
|
108
|
+
if (!found)
|
|
109
|
+
return null;
|
|
110
|
+
return lensMatchesPod(found.lens, podUrl) ? found : null;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* The fields of a directory lens that are in force for THIS process.
|
|
114
|
+
*
|
|
115
|
+
* Workspace and project are the tree's durable scope and always apply. The
|
|
116
|
+
* focus SESSION does not, inside a Claude Code conversation: a session is the
|
|
117
|
+
* conversation's own, and `attachActiveSessionId` writes it to the session lens
|
|
118
|
+
* there for exactly that reason. Read through, a directory-lens session —
|
|
119
|
+
* attached from a plain terminal, or migrated from a `.synap-session` — was
|
|
120
|
+
* sent as `X-Session-Id` by every conversation opened in the tree, long after
|
|
121
|
+
* it went quiet, so each one's writes were filed into that old session (and a
|
|
122
|
+
* stale one was revived by them). Outside Claude Code there is no conversation
|
|
123
|
+
* to own a session, so the directory lens still carries it.
|
|
124
|
+
*/
|
|
125
|
+
export function effectiveDirectoryLens(lens) {
|
|
126
|
+
if (!getClaudeSessionId())
|
|
127
|
+
return lens;
|
|
128
|
+
const { focusSessionId: _conversationOwned, ...scope } = lens;
|
|
129
|
+
return scope;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Where would a write land? An existing lens on the upward walk wins (so
|
|
133
|
+
* `synap use` from a subdirectory re-pins the repo's lens rather than seeding a
|
|
134
|
+
* nested one), but only when that lens sits at or below the home dir. Otherwise
|
|
135
|
+
* the cwd — a lens is never CREATED in an ancestor.
|
|
136
|
+
*/
|
|
137
|
+
export function directoryLensWriteTarget(startDir = process.cwd()) {
|
|
138
|
+
const found = findDirectoryLens(startDir);
|
|
139
|
+
if (found && isAtOrBelowHome(found.dir))
|
|
140
|
+
return found.dir;
|
|
141
|
+
return path.resolve(startDir);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Merge-write the directory lens. Returns the file it wrote, so the caller can
|
|
145
|
+
* TELL the user which path now owns their scope — a silently-resolved lens is
|
|
146
|
+
* exactly what produced the 403 class this module exists to make legible.
|
|
147
|
+
*/
|
|
148
|
+
export function writeDirectoryLens(patch, startDir) {
|
|
149
|
+
const dir = directoryLensWriteTarget(startDir);
|
|
150
|
+
const prev = readLensFile(dir)?.lens ?? {};
|
|
151
|
+
const next = { ...prev, ...patch, updatedAt: Date.now() };
|
|
152
|
+
const file = lensFileIn(dir);
|
|
153
|
+
fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
|
|
154
|
+
writeSecretFile(file, JSON.stringify(next, null, 2) + "\n", { mode: 0o600 });
|
|
155
|
+
return { lens: next, file, dir };
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Clear one field (or the whole lens) from the nearest directory lens.
|
|
159
|
+
* Returns the file touched, or null when there was nothing to clear.
|
|
160
|
+
*/
|
|
161
|
+
export function clearDirectoryLensField(field, startDir) {
|
|
162
|
+
const found = findDirectoryLens(startDir);
|
|
163
|
+
if (!found)
|
|
164
|
+
return null;
|
|
165
|
+
if (!field) {
|
|
166
|
+
try {
|
|
167
|
+
fs.unlinkSync(found.file);
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
return found.file;
|
|
173
|
+
}
|
|
174
|
+
if (!(field in found.lens))
|
|
175
|
+
return null;
|
|
176
|
+
const next = { ...found.lens };
|
|
177
|
+
delete next[field];
|
|
178
|
+
next.updatedAt = Date.now();
|
|
179
|
+
try {
|
|
180
|
+
writeSecretFile(found.file, JSON.stringify(next, null, 2) + "\n", { mode: 0o600 });
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
return found.file;
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=directory-lens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directory-lens.js","sourceRoot":"","sources":["../../src/lib/directory-lens.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What `--from-workspace` / `--from-project` do NOT carry out of a live pod.
|
|
3
|
+
* ==========================================================================
|
|
4
|
+
*
|
|
5
|
+
* `synap market publish --from-workspace <id>` serialises a LIVE workspace
|
|
6
|
+
* through the pod's `workspaceToPackageDefinition`
|
|
7
|
+
* (synap-backend/packages/api/src/services/workspace-to-package-definition.ts)
|
|
8
|
+
* and posts the result. `--from-project` walks the same serialiser for every
|
|
9
|
+
* workspace a project `uses`, then composes a suite — so the same drop-list
|
|
10
|
+
* applies to each constituent. That serialiser is a **lossy projection**: it
|
|
11
|
+
* emits 21 of `packageDefinitionSchema`'s keys and has no projection at all
|
|
12
|
+
* for the ones below. A workspace carrying them exports WITHOUT them — no
|
|
13
|
+
* error, no signal.
|
|
14
|
+
*
|
|
15
|
+
* Loops stay here deliberately: there is no durable loop row to reverse-
|
|
16
|
+
* serialise (`createLoopFromDefinition` dissolves into playbooks + triggers).
|
|
17
|
+
*
|
|
18
|
+
* `TEMPLATE-DEV-GUIDE.md` used to assert, unqualified, that a
|
|
19
|
+
* `PackageDefinition` "round-trips". It does not, in this direction, and the
|
|
20
|
+
* consequence is the shape this repo keeps paying for: an author who built one
|
|
21
|
+
* of the things below publishes their workspace, ships a thinner-than-expected
|
|
22
|
+
* package, and gets a success message. Same class as `status ?? "installed"` —
|
|
23
|
+
* a claim of completeness nobody verified. (Cards authored in Card Studio USED
|
|
24
|
+
* to be one of these — `workspaceToPackageDefinition` now emits them into
|
|
25
|
+
* `cells[]`, so that loss is fixed; the list below is what remains.)
|
|
26
|
+
*
|
|
27
|
+
* ── WHY THIS LIST IS UNCONDITIONAL, NOT DETECTED ────────────────────────────
|
|
28
|
+
* The CLI never sees the workspace, only the projection's OUTPUT. "key absent
|
|
29
|
+
* because the workspace had none" and "key absent because the serialiser cannot
|
|
30
|
+
* emit it" are indistinguishable from here, and reporting a count would be
|
|
31
|
+
* inventing a fact. So this warns about the DOOR ("this door does not carry
|
|
32
|
+
* these"), which is true on every call, rather than about the payload.
|
|
33
|
+
*
|
|
34
|
+
* Keeping it honest across the repo boundary is
|
|
35
|
+
* `test/exporter-coverage-parity.test.ts`, which reads the exporter's own
|
|
36
|
+
* source and fails if a key listed here has gained an assignment (the list is
|
|
37
|
+
* stale and over-warns) or if a key it claims to emit has lost one.
|
|
38
|
+
*/
|
|
39
|
+
/** A `packageDefinitionSchema` key the workspace serialiser cannot emit. */
|
|
40
|
+
export interface UnemittedKey {
|
|
41
|
+
/** The `PackageDefinition` key, verbatim. */
|
|
42
|
+
key: string;
|
|
43
|
+
/** What an author actually loses, in product words (Card, not cell). */
|
|
44
|
+
loses: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Keys with real user-authored content behind them that the serialiser drops.
|
|
48
|
+
*
|
|
49
|
+
* DELIBERATELY EXCLUDED, and why — these are absent from the export too, but
|
|
50
|
+
* warning about them would be noise rather than a loss:
|
|
51
|
+
* • `icon` / `color` / `domain` / `workspaceType` — a live workspace has no
|
|
52
|
+
* field to read them from (the exporter says so at L107-109). Honest
|
|
53
|
+
* under-convergence, not a silent drop.
|
|
54
|
+
* • `suggestedEntities` / `suggestedRelations` / `phases` / `composesFrom` —
|
|
55
|
+
* authoring-time seed hints and composition metadata. A live workspace has
|
|
56
|
+
* real entities, not "suggestions"; there is nothing to derive them from.
|
|
57
|
+
* • `capability` / `sourcePackage` / `contentHash` — a non-workspace package
|
|
58
|
+
* body and publish-time provenance. Not this door's business.
|
|
59
|
+
*/
|
|
60
|
+
export declare const EXPORTER_UNEMITTED_KEYS: readonly UnemittedKey[];
|
|
61
|
+
/**
|
|
62
|
+
* The keys the serialiser DOES emit. Not consumed at runtime — it is the other
|
|
63
|
+
* half of the parity tripwire's assertion, so that "emits" and "does not emit"
|
|
64
|
+
* are pinned by the same read of the same source and cannot disagree.
|
|
65
|
+
*/
|
|
66
|
+
export declare const EXPORTER_EMITTED_KEYS: readonly string[];
|
|
67
|
+
/**
|
|
68
|
+
* One human line per dropped key, for `--json`'s `warnings[]` and for the
|
|
69
|
+
* terminal hint list. Deliberately phrased as a property of the DOOR.
|
|
70
|
+
*/
|
|
71
|
+
export declare function exporterDropWarnings(): string[];
|