synabun 2.0.9 → 2.1.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/CHANGELOG.md +115 -0
- package/README.md +25 -2
- package/THIRD-PARTY-LICENSES.md +68 -4
- package/bin/synabun.js +71 -24
- package/docs/whatsapp.md +2 -2
- package/hooks/claude-code/pre-websearch.mjs +12 -1
- package/hooks/claude-code/shared.mjs +5 -0
- package/hooks/claude-code/stop.mjs +1 -1
- package/launcher.mjs +9 -3
- package/lib/claude-hooks.js +172 -18
- package/lib/client-config-repair.js +17 -8
- package/lib/desktop-app.js +7 -1
- package/lib/external-tools.js +1 -1
- package/lib/first-launch-protection.js +57 -12
- package/lib/mcp-dist.js +176 -0
- package/lib/release-npm-ci.mjs +138 -0
- package/lib/release-publish.mjs +91 -0
- package/lib/release-scan.mjs +50 -0
- package/lib/server-crash-record.js +123 -0
- package/lib/server-exit.js +318 -0
- package/lib/server-supervisor.js +508 -0
- package/lib/start-launcher.js +43 -5
- package/lib/tools-cli.js +45 -0
- package/lib/update-snapshot.js +228 -0
- package/lib/virustotal.mjs +192 -0
- package/lib/windows-crash-event.js +269 -0
- package/mcp-server/dist/http.d.ts +20 -1
- package/mcp-server/dist/http.d.ts.map +1 -1
- package/mcp-server/dist/http.js +59 -12
- package/mcp-server/dist/http.js.map +1 -1
- package/mcp-server/dist/index.d.ts +33 -27
- package/mcp-server/dist/index.d.ts.map +1 -1
- package/mcp-server/dist/index.js +128 -143
- package/mcp-server/dist/index.js.map +1 -1
- package/mcp-server/dist/preload.js +1 -1
- package/mcp-server/dist/preload.js.map +1 -1
- package/mcp-server/dist/services/codex-config-heal.d.ts +6 -2
- package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
- package/mcp-server/dist/services/codex-config-heal.js +81 -28
- package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
- package/mcp-server/dist/services/memory-judge-backfill.d.ts +6 -0
- package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -1
- package/mcp-server/dist/services/memory-judge-backfill.js +7 -1
- package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -1
- package/mcp-server/dist/services/neural-interface.d.ts +16 -10
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
- package/mcp-server/dist/services/neural-interface.js +67 -11
- package/mcp-server/dist/services/neural-interface.js.map +1 -1
- package/mcp-server/dist/services/server-instructions.d.ts +7 -2
- package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
- package/mcp-server/dist/services/server-instructions.js +20 -62
- package/mcp-server/dist/services/server-instructions.js.map +1 -1
- package/mcp-server/dist/services/tool-catalog.d.ts +120 -0
- package/mcp-server/dist/services/tool-catalog.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-catalog.js +339 -0
- package/mcp-server/dist/services/tool-catalog.js.map +1 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts +12 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.js +40 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.js.map +1 -0
- package/mcp-server/dist/services/tool-list.d.ts +108 -0
- package/mcp-server/dist/services/tool-list.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-list.js +334 -0
- package/mcp-server/dist/services/tool-list.js.map +1 -0
- package/mcp-server/dist/services/tool-runtime.d.ts +161 -0
- package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-runtime.js +400 -0
- package/mcp-server/dist/services/tool-runtime.js.map +1 -0
- package/mcp-server/dist/services/whiteboard-geometry.d.ts +26 -2
- package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -1
- package/mcp-server/dist/services/whiteboard-geometry.js +91 -8
- package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -1
- package/mcp-server/dist/tools/agents.d.ts +10 -10
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +22 -13
- package/mcp-server/dist/tools/agents.js.map +1 -1
- package/mcp-server/dist/tools/browser-batch.d.ts +3 -3
- package/mcp-server/dist/tools/image-tools.d.ts +1 -1
- package/mcp-server/dist/tools/image-tools.js +1 -1
- package/mcp-server/dist/tools/skill-tools.d.ts +505 -0
- package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -0
- package/mcp-server/dist/tools/skill-tools.js +639 -0
- package/mcp-server/dist/tools/skill-tools.js.map +1 -0
- package/mcp-server/dist/tools/skill.d.ts +7 -0
- package/mcp-server/dist/tools/skill.d.ts.map +1 -0
- package/mcp-server/dist/tools/skill.js +11 -0
- package/mcp-server/dist/tools/skill.js.map +1 -0
- package/mcp-server/dist/tools/tool-groups.d.ts +68 -0
- package/mcp-server/dist/tools/tool-groups.d.ts.map +1 -0
- package/mcp-server/dist/tools/tool-groups.js +147 -0
- package/mcp-server/dist/tools/tool-groups.js.map +1 -0
- package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -1
- package/mcp-server/dist/tools/whiteboard-format.js +7 -4
- package/mcp-server/dist/tools/whiteboard-format.js.map +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.d.ts +64 -13
- package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.js +30 -5
- package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
- package/mcp-server/package-lock.json +2 -2
- package/mcp-server/package.json +1 -1
- package/mcp-server/src/http.ts +75 -12
- package/mcp-server/src/index.ts +147 -154
- package/mcp-server/src/preload.ts +12 -12
- package/mcp-server/src/services/codex-config-heal.ts +80 -29
- package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
- package/mcp-server/src/services/neural-interface.ts +79 -20
- package/mcp-server/src/services/server-instructions.ts +27 -63
- package/mcp-server/src/services/tool-catalog.ts +403 -0
- package/mcp-server/src/services/tool-list-refresh-barrier.ts +33 -0
- package/mcp-server/src/services/tool-list.ts +410 -0
- package/mcp-server/src/services/tool-runtime.ts +457 -0
- package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
- package/mcp-server/src/tools/agents.ts +22 -14
- package/mcp-server/src/tools/image-tools.ts +1 -1
- package/mcp-server/src/tools/skill-tools.ts +589 -0
- package/mcp-server/src/tools/skill.ts +13 -0
- package/mcp-server/src/tools/tool-groups.ts +164 -0
- package/mcp-server/src/tools/whiteboard-format.ts +9 -3
- package/mcp-server/src/tools/whiteboard-tools.ts +39 -5
- package/neural-interface/i18n/en.json +3680 -206
- package/neural-interface/i18n/pt-BR.json +3628 -157
- package/neural-interface/lib/account-usage.js +5 -3
- package/neural-interface/lib/app-controls-api.js +161 -0
- package/neural-interface/lib/assistant-api.js +148 -35
- package/neural-interface/lib/assistant-brains/claude.js +4 -3
- package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +12 -2
- package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +32 -9
- package/neural-interface/lib/assistant-brains/codex.js +9 -5
- package/neural-interface/lib/assistant-brains/opencode.js +122 -44
- package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +59 -0
- package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +97 -0
- package/neural-interface/lib/assistant-catalog.js +23 -9
- package/neural-interface/lib/assistant-dispatch.js +874 -156
- package/neural-interface/lib/assistant-envelope.js +22 -2
- package/neural-interface/lib/assistant-persona.js +80 -11
- package/neural-interface/lib/assistant-plan-permissions.js +10 -9
- package/neural-interface/lib/assistant-route-gate-codex.js +3 -0
- package/neural-interface/lib/assistant-route-gate.js +6 -4
- package/neural-interface/lib/assistant-router.js +13 -8
- package/neural-interface/lib/assistant-runtime.js +183 -26
- package/neural-interface/lib/assistant-task-prompt.js +78 -14
- package/neural-interface/lib/assistant-tools.js +51 -0
- package/neural-interface/lib/backup-service.js +620 -3
- package/neural-interface/lib/backup-zip-reader.js +10 -5
- package/neural-interface/lib/backup-zip-writer.js +257 -0
- package/neural-interface/lib/browser-execution.js +10 -2
- package/neural-interface/lib/browser-guard-cdp.js +83 -0
- package/neural-interface/lib/browser-network-guard.js +284 -0
- package/neural-interface/lib/browser-policy-roots.js +64 -0
- package/neural-interface/lib/browser-tool-policy.js +186 -31
- package/neural-interface/lib/claude-agent-bridge.js +4 -2
- package/neural-interface/lib/claude-hooks-status.js +103 -0
- package/neural-interface/lib/claude-model-catalog.js +64 -14
- package/neural-interface/lib/claude-temporary.js +5 -11
- package/neural-interface/lib/cli-install.js +18 -138
- package/neural-interface/lib/cli-resolver.js +4 -1
- package/neural-interface/lib/cli-update-planner.js +32 -31
- package/neural-interface/lib/cloudflared/tunnel.js +42 -2
- package/neural-interface/lib/codex-mcp-approvals.js +11 -0
- package/neural-interface/lib/codex-settings-schema.js +109 -47
- package/neural-interface/lib/codex-worker-app-server.js +172 -0
- package/neural-interface/lib/confined-fs.js +30 -3
- package/neural-interface/lib/crash-notice-api.js +98 -0
- package/neural-interface/lib/editor-completion-opencode.js +164 -67
- package/neural-interface/lib/exec-loop-command.js +4 -1
- package/neural-interface/lib/http-guards.js +8 -2
- package/neural-interface/lib/install-details.js +4 -1
- package/neural-interface/lib/launch-cache.js +293 -0
- package/neural-interface/lib/login-start.js +524 -0
- package/neural-interface/lib/mcp-agent-servers.js +67 -0
- package/neural-interface/lib/mcp-client-config.js +0 -29
- package/neural-interface/lib/mcp-external-servers.js +323 -0
- package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
- package/neural-interface/lib/morelogin.js +30 -0
- package/neural-interface/lib/native-loop-providers.js +565 -50
- package/neural-interface/lib/native-loop-runtime.js +287 -41
- package/neural-interface/lib/opencode-config.js +557 -0
- package/neural-interface/lib/opencode-db-schema.js +58 -0
- package/neural-interface/lib/opencode-history.js +49 -204
- package/neural-interface/lib/opencode-mcp-ready.js +108 -0
- package/neural-interface/lib/opencode-native-boundary.js +44 -0
- package/neural-interface/lib/opencode-protocol.js +310 -0
- package/neural-interface/lib/opencode-provider-view.js +81 -0
- package/neural-interface/lib/opencode-runtime-config.js +32 -15
- package/neural-interface/lib/opencode-serve-records.js +624 -0
- package/neural-interface/lib/opencode-v2-client.js +53 -131
- package/neural-interface/lib/opencode-v2-ws-requests.js +35 -195
- package/neural-interface/lib/opencode-worker-activity.js +229 -0
- package/neural-interface/lib/opencode-worker-data.js +27 -0
- package/neural-interface/lib/opencode2-client.js +796 -0
- package/neural-interface/lib/opencode2-proxy.js +76 -0
- package/neural-interface/lib/opencode2-translate.js +1057 -0
- package/neural-interface/lib/owner-authority.js +249 -0
- package/neural-interface/lib/page-session.js +275 -0
- package/neural-interface/lib/peer-identity.js +763 -0
- package/neural-interface/lib/process-identity.js +361 -0
- package/neural-interface/lib/request-gate.js +17 -2
- package/neural-interface/lib/rulesets/api.js +1 -1
- package/neural-interface/lib/rulesets/fs-safe.js +1 -1
- package/neural-interface/lib/rulesets/installer.js +92 -27
- package/neural-interface/lib/rulesets/json-edit.js +1 -1
- package/neural-interface/lib/session-cache-builder.js +18 -10
- package/neural-interface/lib/session-title-generator.js +58 -18
- package/neural-interface/lib/skills/api-v2.js +495 -0
- package/neural-interface/lib/skills/bundle.js +418 -0
- package/neural-interface/lib/skills/catalog.js +294 -0
- package/neural-interface/lib/skills/creation-api.js +191 -0
- package/neural-interface/lib/skills/creation-brief.js +247 -0
- package/neural-interface/lib/skills/creation-sources.js +267 -0
- package/neural-interface/lib/skills/creation.js +843 -0
- package/neural-interface/lib/skills/deploy.js +564 -0
- package/neural-interface/lib/skills/digest.js +119 -0
- package/neural-interface/lib/skills/discover.js +111 -0
- package/neural-interface/lib/skills/draft-confinement.js +214 -0
- package/neural-interface/lib/skills/eval-confinement.js +397 -0
- package/neural-interface/lib/skills/eval-sandbox.js +280 -0
- package/neural-interface/lib/skills/eval-trace.js +119 -0
- package/neural-interface/lib/skills/evals-api.js +212 -0
- package/neural-interface/lib/skills/evals.js +1573 -0
- package/neural-interface/lib/skills/export.js +200 -0
- package/neural-interface/lib/skills/frontmatter.js +64 -0
- package/neural-interface/lib/skills/health.js +75 -0
- package/neural-interface/lib/skills/json-edit.js +165 -0
- package/neural-interface/lib/skills/known-bridge-digests.json +387 -0
- package/neural-interface/lib/skills/known-bridges.js +96 -0
- package/neural-interface/lib/skills/library.js +823 -0
- package/neural-interface/lib/skills/local-store.js +21 -0
- package/neural-interface/lib/skills/migration.js +433 -0
- package/neural-interface/lib/skills/operations.js +45 -0
- package/neural-interface/lib/skills/ownership.js +427 -0
- package/neural-interface/lib/skills/packages.js +88 -0
- package/neural-interface/lib/skills/reviews.js +212 -0
- package/neural-interface/lib/skills/revision.js +80 -0
- package/neural-interface/lib/skills/roots.js +262 -0
- package/neural-interface/lib/skills/runtime-checks.js +355 -0
- package/neural-interface/lib/skills/scan-assets.js +252 -0
- package/neural-interface/lib/skills/scan-context.js +26 -0
- package/neural-interface/lib/skills/scan-shell.js +324 -0
- package/neural-interface/lib/skills/scan.js +364 -0
- package/neural-interface/lib/skills/scanners.js +54 -0
- package/neural-interface/lib/skills/service.js +522 -0
- package/neural-interface/lib/skills/telemetry.js +113 -0
- package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
- package/neural-interface/lib/style-guide/api.js +4 -3
- package/neural-interface/lib/tool-lists.js +124 -0
- package/neural-interface/lib/untrusted-file-headers.js +27 -0
- package/neural-interface/lib/whatsapp/bridge.js +6 -3
- package/neural-interface/lib/worker-confinement.js +1192 -0
- package/neural-interface/lib/worker-guard-setting.js +56 -0
- package/neural-interface/lib/worker-mcp-channel.js +175 -0
- package/neural-interface/lib/worker-relay-ports.js +227 -0
- package/neural-interface/lib/worker-resume.js +229 -0
- package/neural-interface/lib/worker-sandbox.js +299 -0
- package/neural-interface/package-lock.json +1354 -50
- package/neural-interface/package.json +6 -2
- package/neural-interface/public/index.html +5 -3
- package/neural-interface/public/index2d.html +19 -11
- package/neural-interface/public/onboarding.html +2 -2
- package/neural-interface/public/shared/account-menu.js +8 -5
- package/neural-interface/public/shared/agent-runtime-options.js +76 -16
- package/neural-interface/public/shared/api.js +1527 -1336
- package/neural-interface/public/shared/app-frame.js +192 -0
- package/neural-interface/public/shared/assistant/asst-agents-dock.js +205 -19
- package/neural-interface/public/shared/assistant/asst-api.js +11 -3
- package/neural-interface/public/shared/assistant/asst-ask-styles.js +270 -0
- package/neural-interface/public/shared/assistant/asst-ask-tail.js +118 -0
- package/neural-interface/public/shared/assistant/asst-ask.js +397 -0
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +241 -112
- package/neural-interface/public/shared/assistant/asst-composer.js +1 -1
- package/neural-interface/public/shared/assistant/asst-control.js +157 -26
- package/neural-interface/public/shared/assistant/asst-followups-card.js +383 -0
- package/neural-interface/public/shared/assistant/asst-followups.js +417 -0
- package/neural-interface/public/shared/assistant/asst-hosts.js +49 -2
- package/neural-interface/public/shared/assistant/asst-limit-dock.js +40 -0
- package/neural-interface/public/shared/assistant/asst-limit-wait.js +294 -0
- package/neural-interface/public/shared/assistant/asst-limit.js +168 -0
- package/neural-interface/public/shared/assistant/asst-mascot.js +49 -12
- package/neural-interface/public/shared/assistant/asst-menu.js +84 -12
- package/neural-interface/public/shared/assistant/asst-panel.js +303 -44
- package/neural-interface/public/shared/assistant/asst-rack-styles.js +93 -58
- package/neural-interface/public/shared/assistant/asst-redact.js +186 -0
- package/neural-interface/public/shared/assistant/asst-render.js +770 -100
- package/neural-interface/public/shared/assistant/asst-route.js +83 -12
- package/neural-interface/public/shared/assistant/asst-run-stage.js +231 -0
- package/neural-interface/public/shared/assistant/asst-sanitize.js +193 -3
- package/neural-interface/public/shared/assistant/asst-scene-styles.js +273 -0
- package/neural-interface/public/shared/assistant/asst-scenes.js +530 -0
- package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +3 -15
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +67 -4
- package/neural-interface/public/shared/assistant/asst-slash.js +9 -7
- package/neural-interface/public/shared/assistant/asst-state.js +25 -15
- package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
- package/neural-interface/public/shared/assistant/asst-styles.js +102 -21
- package/neural-interface/public/shared/assistant/asst-tool-kinds.js +165 -13
- package/neural-interface/public/shared/automation-context.js +5 -2
- package/neural-interface/public/shared/cc-dropdown.js +550 -0
- package/neural-interface/public/shared/cdx/cdx-context-menu.js +10 -9
- package/neural-interface/public/shared/cdx/cdx-copy.js +16 -0
- package/neural-interface/public/shared/cdx/cdx-output-model.js +22 -1
- package/neural-interface/public/shared/cdx/cdx-output.js +8 -7
- package/neural-interface/public/shared/cdx/cdx-panel.js +58 -150
- package/neural-interface/public/shared/cdx/cdx-protocol.js +28 -1
- package/neural-interface/public/shared/cdx/cdx-render.js +62 -32
- package/neural-interface/public/shared/cdx/cdx-requests.js +3 -3
- package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
- package/neural-interface/public/shared/cdx/cdx-styles.js +15 -3
- package/neural-interface/public/shared/cdx/cdx-tabs.js +201 -74
- package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
- package/neural-interface/public/shared/cli-install-view.js +4 -8
- package/neural-interface/public/shared/cli-status.js +2 -2
- package/neural-interface/public/shared/constants.js +2 -2
- package/neural-interface/public/shared/context-menu-areas.js +34 -0
- package/neural-interface/public/shared/context-menu-logic.js +243 -0
- package/neural-interface/public/shared/context-menu-model.js +366 -0
- package/neural-interface/public/shared/context-menu.js +372 -0
- package/neural-interface/public/shared/cp/cp-context-menu.js +10 -9
- package/neural-interface/public/shared/cp/cp-markdown.js +4 -57
- package/neural-interface/public/shared/crash-notice-view.js +168 -0
- package/neural-interface/public/shared/data-cache.js +223 -0
- package/neural-interface/public/shared/desktop-bridge.js +11 -0
- package/neural-interface/public/shared/desktop-titlebar-view.js +78 -1
- package/neural-interface/public/shared/desktop-titlebar.js +214 -28
- package/neural-interface/public/shared/desktop-update-view.js +35 -5
- package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
- package/neural-interface/public/shared/file-links.js +73 -8
- package/neural-interface/public/shared/html-shell.js +78 -86
- package/neural-interface/public/shared/i18n.js +2 -0
- package/neural-interface/public/shared/launch-prefetch.js +59 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +7 -7
- package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +4 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-caps.js +3 -2
- package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +3 -101
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +9 -2
- package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +1 -3
- package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +2 -6
- package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +11 -11
- package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +0 -3
- package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +8 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +7 -7
- package/neural-interface/public/shared/ocp-v2/ocp-v2-modelpicker.js +54 -17
- package/neural-interface/public/shared/ocp-v2/ocp-v2-outcome-notifications.js +16 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +140 -254
- package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
- package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +97 -203
- package/neural-interface/public/shared/ocp-v2/ocp-v2-providers.js +11 -9
- package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +15 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -22
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send-logic.js +401 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +20 -13
- package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +8 -61
- package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +53 -82
- package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +16 -15
- package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +8 -21
- package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +1 -99
- package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +5 -35
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +48 -40
- package/neural-interface/public/shared/ocp-v2/{ocp-v2-profile-inheritance.js → ocp-v2-tools-inheritance.js} +16 -16
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +205 -62
- package/neural-interface/public/shared/opencode-providers.js +39 -0
- package/neural-interface/public/shared/page-session.js +96 -0
- package/neural-interface/public/shared/registry.js +5 -3
- package/neural-interface/public/shared/retired-2d.js +3 -0
- package/neural-interface/public/shared/schedules-view.js +452 -0
- package/neural-interface/public/shared/settings/settings-app-pane.js +335 -0
- package/neural-interface/public/shared/settings/settings-app.js +243 -0
- package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
- package/neural-interface/public/shared/settings/settings-data.js +90 -0
- package/neural-interface/public/shared/settings/settings-ia.js +23 -6
- package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
- package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +265 -0
- package/neural-interface/public/shared/settings/settings-skill-folders.js +130 -0
- package/neural-interface/public/shared/settings/settings-worker-guard.js +72 -0
- package/neural-interface/public/shared/sidepanel-session-status.js +49 -0
- package/neural-interface/public/shared/skills/ss-args.js +96 -0
- package/neural-interface/public/shared/skills/ss-creation.js +219 -0
- package/neural-interface/public/shared/skills/ss-dialog.js +130 -0
- package/neural-interface/public/shared/skills/ss-doc.js +173 -0
- package/neural-interface/public/shared/skills/ss-eval-adapters.js +269 -0
- package/neural-interface/public/shared/skills/ss-eval-grade.js +359 -0
- package/neural-interface/public/shared/skills/ss-eval-optimise.js +153 -0
- package/neural-interface/public/shared/skills/ss-eval-plan.js +315 -0
- package/neural-interface/public/shared/skills/ss-evals.js +417 -0
- package/neural-interface/public/shared/skills/ss-form.js +159 -0
- package/neural-interface/public/shared/skills/ss-frontmatter.js +376 -0
- package/neural-interface/public/shared/skills/ss-ids.js +32 -0
- package/neural-interface/public/shared/skills/ss-lint.js +141 -0
- package/neural-interface/public/shared/skills/ss-presets.js +117 -0
- package/neural-interface/public/shared/skills/ss-profiles.js +475 -0
- package/neural-interface/public/shared/skills/ss-resources.js +165 -0
- package/neural-interface/public/shared/skills/ss-review-view.js +190 -0
- package/neural-interface/public/shared/skills/ss-tabs.js +79 -0
- package/neural-interface/public/shared/skills/ss-unicode.js +135 -0
- package/neural-interface/public/shared/skills/studio/sx-actions.js +222 -0
- package/neural-interface/public/shared/skills/studio/sx-cm.js +101 -0
- package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +749 -0
- package/neural-interface/public/shared/skills/studio/sx-create-ai.js +236 -0
- package/neural-interface/public/shared/skills/studio/sx-create-view.js +254 -0
- package/neural-interface/public/shared/skills/studio/sx-create.js +126 -0
- package/neural-interface/public/shared/skills/studio/sx-diff-view.js +43 -0
- package/neural-interface/public/shared/skills/studio/sx-diff.js +133 -0
- package/neural-interface/public/shared/skills/studio/sx-docs.js +480 -0
- package/neural-interface/public/shared/skills/studio/sx-dom.js +229 -0
- package/neural-interface/public/shared/skills/studio/sx-edit-view.js +848 -0
- package/neural-interface/public/shared/skills/studio/sx-editor-model.js +193 -0
- package/neural-interface/public/shared/skills/studio/sx-errors.js +22 -0
- package/neural-interface/public/shared/skills/studio/sx-geometry.js +82 -0
- package/neural-interface/public/shared/skills/studio/sx-import.js +262 -0
- package/neural-interface/public/shared/skills/studio/sx-inspector.js +308 -0
- package/neural-interface/public/shared/skills/studio/sx-install-flow.js +279 -0
- package/neural-interface/public/shared/skills/studio/sx-install.js +296 -0
- package/neural-interface/public/shared/skills/studio/sx-library-view.js +639 -0
- package/neural-interface/public/shared/skills/studio/sx-library.js +475 -0
- package/neural-interface/public/shared/skills/studio/sx-main.js +681 -0
- package/neural-interface/public/shared/skills/studio/sx-operations-view.js +98 -0
- package/neural-interface/public/shared/skills/studio/sx-operations.js +11 -0
- package/neural-interface/public/shared/skills/studio/sx-plan.js +158 -0
- package/neural-interface/public/shared/skills/studio/sx-publish-view.js +621 -0
- package/neural-interface/public/shared/skills/studio/sx-review-view.js +369 -0
- package/neural-interface/public/shared/skills/studio/sx-review.js +148 -0
- package/neural-interface/public/shared/skills/studio/sx-scan.js +35 -0
- package/neural-interface/public/shared/skills/studio/sx-snippets.js +96 -0
- package/neural-interface/public/shared/skills/studio/sx-subject.js +248 -0
- package/neural-interface/public/shared/skills/studio/sx-test-view.js +994 -0
- package/neural-interface/public/shared/skills/studio/sx-test.js +328 -0
- package/neural-interface/public/shared/skills/studio/sx-versions-view.js +241 -0
- package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +425 -0
- package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
- package/neural-interface/public/shared/styles.css +2065 -2150
- package/neural-interface/public/shared/synabun-mascot.js +1115 -9
- package/neural-interface/public/shared/tool-list-picker.js +55 -0
- package/neural-interface/public/shared/tool-list-storage.js +24 -0
- package/neural-interface/public/shared/tool-list-view.js +112 -0
- package/neural-interface/public/shared/tour/tour-actor.js +268 -0
- package/neural-interface/public/shared/tour/tour-beats.js +336 -0
- package/neural-interface/public/shared/tour/tour-engine.js +884 -0
- package/neural-interface/public/shared/tour/tour-ink.js +390 -0
- package/neural-interface/public/shared/tour/tour-stage.js +440 -0
- package/neural-interface/public/shared/tour/tour-state.js +40 -0
- package/neural-interface/public/shared/tour/tour-tips.js +99 -0
- package/neural-interface/public/shared/tour/tour.css +144 -0
- package/neural-interface/public/shared/ui-assistant.js +27 -5
- package/neural-interface/public/shared/ui-automation-studio.js +155 -152
- package/neural-interface/public/shared/ui-bookmarks.js +2 -1
- package/neural-interface/public/shared/ui-claude-panel.js +153 -13
- package/neural-interface/public/shared/ui-codex-panel.js +1 -0
- package/neural-interface/public/shared/ui-command-runner.js +6 -3
- package/neural-interface/public/shared/ui-context-menu.js +295 -0
- package/neural-interface/public/shared/ui-cost-widget.js +57 -59
- package/neural-interface/public/shared/ui-crash-notice.js +187 -0
- package/neural-interface/public/shared/ui-detail.js +21 -9
- package/neural-interface/public/shared/ui-file-links.js +1 -0
- package/neural-interface/public/shared/ui-image-gallery.js +56 -52
- package/neural-interface/public/shared/ui-invite.js +18 -2
- package/neural-interface/public/shared/ui-keybinds.js +26 -1
- package/neural-interface/public/shared/ui-link.js +9 -9
- package/neural-interface/public/shared/ui-menubar.js +229 -40
- package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
- package/neural-interface/public/shared/ui-native-loop-router.js +9 -1
- package/neural-interface/public/shared/ui-navbar.js +4 -24
- package/neural-interface/public/shared/ui-opencode-panel-v2.js +1 -0
- package/neural-interface/public/shared/ui-panels.js +96 -65
- package/neural-interface/public/shared/ui-schedules-studio.js +2267 -953
- package/neural-interface/public/shared/ui-sessions.js +20 -7
- package/neural-interface/public/shared/ui-settings.js +679 -496
- package/neural-interface/public/shared/ui-sidebar.js +4 -4
- package/neural-interface/public/shared/ui-sidepanel-session-entry.js +13 -0
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +56 -5
- package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
- package/neural-interface/public/shared/ui-sidepanel-tray.js +84 -8
- package/neural-interface/public/shared/ui-sidepanel-windows.js +16 -11
- package/neural-interface/public/shared/ui-skills.js +42 -2781
- package/neural-interface/public/shared/ui-styleguide.js +5 -2
- package/neural-interface/public/shared/ui-sync.js +22 -5
- package/neural-interface/public/shared/ui-terminal.js +159 -199
- package/neural-interface/public/shared/ui-tool-updates.js +3 -1
- package/neural-interface/public/shared/ui-tooltip.js +10 -5
- package/neural-interface/public/shared/ui-trash.js +14 -5
- package/neural-interface/public/shared/ui-tutorial-layout.js +3 -117
- package/neural-interface/public/shared/ui-tutorial-preview.js +103 -0
- package/neural-interface/public/shared/ui-tutorial-steps.js +3 -61
- package/neural-interface/public/shared/ui-tutorial.css +47 -100
- package/neural-interface/public/shared/ui-tutorial.js +285 -499
- package/neural-interface/public/shared/ui-update.js +121 -16
- package/neural-interface/public/shared/ui-whiteboard.js +313 -59
- package/neural-interface/public/shared/ui-worker-end.js +4 -0
- package/neural-interface/public/shared/ui-workspaces.js +3 -2
- package/neural-interface/public/shared/view-variant.js +66 -0
- package/neural-interface/public/shared/whatsapp/wa-view.js +1 -1
- package/neural-interface/public/shared/whiteboard-list.js +172 -0
- package/neural-interface/public/shared/worker-end.js +10 -3
- package/neural-interface/public/shared/worker-restart.js +12 -0
- package/neural-interface/public/sidepanel-session.html +1 -1
- package/neural-interface/public/variant/2d/background.js +2 -1
- package/neural-interface/public/variant/2d/context-menu.js +8 -2
- package/neural-interface/public/variant/2d/graph.js +42 -20
- package/neural-interface/public/variant/2d/hulls.js +14 -9
- package/neural-interface/public/variant/2d/lasso.js +11 -7
- package/neural-interface/public/variant/2d/main.js +36 -2
- package/neural-interface/public/variant/2d/minimap.js +11 -13
- package/neural-interface/public/variant/3d/main.js +72 -6
- package/neural-interface/public/variant/3d/map-controls.js +24 -9
- package/neural-interface/public/vendor/caveat/OFL.txt +93 -0
- package/neural-interface/public/vendor/caveat/caveat-latin-400-normal.woff2 +0 -0
- package/neural-interface/public/vendor/caveat/caveat-latin-700-normal.woff2 +0 -0
- package/neural-interface/public/vendor/caveat/caveat.css +17 -0
- package/neural-interface/public/vendor/caveat/manifest.json +12 -0
- package/neural-interface/public/vendor/highlight/LICENSE +29 -0
- package/neural-interface/public/vendor/highlight/github-dark-dimmed-11.12.0.min.css +9 -0
- package/neural-interface/public/vendor/highlight/highlight-11.12.0.min.js +1262 -0
- package/neural-interface/public/vendor/highlight/manifest.json +5 -0
- package/neural-interface/public/vendor/html2canvas/LICENSE +22 -0
- package/neural-interface/public/vendor/html2canvas/html2canvas-1.4.1.min.js +20 -0
- package/neural-interface/public/vendor/html2canvas/manifest.json +4 -0
- package/neural-interface/public/vendor/marked/LICENSE.md +44 -0
- package/neural-interface/public/vendor/marked/manifest.json +4 -0
- package/neural-interface/public/vendor/marked/marked-14.1.4.esm.js +2502 -0
- package/neural-interface/public/vendor/mermaid/LICENSE +21 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/abnfDiagram-PAR5IXJI.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architecture-7GRP2DOG-M7G3SWCO.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architectureDiagram-2PNWDENW.mjs +36 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/blockDiagram-PUWGUBOA.mjs +129 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/c4Diagram-3NLNHZP5.mjs +38 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-2NMSUPJH.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-32MCUW2P.mjs +206 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3NPNU5GP.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3SMGFRGC.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4CAJECRU.mjs +14 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4OADUZ5Q.mjs +10 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IMINLNL.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IQCK5RY.mjs +3 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5MVFYWFU.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AQ6EADP3.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AZZRMDJM.mjs +15 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-CLS4B6BI.mjs +168 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-DZP67EKU.mjs +70 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-EICP7K5X.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-GDY2UAB4.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-H7VHZCWX.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-IJBDOHL6.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JFFVMJIA.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JQRUD6KW.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JZGDI5JK.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-KD6SPIPN.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NF366YI6.mjs +231 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NUDPKBEF.mjs +32 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-OJINMHTS.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Q6NGPIAH.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-QJSWEUOL.mjs +132 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-R7JDMUKV.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RCXAM2GK.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RT4MAWR3.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SFMINX4W.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SLDQ463B.mjs +88 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-STOV2HOB.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-T5OCTHI4.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-TCEHX7R3.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VQC2EELJ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VRZKL3EH.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VTHDQ7BV.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VY2OSTJR.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-WUBWJARI.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YBMQ5JGJ.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YDMLL4PJ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Z6V44ARX.mjs +62 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZJJ7VQJP.mjs +339 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZNLK7HRR.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZQN7UHIL.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZS6VBONO.mjs +174 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZUNWM646.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-FGAMPOII.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-v2-AI5RJEK4.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cose-bilkent-Q72C2OJ3.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefin-OW5HDTMX-6OCTWOGG.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefinDiagram-2OOWUY4M.mjs +62 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/dagre-MPVFI544.mjs +4 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-AE4NWXFW.mjs +24 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-BUJID5UG.mjs +41 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-C44CZ5NI.mjs +24 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-EYOLEXXS.mjs +30 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-P7B6PWOJ.mjs +3 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ebnfDiagram-FTPZ3PV6.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/erDiagram-H3ERR26V.mjs +99 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/eventmodeling-NTZA5JFV-WKSWQHFS.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/flowDiagram-YHGXBVSY.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ganttDiagram-RWFTB7ED.mjs +292 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraph-4MIJSDKK-JH2HKYUO.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraphDiagram-MAR6GKOP.mjs +106 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/info-A6RAGUB7-3TX3OFWW.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/infoDiagram-LV547AFH.mjs +2 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ishikawaDiagram-PUJQGNDS.mjs +70 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/journeyDiagram-EW5QJT4W.mjs +139 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/kanban-definition-JSQERAZB.mjs +89 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/katex-CZ4GXH2S.mjs +257 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/mindmap-definition-VBJCLQLM.mjs +96 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/packet-AYTQ26CC-MKII6P3Y.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pegDiagram-7WILOQTJ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pie-WAS4IAKB-6B4OPMJ5.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pieDiagram-EFA7CBSB.mjs +39 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/quadrantDiagram-WARVYA7P.mjs +7 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/radar-RG4KPBEZ-UPTR3XUL.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-74A4TZTK-MEFUPFMS.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-abnf-HS5TGJTU-R5YG2S5D.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-ebnf-LZEXJU2U-G5QEX2L6.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-peg-WCYAUIDC-H5I2ZRY2.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroadDiagram-5NIQVNLQ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/requirementDiagram-SXREGOKZ.mjs +84 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sankeyDiagram-VV74M2VP.mjs +40 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sequenceDiagram-52ZFIFDL.mjs +162 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sizeCapture-FGA5AGYP.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-3QAKPIVI.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-v2-HN2P4RDZ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanes-QLVP42QD.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanesDiagram-JKPDBFCN.mjs +8 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/timeline-definition-GS45XAXW.mjs +120 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treeView-Q6P3EWNA-ZRJGANNQ.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treemap-WGGIJYW6-K64VN5AS.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/vennDiagram-QIQU2ZRJ.mjs +34 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardley-WFR3VGLG-D67OWCCH.mjs +1 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardleyDiagram-U6YNM66N.mjs +78 -0
- package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/xychartDiagram-XJAZEDNM.mjs +7 -0
- package/neural-interface/public/vendor/mermaid/manifest.json +4 -0
- package/neural-interface/public/vendor/mermaid/mermaid-11.17.2.esm.min.mjs +14 -0
- package/neural-interface/public/vendor/yaml/LICENSE +13 -0
- package/neural-interface/public/vendor/yaml/manifest.json +4 -0
- package/neural-interface/public/vendor/yaml/yaml-2.9.1.min.js +137 -0
- package/neural-interface/scripts/build-vendor.mjs +116 -3
- package/neural-interface/server.js +4834 -2541
- package/neural-interface/templates/rulesets/core.md +1 -1
- package/neural-interface/templates/rulesets/legacy-hashes.json +18 -0
- package/neural-interface/templates/rulesets/manifest.json +8 -8
- package/npm-shrinkwrap.json +18 -12
- package/package.json +16 -3
- package/postinstall.js +9 -4
- package/preuninstall.js +1 -1
- package/setup.js +97 -322
- package/skills/skill-forge/SKILL.md +177 -0
- package/skills/skill-forge/evals/evals.json +54 -0
- package/skills/skill-forge/references/package.md +105 -0
- package/skills/skill-forge/references/review-and-recovery.md +63 -0
- package/skills/skill-forge/references/runtime-profiles.md +20 -0
- package/skills/skill-forge/references/sources-and-capabilities.md +51 -0
- package/skills/synabun/SKILL.md +10 -7
- package/skills/synabun/modules/audit.md +1 -5
- package/skills/synabun/modules/changelog.md +1 -5
- package/skills/synabun/modules/idea.md +1 -1
- package/skills/synabun/modules/memorize.md +6 -9
- package/skills/synabun/modules/schedule.md +5 -5
- package/mcp-server/dist/services/profiles.d.ts +0 -120
- package/mcp-server/dist/services/profiles.d.ts.map +0 -1
- package/mcp-server/dist/services/profiles.js +0 -387
- package/mcp-server/dist/services/profiles.js.map +0 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +0 -97
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +0 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.js +0 -228
- package/mcp-server/dist/tools/leonardo-browser-tools.js.map +0 -1
- package/mcp-server/dist/tools/leonardo.d.ts +0 -13
- package/mcp-server/dist/tools/leonardo.d.ts.map +0 -1
- package/mcp-server/dist/tools/leonardo.js +0 -21
- package/mcp-server/dist/tools/leonardo.js.map +0 -1
- package/mcp-server/dist/tools/profile.d.ts +0 -17
- package/mcp-server/dist/tools/profile.d.ts.map +0 -1
- package/mcp-server/dist/tools/profile.js +0 -114
- package/mcp-server/dist/tools/profile.js.map +0 -1
- package/mcp-server/src/services/profiles.ts +0 -428
- package/mcp-server/src/tools/leonardo-browser-tools.ts +0 -291
- package/mcp-server/src/tools/leonardo.ts +0 -30
- package/mcp-server/src/tools/profile.ts +0 -122
- package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +0 -47
- package/neural-interface/lib/assistant-brains/opencode-route-gate.js +0 -62
- package/neural-interface/lib/mcp-profile-servers.js +0 -120
- package/neural-interface/public/shared/ui-tutorial-draw.js +0 -311
- package/skills/leonardo/SKILL.md +0 -168
- package/skills/leonardo/icon.svg +0 -1
- package/skills/leonardo/modules/browser-guide.md +0 -378
- package/skills/leonardo/modules/image-prompter.md +0 -283
- package/skills/leonardo/modules/model-advisor.md +0 -100
- package/skills/leonardo/modules/prompt-library.md +0 -110
- package/skills/leonardo/modules/style-guide.md +0 -186
- package/skills/leonardo/modules/video-prompter.md +0 -317
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Neural Interface — Skills Studio 2.0: a difference, drawn
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// The rows of sx-diff.js as a unified diff: one block per file (its status, lines added and removed, a note when
|
|
6
|
+
// it is binary or too large to compare line by line), each hunk with both line numbers. Added and removed lines are
|
|
7
|
+
// marked by a sign and a word for screen readers, not by colour alone. Every line is text.
|
|
8
|
+
|
|
9
|
+
import { html, ICONS, plural } from './sx-dom.js';
|
|
10
|
+
|
|
11
|
+
const SIGN = { '+': '+', '-': '−', ' ': ' ' };
|
|
12
|
+
|
|
13
|
+
function rowsHTML(t, rows) {
|
|
14
|
+
return rows.map((r) => html`<tr class="sx-diff-row sx-diff-row--${r.t === '+' ? 'add' : r.t === '-' ? 'del' : 'ctx'}">
|
|
15
|
+
<td class="sx-diff-num">${r.a ?? ''}</td><td class="sx-diff-num">${r.b ?? ''}</td>
|
|
16
|
+
<td class="sx-diff-sign"><span aria-hidden="true">${SIGN[r.t]}</span><span class="sx-sr">${r.t === '+' ? t('skillsStudio.diff.added') : r.t === '-' ? t('skillsStudio.diff.removed') : ''}</span></td>
|
|
17
|
+
<td class="sx-diff-text">${r.text}</td></tr>`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {Function} t
|
|
22
|
+
* @param {{ files: { path, status, hunks: { header, rows }[], added, removed, note }[], same?: boolean, unchanged?: number }} model
|
|
23
|
+
* @param {{ open?: number }} [o] how many files start expanded
|
|
24
|
+
*/
|
|
25
|
+
export function diffHTML(t, model, { open = 3 } = {}) {
|
|
26
|
+
if (!model || model.same || !model.files?.length) return html`<p class="sx-help">${t('skillsStudio.diff.same')}</p>`;
|
|
27
|
+
return html`<div class="sx-diff">${model.files.map((f, i) => html`<details class="sx-diff-file"${i < open ? ' open' : ''}>
|
|
28
|
+
<summary><span class="sx-diff-status sx-diff-status--${f.status}">${t(`skillsStudio.diff.status.${f.status}`)}</span><span class="sx-diff-path">${f.path}</span>
|
|
29
|
+
${f.added || f.removed ? html`<span class="sx-diff-counts"><span class="sx-diff-plus">+${f.added}</span> <span class="sx-diff-minus">−${f.removed}</span></span>` : ''}</summary>
|
|
30
|
+
${f.eol ? html`<p class="sx-help">${t('skillsStudio.diff.eol', { from: f.eol.from.toUpperCase(), to: f.eol.to.toUpperCase() })}</p>` : ''}
|
|
31
|
+
${f.note ? html`<p class="sx-help">${t(`skillsStudio.diff.note.${f.note === 'binary' ? 'binary' : f.note.startsWith('too large') ? 'large' : 'far'}`)}</p>` : ''}
|
|
32
|
+
${(f.hunks || []).map((h) => html`<table class="sx-diff-hunk"><caption class="sx-diff-head">${h.header}</caption><tbody>${rowsHTML(t, h.rows)}</tbody></table>`)}
|
|
33
|
+
</details>`)}
|
|
34
|
+
${model.unchanged ? html`<p class="sx-help">${plural(t, 'skillsStudio.diff.unchanged', model.unchanged)}</p>` : ''}</div>`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** A text's diff (lineDiff) as a one-file model for diffHTML. */
|
|
38
|
+
export function textDiffModel(path, diff) {
|
|
39
|
+
if (diff.same) return { same: true, files: [] };
|
|
40
|
+
return { files: [{ path, status: 'modified', hunks: diff.hunks, added: diff.added, removed: diff.removed, note: diff.tooFar ? 'too different to compare line by line' : null }] };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const DIFF_ICON = ICONS.code;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Neural Interface — Skills Studio 2.0: differences, as rows to draw
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// Two sources of a diff: the service's (GET /v2/diff between revisions, GET /v2/compare between a revision and a
|
|
6
|
+
// folder: files with unified hunks, lib/skills/library.js), and one worked out here between two texts the page
|
|
7
|
+
// holds (a draft's file against its version, an installed SKILL.md against the shipped one), with the same line
|
|
8
|
+
// alignment the editor uses to keep line endings (ss-doc.js commonLines, Myers). Both end as the same rows: one
|
|
9
|
+
// per line, marked added, removed or context, with its line numbers. No DOM; imports only ss-doc.js.
|
|
10
|
+
|
|
11
|
+
import { commonLines } from '../ss-doc.js';
|
|
12
|
+
|
|
13
|
+
export const CONTEXT_LINES = 3;
|
|
14
|
+
export const MAX_DIFF_EDITS = 4000;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The difference of two texts as hunks of rows, or `tooFar: true` when they differ in more lines than are aligned.
|
|
18
|
+
* @returns {{ hunks: { header: string, rows: { t: ' '|'+'|'-', text: string, a: number|null, b: number|null }[] }[],
|
|
19
|
+
* added: number, removed: number, same: boolean, tooFar: boolean }}
|
|
20
|
+
*/
|
|
21
|
+
export function lineDiff(before, after, { context = CONTEXT_LINES, maxEdits = MAX_DIFF_EDITS } = {}) {
|
|
22
|
+
const a = String(before ?? '').replace(/\r\n?/g, '\n').split('\n');
|
|
23
|
+
const b = String(after ?? '').replace(/\r\n?/g, '\n').split('\n');
|
|
24
|
+
if (a.join('\n') === b.join('\n')) return { hunks: [], added: 0, removed: 0, same: true, tooFar: false };
|
|
25
|
+
const kept = commonLines(a, b, maxEdits);
|
|
26
|
+
if (!kept) return { hunks: [], added: 0, removed: 0, same: false, tooFar: true };
|
|
27
|
+
// The edit script: every line of `a` is kept or removed, every line of `b` kept or added, in order.
|
|
28
|
+
const ops = [];
|
|
29
|
+
let i = 0; let j = 0;
|
|
30
|
+
const removeTo = (end) => { for (; i < end; i++) ops.push({ t: '-', text: a[i], a: i + 1, b: null }); };
|
|
31
|
+
const addTo = (end) => { for (; j < end; j++) ops.push({ t: '+', text: b[j], a: null, b: j + 1 }); };
|
|
32
|
+
for (const [ka, kb] of kept) {
|
|
33
|
+
removeTo(ka);
|
|
34
|
+
addTo(kb);
|
|
35
|
+
ops.push({ t: ' ', text: a[i], a: i + 1, b: j + 1 });
|
|
36
|
+
i++; j++;
|
|
37
|
+
}
|
|
38
|
+
removeTo(a.length);
|
|
39
|
+
addTo(b.length);
|
|
40
|
+
const added = ops.filter((o) => o.t === '+').length;
|
|
41
|
+
const removed = ops.filter((o) => o.t === '-').length;
|
|
42
|
+
return { hunks: groupHunks(ops, context), added, removed, same: false, tooFar: false };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Changed rows with `context` rows around them; two changes closer than twice that share a hunk. */
|
|
46
|
+
function groupHunks(ops, context) {
|
|
47
|
+
const changed = [];
|
|
48
|
+
ops.forEach((o, k) => { if (o.t !== ' ') changed.push(k); });
|
|
49
|
+
const groups = [];
|
|
50
|
+
for (const k of changed) {
|
|
51
|
+
const last = groups[groups.length - 1];
|
|
52
|
+
if (last && k - last.end <= context * 2 + 1) last.end = k;
|
|
53
|
+
else groups.push({ start: k, end: k });
|
|
54
|
+
}
|
|
55
|
+
return groups.map(({ start, end }) => {
|
|
56
|
+
const rows = ops.slice(Math.max(0, start - context), Math.min(ops.length, end + 1 + context));
|
|
57
|
+
const aRows = rows.filter((r) => r.t !== '+');
|
|
58
|
+
const bRows = rows.filter((r) => r.t !== '-');
|
|
59
|
+
const aStart = aRows.find((r) => r.a !== null)?.a ?? 0;
|
|
60
|
+
const bStart = bRows.find((r) => r.b !== null)?.b ?? 0;
|
|
61
|
+
return { header: `@@ -${aStart},${aRows.length} +${bStart},${bRows.length} @@`, rows };
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** A hunk of the service (`{ header, lines: ['+x', '-y', ' z'] }`) as rows with line numbers. */
|
|
66
|
+
export function serviceHunkRows(hunk) {
|
|
67
|
+
const m = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/.exec(String(hunk?.header ?? ''));
|
|
68
|
+
let a = m ? Number(m[1]) : 1;
|
|
69
|
+
let b = m ? Number(m[2]) : 1;
|
|
70
|
+
const rows = [];
|
|
71
|
+
for (const line of Array.isArray(hunk?.lines) ? hunk.lines : []) {
|
|
72
|
+
const t = line[0] === '+' || line[0] === '-' ? line[0] : ' ';
|
|
73
|
+
const text = String(line).slice(1).replace(/\r$/, ''); // a CRLF line's \r is not shown (the file's line endings are said once)
|
|
74
|
+
if (t === '+') rows.push({ t, text, a: null, b: b++ });
|
|
75
|
+
else if (t === '-') rows.push({ t, text, a: a++, b: null });
|
|
76
|
+
else rows.push({ t, text, a: a++, b: b++ });
|
|
77
|
+
}
|
|
78
|
+
return { header: String(hunk?.header ?? ''), rows };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** What a service diff changed, file by file: counts and each file's rows (text files with hunks). */
|
|
82
|
+
export function serviceDiff(diff) {
|
|
83
|
+
const files = (Array.isArray(diff?.files) ? diff.files : []).map((f) => {
|
|
84
|
+
const hunks = Array.isArray(f.hunks) ? f.hunks.map(serviceHunkRows) : [];
|
|
85
|
+
const added = hunks.reduce((n, h) => n + h.rows.filter((r) => r.t === '+').length, 0);
|
|
86
|
+
const removed = hunks.reduce((n, h) => n + h.rows.filter((r) => r.t === '-').length, 0);
|
|
87
|
+
const eol = f.before?.eol && f.after?.eol && f.before.eol !== f.after.eol ? { from: String(f.before.eol), to: String(f.after.eol) } : null;
|
|
88
|
+
return { path: String(f.path ?? ''), status: f.status, hunks, added, removed, note: f.note || null, before: f.before || null, after: f.after || null, eol };
|
|
89
|
+
});
|
|
90
|
+
return {
|
|
91
|
+
same: !!diff?.same || files.length === 0,
|
|
92
|
+
files,
|
|
93
|
+
unchanged: Number(diff?.unchanged) || 0,
|
|
94
|
+
counts: {
|
|
95
|
+
added: files.filter((f) => f.status === 'added').length,
|
|
96
|
+
removed: files.filter((f) => f.status === 'removed').length,
|
|
97
|
+
modified: files.filter((f) => f.status === 'modified' || f.status === 'mode').length,
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** The changed files of a draft against a revision: by each file's digest (the same bytes are the same file). */
|
|
103
|
+
export function changedFiles(fromFiles = [], toFiles = []) {
|
|
104
|
+
const a = new Map((fromFiles || []).map((f) => [f.path, f.digest]));
|
|
105
|
+
const b = new Map((toFiles || []).map((f) => [f.path, f.digest]));
|
|
106
|
+
const out = [];
|
|
107
|
+
for (const [path, digest] of b) {
|
|
108
|
+
if (!a.has(path)) out.push({ path, status: 'added' });
|
|
109
|
+
else if (a.get(path) !== digest) out.push({ path, status: 'modified' });
|
|
110
|
+
}
|
|
111
|
+
for (const path of a.keys()) if (!b.has(path)) out.push({ path, status: 'removed' });
|
|
112
|
+
return out.sort((x, y) => (x.path < y.path ? -1 : x.path > y.path ? 1 : 0));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** A service diff read the other way round (from `to` back to `from`): what an update changes, from a copy's side. */
|
|
116
|
+
export function invertDiff(diff) {
|
|
117
|
+
const flip = { added: 'removed', removed: 'added' };
|
|
118
|
+
return {
|
|
119
|
+
...diff,
|
|
120
|
+
from: diff?.to || null,
|
|
121
|
+
to: diff?.from || null,
|
|
122
|
+
files: (Array.isArray(diff?.files) ? diff.files : []).map((f) => ({
|
|
123
|
+
...f,
|
|
124
|
+
status: flip[f.status] || f.status,
|
|
125
|
+
before: f.after || null,
|
|
126
|
+
after: f.before || null,
|
|
127
|
+
hunks: Array.isArray(f.hunks) ? f.hunks.map((h) => {
|
|
128
|
+
const m = /^@@ -(\d+(?:,\d+)?) \+(\d+(?:,\d+)?) @@/.exec(String(h.header ?? ''));
|
|
129
|
+
return { header: m ? `@@ -${m[2]} +${m[1]} @@` : h.header, lines: (h.lines || []).map((l) => (l[0] === '+' ? `-${l.slice(1)}` : l[0] === '-' ? `+${l.slice(1)}` : l)) };
|
|
130
|
+
}) : f.hunks,
|
|
131
|
+
})),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Neural Interface — Skills Studio 2.0: a workspace's documents
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// One workspace per thing opened from the Library (sx-subject.js says its mode). It holds the files it has, the
|
|
6
|
+
// documents open in it (P0's tab: the file as last read or saved, and the text being edited, ss-tabs.js), and how
|
|
7
|
+
// each is read and saved:
|
|
8
|
+
// library files of the skill's draft (or of its latest revision until the first save makes a draft); a save
|
|
9
|
+
// puts the file into the draft (PATCH /v2/drafts/:id) over the draft's etag. Another change of the
|
|
10
|
+
// draft meanwhile (the Assistant, another window) is a conflict only when it touched this file: the
|
|
11
|
+
// file's digest in the draft is still the one the document was opened at, so the save is sent again
|
|
12
|
+
// over the new etag; otherwise the owner decides.
|
|
13
|
+
// adopt the folder as it is on disk (read through the P0 routes or the catalog); the first save takes the
|
|
14
|
+
// folder into the library (POST /v2/skills { artifactId }), makes its draft and puts the file there.
|
|
15
|
+
// The folder that was adopted must still hold what the document was opened at, else the owner decides.
|
|
16
|
+
// inplace an agent or a command, saved where it is over the revision it was opened at (P0's PUT).
|
|
17
|
+
// readonly shown, never saved.
|
|
18
|
+
// What is typed while a save is on its way stays unsaved (the save sends a snapshot). Unsaved text is copied to
|
|
19
|
+
// this browser (IndexedDB, editor-safety.js) a second after the last change and offered back when the workspace
|
|
20
|
+
// opens again; a copy left by the Studio before 2.0 is found under its old key. The page's I/O is handed in.
|
|
21
|
+
|
|
22
|
+
import { createDocTab, refreshDirty, saveSnapshot, applySaved, revertTab, replaceSaved, draftKey } from '../ss-tabs.js';
|
|
23
|
+
import { toEditing, isEditableText } from '../ss-doc.js';
|
|
24
|
+
import { createBackups, localBackupStore, describeBackup, textFingerprint } from '../../editor/editor-safety.js';
|
|
25
|
+
import { subjectOf } from './sx-subject.js';
|
|
26
|
+
|
|
27
|
+
export const HEAD = 'SKILL.md';
|
|
28
|
+
export const DRAFTS_INDEX_KEY = 'synabun-skills-drafts-v2';
|
|
29
|
+
const LEGACY_INDEX_KEY = 'synabun-skills-drafts-v1';
|
|
30
|
+
|
|
31
|
+
let _backups = null;
|
|
32
|
+
/** The browser's copies of unsaved text (one database for the Studio, as before 2.0). */
|
|
33
|
+
export function backups() {
|
|
34
|
+
if (!_backups) _backups = createBackups({ store: localBackupStore({ name: 'synabun-skills-studio' }) });
|
|
35
|
+
return _backups;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function readIndex(key) {
|
|
39
|
+
try {
|
|
40
|
+
const value = JSON.parse(globalThis.localStorage?.getItem(key) || '{}');
|
|
41
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
42
|
+
} catch { return {}; }
|
|
43
|
+
}
|
|
44
|
+
function writeIndex(key, index) {
|
|
45
|
+
try { globalThis.localStorage?.setItem(key, JSON.stringify(index)); } catch { /* full or blocked: the copy itself is kept */ }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** The unsaved copies kept from earlier sessions: `{ key: { wsKey, path, label, at } }`. */
|
|
49
|
+
export const keptDrafts = () => readIndex(DRAFTS_INDEX_KEY);
|
|
50
|
+
|
|
51
|
+
/** The P0 key of a document (its decoded artifact id and its path then), to find a copy it left. */
|
|
52
|
+
function legacyKey(ws, path) {
|
|
53
|
+
const entry = ws.row?.primary;
|
|
54
|
+
if (!entry?.path || !['skill', 'command', 'agent'].includes(entry.kind)) return null;
|
|
55
|
+
const legacyPath = entry.kind === 'skill' ? (path === HEAD ? null : path) : null;
|
|
56
|
+
return draftKey(`${entry.kind}:${entry.path}`, legacyPath);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** A tab's text changed: copy it while it is unsaved, forget the copy once it is not. */
|
|
60
|
+
export function noteDraft(ws, tab) {
|
|
61
|
+
if (!tab || tab.binary || tab.readOnly) return;
|
|
62
|
+
const key = draftKey(ws.key, tab.path);
|
|
63
|
+
if (!tab.dirty) { dropDraft(ws, tab); return; }
|
|
64
|
+
backups().touch(key, () => (tab.dirty
|
|
65
|
+
? { path: tab.label, text: tab.content, base: textFingerprint(toEditing(tab.saved.raw).text), eol: tab.eol === '\r\n' ? 'CRLF' : 'LF' }
|
|
66
|
+
: null));
|
|
67
|
+
const index = readIndex(DRAFTS_INDEX_KEY);
|
|
68
|
+
if (!index[key]) {
|
|
69
|
+
index[key] = { wsKey: ws.key, path: tab.path, label: tab.label, name: ws.label, at: Date.now() };
|
|
70
|
+
writeIndex(DRAFTS_INDEX_KEY, index);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Forget a tab's copy (saved, or discarded on purpose). */
|
|
75
|
+
export function dropDraft(ws, tab) {
|
|
76
|
+
for (const [indexKey, key] of [[DRAFTS_INDEX_KEY, draftKey(ws.key, tab.path)], [LEGACY_INDEX_KEY, legacyKey(ws, tab.path)]]) {
|
|
77
|
+
if (!key) continue;
|
|
78
|
+
try { backups().drop(key, { always: true }); } catch { /* the store's own failure */ }
|
|
79
|
+
const index = readIndex(indexKey);
|
|
80
|
+
if (index[key]) { delete index[key]; writeIndex(indexKey, index); }
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Forget copies by their keys (the Library's "Discard" of kept changes). */
|
|
85
|
+
export function dropKept(keys) {
|
|
86
|
+
const index = readIndex(DRAFTS_INDEX_KEY);
|
|
87
|
+
for (const key of keys) { try { backups().drop(key, { always: true }); } catch { /* */ } delete index[key]; }
|
|
88
|
+
writeIndex(DRAFTS_INDEX_KEY, index);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Put a tab's unsaved text from an earlier session back. A copy made over a file that changed since is offered
|
|
93
|
+
* through `ask` (true: put it back), never put back unasked.
|
|
94
|
+
* @returns {Promise<boolean>}
|
|
95
|
+
*/
|
|
96
|
+
export async function restoreDraft(ws, tab, { ask = async () => false } = {}) {
|
|
97
|
+
if (tab.binary || tab.readOnly) return false;
|
|
98
|
+
let record = null;
|
|
99
|
+
for (const key of [draftKey(ws.key, tab.path), legacyKey(ws, tab.path)]) {
|
|
100
|
+
if (!key) continue;
|
|
101
|
+
try { record = await backups().read(key); } catch { record = null; }
|
|
102
|
+
if (record) break;
|
|
103
|
+
}
|
|
104
|
+
if (!record) return false;
|
|
105
|
+
const verdict = describeBackup(record, toEditing(tab.saved.raw).text);
|
|
106
|
+
if (verdict === 'same') { dropDraft(ws, tab); return false; }
|
|
107
|
+
if (verdict === 'restore-changed' && !(await ask(tab))) return false;
|
|
108
|
+
tab.content = record.text;
|
|
109
|
+
refreshDirty(tab);
|
|
110
|
+
noteDraft(ws, tab);
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ── The workspace ──
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* A workspace for a Library row.
|
|
118
|
+
* @param {object} row sx-library.js
|
|
119
|
+
* @param {{ guest?: boolean, caps?: Set<string> }} o
|
|
120
|
+
*/
|
|
121
|
+
export function newWorkspace(row, { guest = false, caps = new Set() } = {}) {
|
|
122
|
+
const subject = subjectOf(row, { guest, caps });
|
|
123
|
+
return {
|
|
124
|
+
key: row.key, row, subject,
|
|
125
|
+
kind: row.kind,
|
|
126
|
+
label: row.label || row.name || row.folderName || '',
|
|
127
|
+
files: [], dirs: [], filesTruncated: false,
|
|
128
|
+
skill: row.skill || null, draft: null, revision: null, entry: null,
|
|
129
|
+
tabs: [], active: -1,
|
|
130
|
+
loaded: false, error: null,
|
|
131
|
+
queue: Promise.resolve(),
|
|
132
|
+
tab: 'edit',
|
|
133
|
+
seenElsewhere: new Set(),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const tabArtifact = (ws) => ({ id: ws.key, name: ws.label });
|
|
138
|
+
|
|
139
|
+
function libraryTreeFromRevision(rev) {
|
|
140
|
+
return (rev?.manifest?.files || []).map((f) => ({ path: f.path, size: f.bytes, digest: f.digest, binary: f.encoding === 'binary', executable: f.mode === '0755' }));
|
|
141
|
+
}
|
|
142
|
+
function libraryTreeFromDraft(draft, rev) {
|
|
143
|
+
const sizes = new Map((rev?.manifest?.files || []).map((f) => [f.path, f]));
|
|
144
|
+
return (draft?.files || []).map((f) => {
|
|
145
|
+
const known = sizes.get(f.path);
|
|
146
|
+
return { path: f.path, digest: f.digest, executable: !!f.executable, size: known && known.digest === f.digest ? known.bytes : null, binary: known && known.digest === f.digest ? known.encoding === 'binary' : null };
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
function flattenTree(nodes, out = []) {
|
|
150
|
+
for (const n of nodes || []) {
|
|
151
|
+
if (n.type === 'dir') flattenTree(n.children, out);
|
|
152
|
+
else out.push({ path: n.path, size: n.size ?? null, binary: null, digest: null });
|
|
153
|
+
}
|
|
154
|
+
return out;
|
|
155
|
+
}
|
|
156
|
+
function dirsOfTree(nodes, out = []) {
|
|
157
|
+
for (const n of nodes || []) if (n.type === 'dir') { out.push(n.path); dirsOfTree(n.children, out); }
|
|
158
|
+
return out;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Read what a workspace holds (its files, its library state) and open its head document.
|
|
163
|
+
* @param {object} ws @param {object} io api.js @param {{ ask?: Function }} [o]
|
|
164
|
+
*/
|
|
165
|
+
export async function loadWorkspace(ws, io, { ask } = {}) {
|
|
166
|
+
const s = ws.subject;
|
|
167
|
+
ws.error = null;
|
|
168
|
+
if (s.readsFiles === 'library' && ws.skill) {
|
|
169
|
+
const skill = (await io.fetchSkill(ws.skill.skillId)).skill;
|
|
170
|
+
ws.skill = skill;
|
|
171
|
+
const draftId = skill.pointers?.draft || null;
|
|
172
|
+
const showRevision = s.mode === 'readonly' ? (skill.pointers?.upstream || skill.pointers?.approved || skill.pointers?.latest) : skill.pointers?.latest;
|
|
173
|
+
try { ws.revision = showRevision ? (await io.fetchSkillsRevision(showRevision)).revision : null; }
|
|
174
|
+
catch (err) {
|
|
175
|
+
if (!['blob_missing', 'blob_corrupt', 'blob_invalid', 'revision_corrupt'].includes(err?.code || err?.body?.code)) throw err;
|
|
176
|
+
// Keep history reachable when the latest version cannot be opened. Strict file/install reads still fail.
|
|
177
|
+
ws.revision = null; ws.files = []; ws.dirs = []; ws.loaded = true; ws.tab = 'versions';
|
|
178
|
+
return ws;
|
|
179
|
+
}
|
|
180
|
+
if (draftId && s.mode !== 'readonly') {
|
|
181
|
+
try { ws.draft = (await io.fetchSkillsDraft(draftId)).draft; } catch (err) { if (err?.status !== 404) throw err; ws.draft = null; }
|
|
182
|
+
}
|
|
183
|
+
ws.files = ws.draft ? libraryTreeFromDraft(ws.draft, ws.revision) : libraryTreeFromRevision(ws.revision);
|
|
184
|
+
ws.dirs = ws.draft ? (ws.draft.dirs || []) : (ws.revision?.manifest?.dirs || []);
|
|
185
|
+
} else if (ws.row.primary) {
|
|
186
|
+
const detail = (await io.fetchSkillsCatalogEntry(ws.row.primary.id)).entry;
|
|
187
|
+
ws.entry = detail;
|
|
188
|
+
if (ws.kind === 'skill') {
|
|
189
|
+
ws.files = [{ path: HEAD, size: detail.size ?? null, binary: !!detail.binary, digest: detail.revision || null }, ...flattenTree(detail.files || []).filter((f) => f.path !== HEAD)];
|
|
190
|
+
ws.dirs = dirsOfTree(detail.files || []);
|
|
191
|
+
ws.filesTruncated = !!detail.filesTruncated;
|
|
192
|
+
} else {
|
|
193
|
+
ws.files = [{ path: null, size: detail.size ?? null, binary: !!detail.binary, digest: detail.revision || null }];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
ws.loaded = true;
|
|
197
|
+
const head = ws.kind === 'skill' ? HEAD : null;
|
|
198
|
+
if (!ws.tabs.some((t) => t.path === head)) await openFile(ws, head, io, { ask });
|
|
199
|
+
return ws;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** The digest the service gives a text file (`sha256:` of its UTF-8 bytes), or null where the page cannot work it out. */
|
|
203
|
+
export async function sha256Text(text) {
|
|
204
|
+
const subtle = globalThis.crypto?.subtle;
|
|
205
|
+
if (!subtle || typeof TextEncoder !== 'function') return null;
|
|
206
|
+
try {
|
|
207
|
+
const hash = new Uint8Array(await subtle.digest('SHA-256', new TextEncoder().encode(String(text ?? ''))));
|
|
208
|
+
return `sha256:${[...hash].map((b) => b.toString(16).padStart(2, '0')).join('')}`;
|
|
209
|
+
} catch { return null; }
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** The library's draft file list as a digest by path. */
|
|
213
|
+
const digestIn = (draft, path) => (draft?.files || []).find((f) => f.path === path)?.digest || null;
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Read one file of a workspace into a document: `{ raw, revision, binary, size, base64 }`.
|
|
217
|
+
*/
|
|
218
|
+
export async function readFile(ws, path, io) {
|
|
219
|
+
const s = ws.subject;
|
|
220
|
+
if (s.readsFiles === 'library' && (ws.draft || ws.revision)) {
|
|
221
|
+
if (ws.draft) {
|
|
222
|
+
const got = await io.fetchSkillsDraftFile(ws.draft.draftId, path);
|
|
223
|
+
const digest = digestIn(ws.draft, path);
|
|
224
|
+
if (typeof got.contentBase64 === 'string') return { raw: '', revision: digest, binary: true, base64: got.contentBase64, size: Math.floor(got.contentBase64.length * 3 / 4) };
|
|
225
|
+
return { raw: got.content ?? '', revision: digest, binary: false, size: (got.content || '').length };
|
|
226
|
+
}
|
|
227
|
+
const got = await io.fetchSkillsRevisionFile(ws.revision.revisionId, path);
|
|
228
|
+
if (got.encoding !== 'utf-8' || typeof got.contentBase64 === 'string') return { raw: '', revision: got.digest || null, binary: true, base64: got.contentBase64 || null, size: null };
|
|
229
|
+
return { raw: got.content ?? '', revision: got.digest || null, binary: false };
|
|
230
|
+
}
|
|
231
|
+
if (s.readsFiles === 'v1' && s.v1Id) {
|
|
232
|
+
if (path === HEAD || path === null) {
|
|
233
|
+
const got = await io.fetchSkillsArtifact(s.v1Id);
|
|
234
|
+
return { raw: got.binary ? '' : (got.rawContent ?? ''), revision: got.revision ?? null, binary: !!got.binary, size: got.size ?? null, readOnly: got.readOnly === true };
|
|
235
|
+
}
|
|
236
|
+
const got = await io.fetchSkillsSubFile(s.v1Id, path);
|
|
237
|
+
return { raw: got.binary ? '' : (got.content ?? ''), revision: got.revision ?? null, binary: !!got.binary, size: got.size ?? null, readOnly: got.readOnly === true };
|
|
238
|
+
}
|
|
239
|
+
if (path === HEAD || path === null) {
|
|
240
|
+
const entry = ws.entry || (await io.fetchSkillsCatalogEntry(ws.row.primary.id)).entry;
|
|
241
|
+
if (entry.binary) return { raw: '', revision: entry.revision || null, binary: true, size: entry.size ?? null };
|
|
242
|
+
if (typeof entry.text !== 'string') throw Object.assign(new Error(entry.textError || 'unreadable'), { code: 'unreadable' });
|
|
243
|
+
return { raw: entry.text, revision: entry.revision || null, binary: false };
|
|
244
|
+
}
|
|
245
|
+
if (s.readsFiles === 'catalog') {
|
|
246
|
+
const got = await io.fetchSkillsCatalogFile(ws.row.primary.id, path);
|
|
247
|
+
if (got.binary) return { raw: '', revision: null, binary: true, base64: got.contentBase64 || null, size: got.size ?? null };
|
|
248
|
+
// The route sends no revision: the text's own digest is what an adoption is checked against.
|
|
249
|
+
return { raw: got.content ?? '', revision: await sha256Text(got.content ?? ''), binary: false, size: got.size ?? null };
|
|
250
|
+
}
|
|
251
|
+
// Only the head file is read here: the route that reads a catalog skill's other files (GET /v2/catalog/:id/file,
|
|
252
|
+
// capability `catalog-file`) is missing from a server older than the page. A restart brings it.
|
|
253
|
+
if (s.readsFiles === 'head') throw Object.assign(new Error('This file opens after SynaBun restarts.'), { code: 'needs-restart' });
|
|
254
|
+
throw Object.assign(new Error('This file can be opened once the skill is in the library.'), { code: 'needs-library' });
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Open a file in a tab (or the tab it already has). The workspace it was asked for is the one it lands in, however
|
|
259
|
+
* long the read takes (a read that comes back after another workspace was opened is never put in that one).
|
|
260
|
+
* @returns {Promise<object>} the tab
|
|
261
|
+
*/
|
|
262
|
+
export async function openFile(ws, path, io, { ask } = {}) {
|
|
263
|
+
const open = ws.tabs.find((t) => t.path === path);
|
|
264
|
+
if (open) { ws.active = ws.tabs.indexOf(open); return open; }
|
|
265
|
+
const got = await readFile(ws, path, io);
|
|
266
|
+
const again = ws.tabs.find((t) => t.path === path);
|
|
267
|
+
if (again) { ws.active = ws.tabs.indexOf(again); return again; }
|
|
268
|
+
const readOnly = !ws.subject.editable || got.readOnly === true;
|
|
269
|
+
const tab = createDocTab({ artifact: tabArtifact(ws), artifactContent: null, path, raw: got.raw, revision: got.revision, binary: got.binary, size: got.size, readOnly, label: path === null ? `${ws.label}.md` : String(path) });
|
|
270
|
+
tab.base64 = got.base64 || null;
|
|
271
|
+
await restoreDraft(ws, tab, { ask });
|
|
272
|
+
ws.tabs.push(tab);
|
|
273
|
+
ws.active = ws.tabs.length - 1;
|
|
274
|
+
return tab;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** The documents of a workspace with unsaved text. */
|
|
278
|
+
export const dirtyTabs = (ws) => (ws?.tabs || []).filter((t) => t.dirty);
|
|
279
|
+
|
|
280
|
+
/** Close a tab (the caller asked about unsaved text first). */
|
|
281
|
+
export function closeTab(ws, tab) {
|
|
282
|
+
const at = ws.tabs.indexOf(tab);
|
|
283
|
+
if (at < 0) return;
|
|
284
|
+
ws.tabs.splice(at, 1);
|
|
285
|
+
if (ws.active >= ws.tabs.length) ws.active = ws.tabs.length - 1;
|
|
286
|
+
else if (ws.active > at) ws.active--;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/** Give a tab's unsaved text up. */
|
|
290
|
+
export function discardTab(ws, tab) {
|
|
291
|
+
revertTab(tab);
|
|
292
|
+
dropDraft(ws, tab);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// ── Saving ──
|
|
296
|
+
|
|
297
|
+
/** Run `fn` after every save already asked for in this workspace (one write at a time). */
|
|
298
|
+
function serially(ws, fn) {
|
|
299
|
+
const run = ws.queue.then(fn, fn);
|
|
300
|
+
ws.queue = run.catch(() => {});
|
|
301
|
+
return run;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/** The library state after an adoption or a new draft: the subject becomes "library". */
|
|
305
|
+
function becomeLibrary(ws, skill) {
|
|
306
|
+
ws.skill = skill;
|
|
307
|
+
ws.row = { ...ws.row, skill };
|
|
308
|
+
ws.subject = { ...ws.subject, mode: 'library', readsFiles: 'library', hasVersions: true, canPublish: true, canAdopt: false, skillId: skill.skillId };
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/** Take an owner's folder into the library now, without a change (Edit → "Add to library"): the documents stay. */
|
|
312
|
+
export async function adoptWorkspace(ws, io, { key = () => '' } = {}) {
|
|
313
|
+
if (ws.subject.mode !== 'adopt') return ws;
|
|
314
|
+
const adopted = await io.adoptSkillsArtifact(ws.row.primary.id, { idempotencyKey: key('adopt') });
|
|
315
|
+
becomeLibrary(ws, adopted.skill);
|
|
316
|
+
const skill = (await io.fetchSkill(adopted.skill.skillId)).skill;
|
|
317
|
+
ws.skill = skill;
|
|
318
|
+
if (skill.pointers?.latest) ws.revision = (await io.fetchSkillsRevision(skill.pointers.latest)).revision;
|
|
319
|
+
return ws;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** Start the workspace again from what the library holds now (after a restore made a new draft). */
|
|
323
|
+
export function resetDocuments(ws) {
|
|
324
|
+
for (const tab of ws.tabs) dropDraft(ws, tab);
|
|
325
|
+
ws.tabs = [];
|
|
326
|
+
ws.active = -1;
|
|
327
|
+
ws.draft = null;
|
|
328
|
+
ws.revision = null;
|
|
329
|
+
ws.loaded = false;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Make sure the workspace has a draft to save into: the skill's own (adopting the folder first when it is not in
|
|
334
|
+
* the library yet). Answers what the draft holds for each open document, so a document whose file changed under it
|
|
335
|
+
* is caught before anything is written.
|
|
336
|
+
* @returns {Promise<{ changedUnder: string[] }>} paths whose file in the draft is not what their tab was opened at
|
|
337
|
+
*/
|
|
338
|
+
export async function ensureDraft(ws, io, { key = () => '' } = {}) {
|
|
339
|
+
if (ws.subject.mode === 'adopt') {
|
|
340
|
+
const adopted = await io.adoptSkillsArtifact(ws.row.primary.id, { idempotencyKey: key('adopt') });
|
|
341
|
+
becomeLibrary(ws, adopted.skill);
|
|
342
|
+
}
|
|
343
|
+
if (ws.subject.mode !== 'library' || !ws.skill) throw Object.assign(new Error('This cannot be saved.'), { code: 'read-only' });
|
|
344
|
+
if (!ws.draft) {
|
|
345
|
+
const skill = (await io.fetchSkill(ws.skill.skillId)).skill;
|
|
346
|
+
ws.skill = skill;
|
|
347
|
+
if (skill.pointers?.draft) {
|
|
348
|
+
try { ws.draft = (await io.fetchSkillsDraft(skill.pointers.draft)).draft; } catch (err) { if (err?.status !== 404) throw err; }
|
|
349
|
+
}
|
|
350
|
+
if (!ws.draft) ws.draft = (await io.createSkillsDraft({ skillId: skill.skillId, baseRevisionId: skill.pointers?.latest || null }, { idempotencyKey: key('draft') })).draft;
|
|
351
|
+
if (!ws.revision && skill.pointers?.latest) { try { ws.revision = (await io.fetchSkillsRevision(skill.pointers.latest)).revision; } catch { /* the tree below says enough */ } }
|
|
352
|
+
ws.files = libraryTreeFromDraft(ws.draft, ws.revision);
|
|
353
|
+
ws.dirs = ws.draft.dirs || [];
|
|
354
|
+
}
|
|
355
|
+
const changedUnder = ws.tabs.filter((t) => !t.binary && t.path && t.saved.revision && digestIn(ws.draft, t.path) && digestIn(ws.draft, t.path) !== t.saved.revision).map((t) => t.path);
|
|
356
|
+
return { changedUnder };
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Save one document.
|
|
361
|
+
* @returns {Promise<{ status: 'saved'|'unchanged'|'conflict'|'gone', stillDirty?: boolean, current?: string|null, diagnostics?: object[] }>}
|
|
362
|
+
* `conflict`: the file changed elsewhere since the document was opened (`current` its revision now); nothing was written.
|
|
363
|
+
*/
|
|
364
|
+
export function saveTab(ws, tab, io, { over = null, key = () => '' } = {}) {
|
|
365
|
+
return serially(ws, async () => {
|
|
366
|
+
if (!tab || tab.binary || tab.readOnly || !ws.tabs.includes(tab)) return { status: 'unchanged' };
|
|
367
|
+
if (!tab.dirty && !over) return { status: 'unchanged' };
|
|
368
|
+
const sent = saveSnapshot(tab);
|
|
369
|
+
tab.saving = sent;
|
|
370
|
+
try {
|
|
371
|
+
if (ws.subject.mode === 'inplace') {
|
|
372
|
+
const res = await io.saveSkillsArtifact(ws.subject.v1Id, sent.raw, { expectedRevision: over || sent.expectedRevision });
|
|
373
|
+
const stillDirty = applySaved(tab, sent, res?.revision ?? null);
|
|
374
|
+
noteDraft(ws, tab);
|
|
375
|
+
return { status: 'saved', stillDirty, diagnostics: res?.diagnostics || [] };
|
|
376
|
+
}
|
|
377
|
+
const { changedUnder } = await ensureDraft(ws, io, { key });
|
|
378
|
+
if (!over && changedUnder.includes(tab.path)) return { status: 'conflict', current: digestIn(ws.draft, tab.path) };
|
|
379
|
+
const put = async (etag) => io.patchSkillsDraft(ws.draft.draftId, { expectedEtag: etag, ops: [{ op: 'put', path: tab.path, content: sent.raw }] });
|
|
380
|
+
let res;
|
|
381
|
+
try { res = await put(ws.draft.etag); } catch (err) {
|
|
382
|
+
if (err?.status !== 409 || err?.code !== 'draft_conflict') throw err;
|
|
383
|
+
// Someone changed the draft: a conflict for this document only when they changed its file.
|
|
384
|
+
const now = (await io.fetchSkillsDraft(ws.draft.draftId)).draft;
|
|
385
|
+
ws.draft = now;
|
|
386
|
+
ws.files = libraryTreeFromDraft(now, ws.revision);
|
|
387
|
+
const theirs = digestIn(now, tab.path);
|
|
388
|
+
if (!over && theirs && theirs !== tab.saved.revision) return { status: 'conflict', current: theirs };
|
|
389
|
+
res = await put(now.etag);
|
|
390
|
+
}
|
|
391
|
+
ws.draft = res.draft;
|
|
392
|
+
ws.files = libraryTreeFromDraft(ws.draft, ws.revision);
|
|
393
|
+
ws.dirs = ws.draft.dirs || [];
|
|
394
|
+
const stillDirty = applySaved(tab, sent, digestIn(ws.draft, tab.path));
|
|
395
|
+
noteDraft(ws, tab);
|
|
396
|
+
return { status: 'saved', stillDirty };
|
|
397
|
+
} catch (err) {
|
|
398
|
+
if (err?.status === 409 && err?.code === 'revision_conflict') return { status: err.body?.currentRevision ? 'conflict' : 'gone', current: err.body?.currentRevision || null };
|
|
399
|
+
throw err;
|
|
400
|
+
} finally {
|
|
401
|
+
tab.saving = null;
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/** Read a document's file again and show it, dropping its unsaved text. */
|
|
407
|
+
export async function reloadTab(ws, tab, io) {
|
|
408
|
+
if (ws.subject.mode === 'library' && ws.draft) ws.draft = (await io.fetchSkillsDraft(ws.draft.draftId)).draft;
|
|
409
|
+
const got = await readFile(ws, tab.path, io);
|
|
410
|
+
replaceSaved(tab, got.raw, got.revision);
|
|
411
|
+
tab.binary = !!got.binary;
|
|
412
|
+
tab.base64 = got.base64 || null;
|
|
413
|
+
dropDraft(ws, tab);
|
|
414
|
+
return tab;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/** Save a draft as a new version (a revision); `unchanged` when it holds exactly its base's bytes. */
|
|
418
|
+
export async function saveVersion(ws, io, { note = '', key = () => '' } = {}) {
|
|
419
|
+
await ensureDraft(ws, io, { key });
|
|
420
|
+
const out = await io.snapshotSkillsDraft(ws.draft.draftId, { expectedEtag: ws.draft.etag, note }, { idempotencyKey: key('snapshot') });
|
|
421
|
+
ws.draft = out.draft;
|
|
422
|
+
ws.revision = out.revision;
|
|
423
|
+
ws.skill = (await io.fetchSkill(ws.skill.skillId)).skill;
|
|
424
|
+
return out;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// ── Files of a library draft ──
|
|
428
|
+
|
|
429
|
+
/** Put new files (or a new folder) into the draft: `items` [{ path, content | contentBase64, executable? }]. */
|
|
430
|
+
export async function putFiles(ws, items, io, { key = () => '' } = {}) {
|
|
431
|
+
return serially(ws, async () => {
|
|
432
|
+
await ensureDraft(ws, io, { key });
|
|
433
|
+
const ops = items.map((it) => (it.mkdir ? { op: 'mkdir', path: it.path } : { op: 'put', path: it.path, ...(typeof it.contentBase64 === 'string' ? { contentBase64: it.contentBase64 } : { content: String(it.content ?? '') }), ...(it.executable ? { executable: true } : {}) }));
|
|
434
|
+
const res = await io.patchSkillsDraft(ws.draft.draftId, { expectedEtag: ws.draft.etag, ops });
|
|
435
|
+
ws.draft = res.draft;
|
|
436
|
+
ws.files = libraryTreeFromDraft(ws.draft, ws.revision);
|
|
437
|
+
ws.dirs = ws.draft.dirs || [];
|
|
438
|
+
return res.draft;
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/** Delete a file or a folder from the draft. A document of it with unsaved text is kept (or dropped) by the caller. */
|
|
443
|
+
export async function deleteFile(ws, path, io, { key = () => '' } = {}) {
|
|
444
|
+
return serially(ws, async () => {
|
|
445
|
+
await ensureDraft(ws, io, { key });
|
|
446
|
+
const res = await io.patchSkillsDraft(ws.draft.draftId, { expectedEtag: ws.draft.etag, ops: [{ op: 'delete', path }] });
|
|
447
|
+
ws.draft = res.draft;
|
|
448
|
+
ws.files = libraryTreeFromDraft(ws.draft, ws.revision);
|
|
449
|
+
ws.dirs = ws.draft.dirs || [];
|
|
450
|
+
return res.draft;
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/** Rename a file of the draft; its open document follows. */
|
|
455
|
+
export async function renameFile(ws, from, to, io, { key = () => '' } = {}) {
|
|
456
|
+
return serially(ws, async () => {
|
|
457
|
+
await ensureDraft(ws, io, { key });
|
|
458
|
+
const res = await io.patchSkillsDraft(ws.draft.draftId, { expectedEtag: ws.draft.etag, ops: [{ op: 'rename', from, to }] });
|
|
459
|
+
ws.draft = res.draft;
|
|
460
|
+
ws.files = libraryTreeFromDraft(ws.draft, ws.revision);
|
|
461
|
+
const tab = ws.tabs.find((t) => t.path === from);
|
|
462
|
+
if (tab) {
|
|
463
|
+
dropDraft(ws, tab);
|
|
464
|
+
tab.path = to;
|
|
465
|
+
tab.label = to;
|
|
466
|
+
tab.id = `${ws.key}:${to}`;
|
|
467
|
+
noteDraft(ws, tab);
|
|
468
|
+
}
|
|
469
|
+
return res.draft;
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/** Whether bytes a person picked are text the editor keeps as text (else they are uploaded byte for byte). */
|
|
474
|
+
export const isTextFile = (bytes) => isEditableText(bytes);
|
|
475
|
+
|
|
476
|
+
/** After a change made elsewhere (the sync event), whether an open document's file is no longer what it holds. */
|
|
477
|
+
export function changedElsewhere(ws, draft) {
|
|
478
|
+
if (!draft || !ws.draft || draft.draftId !== ws.draft.draftId) return [];
|
|
479
|
+
return ws.tabs.filter((t) => !t.saving && t.path && digestIn(draft, t.path) && digestIn(draft, t.path) !== t.saved.revision).map((t) => t.path);
|
|
480
|
+
}
|