@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,41 +4,318 @@
|
|
|
4
4
|
"status": "active",
|
|
5
5
|
"triggerConfig": {
|
|
6
6
|
"eventPattern": "entity.create.completed",
|
|
7
|
-
"filters": {
|
|
7
|
+
"filters": {
|
|
8
|
+
"profileSlug": "bookmark"
|
|
9
|
+
}
|
|
8
10
|
},
|
|
9
11
|
"flowDefinition": {
|
|
10
12
|
"nodes": [
|
|
11
13
|
{
|
|
12
14
|
"id": "trigger",
|
|
13
15
|
"type": "trigger",
|
|
14
|
-
"position": {
|
|
16
|
+
"position": {
|
|
17
|
+
"x": 0,
|
|
18
|
+
"y": 0
|
|
19
|
+
},
|
|
15
20
|
"data": {
|
|
16
21
|
"triggerType": "event",
|
|
17
22
|
"label": "Bookmark created",
|
|
18
23
|
"config": {
|
|
19
24
|
"eventPattern": "entity.create.completed",
|
|
20
|
-
"filters": {
|
|
25
|
+
"filters": {
|
|
26
|
+
"profileSlug": "bookmark"
|
|
27
|
+
}
|
|
21
28
|
}
|
|
22
29
|
}
|
|
23
30
|
},
|
|
24
31
|
{
|
|
25
|
-
"id": "
|
|
26
|
-
"type": "
|
|
27
|
-
"position": {
|
|
32
|
+
"id": "read",
|
|
33
|
+
"type": "entity_read",
|
|
34
|
+
"position": {
|
|
35
|
+
"x": 0,
|
|
36
|
+
"y": 120
|
|
37
|
+
},
|
|
28
38
|
"data": {
|
|
29
|
-
"label": "
|
|
30
|
-
"
|
|
31
|
-
|
|
39
|
+
"label": "Read the bookmark",
|
|
40
|
+
"entityId": "{{trigger.payload.subjectId}}"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"id": "graph-context",
|
|
45
|
+
"type": "related_entities",
|
|
46
|
+
"position": {
|
|
47
|
+
"x": 0,
|
|
48
|
+
"y": 240
|
|
49
|
+
},
|
|
50
|
+
"data": {
|
|
51
|
+
"label": "What the graph already connects (deterministic)",
|
|
52
|
+
"entityId": "{{trigger.payload.subjectId}}",
|
|
53
|
+
"direction": "both",
|
|
54
|
+
"limit": 10
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"id": "channel-talk",
|
|
59
|
+
"type": "messages_query",
|
|
60
|
+
"position": {
|
|
61
|
+
"x": 0,
|
|
62
|
+
"y": 360
|
|
63
|
+
},
|
|
64
|
+
"data": {
|
|
65
|
+
"label": "What was being discussed (deterministic)",
|
|
66
|
+
"channelId": "{{steps.read.output.entity.properties.sourceChannelId}}",
|
|
67
|
+
"limit": 10,
|
|
68
|
+
"errorHandling": {
|
|
69
|
+
"continueOnError": true
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"id": "sharer-lookup",
|
|
75
|
+
"type": "query",
|
|
76
|
+
"position": {
|
|
77
|
+
"x": 0,
|
|
78
|
+
"y": 480
|
|
79
|
+
},
|
|
80
|
+
"data": {
|
|
81
|
+
"label": "Resolve the sharer to a person entity (deterministic)",
|
|
82
|
+
"profileSlug": "person",
|
|
83
|
+
"filter": "{\"title\": \"{{steps.read.output.entity.properties.sharedBy}}\"}",
|
|
84
|
+
"limit": 5
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"id": "has-sharer-name",
|
|
89
|
+
"type": "condition",
|
|
90
|
+
"position": {
|
|
91
|
+
"x": 0,
|
|
92
|
+
"y": 600
|
|
93
|
+
},
|
|
94
|
+
"data": {
|
|
95
|
+
"label": "Bookmark carries a sharer name?",
|
|
96
|
+
"expression": "steps.read.output.entity.properties.sharedBy !== ''"
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"id": "sharer-matches",
|
|
101
|
+
"type": "condition",
|
|
102
|
+
"position": {
|
|
103
|
+
"x": 0,
|
|
104
|
+
"y": 720
|
|
105
|
+
},
|
|
106
|
+
"data": {
|
|
107
|
+
"label": "Matched person's title equals the sharer name exactly?",
|
|
108
|
+
"expression": "steps.sharer-lookup.output.entities.0.title === steps.read.output.entity.properties.sharedBy"
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"id": "link-sharer",
|
|
113
|
+
"type": "output",
|
|
114
|
+
"position": {
|
|
115
|
+
"x": 0,
|
|
116
|
+
"y": 840
|
|
117
|
+
},
|
|
118
|
+
"data": {
|
|
119
|
+
"label": "Link bookmark → the person who shared it",
|
|
120
|
+
"outputType": "relation_create",
|
|
121
|
+
"config": {
|
|
122
|
+
"fromEntityId": "{{trigger.payload.subjectId}}",
|
|
123
|
+
"toEntityId": "{{steps.sharer-lookup.output.entities.0.id}}",
|
|
124
|
+
"relationType": "created_by"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"id": "classify",
|
|
130
|
+
"type": "capability",
|
|
131
|
+
"position": {
|
|
132
|
+
"x": 0,
|
|
133
|
+
"y": 1200
|
|
134
|
+
},
|
|
135
|
+
"data": {
|
|
136
|
+
"label": "Describe + classify (AI) — title only if the captured one is unusable",
|
|
137
|
+
"verbId": "ai.generate",
|
|
32
138
|
"inputMapping": {
|
|
33
|
-
"
|
|
139
|
+
"system": "You name saved links for a personal knowledge base. Answer with a JSON object and nothing else: {\"title\": string, \"description\": string, \"category\": string}. `title` is a short human-readable name for the link (max 70 chars, no URL, no quotes). `description` is one factual sentence (max 200 chars). `category` is ONE lowercase word from: article, video, tool, repo, docs, social, media, other. You CANNOT open the link. Use ONLY the URL, the already-resolved graph context and the surrounding conversation you are given. `description` and `category` are ALWAYS used. `title` is used ONLY when the link has no usable name yet — when 'Current name' below already reads like a real title, it is kept and yours is discarded, so never optimise for replacing it. If the material does not tell you what the page is, set `title` to the empty string rather than guessing.",
|
|
140
|
+
"prompt": "URL: {{steps.read.output.entity.properties.url}}\nCurrent name: {{steps.read.output.entity.title}}\nShared by: {{steps.read.output.entity.properties.sharedBy}}\n\nAlready connected in the graph:\n{{steps.graph-context.output.entities}}\n\nSurrounding conversation in the source channel:\n{{steps.channel-talk.output.messages}}\n\nThe message it arrived in:\n{{steps.read.output.entity.preview}}",
|
|
141
|
+
"json": "true",
|
|
142
|
+
"maxTokens": "2000"
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"id": "apply-enrich",
|
|
148
|
+
"type": "output",
|
|
149
|
+
"position": {
|
|
150
|
+
"x": -400,
|
|
151
|
+
"y": 1500
|
|
152
|
+
},
|
|
153
|
+
"data": {
|
|
154
|
+
"label": "Write description + category back (always)",
|
|
155
|
+
"outputType": "entity_update",
|
|
156
|
+
"config": {
|
|
157
|
+
"entityId": "{{trigger.payload.subjectId}}",
|
|
158
|
+
"properties": {
|
|
159
|
+
"description": "{{steps.classify.output.description}}",
|
|
160
|
+
"category": "{{steps.classify.output.category}}"
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"id": "title-present",
|
|
167
|
+
"type": "condition",
|
|
168
|
+
"position": {
|
|
169
|
+
"x": 400,
|
|
170
|
+
"y": 1350
|
|
171
|
+
},
|
|
172
|
+
"data": {
|
|
173
|
+
"label": "Did the link arrive with a name?",
|
|
174
|
+
"expression": "steps.read.output.entity.title !== ''"
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"id": "title-not-url",
|
|
179
|
+
"type": "condition",
|
|
180
|
+
"position": {
|
|
181
|
+
"x": 400,
|
|
182
|
+
"y": 1470
|
|
183
|
+
},
|
|
184
|
+
"data": {
|
|
185
|
+
"label": "…and is that name more than the raw URL?",
|
|
186
|
+
"expression": "steps.read.output.entity.title !== steps.read.output.entity.properties.url"
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"id": "usable-title",
|
|
191
|
+
"type": "guard",
|
|
192
|
+
"position": {
|
|
193
|
+
"x": 0,
|
|
194
|
+
"y": 1320
|
|
195
|
+
},
|
|
196
|
+
"data": {
|
|
197
|
+
"label": "Only rename when the model produced a real title",
|
|
198
|
+
"checks": [
|
|
199
|
+
{
|
|
200
|
+
"path": "steps.classify.output.title",
|
|
201
|
+
"minLength": 3,
|
|
202
|
+
"message": "ai.generate returned no usable title — leaving the bookmark's captured name untouched."
|
|
203
|
+
}
|
|
204
|
+
]
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"id": "apply-title",
|
|
209
|
+
"type": "output",
|
|
210
|
+
"position": {
|
|
211
|
+
"x": 0,
|
|
212
|
+
"y": 1440
|
|
213
|
+
},
|
|
214
|
+
"data": {
|
|
215
|
+
"label": "Write the AI title back (only when the captured one was unusable)",
|
|
216
|
+
"outputType": "entity_update",
|
|
217
|
+
"config": {
|
|
218
|
+
"entityId": "{{trigger.payload.subjectId}}",
|
|
219
|
+
"title": "{{steps.classify.output.title}}",
|
|
220
|
+
"properties": {
|
|
221
|
+
"title": "{{steps.classify.output.title}}"
|
|
222
|
+
}
|
|
34
223
|
}
|
|
35
224
|
}
|
|
36
225
|
}
|
|
37
226
|
],
|
|
38
|
-
"edges": [
|
|
227
|
+
"edges": [
|
|
228
|
+
{
|
|
229
|
+
"id": "trigger->read",
|
|
230
|
+
"source": "trigger",
|
|
231
|
+
"target": "read"
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"id": "read->graph-context",
|
|
235
|
+
"source": "read",
|
|
236
|
+
"target": "graph-context"
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
"id": "graph-context->channel-talk",
|
|
240
|
+
"source": "graph-context",
|
|
241
|
+
"target": "channel-talk"
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
"id": "channel-talk->sharer-lookup",
|
|
245
|
+
"source": "channel-talk",
|
|
246
|
+
"target": "sharer-lookup"
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
"id": "sharer-lookup->has-sharer-name",
|
|
250
|
+
"source": "sharer-lookup",
|
|
251
|
+
"target": "has-sharer-name"
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
"id": "has-sharer-name->sharer-matches",
|
|
255
|
+
"source": "has-sharer-name",
|
|
256
|
+
"target": "sharer-matches",
|
|
257
|
+
"sourceHandle": "yes"
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
"id": "sharer-matches->link-sharer",
|
|
261
|
+
"source": "sharer-matches",
|
|
262
|
+
"target": "link-sharer",
|
|
263
|
+
"sourceHandle": "yes"
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
"id": "link-sharer->classify",
|
|
267
|
+
"source": "link-sharer",
|
|
268
|
+
"target": "classify"
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
"id": "has-sharer-name->classify",
|
|
272
|
+
"source": "has-sharer-name",
|
|
273
|
+
"target": "classify",
|
|
274
|
+
"sourceHandle": "no"
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
"id": "sharer-matches->classify",
|
|
278
|
+
"source": "sharer-matches",
|
|
279
|
+
"target": "classify",
|
|
280
|
+
"sourceHandle": "no"
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
"id": "classify->apply-enrich",
|
|
284
|
+
"source": "classify",
|
|
285
|
+
"target": "apply-enrich"
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
"id": "classify->title-present",
|
|
289
|
+
"source": "classify",
|
|
290
|
+
"target": "title-present"
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
"id": "title-present->title-not-url",
|
|
294
|
+
"source": "title-present",
|
|
295
|
+
"target": "title-not-url",
|
|
296
|
+
"sourceHandle": "yes"
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
"id": "title-present->usable-title",
|
|
300
|
+
"source": "title-present",
|
|
301
|
+
"target": "usable-title",
|
|
302
|
+
"sourceHandle": "no"
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
"id": "title-not-url->usable-title",
|
|
306
|
+
"source": "title-not-url",
|
|
307
|
+
"target": "usable-title",
|
|
308
|
+
"sourceHandle": "no"
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
"id": "usable-title->apply-title",
|
|
312
|
+
"source": "usable-title",
|
|
313
|
+
"target": "apply-title"
|
|
314
|
+
}
|
|
315
|
+
]
|
|
39
316
|
},
|
|
40
317
|
"metadata": {
|
|
41
318
|
"source": "bridge-template",
|
|
42
|
-
"note": "
|
|
319
|
+
"note": "DETERMINISTIC-FIRST. Everything reachable without a network call runs BEFORE the AI: `related_entities` (what the graph already connects to this bookmark — the capture flow's `references` edge to the channel's bound entity lands here), `messages_query` on the captured `sourceChannelId` (what was being discussed), and a `query` on `person` resolving the captured `sharedBy` display name to a real person entity, linked with the EXISTING `created_by` relation def (no new def, no new node type). ai.generate is now the LAST RESORT: it runs only when the captured name is empty OR is the raw URL — the branch `title-present`→`title-not-url` — so a human-written message preview is never overwritten by a guess, and when the AI does run it is handed the deterministic context instead of a bare URL. Two condition merges (`title-present`, `classify`) rely on markDescendantsSkipped's live-parent rule (graph-topology.ts) — a merge node survives as long as ONE incoming edge is unpruned. `sharer-lookup` uses the STRING filter form on purpose: `parseQueryFilterConditions` template-resolves a STRING filter but pushes an OBJECT filter's values RAW, so the object form would have compared the literal `{{...}}` text and silently matched nothing. The `sharer-matches` exact-equality condition is the containment for that string form: if a name with a quote breaks JSON.parse the filter is dropped and the query widens, and this check refuses to link a non-identical title. `messages_query` carries `continueOnError` because it THROWS when the channel is not visible in the automation's workspace. NOT expressible today: host→company/tool matching. `transform` has no host/hostname pipe and the query filter compiles only exact equality ($eq/$gt/$gte/$lt/$lte/$ne — no substring/LIKE), so a host could never be matched against a `website` property holding a full URL. A page `fetch` is deliberately still absent."
|
|
43
320
|
}
|
|
44
|
-
}
|
|
321
|
+
}
|
|
@@ -22,18 +22,36 @@
|
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
|
-
"id": "
|
|
25
|
+
"id": "content",
|
|
26
26
|
"type": "transform",
|
|
27
27
|
"position": { "x": 0, "y": 120 },
|
|
28
28
|
"data": {
|
|
29
|
-
"label": "
|
|
30
|
-
"expression": "{{trigger.payload.data.content}} |
|
|
29
|
+
"label": "Trim message text",
|
|
30
|
+
"expression": "{{trigger.payload.data.content}} | trim"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "title-src",
|
|
35
|
+
"type": "transform",
|
|
36
|
+
"position": { "x": 0, "y": 240 },
|
|
37
|
+
"data": {
|
|
38
|
+
"label": "Human text (title source)",
|
|
39
|
+
"expression": "{{trigger.payload.data.messagePreview}} | trim"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "extract",
|
|
44
|
+
"type": "transform",
|
|
45
|
+
"position": { "x": 0, "y": 360 },
|
|
46
|
+
"data": {
|
|
47
|
+
"label": "Extract URLs · drop bare-link noise",
|
|
48
|
+
"expression": "{{trigger.payload.data.content}} | url_extract | unique | filter:item !== steps.content.output.result | slice:5"
|
|
31
49
|
}
|
|
32
50
|
},
|
|
33
51
|
{
|
|
34
52
|
"id": "url-loop",
|
|
35
53
|
"type": "loop",
|
|
36
|
-
"position": { "x": 0, "y":
|
|
54
|
+
"position": { "x": 0, "y": 480 },
|
|
37
55
|
"data": {
|
|
38
56
|
"label": "For each URL",
|
|
39
57
|
"iteratorExpression": "steps.extract.output.result",
|
|
@@ -43,28 +61,72 @@
|
|
|
43
61
|
{
|
|
44
62
|
"id": "create-bookmark",
|
|
45
63
|
"type": "output",
|
|
46
|
-
"position": { "x": 0, "y":
|
|
64
|
+
"position": { "x": 0, "y": 600 },
|
|
47
65
|
"data": {
|
|
48
66
|
"label": "Create bookmark",
|
|
49
67
|
"outputType": "entity_create",
|
|
50
68
|
"config": {
|
|
51
69
|
"profileSlug": "bookmark",
|
|
52
|
-
"title": "{{
|
|
70
|
+
"title": "{{steps.title-src.output.result}}",
|
|
53
71
|
"dedupeBy": "url",
|
|
72
|
+
"body": "{{steps.content.output.result}}",
|
|
54
73
|
"properties": {
|
|
55
74
|
"url": "{{loop.item}}",
|
|
75
|
+
"sourceChannelId": "{{trigger.payload.data.channelId}}",
|
|
76
|
+
"sourceThreadId": "{{trigger.payload.data.threadId}}",
|
|
56
77
|
"sourceMessageId": "{{trigger.payload.data.messageId}}",
|
|
57
|
-
"
|
|
78
|
+
"sourceProvider": "{{trigger.payload.data.provider}}",
|
|
79
|
+
"sharedBy": "{{trigger.payload.data.participantName}}"
|
|
58
80
|
}
|
|
59
81
|
}
|
|
60
82
|
}
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"id": "has-bookmark-id",
|
|
86
|
+
"type": "condition",
|
|
87
|
+
"position": { "x": 0, "y": 720 },
|
|
88
|
+
"data": {
|
|
89
|
+
"label": "Bookmark row exists?",
|
|
90
|
+
"expression": "steps.create-bookmark.output.entityId !== ''"
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"id": "has-context-entity",
|
|
95
|
+
"type": "condition",
|
|
96
|
+
"position": { "x": 0, "y": 840 },
|
|
97
|
+
"data": {
|
|
98
|
+
"label": "Channel bound to an entity?",
|
|
99
|
+
"expression": "trigger.payload.data.entityId !== ''"
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"id": "link-context",
|
|
104
|
+
"type": "output",
|
|
105
|
+
"position": { "x": 0, "y": 960 },
|
|
106
|
+
"data": {
|
|
107
|
+
"label": "Link bookmark → channel's entity",
|
|
108
|
+
"outputType": "relation_create",
|
|
109
|
+
"config": {
|
|
110
|
+
"fromEntityId": "{{steps.create-bookmark.output.entityId}}",
|
|
111
|
+
"toEntityId": "{{trigger.payload.data.entityId}}",
|
|
112
|
+
"relationType": "references"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
61
115
|
}
|
|
62
116
|
],
|
|
63
117
|
"edges": [
|
|
64
|
-
{ "id": "e1", "source": "trigger", "target": "
|
|
65
|
-
{ "id": "e2", "source": "
|
|
66
|
-
{ "id": "e3", "source": "
|
|
118
|
+
{ "id": "e1", "source": "trigger", "target": "content" },
|
|
119
|
+
{ "id": "e2", "source": "content", "target": "title-src" },
|
|
120
|
+
{ "id": "e3", "source": "title-src", "target": "extract" },
|
|
121
|
+
{ "id": "e4", "source": "extract", "target": "url-loop" },
|
|
122
|
+
{ "id": "e5", "source": "url-loop", "target": "create-bookmark" },
|
|
123
|
+
{ "id": "e6", "source": "create-bookmark", "target": "has-bookmark-id" },
|
|
124
|
+
{ "id": "e7", "source": "has-bookmark-id", "target": "has-context-entity", "sourceHandle": "yes" },
|
|
125
|
+
{ "id": "e8", "source": "has-context-entity", "target": "link-context", "sourceHandle": "yes" }
|
|
67
126
|
]
|
|
68
127
|
},
|
|
69
|
-
"metadata": {
|
|
128
|
+
"metadata": {
|
|
129
|
+
"source": "bridge-template",
|
|
130
|
+
"note": "Phase 1 is DETERMINISTIC — no AI. It keeps the context the trigger already hands it: data.channelId (the INTERNAL Synap channel UUID, not the Discord snowflake, which is kept separately as sourceThreadId), data.content (as the bookmark body) and data.entityId (the channel's bound entity, linked with a `references` relation). Title = the message's own 120-char preview; when the message is nothing but a link the item is dropped by the noise filter rather than named, so the title is never invented. Noise filter: `filter:item !== steps.content.output.result` keeps only links that arrived WITH human text — a Tenor/giphy GIF drop or a bare Google Photos paste is exactly a message whose whole trimmed text IS the URL. A host denylist is NOT expressible: the condition grammar (condition-eval.ts) has no substring/regex operator and `contains` is exact set intersection, so any 'tenor.com'-style match would silently never fire."
|
|
131
|
+
}
|
|
70
132
|
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""The synap box runner: the only program the box template starts on boot.
|
|
3
|
+
|
|
4
|
+
It holds no credential. It fetches its job from the provisioner by the
|
|
5
|
+
provisioner's address on the isolated bridge, installs the declared packages
|
|
6
|
+
(setup phase: the egress proxy lets only the package registries through),
|
|
7
|
+
tells the provisioner it is done installing (run phase: only the declared
|
|
8
|
+
hosts), runs the script as the unprivileged user `box` with /in and /out,
|
|
9
|
+
streams the log back, uploads /out and reports the exit code.
|
|
10
|
+
|
|
11
|
+
The provisioner, not this program, enforces the wall clock: it destroys the box.
|
|
12
|
+
Python stdlib only (Debian 12's python3), so the template needs nothing else to
|
|
13
|
+
run it. Contract with the provisioner: synap-cli src/lib/box/job-server.ts.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
import os
|
|
18
|
+
import pwd
|
|
19
|
+
import subprocess
|
|
20
|
+
import sys
|
|
21
|
+
import threading
|
|
22
|
+
import time
|
|
23
|
+
import urllib.parse
|
|
24
|
+
import urllib.request
|
|
25
|
+
|
|
26
|
+
BASE = os.environ.get("SYNAP_BOX_PROVISIONER", "http://10.20.0.1:8790")
|
|
27
|
+
PROXY = os.environ.get("SYNAP_BOX_PROXY", "http://10.20.0.1:3128")
|
|
28
|
+
WORK, IN, OUT = "/work", "/in", "/out"
|
|
29
|
+
USER = "box"
|
|
30
|
+
MAX_OUT_FILES = 20
|
|
31
|
+
|
|
32
|
+
# The job server is on the bridge: never through the proxy.
|
|
33
|
+
_direct = urllib.request.build_opener(urllib.request.ProxyHandler({}))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def call(method, path, body=None, ctype="application/json", timeout=60):
|
|
37
|
+
data = body if isinstance(body, (bytes, type(None))) else json.dumps(body).encode()
|
|
38
|
+
req = urllib.request.Request(BASE + path, data=data, method=method)
|
|
39
|
+
if data is not None:
|
|
40
|
+
req.add_header("Content-Type", ctype)
|
|
41
|
+
with _direct.open(req, timeout=timeout) as res:
|
|
42
|
+
raw = res.read()
|
|
43
|
+
return json.loads(raw) if raw and res.headers.get("content-type", "").startswith("application/json") else None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def fetch_job():
|
|
47
|
+
# The box may boot before the provisioner registers its address: retry.
|
|
48
|
+
deadline = time.time() + 180
|
|
49
|
+
while True:
|
|
50
|
+
try:
|
|
51
|
+
return call("GET", "/job")
|
|
52
|
+
except Exception as err: # noqa: BLE001
|
|
53
|
+
if time.time() > deadline:
|
|
54
|
+
raise SystemExit(f"no job from {BASE}: {err}")
|
|
55
|
+
time.sleep(2)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class LogPump:
|
|
59
|
+
"""Batches output and POSTs it to /log about twice a second."""
|
|
60
|
+
|
|
61
|
+
def __init__(self):
|
|
62
|
+
self.buf = bytearray()
|
|
63
|
+
self.lock = threading.Lock()
|
|
64
|
+
self.stop = threading.Event()
|
|
65
|
+
self.thread = threading.Thread(target=self._loop, daemon=True)
|
|
66
|
+
self.thread.start()
|
|
67
|
+
|
|
68
|
+
def write(self, data):
|
|
69
|
+
with self.lock:
|
|
70
|
+
self.buf += data
|
|
71
|
+
|
|
72
|
+
def _flush(self):
|
|
73
|
+
with self.lock:
|
|
74
|
+
chunk, self.buf = bytes(self.buf), bytearray()
|
|
75
|
+
if chunk:
|
|
76
|
+
try:
|
|
77
|
+
call("POST", "/log", chunk, "application/octet-stream")
|
|
78
|
+
except Exception: # noqa: BLE001 - a lost log chunk must not kill the run
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
def _loop(self):
|
|
82
|
+
while not self.stop.wait(0.5):
|
|
83
|
+
self._flush()
|
|
84
|
+
|
|
85
|
+
def close(self):
|
|
86
|
+
self.stop.set()
|
|
87
|
+
self.thread.join()
|
|
88
|
+
self._flush()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def demote(user):
|
|
92
|
+
pw = pwd.getpwnam(user)
|
|
93
|
+
|
|
94
|
+
def run_as():
|
|
95
|
+
os.setgroups([])
|
|
96
|
+
os.setgid(pw.pw_gid)
|
|
97
|
+
os.setuid(pw.pw_uid)
|
|
98
|
+
|
|
99
|
+
return run_as, pw
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def run(cmd, env, pump):
|
|
103
|
+
"""Run cmd as the box user; its stdout+stderr go to the log. Returns the exit code."""
|
|
104
|
+
pre, pw = demote(USER)
|
|
105
|
+
pump.write(("$ " + " ".join(cmd) + "\n").encode())
|
|
106
|
+
proc = subprocess.Popen(
|
|
107
|
+
cmd, cwd=WORK, env={**env, "HOME": pw.pw_dir, "USER": USER},
|
|
108
|
+
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, preexec_fn=pre,
|
|
109
|
+
)
|
|
110
|
+
for chunk in iter(lambda: proc.stdout.read1(65536), b""):
|
|
111
|
+
pump.write(chunk)
|
|
112
|
+
return proc.wait()
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def installed(job, env):
|
|
116
|
+
try:
|
|
117
|
+
if job["language"] == "python":
|
|
118
|
+
out = subprocess.run(
|
|
119
|
+
["uv", "pip", "list", "--python", f"{WORK}/.venv/bin/python", "--format", "json"],
|
|
120
|
+
cwd=WORK, env=env, capture_output=True, timeout=60,
|
|
121
|
+
).stdout
|
|
122
|
+
return [{"name": p["name"], "version": p["version"], "manager": "pip"} for p in json.loads(out or b"[]")]
|
|
123
|
+
out = subprocess.run(
|
|
124
|
+
["pnpm", "list", "--json", "--depth", "0"], cwd=WORK, env=env, capture_output=True, timeout=60,
|
|
125
|
+
).stdout
|
|
126
|
+
deps = (json.loads(out or b"[{}]") or [{}])[0].get("dependencies", {}) or {}
|
|
127
|
+
return [{"name": n, "version": d.get("version", ""), "manager": "npm"} for n, d in deps.items()]
|
|
128
|
+
except Exception: # noqa: BLE001 - the list is evidence, not the verdict
|
|
129
|
+
return []
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def upload_out(pump):
|
|
133
|
+
sent = 0
|
|
134
|
+
for root, _dirs, files in os.walk(OUT):
|
|
135
|
+
for name in sorted(files):
|
|
136
|
+
full = os.path.join(root, name)
|
|
137
|
+
if os.path.islink(full) or not os.path.isfile(full):
|
|
138
|
+
continue
|
|
139
|
+
rel = os.path.relpath(full, OUT)
|
|
140
|
+
if sent >= MAX_OUT_FILES:
|
|
141
|
+
pump.write(f"\n[synap box] /out/{rel} skipped: at most {MAX_OUT_FILES} files\n".encode())
|
|
142
|
+
continue
|
|
143
|
+
try:
|
|
144
|
+
with open(full, "rb") as fh:
|
|
145
|
+
call("PUT", "/out/" + urllib.parse.quote(rel), fh.read(), "application/octet-stream", timeout=600)
|
|
146
|
+
sent += 1
|
|
147
|
+
except Exception as err: # noqa: BLE001
|
|
148
|
+
pump.write(f"\n[synap box] /out/{rel} not uploaded: {err}\n".encode())
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def main():
|
|
152
|
+
job = fetch_job()
|
|
153
|
+
pump = LogPump()
|
|
154
|
+
for d in (WORK, IN, OUT):
|
|
155
|
+
os.makedirs(d, exist_ok=True)
|
|
156
|
+
pw = pwd.getpwnam(USER)
|
|
157
|
+
for d in (WORK, OUT):
|
|
158
|
+
os.chown(d, pw.pw_uid, pw.pw_gid)
|
|
159
|
+
|
|
160
|
+
entry = os.path.normpath(job["entrypoint"])
|
|
161
|
+
if entry.startswith("..") or os.path.isabs(entry):
|
|
162
|
+
raise SystemExit(f"bad entrypoint {job['entrypoint']!r}")
|
|
163
|
+
os.makedirs(os.path.dirname(os.path.join(WORK, entry)), exist_ok=True)
|
|
164
|
+
with open(os.path.join(WORK, entry), "w") as fh:
|
|
165
|
+
fh.write(job["code"])
|
|
166
|
+
os.chown(os.path.join(WORK, entry), pw.pw_uid, pw.pw_gid)
|
|
167
|
+
with open(os.path.join(IN, "parameters.json"), "w") as fh:
|
|
168
|
+
json.dump(job.get("parameters"), fh)
|
|
169
|
+
|
|
170
|
+
env = {
|
|
171
|
+
"PATH": "/usr/local/bin:/usr/bin:/bin",
|
|
172
|
+
"LANG": "C.UTF-8",
|
|
173
|
+
"HTTP_PROXY": PROXY, "HTTPS_PROXY": PROXY, "http_proxy": PROXY, "https_proxy": PROXY,
|
|
174
|
+
"npm_config_proxy": PROXY, "npm_config_https_proxy": PROXY,
|
|
175
|
+
# Node's own fetch ignores HTTP(S)_PROXY unless told (Node >= 22.21 / 24).
|
|
176
|
+
"NODE_USE_ENV_PROXY": "1",
|
|
177
|
+
"UV_CACHE_DIR": f"{WORK}/.cache/uv", "npm_config_cache": f"{WORK}/.cache/npm",
|
|
178
|
+
"SYNAP_PARAMETERS_FILE": f"{IN}/parameters.json", "SYNAP_OUT_DIR": OUT,
|
|
179
|
+
}
|
|
180
|
+
pkgs = job.get("packages") or []
|
|
181
|
+
if job["language"] == "python":
|
|
182
|
+
code = run(["uv", "venv", f"{WORK}/.venv"], env, pump)
|
|
183
|
+
if code == 0 and pkgs:
|
|
184
|
+
code = run(["uv", "pip", "install", "--python", f"{WORK}/.venv/bin/python",
|
|
185
|
+
*[f"{p['name']}=={p['version']}" for p in pkgs]], env, pump)
|
|
186
|
+
cmd = [f"{WORK}/.venv/bin/python", entry]
|
|
187
|
+
else:
|
|
188
|
+
with open(os.path.join(WORK, "package.json"), "w") as fh:
|
|
189
|
+
json.dump({"private": True}, fh)
|
|
190
|
+
os.chown(os.path.join(WORK, "package.json"), pw.pw_uid, pw.pw_gid)
|
|
191
|
+
code = run(["pnpm", "add", *[f"{p['name']}@{p['version']}" for p in pkgs]], env, pump) if pkgs else 0
|
|
192
|
+
cmd = ["node", entry]
|
|
193
|
+
|
|
194
|
+
call("POST", "/phase", {"phase": "run"})
|
|
195
|
+
if code == 0:
|
|
196
|
+
code = run(cmd, env, pump)
|
|
197
|
+
else:
|
|
198
|
+
pump.write(f"\n[synap box] packages did not install (exit {code}); the script was not run\n".encode())
|
|
199
|
+
|
|
200
|
+
upload_out(pump)
|
|
201
|
+
pump.close()
|
|
202
|
+
call("POST", "/done", {"exitCode": code, "installedPackages": installed(job, env)})
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
if __name__ == "__main__":
|
|
206
|
+
sys.exit(main())
|