@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
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* A Capability is a named, installable PACK of what you (and your AI) can do
|
|
5
5
|
* (e.g. "Google Workspace", "Discord Bot"). Tools = its connection; skills = its
|
|
6
6
|
* verbs; templates = the catalog. The user only ever lists, adds, and enables a
|
|
7
|
-
* PACK — never a bare tool or skill (CAPABILITIES-NORTH-STAR
|
|
7
|
+
* PACK — never a bare tool or skill (CAPABILITIES-NORTH-STAR, team pod).
|
|
8
8
|
*
|
|
9
9
|
* A capability VERB is a runnable action backed by a skill (verbId = skill name).
|
|
10
10
|
* `run` is the launcher: it parses arbitrary `--key value` flags into the skill's
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
import chalk from "chalk";
|
|
16
16
|
import ora from "ora";
|
|
17
17
|
import prompts from "prompts";
|
|
18
|
-
import {
|
|
18
|
+
import { openExternalUrl } from "../lib/safe-open.js";
|
|
19
19
|
import { readFileSync } from "node:fs";
|
|
20
|
-
import { resolveHubConfig, hubGet, hubPost, hubDelete, HubError, renderHubError } from "../lib/hub-client.js";
|
|
20
|
+
import { resolveHubConfig, hubGet, hubPost, hubPatch, hubDelete, HubError, renderHubError } from "../lib/hub-client.js";
|
|
21
21
|
import { unwrapList } from "../lib/unwrapList.js";
|
|
22
22
|
import { log } from "../utils/logger.js";
|
|
23
23
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
@@ -231,7 +231,12 @@ function accountLabel(account) {
|
|
|
231
231
|
const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(account);
|
|
232
232
|
return isUuid ? null : account;
|
|
233
233
|
}
|
|
234
|
-
/**
|
|
234
|
+
/**
|
|
235
|
+
* A USABLE pack: name + its verbs grouped by exec-mode as compact NAME lists.
|
|
236
|
+
* The full `--param` signature is NOT dumped per verb here — that turned a pack
|
|
237
|
+
* with 20+ verbs into an unscannable wall. The list answers "what can this do?";
|
|
238
|
+
* `synap cap show <verb>` gives the exact run command for the one you pick.
|
|
239
|
+
*/
|
|
235
240
|
function renderUsable(card) {
|
|
236
241
|
const label = accountLabel(card.connection?.account);
|
|
237
242
|
const acct = label
|
|
@@ -239,13 +244,19 @@ function renderUsable(card) {
|
|
|
239
244
|
: card.connection?.provider
|
|
240
245
|
? chalk.dim(` · ${card.connection.provider} connected`)
|
|
241
246
|
: "";
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
247
|
+
const runnable = card.verbs.filter((x) => x.runnable);
|
|
248
|
+
const instant = runnable.filter((v) => v.type === "read");
|
|
249
|
+
const approval = runnable.filter((v) => v.type !== "read");
|
|
250
|
+
const count = chalk.dim(` · ${runnable.length} verb${runnable.length === 1 ? "" : "s"}` +
|
|
251
|
+
(approval.length ? `, ${approval.length} need approval` : ""));
|
|
252
|
+
console.log(` ${chalk.green("●")} ${chalk.bold(card.name)}${acct}${count}`);
|
|
253
|
+
if (instant.length > 0) {
|
|
254
|
+
console.log(` ${chalk.green("▸")} ${chalk.dim("instant ")} ` +
|
|
255
|
+
instant.map((v) => chalk.cyan(v.verbId)).join(" "));
|
|
256
|
+
}
|
|
257
|
+
if (approval.length > 0) {
|
|
258
|
+
console.log(` ${chalk.yellow("✋")} ${chalk.dim("approval")} ` +
|
|
259
|
+
approval.map((v) => chalk.cyan(v.verbId)).join(" "));
|
|
249
260
|
}
|
|
250
261
|
const off = card.verbs.filter((v) => !v.runnable);
|
|
251
262
|
if (off.length > 0) {
|
|
@@ -257,6 +268,12 @@ function renderUsable(card) {
|
|
|
257
268
|
function renderSetup(card) {
|
|
258
269
|
console.log(` ${chalk.yellow("◌")} ${chalk.bold(card.name)} ${chalk.dim(statusSummary(card))}`);
|
|
259
270
|
console.log(` ${chalk.cyan("→")} ${card.nextAction.hint || `synap cap enable "${card.name}"`}`);
|
|
271
|
+
// An expired connection reads as "needs a step", but the fix is a RECONNECT, not
|
|
272
|
+
// an enable — show the exact command so the hint's "Reconnect …" prose is
|
|
273
|
+
// actionable (the state word tells WHAT, this tells HOW).
|
|
274
|
+
if (card.connection?.state === "expired") {
|
|
275
|
+
console.log(` ${chalk.dim(`synap cap connect "${card.name}" --reconnect`)}`);
|
|
276
|
+
}
|
|
260
277
|
}
|
|
261
278
|
/** An AVAILABLE pack: one line — name · what it offers · add command. */
|
|
262
279
|
function renderAvailable(card) {
|
|
@@ -280,7 +297,7 @@ export async function capabilityList(opts) {
|
|
|
280
297
|
cfg = await resolveHubConfig();
|
|
281
298
|
}
|
|
282
299
|
catch (err) {
|
|
283
|
-
|
|
300
|
+
renderHubError(err);
|
|
284
301
|
process.exit(1);
|
|
285
302
|
}
|
|
286
303
|
const workspaceId = requireWorkspace(opts, cfg);
|
|
@@ -297,7 +314,7 @@ export async function capabilityList(opts) {
|
|
|
297
314
|
return;
|
|
298
315
|
}
|
|
299
316
|
spinner?.fail(chalk.red("Failed to fetch capabilities"));
|
|
300
|
-
|
|
317
|
+
renderHubError(err);
|
|
301
318
|
process.exit(1);
|
|
302
319
|
}
|
|
303
320
|
if (opts.json) {
|
|
@@ -349,18 +366,8 @@ export async function capabilityList(opts) {
|
|
|
349
366
|
// ── Connection sub-flow (shared by enable + connect) ────────────────────────
|
|
350
367
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
351
368
|
function openBrowser(url) {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
? `open "${url}"`
|
|
355
|
-
: platform === "win32"
|
|
356
|
-
? `start "" "${url}"`
|
|
357
|
-
: `xdg-open "${url}"`;
|
|
358
|
-
try {
|
|
359
|
-
exec(cmd);
|
|
360
|
-
}
|
|
361
|
-
catch {
|
|
362
|
-
// silently ignore — caller already prints the URL
|
|
363
|
-
}
|
|
369
|
+
// argv only, http(s) only — never a shell string (`safe-open.ts`).
|
|
370
|
+
openExternalUrl(url);
|
|
364
371
|
}
|
|
365
372
|
/** Short host label for a pod (for picker headers: "→ pod.perso…"). */
|
|
366
373
|
function podHostLabel(cfg) {
|
|
@@ -395,85 +402,66 @@ async function pollUntilConnected(cfg, provider, workspaceId, timeoutMs = 180_00
|
|
|
395
402
|
return null;
|
|
396
403
|
}
|
|
397
404
|
/**
|
|
398
|
-
*
|
|
399
|
-
*
|
|
400
|
-
*
|
|
405
|
+
* The ONE "connect a provider" path: resolve `/connectors/connect`, open the
|
|
406
|
+
* browser on the returned `redirectUrl`, and poll (self-completing door) until
|
|
407
|
+
* the provider flips to `connected`. Shared by the capability-card flow
|
|
408
|
+
* (`ensureConnection`) and the raw-provider discovery picker
|
|
409
|
+
* (`capabilityConnect` with no name), so both reach the identical endpoint,
|
|
410
|
+
* status handling, browser-open and poll semantics.
|
|
411
|
+
*
|
|
412
|
+
* `cardName` is the discriminator: when present the caller is connecting on
|
|
413
|
+
* behalf of a capability card, which enables the card-aware `provider_required`
|
|
414
|
+
* / `provider_unavailable` branches and the named re-run + timeout hints. The
|
|
415
|
+
* raw-picker path already holds an explicit provider id, so those branches do
|
|
416
|
+
* not apply to it — it goes straight from `connected` to the `redirectUrl`
|
|
417
|
+
* check, exactly as before.
|
|
418
|
+
*
|
|
419
|
+
* `displayName` is the local fallback name: the provider's list label for the
|
|
420
|
+
* raw picker, the provider id for the card flow (a card has no separate label).
|
|
401
421
|
*/
|
|
402
|
-
async function
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
// ── Vault credential — prompt + re-apply WITH params ──────────────────────
|
|
412
|
-
// The correct wiring: apply the template with the credential as a param, so the
|
|
413
|
-
// governed applier stores the secret AND links it into the connection tool's
|
|
414
|
-
// credentialRef (pod-wide, for a pod-scoped cap). A bare /vault/secrets write
|
|
415
|
-
// creates an orphan secret that is NOT linked to the tool — the old bug.
|
|
416
|
-
if (conn.kind === "vault") {
|
|
417
|
-
const hasParams = (card.installParams ?? []).some((p) => p.required);
|
|
418
|
-
if (hasParams) {
|
|
419
|
-
const collected = await collectInstallParams(card);
|
|
420
|
-
if (!collected)
|
|
421
|
-
return false;
|
|
422
|
-
return applyTemplateWithParams(cfg, card, workspaceId, collected, "Connecting");
|
|
423
|
-
}
|
|
424
|
-
// Fallback (older pod without install-params on the card): store the secret
|
|
425
|
-
// directly. May need manual linking if the tool's credentialRef doesn't match.
|
|
426
|
-
const service = conn.provider ?? card.key;
|
|
427
|
-
const answer = await prompts({
|
|
428
|
-
type: "password",
|
|
429
|
-
name: "value",
|
|
430
|
-
message: `Paste the ${chalk.bold(card.name)} credential (${service})`,
|
|
431
|
-
});
|
|
432
|
-
const value = answer.value;
|
|
433
|
-
if (!value) {
|
|
434
|
-
log.dim("Cancelled — no credential entered.");
|
|
435
|
-
return false;
|
|
436
|
-
}
|
|
437
|
-
const spinner = ora({ text: "Storing credential in the vault…", color: "cyan" }).start();
|
|
438
|
-
try {
|
|
439
|
-
await hubPost("/vault/secrets", { name: `${card.name} credential`, value, service, type: "credential", workspaceId }, cfg);
|
|
440
|
-
spinner.succeed(chalk.green(`Stored ${chalk.bold(service)} credential.`));
|
|
441
|
-
return true;
|
|
442
|
-
}
|
|
443
|
-
catch (err) {
|
|
444
|
-
spinner.fail(chalk.red("Failed to store credential"));
|
|
445
|
-
log.error(err.message);
|
|
446
|
-
return false;
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
// ── Provider OAuth (nango://) — open + poll ───────────────────────────────
|
|
450
|
-
const provider = conn.provider ?? card.key;
|
|
451
|
-
const spinner = ora({ text: `Resolving ${chalk.bold(provider)} connection…`, color: "cyan" }).start();
|
|
422
|
+
async function connectProviderOAuth(cfg, provider, workspaceId, ctx) {
|
|
423
|
+
const isCard = ctx.cardName !== undefined;
|
|
424
|
+
// Card flow prefers the server's displayName (falling back to the provider
|
|
425
|
+
// id); the raw picker uses its own list label verbatim.
|
|
426
|
+
const name = (r) => isCard ? String(r.displayName ?? ctx.displayName) : ctx.displayName;
|
|
427
|
+
const spinner = ora({
|
|
428
|
+
text: isCard ? `Resolving ${chalk.bold(provider)} connection…` : `Connecting ${chalk.bold(ctx.displayName)}…`,
|
|
429
|
+
color: "cyan",
|
|
430
|
+
}).start();
|
|
452
431
|
let res;
|
|
453
432
|
try {
|
|
454
|
-
|
|
433
|
+
const body = { provider };
|
|
434
|
+
if (workspaceId)
|
|
435
|
+
body.workspaceId = workspaceId;
|
|
436
|
+
if (ctx.forceReauth)
|
|
437
|
+
body.forceReauth = true;
|
|
438
|
+
res = (await hubPost("/connectors/connect", body, cfg));
|
|
455
439
|
spinner.stop();
|
|
456
440
|
}
|
|
457
441
|
catch (err) {
|
|
458
|
-
|
|
459
|
-
|
|
442
|
+
if (isCard)
|
|
443
|
+
spinner.fail(chalk.red("Failed to resolve connection"));
|
|
444
|
+
else
|
|
445
|
+
spinner.stop();
|
|
446
|
+
renderHubError(err);
|
|
460
447
|
return false;
|
|
461
448
|
}
|
|
462
449
|
const status = String(res.status);
|
|
463
450
|
if (status === "connected") {
|
|
464
|
-
|
|
451
|
+
// Both flows prefer the server's displayName here (falling back locally).
|
|
452
|
+
log.success(`${res.displayName ?? ctx.displayName} is already connected.`);
|
|
465
453
|
return true;
|
|
466
454
|
}
|
|
467
|
-
if (status === "provider_required") {
|
|
468
|
-
log.warn(`Couldn't resolve a single provider for ${
|
|
469
|
-
log.dim(`Connect it directly: synap cap connect "${
|
|
455
|
+
if (isCard && status === "provider_required") {
|
|
456
|
+
log.warn(`Couldn't resolve a single provider for ${ctx.cardName}.`);
|
|
457
|
+
log.dim(`Connect it directly: synap cap connect "${ctx.cardName}"`);
|
|
470
458
|
return false;
|
|
471
459
|
}
|
|
472
|
-
if (status === "provider_unavailable") {
|
|
460
|
+
if (isCard && status === "provider_unavailable") {
|
|
473
461
|
// The pod can't offer this provider (not declared / unreachable / malformed).
|
|
474
462
|
// The server message already names the real cause + remedy — print it as-is.
|
|
475
463
|
// No browser, and no "re-run connect": re-running cannot fix a server-side gap.
|
|
476
|
-
log.warn(`${
|
|
464
|
+
log.warn(`${ctx.cardName} is unavailable on this pod.`);
|
|
477
465
|
if (res.message)
|
|
478
466
|
log.dim(String(res.message));
|
|
479
467
|
return false;
|
|
@@ -483,14 +471,14 @@ async function ensureConnection(cfg, workspaceId, card) {
|
|
|
483
471
|
// returns a status we don't handle must NOT send the user to "undefined".
|
|
484
472
|
const redirectUrl = typeof res.redirectUrl === "string" && res.redirectUrl.length > 0 ? res.redirectUrl : null;
|
|
485
473
|
if (!redirectUrl) {
|
|
486
|
-
log.warn(`Couldn't start a connection for ${res
|
|
474
|
+
log.warn(`Couldn't start a connection for ${name(res)}.`);
|
|
487
475
|
if (res.message)
|
|
488
476
|
log.dim(String(res.message));
|
|
489
|
-
else
|
|
477
|
+
else if (isCard)
|
|
490
478
|
log.dim(`The pod returned status "${status}" with no connect URL. Check the pod's connector configuration.`);
|
|
491
479
|
return false;
|
|
492
480
|
}
|
|
493
|
-
log.heading(`Connect ${res
|
|
481
|
+
log.heading(`Connect ${name(res)}`);
|
|
494
482
|
console.log();
|
|
495
483
|
console.log(` Opening OAuth flow in your browser…`);
|
|
496
484
|
console.log(` ${chalk.dim("If it didn't open, paste this URL:")}`);
|
|
@@ -500,13 +488,105 @@ async function ensureConnection(cfg, workspaceId, card) {
|
|
|
500
488
|
const waitSpinner = ora({ text: "Waiting for you to finish in the browser…", color: "cyan" }).start();
|
|
501
489
|
const connected = await pollUntilConnected(cfg, provider, workspaceId);
|
|
502
490
|
if (connected) {
|
|
503
|
-
waitSpinner.succeed(chalk.green(`Connected ${chalk.bold(
|
|
491
|
+
waitSpinner.succeed(chalk.green(`Connected ${chalk.bold(name(connected))}!`));
|
|
504
492
|
return true;
|
|
505
493
|
}
|
|
506
494
|
waitSpinner.stop();
|
|
507
|
-
log.dim(
|
|
495
|
+
log.dim(isCard
|
|
496
|
+
? `Didn't detect a connection yet — finish the browser flow, then re-run \`synap cap enable "${ctx.cardName}"\`.`
|
|
497
|
+
: `Didn't detect a connection yet — finish the browser flow, then re-run.`);
|
|
508
498
|
return false;
|
|
509
499
|
}
|
|
500
|
+
/**
|
|
501
|
+
* Ensure a capability's required connection is satisfied. Provider (nango://) →
|
|
502
|
+
* OAuth open + poll. Vault (vault://) → rotate the linked secret in place, or
|
|
503
|
+
* apply the template with the key as a param. Returns true when the connection
|
|
504
|
+
* is (now) satisfied, false otherwise.
|
|
505
|
+
*/
|
|
506
|
+
async function ensureConnection(cfg, workspaceId, card,
|
|
507
|
+
// Force a re-auth even when the card reports `state === "connected"`. The card
|
|
508
|
+
// reflects a connection RECORD's existence, not its token's HEALTH, so an
|
|
509
|
+
// expired/revoked token still reads "connected" — only a forced fresh OAuth
|
|
510
|
+
// fixes it. Threaded to the provider door as `forceReauth`.
|
|
511
|
+
forceReconnect = false) {
|
|
512
|
+
const conn = card.connection;
|
|
513
|
+
if (!conn || !conn.required)
|
|
514
|
+
return true;
|
|
515
|
+
if (conn.state === "connected" && !forceReconnect)
|
|
516
|
+
return true;
|
|
517
|
+
// NOTE: `state === "unavailable"` is deliberately NOT short-circuited here.
|
|
518
|
+
// Resolving the door is not an OAuth attempt — it returns 200 with the pod's
|
|
519
|
+
// own diagnosis (which of not_declared / unauthenticated / unreachable, and
|
|
520
|
+
// the remedy). Only OPENING A BROWSER is doomed, and that is already guarded
|
|
521
|
+
// below. Short-circuiting on the card's cached state costs the real message.
|
|
522
|
+
// ── Vault credential ──────────────────────────────────────────────────────
|
|
523
|
+
// Every write here must land on the secret the capability's tools actually
|
|
524
|
+
// read. A bare /vault/secrets write creates an orphan secret NOT linked to the
|
|
525
|
+
// tool's credentialRef: the CLI used to fall back to it and print success
|
|
526
|
+
// while every run kept the old key (unipile-linkedin, 2026-10-27).
|
|
527
|
+
if (conn.kind === "vault") {
|
|
528
|
+
// Installed with a live key → rotate THAT secret in place. The tools keep
|
|
529
|
+
// their vault://<id>, so the new value is what the next run reads; no
|
|
530
|
+
// template re-apply, so optional install params are not blanked either.
|
|
531
|
+
if (card.id && conn.account && conn.state === "connected") {
|
|
532
|
+
return rotateLinkedSecret(cfg, card, card.id, conn.account);
|
|
533
|
+
}
|
|
534
|
+
// No live key yet → apply the template WITH params: the governed applier
|
|
535
|
+
// stores the secret AND links it into the tool's credentialRef.
|
|
536
|
+
const hasParams = (card.installParams ?? []).some((p) => p.required);
|
|
537
|
+
if (hasParams) {
|
|
538
|
+
const collected = await collectInstallParams(card);
|
|
539
|
+
if (!collected)
|
|
540
|
+
return false;
|
|
541
|
+
return applyTemplateWithParams(cfg, card, workspaceId, collected, "Connecting");
|
|
542
|
+
}
|
|
543
|
+
log.error(`Can't connect ${card.name} from here.`);
|
|
544
|
+
log.dim(card.id
|
|
545
|
+
? "The pod lists no install params for it and its tools point at no vault key that still exists."
|
|
546
|
+
: "The pod lists no install params for it.");
|
|
547
|
+
log.dim(`Re-install it with its key: synap cap add "${card.key}"`);
|
|
548
|
+
return false;
|
|
549
|
+
}
|
|
550
|
+
// ── Provider OAuth (nango://) — open + poll ───────────────────────────────
|
|
551
|
+
const provider = conn.provider ?? card.key;
|
|
552
|
+
return connectProviderOAuth(cfg, provider, workspaceId, {
|
|
553
|
+
// A card has no separate display name — the provider id is the fallback the
|
|
554
|
+
// server's displayName (when sent) takes precedence over.
|
|
555
|
+
displayName: provider,
|
|
556
|
+
cardName: card.name,
|
|
557
|
+
forceReauth: forceReconnect,
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Replace the value of the vault secret a capability's tools point at
|
|
562
|
+
* (`secretId` = the card's `connection.account`, the id in their
|
|
563
|
+
* `vault://<id>`), through the capability credentials door. The row keeps its
|
|
564
|
+
* id, so every reference to it now resolves to the new value and no second
|
|
565
|
+
* secret is written.
|
|
566
|
+
*/
|
|
567
|
+
async function rotateLinkedSecret(cfg, card, capabilityId, secretId) {
|
|
568
|
+
const answer = await prompts({
|
|
569
|
+
type: "password",
|
|
570
|
+
name: "value",
|
|
571
|
+
message: `Paste the new ${chalk.bold(card.name)} key`,
|
|
572
|
+
});
|
|
573
|
+
const value = answer.value;
|
|
574
|
+
if (!value) {
|
|
575
|
+
log.dim("Cancelled — no key entered. The current key is unchanged.");
|
|
576
|
+
return false;
|
|
577
|
+
}
|
|
578
|
+
const spinner = ora({ text: `Replacing the ${chalk.bold(card.name)} key…`, color: "cyan" }).start();
|
|
579
|
+
try {
|
|
580
|
+
await hubPatch(`/capabilities/${capabilityId}/credentials/${secretId}`, { value }, cfg);
|
|
581
|
+
spinner.succeed(chalk.green(`Replaced the ${chalk.bold(card.name)} key. Its tools use the new one from the next run.`));
|
|
582
|
+
return true;
|
|
583
|
+
}
|
|
584
|
+
catch (err) {
|
|
585
|
+
spinner.fail(chalk.red(`Couldn't replace the ${card.name} key — the current key is unchanged`));
|
|
586
|
+
renderHubError(err);
|
|
587
|
+
return false;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
510
590
|
/**
|
|
511
591
|
* Prompt for a template's REQUIRED install params (masked for secrets). Returns
|
|
512
592
|
* the params map, or null if the user cancelled. Empty map when nothing required.
|
|
@@ -565,58 +645,9 @@ async function applyTemplateWithParams(cfg, card, workspaceId, params, verb) {
|
|
|
565
645
|
}
|
|
566
646
|
catch (err) {
|
|
567
647
|
spinner.fail(chalk.red(`Failed to ${verb.toLowerCase()} ${card.name}`));
|
|
568
|
-
log.error(err.message);
|
|
569
|
-
return false;
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
/**
|
|
573
|
-
* Connect a RAW provider chosen from the discovery picker (not tied to a
|
|
574
|
-
* capability card): start OAuth via `/connectors/connect`, open the browser, and
|
|
575
|
-
* poll until it flips to connected. Same door + poll the card flow uses.
|
|
576
|
-
*/
|
|
577
|
-
async function connectProviderFlow(cfg, provider, displayName, workspaceId) {
|
|
578
|
-
const spinner = ora({ text: `Connecting ${chalk.bold(displayName)}…`, color: "cyan" }).start();
|
|
579
|
-
let res;
|
|
580
|
-
try {
|
|
581
|
-
const body = { provider };
|
|
582
|
-
if (workspaceId)
|
|
583
|
-
body.workspaceId = workspaceId;
|
|
584
|
-
res = (await hubPost("/connectors/connect", body, cfg));
|
|
585
|
-
spinner.stop();
|
|
586
|
-
}
|
|
587
|
-
catch (err) {
|
|
588
|
-
spinner.stop();
|
|
589
648
|
renderHubError(err);
|
|
590
649
|
return false;
|
|
591
650
|
}
|
|
592
|
-
if (String(res.status) === "connected") {
|
|
593
|
-
log.success(`${res.displayName ?? displayName} is already connected.`);
|
|
594
|
-
return true;
|
|
595
|
-
}
|
|
596
|
-
const redirectUrl = typeof res.redirectUrl === "string" && res.redirectUrl.length > 0 ? res.redirectUrl : null;
|
|
597
|
-
if (!redirectUrl) {
|
|
598
|
-
log.warn(`Couldn't start a connection for ${displayName}.`);
|
|
599
|
-
// Covers provider_unavailable too — the server message names the real cause.
|
|
600
|
-
if (res.message)
|
|
601
|
-
log.dim(String(res.message));
|
|
602
|
-
return false;
|
|
603
|
-
}
|
|
604
|
-
log.heading(`Connect ${displayName}`);
|
|
605
|
-
console.log();
|
|
606
|
-
console.log(` Opening OAuth flow in your browser…`);
|
|
607
|
-
console.log(` ${chalk.dim("If it didn't open, paste this URL:")}`);
|
|
608
|
-
console.log(` ${chalk.underline(chalk.cyan(redirectUrl))}`);
|
|
609
|
-
console.log();
|
|
610
|
-
openBrowser(redirectUrl);
|
|
611
|
-
const waitSpinner = ora({ text: "Waiting for you to finish in the browser…", color: "cyan" }).start();
|
|
612
|
-
const connected = await pollUntilConnected(cfg, provider, workspaceId);
|
|
613
|
-
if (connected) {
|
|
614
|
-
waitSpinner.succeed(chalk.green(`Connected ${chalk.bold(displayName)}!`));
|
|
615
|
-
return true;
|
|
616
|
-
}
|
|
617
|
-
waitSpinner.stop();
|
|
618
|
-
log.dim(`Didn't detect a connection yet — finish the browser flow, then re-run.`);
|
|
619
|
-
return false;
|
|
620
651
|
}
|
|
621
652
|
export async function capabilityAdd(name, opts) {
|
|
622
653
|
let cfg;
|
|
@@ -624,7 +655,7 @@ export async function capabilityAdd(name, opts) {
|
|
|
624
655
|
cfg = await resolveHubConfig();
|
|
625
656
|
}
|
|
626
657
|
catch (err) {
|
|
627
|
-
|
|
658
|
+
renderHubError(err);
|
|
628
659
|
process.exit(1);
|
|
629
660
|
}
|
|
630
661
|
const workspaceId = requireWorkspace(opts, cfg);
|
|
@@ -644,7 +675,7 @@ export async function capabilityAdd(name, opts) {
|
|
|
644
675
|
return;
|
|
645
676
|
}
|
|
646
677
|
spinner.fail(chalk.red("Failed to fetch catalog"));
|
|
647
|
-
|
|
678
|
+
renderHubError(err);
|
|
648
679
|
process.exit(1);
|
|
649
680
|
}
|
|
650
681
|
// No name given → open an interactive picker over the AVAILABLE (not-yet-
|
|
@@ -750,7 +781,7 @@ export async function capabilityAdd(name, opts) {
|
|
|
750
781
|
}
|
|
751
782
|
catch (err) {
|
|
752
783
|
addSpinner.fail(chalk.red(`Failed to add ${card.name}`));
|
|
753
|
-
|
|
784
|
+
renderHubError(err);
|
|
754
785
|
process.exit(1);
|
|
755
786
|
}
|
|
756
787
|
}
|
|
@@ -762,6 +793,21 @@ export async function capabilityAdd(name, opts) {
|
|
|
762
793
|
catch {
|
|
763
794
|
// best-effort — the add already succeeded
|
|
764
795
|
}
|
|
796
|
+
// CONNECT-first: a provider capability that isn't connected yet runs the OAuth
|
|
797
|
+
// flow now, so `cap add` is the ONE happy-path door (apply → connect → enable
|
|
798
|
+
// reads). `ensureConnection` is the shared provider+OAuth path; a failed or
|
|
799
|
+
// timed-out OAuth returns false and leaves the card un-connected — recover
|
|
800
|
+
// with `synap cap connect "<name>"`. No-connection / already-connected cards
|
|
801
|
+
// skip this.
|
|
802
|
+
if (after?.connection?.required && after.connection.state !== "connected") {
|
|
803
|
+
await ensureConnection(cfg, workspaceId, after);
|
|
804
|
+
try {
|
|
805
|
+
after = findCard(await fetchCatalog(cfg, workspaceId), card.name) ?? after;
|
|
806
|
+
}
|
|
807
|
+
catch {
|
|
808
|
+
// keep the pre-connect card
|
|
809
|
+
}
|
|
810
|
+
}
|
|
765
811
|
// AUTO-ENABLE the read verbs, so install is ONE step, not two. Reads are the
|
|
766
812
|
// `auto`-governance, no-side-effect verbs — safe to turn on without asking.
|
|
767
813
|
// Write/action verbs stay OFF (they ask approval per run); enable them later
|
|
@@ -821,7 +867,7 @@ export async function capabilityEnable(name, opts) {
|
|
|
821
867
|
cfg = await resolveHubConfig();
|
|
822
868
|
}
|
|
823
869
|
catch (err) {
|
|
824
|
-
|
|
870
|
+
renderHubError(err);
|
|
825
871
|
process.exit(1);
|
|
826
872
|
}
|
|
827
873
|
const workspaceId = requireWorkspace(opts, cfg);
|
|
@@ -841,7 +887,7 @@ export async function capabilityEnable(name, opts) {
|
|
|
841
887
|
catalogNeedsDeploy();
|
|
842
888
|
return;
|
|
843
889
|
}
|
|
844
|
-
|
|
890
|
+
renderHubError(err);
|
|
845
891
|
process.exit(1);
|
|
846
892
|
}
|
|
847
893
|
if (cards.length === 0) {
|
|
@@ -901,7 +947,7 @@ export async function capabilityEnable(name, opts) {
|
|
|
901
947
|
return;
|
|
902
948
|
}
|
|
903
949
|
spinner.fail(chalk.red("Failed to fetch catalog"));
|
|
904
|
-
|
|
950
|
+
renderHubError(err);
|
|
905
951
|
process.exit(1);
|
|
906
952
|
}
|
|
907
953
|
if (!card) {
|
|
@@ -1000,7 +1046,7 @@ export async function capabilityEnable(name, opts) {
|
|
|
1000
1046
|
}
|
|
1001
1047
|
catch (err) {
|
|
1002
1048
|
work.fail(chalk.red("Failed while applying your selection"));
|
|
1003
|
-
|
|
1049
|
+
renderHubError(err);
|
|
1004
1050
|
process.exit(1);
|
|
1005
1051
|
}
|
|
1006
1052
|
// (d) Report what changed + an example run.
|
|
@@ -1021,7 +1067,7 @@ export async function capabilityConnect(name, opts) {
|
|
|
1021
1067
|
cfg = await resolveHubConfig();
|
|
1022
1068
|
}
|
|
1023
1069
|
catch (err) {
|
|
1024
|
-
|
|
1070
|
+
renderHubError(err);
|
|
1025
1071
|
process.exit(1);
|
|
1026
1072
|
}
|
|
1027
1073
|
const workspaceId = requireWorkspace(opts, cfg);
|
|
@@ -1042,7 +1088,7 @@ export async function capabilityConnect(name, opts) {
|
|
|
1042
1088
|
}
|
|
1043
1089
|
catch (err) {
|
|
1044
1090
|
spin.stop();
|
|
1045
|
-
|
|
1091
|
+
renderHubError(err);
|
|
1046
1092
|
log.dim("Couldn't reach the pod's connector service.");
|
|
1047
1093
|
return;
|
|
1048
1094
|
}
|
|
@@ -1062,18 +1108,22 @@ export async function capabilityConnect(name, opts) {
|
|
|
1062
1108
|
}
|
|
1063
1109
|
const connectable = providers.filter((p) => !p.connected);
|
|
1064
1110
|
const connected = providers.filter((p) => p.connected);
|
|
1065
|
-
|
|
1111
|
+
// --reconnect: the point is to re-auth an ALREADY-connected (but expired)
|
|
1112
|
+
// service, so connected rows become selectable (a fresh OAuth) rather than
|
|
1113
|
+
// disabled, and "everything already connected" is no longer a dead end.
|
|
1114
|
+
if (connectable.length === 0 && !opts.reconnect) {
|
|
1066
1115
|
log.heading("Everything connectable here is already connected.");
|
|
1067
1116
|
for (const p of connected)
|
|
1068
1117
|
log.dim(` ${chalk.green("✓")} ${p.displayName ?? p.provider}`);
|
|
1118
|
+
log.dim("Reconnect an expired one: synap cap connect --reconnect");
|
|
1069
1119
|
return;
|
|
1070
1120
|
}
|
|
1071
1121
|
const answer = await prompts({
|
|
1072
1122
|
type: "select",
|
|
1073
1123
|
name: "id",
|
|
1074
|
-
message:
|
|
1075
|
-
// Connectable first; already-connected
|
|
1076
|
-
//
|
|
1124
|
+
message: `${opts.reconnect ? "Reconnect" : "Connect"} a service ${chalk.dim(`→ ${podHostLabel(cfg)}`)}`,
|
|
1125
|
+
// Connectable first; already-connected grouped at the end — disabled in the
|
|
1126
|
+
// normal flow, but SELECTABLE (for a forced re-auth) under --reconnect.
|
|
1077
1127
|
choices: [
|
|
1078
1128
|
...connectable.map((p) => ({
|
|
1079
1129
|
title: p.displayName ?? p.provider,
|
|
@@ -1081,9 +1131,9 @@ export async function capabilityConnect(name, opts) {
|
|
|
1081
1131
|
})),
|
|
1082
1132
|
...connected.map((p) => ({
|
|
1083
1133
|
title: `${p.displayName ?? p.provider} ${chalk.green("✓ connected")}`,
|
|
1084
|
-
description: "already connected",
|
|
1134
|
+
description: opts.reconnect ? "re-authenticate" : "already connected",
|
|
1085
1135
|
value: p.id,
|
|
1086
|
-
disabled:
|
|
1136
|
+
disabled: !opts.reconnect,
|
|
1087
1137
|
})),
|
|
1088
1138
|
],
|
|
1089
1139
|
});
|
|
@@ -1092,7 +1142,10 @@ export async function capabilityConnect(name, opts) {
|
|
|
1092
1142
|
return;
|
|
1093
1143
|
}
|
|
1094
1144
|
const chosen = providers.find((p) => p.id === answer.id);
|
|
1095
|
-
const ok = await
|
|
1145
|
+
const ok = await connectProviderOAuth(cfg, chosen.id, workspaceId, {
|
|
1146
|
+
displayName: chosen.displayName ?? chosen.provider,
|
|
1147
|
+
forceReauth: opts.reconnect ?? false,
|
|
1148
|
+
});
|
|
1096
1149
|
// A connect that did not connect must not report success to a caller/script.
|
|
1097
1150
|
if (!ok)
|
|
1098
1151
|
process.exit(1);
|
|
@@ -1111,7 +1164,7 @@ export async function capabilityConnect(name, opts) {
|
|
|
1111
1164
|
return;
|
|
1112
1165
|
}
|
|
1113
1166
|
spinner.fail(chalk.red("Failed to fetch catalog"));
|
|
1114
|
-
|
|
1167
|
+
renderHubError(err);
|
|
1115
1168
|
process.exit(1);
|
|
1116
1169
|
}
|
|
1117
1170
|
if (!card) {
|
|
@@ -1124,7 +1177,13 @@ export async function capabilityConnect(name, opts) {
|
|
|
1124
1177
|
log.dim(`Enable its verbs: synap cap enable "${card.name}"`);
|
|
1125
1178
|
return;
|
|
1126
1179
|
}
|
|
1127
|
-
|
|
1180
|
+
if (opts.reconnect) {
|
|
1181
|
+
log.heading(`Reconnecting ${card.name}`);
|
|
1182
|
+
log.dim(card.connection.kind === "vault"
|
|
1183
|
+
? "Replacing the key its tools use."
|
|
1184
|
+
: "Forcing a fresh sign-in — your previous access had expired or was revoked.");
|
|
1185
|
+
}
|
|
1186
|
+
const ok = await ensureConnection(cfg, workspaceId, card, opts.reconnect ?? false);
|
|
1128
1187
|
if (!ok)
|
|
1129
1188
|
process.exit(1);
|
|
1130
1189
|
log.dim(`Now enable its verbs: synap cap enable "${card.name}"`);
|
|
@@ -1147,7 +1206,7 @@ export async function capabilityDisconnect(name, opts) {
|
|
|
1147
1206
|
cfg = await resolveHubConfig();
|
|
1148
1207
|
}
|
|
1149
1208
|
catch (err) {
|
|
1150
|
-
|
|
1209
|
+
renderHubError(err);
|
|
1151
1210
|
process.exit(1);
|
|
1152
1211
|
}
|
|
1153
1212
|
const workspaceId = requireWorkspace(opts, cfg);
|
|
@@ -1231,7 +1290,7 @@ export async function capabilityShow(name, opts) {
|
|
|
1231
1290
|
cfg = await resolveHubConfig();
|
|
1232
1291
|
}
|
|
1233
1292
|
catch (err) {
|
|
1234
|
-
|
|
1293
|
+
renderHubError(err);
|
|
1235
1294
|
process.exit(1);
|
|
1236
1295
|
}
|
|
1237
1296
|
const workspaceId = requireWorkspace(opts, cfg);
|
|
@@ -1248,7 +1307,7 @@ export async function capabilityShow(name, opts) {
|
|
|
1248
1307
|
return;
|
|
1249
1308
|
}
|
|
1250
1309
|
spinner?.fail(chalk.red("Failed to fetch catalog"));
|
|
1251
|
-
|
|
1310
|
+
renderHubError(err);
|
|
1252
1311
|
process.exit(1);
|
|
1253
1312
|
}
|
|
1254
1313
|
if (!card) {
|
|
@@ -1269,20 +1328,34 @@ export async function capabilityShow(name, opts) {
|
|
|
1269
1328
|
const c = card.connection;
|
|
1270
1329
|
// `unavailable` is dim, not red: it isn't the user's fault and isn't fixable
|
|
1271
1330
|
// by connecting — red reads as "you have a missing credential to go fix".
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1331
|
+
// Unverified: the pod could not actually check (a broker/list fault), not a
|
|
1332
|
+
// checked absence. Render distinctly from a plain "missing" — that would
|
|
1333
|
+
// falsely claim "you never connected this".
|
|
1334
|
+
const stateColor = c.unverified
|
|
1335
|
+
? chalk.yellow
|
|
1336
|
+
: c.state === "connected"
|
|
1337
|
+
? chalk.green
|
|
1338
|
+
: c.state === "expired"
|
|
1339
|
+
? chalk.yellow
|
|
1340
|
+
: c.state === "unavailable"
|
|
1341
|
+
? chalk.dim
|
|
1342
|
+
: chalk.red;
|
|
1343
|
+
const stateLabel = c.unverified ? "couldn't check" : c.state;
|
|
1279
1344
|
const parts = [
|
|
1280
1345
|
c.kind ?? "connection",
|
|
1281
1346
|
c.provider ? chalk.dim(`(${c.provider})`) : "",
|
|
1282
1347
|
c.account ? chalk.dim(c.account) : "",
|
|
1283
|
-
stateColor(
|
|
1348
|
+
stateColor(stateLabel),
|
|
1284
1349
|
].filter(Boolean);
|
|
1285
1350
|
console.log(` ${chalk.bold("Connection")} ${parts.join(" ")}`);
|
|
1351
|
+
// Expired = previously connected, token now dead. Surface the exact command to
|
|
1352
|
+
// fix it — the state word alone tells WHAT, not HOW (the user's original ask).
|
|
1353
|
+
if (c.state === "expired") {
|
|
1354
|
+
console.log(` ${chalk.yellow("→")} its access expired — reconnect: synap cap connect "${card.name}" --reconnect`);
|
|
1355
|
+
}
|
|
1356
|
+
else if (c.unverified) {
|
|
1357
|
+
console.log(` ${chalk.yellow("→")} ${c.unverified.message || c.unverified.reason} — retry: synap cap show "${card.name}"`);
|
|
1358
|
+
}
|
|
1286
1359
|
}
|
|
1287
1360
|
else {
|
|
1288
1361
|
console.log(` ${chalk.bold("Connection")} ${chalk.dim("none required")}`);
|
|
@@ -1317,7 +1390,7 @@ export async function capabilityRun(verb, params, opts) {
|
|
|
1317
1390
|
cfg = await resolveHubConfig();
|
|
1318
1391
|
}
|
|
1319
1392
|
catch (err) {
|
|
1320
|
-
|
|
1393
|
+
renderHubError(err);
|
|
1321
1394
|
process.exit(1);
|
|
1322
1395
|
}
|
|
1323
1396
|
const workspaceId = requireWorkspace(opts, cfg);
|
|
@@ -1395,7 +1468,7 @@ export async function capabilityTest(verb, opts) {
|
|
|
1395
1468
|
cfg = await resolveHubConfig();
|
|
1396
1469
|
}
|
|
1397
1470
|
catch (err) {
|
|
1398
|
-
|
|
1471
|
+
renderHubError(err);
|
|
1399
1472
|
process.exit(1);
|
|
1400
1473
|
}
|
|
1401
1474
|
const workspaceId = requireWorkspace(opts, cfg);
|
|
@@ -1406,7 +1479,7 @@ export async function capabilityTest(verb, opts) {
|
|
|
1406
1479
|
}
|
|
1407
1480
|
catch (err) {
|
|
1408
1481
|
spinner.fail(chalk.red("Failed to resolve capability"));
|
|
1409
|
-
|
|
1482
|
+
renderHubError(err);
|
|
1410
1483
|
process.exit(1);
|
|
1411
1484
|
}
|
|
1412
1485
|
if (!skillId) {
|
|
@@ -1422,7 +1495,7 @@ export async function capabilityTest(verb, opts) {
|
|
|
1422
1495
|
}
|
|
1423
1496
|
catch (err) {
|
|
1424
1497
|
spinner.fail(chalk.red(`Dry-run failed for ${verb}`));
|
|
1425
|
-
|
|
1498
|
+
renderHubError(err);
|
|
1426
1499
|
process.exit(1);
|
|
1427
1500
|
}
|
|
1428
1501
|
log.heading(`Dry-run preview — ${chalk.bold(verb)}`);
|
|
@@ -1458,7 +1531,7 @@ export async function capabilityCreate(file, opts) {
|
|
|
1458
1531
|
cfg = await resolveHubConfig();
|
|
1459
1532
|
}
|
|
1460
1533
|
catch (err) {
|
|
1461
|
-
|
|
1534
|
+
renderHubError(err);
|
|
1462
1535
|
process.exit(1);
|
|
1463
1536
|
}
|
|
1464
1537
|
const workspaceId = requireWorkspace(opts, cfg);
|
|
@@ -1501,7 +1574,7 @@ export async function capabilityCreate(file, opts) {
|
|
|
1501
1574
|
}
|
|
1502
1575
|
catch (err) {
|
|
1503
1576
|
spinner.fail(chalk.red("Failed to create capability"));
|
|
1504
|
-
|
|
1577
|
+
renderHubError(err);
|
|
1505
1578
|
process.exit(1);
|
|
1506
1579
|
}
|
|
1507
1580
|
const created = (res.created ?? {});
|