synabun 2.0.10 → 2.2.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 +207 -0
- package/README.md +25 -2
- package/THIRD-PARTY-LICENSES.md +22 -0
- package/bin/synabun.js +71 -24
- package/docs/whatsapp.md +13 -13
- 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/lib/claude-hooks.js +172 -18
- package/lib/client-config-repair.js +30 -17
- package/lib/desktop-app.js +7 -1
- package/lib/external-tools.js +7 -6
- 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 +37 -4
- package/lib/tools-cli.js +45 -0
- package/lib/update-snapshot.js +302 -0
- package/lib/virustotal.mjs +203 -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/budget-judgments.d.ts +1 -1
- package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -1
- package/mcp-server/dist/services/budget-judgments.js +5 -3
- package/mcp-server/dist/services/budget-judgments.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/identity.d.ts +2 -2
- package/mcp-server/dist/services/identity.js +2 -2
- 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/memory-sessions.d.ts +1 -1
- package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -1
- package/mcp-server/dist/services/memory-sessions.js +28 -2
- package/mcp-server/dist/services/memory-sessions.js.map +1 -1
- package/mcp-server/dist/services/neural-interface.d.ts +18 -11
- 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 +22 -63
- 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/typesafe-config.js +1 -1
- package/mcp-server/dist/services/typesafe-config.js.map +1 -1
- 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 +14 -14
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +29 -19
- 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/choice.d.ts +6 -0
- package/mcp-server/dist/tools/choice.d.ts.map +1 -1
- package/mcp-server/dist/tools/choice.js +18 -13
- package/mcp-server/dist/tools/choice.js.map +1 -1
- package/mcp-server/dist/tools/git-tools.js +2 -2
- package/mcp-server/dist/tools/git-tools.js.map +1 -1
- 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/morelogin.d.ts.map +1 -1
- package/mcp-server/dist/tools/morelogin.js +9 -6
- package/mcp-server/dist/tools/morelogin.js.map +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 +642 -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/budget-judgments.ts +5 -3
- package/mcp-server/src/services/codex-config-heal.ts +80 -29
- package/mcp-server/src/services/identity.ts +2 -2
- package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
- package/mcp-server/src/services/memory-sessions.ts +24 -3
- package/mcp-server/src/services/neural-interface.ts +81 -21
- package/mcp-server/src/services/server-instructions.ts +29 -64
- 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/typesafe-config.ts +1 -1
- package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
- package/mcp-server/src/tools/agents.ts +29 -20
- package/mcp-server/src/tools/choice.ts +17 -12
- package/mcp-server/src/tools/git-tools.ts +2 -2
- package/mcp-server/src/tools/image-tools.ts +1 -1
- package/mcp-server/src/tools/morelogin.ts +6 -4
- package/mcp-server/src/tools/skill-tools.ts +592 -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 +3953 -228
- package/neural-interface/i18n/pt-BR.json +3901 -179
- package/neural-interface/lib/agent-sandbox-elevation.js +6 -0
- package/neural-interface/lib/antigravity/acp-client-host.js +554 -0
- package/neural-interface/lib/antigravity/acp-connection.js +299 -0
- package/neural-interface/lib/antigravity/bypass.js +100 -0
- package/neural-interface/lib/antigravity/catalog.js +273 -0
- package/neural-interface/lib/antigravity/home.js +260 -0
- package/neural-interface/lib/antigravity/migrate-gemini.js +139 -0
- package/neural-interface/lib/antigravity/panel-bridge.js +1058 -0
- package/neural-interface/lib/antigravity/policy-hook.mjs +78 -0
- package/neural-interface/lib/antigravity/policy.js +74 -0
- package/neural-interface/lib/antigravity/tool-call.js +178 -0
- package/neural-interface/lib/antigravity/transcripts.js +252 -0
- package/neural-interface/lib/antigravity/usage.js +43 -0
- package/neural-interface/lib/assistant-api.js +128 -28
- package/neural-interface/lib/assistant-brains/antigravity.js +423 -0
- 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 +120 -54
- package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +28 -13
- package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +59 -19
- package/neural-interface/lib/assistant-budget-planner.js +7 -3
- package/neural-interface/lib/assistant-budget-split.js +42 -15
- package/neural-interface/lib/assistant-catalog.js +81 -15
- package/neural-interface/lib/assistant-config.js +6 -3
- package/neural-interface/lib/assistant-dispatch.js +1073 -200
- package/neural-interface/lib/assistant-envelope.js +156 -2
- package/neural-interface/lib/assistant-evidence.js +80 -0
- package/neural-interface/lib/assistant-hidden-models.js +7 -5
- package/neural-interface/lib/assistant-persona.js +93 -19
- package/neural-interface/lib/assistant-plan-permissions.js +25 -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 +245 -38
- package/neural-interface/lib/assistant-runtime.js +214 -30
- package/neural-interface/lib/assistant-task-prompt.js +87 -21
- package/neural-interface/lib/assistant-tools.js +51 -0
- package/neural-interface/lib/assistant-usage.js +29 -0
- package/neural-interface/lib/backup-service.js +626 -4
- 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 +84 -0
- package/neural-interface/lib/browser-network-guard.js +476 -0
- package/neural-interface/lib/browser-policy-roots.js +64 -0
- package/neural-interface/lib/browser-tool-policy.js +198 -38
- package/neural-interface/lib/claude-agent-bridge.js +11 -2
- package/neural-interface/lib/claude-history-cache.js +210 -0
- package/neural-interface/lib/claude-hooks-status.js +103 -0
- package/neural-interface/lib/claude-model-catalog.js +5 -1
- package/neural-interface/lib/claude-temporary.js +5 -11
- package/neural-interface/lib/cli-install-api.js +1 -1
- package/neural-interface/lib/cli-install.js +163 -154
- package/neural-interface/lib/cli-resolver.js +13 -3
- package/neural-interface/lib/cli-update-planner.js +52 -41
- package/neural-interface/lib/cloudflared/tunnel.js +42 -2
- package/neural-interface/lib/codex-mcp-approvals.js +11 -0
- package/neural-interface/lib/codex-rollout-reader.js +52 -0
- package/neural-interface/lib/codex-worker-app-server.js +191 -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 +68 -68
- package/neural-interface/lib/editor-git-api.js +3 -20
- package/neural-interface/lib/exec-loop-command.js +13 -10
- package/neural-interface/lib/file-explorer-api.js +74 -40
- package/neural-interface/lib/file-explorer-watch.js +9 -3
- package/neural-interface/lib/git-bash/install.js +52 -6
- package/neural-interface/lib/git-bash/manager.js +59 -17
- package/neural-interface/lib/git-bash/pins.js +12 -0
- package/neural-interface/lib/git-read.js +2 -2
- package/neural-interface/lib/git-status-runner.js +390 -0
- package/neural-interface/lib/git-status-watchdog.js +40 -0
- package/neural-interface/lib/greeting/budgets.js +1 -0
- package/neural-interface/lib/greeting/engine.js +2 -2
- package/neural-interface/lib/greeting/sources.js +10 -12
- package/neural-interface/lib/greeting/writer.js +1 -1
- package/neural-interface/lib/http-guards.js +8 -2
- package/neural-interface/lib/install-details.js +4 -1
- package/neural-interface/lib/mcp-agent-servers.js +67 -0
- package/neural-interface/lib/mcp-client-config.js +35 -33
- package/neural-interface/lib/mcp-external-servers.js +332 -0
- package/neural-interface/lib/mcp-installer.js +2 -1
- package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
- package/neural-interface/lib/memory-session-capture.js +17 -3
- package/neural-interface/lib/morelogin.js +177 -25
- package/neural-interface/lib/native-loop-providers.js +965 -66
- package/neural-interface/lib/native-loop-runtime.js +290 -42
- package/neural-interface/lib/opencode-config.js +557 -0
- package/neural-interface/lib/opencode-db-schema.js +17 -34
- package/neural-interface/lib/opencode-history.js +48 -218
- package/neural-interface/lib/opencode-mcp-ready.js +108 -0
- package/neural-interface/lib/opencode-native-boundary.js +53 -0
- package/neural-interface/lib/opencode-protocol.js +249 -112
- package/neural-interface/lib/opencode-runtime-config.js +32 -22
- package/neural-interface/lib/opencode-serve-records.js +39 -21
- package/neural-interface/lib/opencode-v2-client.js +48 -153
- package/neural-interface/lib/opencode-v2-ws-requests.js +36 -210
- package/neural-interface/lib/opencode-worker-data.js +27 -0
- package/neural-interface/lib/opencode2-client.js +73 -37
- package/neural-interface/lib/opencode2-proxy.js +23 -63
- package/neural-interface/lib/opencode2-translate.js +51 -2
- package/neural-interface/lib/owner-authority.js +249 -0
- package/neural-interface/lib/page-session.js +275 -0
- package/neural-interface/lib/path-confine.js +12 -9
- package/neural-interface/lib/peer-identity.js +793 -0
- package/neural-interface/lib/pty-host/core.js +28 -1
- package/neural-interface/lib/pty-host/lifetime.js +12 -0
- package/neural-interface/lib/pty-host/manager.js +12 -1
- package/neural-interface/lib/remote-policy.js +1 -1
- package/neural-interface/lib/request-gate.js +20 -2
- package/neural-interface/lib/rulesets/api.js +7 -6
- package/neural-interface/lib/rulesets/fs-safe.js +1 -1
- package/neural-interface/lib/rulesets/installer.js +180 -36
- package/neural-interface/lib/rulesets/json-edit.js +1 -1
- package/neural-interface/lib/rulesets/render.js +1 -1
- package/neural-interface/lib/session-cache-builder.js +81 -31
- package/neural-interface/lib/session-title-generator.js +58 -36
- 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 +298 -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 +285 -0
- package/neural-interface/lib/skills/creation.js +843 -0
- package/neural-interface/lib/skills/deploy.js +569 -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 +216 -0
- package/neural-interface/lib/skills/eval-confinement.js +399 -0
- package/neural-interface/lib/skills/eval-sandbox.js +280 -0
- package/neural-interface/lib/skills/eval-trace.js +135 -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 +203 -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 +441 -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 +269 -0
- package/neural-interface/lib/skills/runtime-checks.js +358 -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 +530 -0
- package/neural-interface/lib/skills/telemetry.js +119 -0
- package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
- package/neural-interface/lib/synabun-version.js +1 -1
- package/neural-interface/lib/system-activity.js +1 -1
- package/neural-interface/lib/tool-lists.js +124 -0
- package/neural-interface/lib/whatsapp/brain.js +7 -3
- package/neural-interface/lib/whatsapp/bridge.js +6 -3
- package/neural-interface/lib/whatsapp/cards.js +22 -9
- package/neural-interface/lib/whatsapp/config.js +1 -1
- package/neural-interface/lib/worker-confinement.js +1268 -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-question.js +74 -0
- package/neural-interface/lib/worker-relay-ports.js +227 -0
- package/neural-interface/lib/worker-resume.js +238 -0
- package/neural-interface/lib/worker-sandbox.js +329 -0
- package/neural-interface/package-lock.json +34 -12
- package/neural-interface/package.json +3 -2
- package/neural-interface/public/index.html +3 -1
- package/neural-interface/public/index2d.html +19 -11
- package/neural-interface/public/onboarding.html +3 -3
- package/neural-interface/public/shared/account-menu.js +8 -5
- package/neural-interface/public/shared/agent-runtime-options.js +125 -31
- package/neural-interface/public/shared/agy/agy-bypass.js +47 -0
- package/neural-interface/public/shared/agy/agy-client.js +266 -0
- package/neural-interface/public/shared/agy/agy-commands.js +120 -0
- package/neural-interface/public/shared/agy/agy-copy.js +326 -0
- package/neural-interface/public/shared/agy/agy-diff.js +161 -0
- package/neural-interface/public/shared/agy/agy-events.js +469 -0
- package/neural-interface/public/shared/agy/agy-greeting.js +26 -0
- package/neural-interface/public/shared/agy/agy-options.js +232 -0
- package/neural-interface/public/shared/agy/agy-panel.js +2543 -0
- package/neural-interface/public/shared/agy/agy-render.js +392 -0
- package/neural-interface/public/shared/agy/agy-setup.js +149 -0
- package/neural-interface/public/shared/agy/agy-styles.js +604 -0
- package/neural-interface/public/shared/agy/agy-tabs-logic.js +181 -0
- 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 +36 -6
- package/neural-interface/public/shared/assistant/asst-api.js +1 -1
- 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 +105 -44
- package/neural-interface/public/shared/assistant/asst-budget.js +5 -4
- package/neural-interface/public/shared/assistant/asst-control.js +205 -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 +16 -7
- package/neural-interface/public/shared/assistant/asst-limit-dock.js +45 -0
- package/neural-interface/public/shared/assistant/asst-limit-wait.js +327 -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 +7 -6
- package/neural-interface/public/shared/assistant/asst-panel.js +151 -19
- package/neural-interface/public/shared/assistant/asst-rack-styles.js +66 -49
- package/neural-interface/public/shared/assistant/asst-render.js +445 -82
- package/neural-interface/public/shared/assistant/asst-route.js +304 -53
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +9 -8
- package/neural-interface/public/shared/assistant/asst-scenes.js +56 -1
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +117 -10
- package/neural-interface/public/shared/assistant/asst-slash.js +17 -9
- package/neural-interface/public/shared/assistant/asst-state.js +87 -24
- package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
- package/neural-interface/public/shared/assistant/asst-styles.js +93 -21
- package/neural-interface/public/shared/assistant/asst-tool-kinds.js +190 -23
- package/neural-interface/public/shared/assistant/asst-usage.js +2 -1
- package/neural-interface/public/shared/automation-context.js +5 -2
- package/neural-interface/public/shared/cc-dropdown.js +89 -19
- 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 +34 -10
- package/neural-interface/public/shared/cdx/cdx-panel.js +59 -151
- package/neural-interface/public/shared/cdx/cdx-preview.js +12 -0
- package/neural-interface/public/shared/cdx/cdx-protocol.js +39 -11
- package/neural-interface/public/shared/cdx/cdx-render.js +57 -38
- package/neural-interface/public/shared/cdx/cdx-requests.js +14 -4
- 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 +127 -44
- package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
- package/neural-interface/public/shared/cli-install-controller.js +218 -0
- package/neural-interface/public/shared/cli-install-view.js +11 -12
- package/neural-interface/public/shared/cli-status.js +11 -10
- package/neural-interface/public/shared/constants.js +5 -4
- 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 +367 -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-restore.js +162 -0
- package/neural-interface/public/shared/cp/cp-tool-cards.js +10 -3
- package/neural-interface/public/shared/crash-notice-view.js +168 -0
- package/neural-interface/public/shared/desktop-bridge.js +17 -0
- package/neural-interface/public/shared/desktop-titlebar-view.js +12 -0
- package/neural-interface/public/shared/desktop-titlebar.js +13 -4
- package/neural-interface/public/shared/desktop-update-view.js +21 -0
- package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
- package/neural-interface/public/shared/explorer/fx-api.js +4 -4
- package/neural-interface/public/shared/explorer/fx-links.js +3 -3
- package/neural-interface/public/shared/explorer/fx-logic.js +1 -1
- package/neural-interface/public/shared/explorer/fx-main.js +48 -16
- package/neural-interface/public/shared/explorer/fx-tree.js +1 -0
- package/neural-interface/public/shared/greeting/greeting-card.js +2 -2
- package/neural-interface/public/shared/greeting/greeting-view.js +1 -0
- package/neural-interface/public/shared/html-shell.js +30 -15
- package/neural-interface/public/shared/i18n.js +2 -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-changes-logic.js +3 -101
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +2 -0
- 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-outcome-notifications.js +16 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +74 -259
- package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
- package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +95 -202
- package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +13 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +13 -22
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +4 -4
- 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 +2 -91
- 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 +17 -21
- 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 +60 -50
- package/neural-interface/public/shared/page-session.js +96 -0
- package/neural-interface/public/shared/provider-icons.js +4 -2
- 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 +195 -61
- package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
- package/neural-interface/public/shared/settings/settings-data.js +3 -1
- package/neural-interface/public/shared/settings/settings-ia.js +6 -6
- package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
- package/neural-interface/public/shared/settings/settings-rules.js +2 -2
- 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/showcase-privacy.js +6 -1
- package/neural-interface/public/shared/sidepanel-session-status.js +3 -3
- package/neural-interface/public/shared/skills/ss-args.js +96 -0
- package/neural-interface/public/shared/skills/ss-creation.js +222 -0
- package/neural-interface/public/shared/skills/ss-dialog.js +133 -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 +149 -0
- package/neural-interface/public/shared/skills/ss-presets.js +117 -0
- package/neural-interface/public/shared/skills/ss-profiles.js +513 -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 +237 -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 +230 -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-fixes-view.js +359 -0
- package/neural-interface/public/shared/skills/studio/sx-fixes.js +327 -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 +282 -0
- package/neural-interface/public/shared/skills/studio/sx-install.js +313 -0
- package/neural-interface/public/shared/skills/studio/sx-library-view.js +748 -0
- package/neural-interface/public/shared/skills/studio/sx-library.js +479 -0
- package/neural-interface/public/shared/skills/studio/sx-main.js +690 -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 +249 -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 +426 -0
- package/neural-interface/public/shared/state.js +1 -1
- package/neural-interface/public/shared/streaming-markdown.js +58 -0
- package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
- package/neural-interface/public/shared/styles.css +1822 -1756
- package/neural-interface/public/shared/synabun-mascot.js +1115 -9
- package/neural-interface/public/shared/text-cache.js +29 -0
- 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/topright-overflow-logic.js +54 -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-antigravity-panel.js +19 -0
- package/neural-interface/public/shared/ui-assistant.js +2 -0
- package/neural-interface/public/shared/ui-automation-studio.js +111 -140
- package/neural-interface/public/shared/ui-claude-panel.js +376 -82
- package/neural-interface/public/shared/ui-cli-banner.js +23 -167
- 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 +15 -3
- package/neural-interface/public/shared/ui-file-explorer.js +48 -6
- package/neural-interface/public/shared/ui-file-links.js +2 -2
- package/neural-interface/public/shared/ui-games.js +2 -2
- package/neural-interface/public/shared/ui-image-gallery.js +50 -47
- package/neural-interface/public/shared/ui-judgments.js +1 -1
- package/neural-interface/public/shared/ui-keybinds.js +33 -2
- package/neural-interface/public/shared/ui-link.js +3 -3
- package/neural-interface/public/shared/ui-menubar.js +10 -20
- package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
- package/neural-interface/public/shared/ui-native-loop-router.js +11 -1
- package/neural-interface/public/shared/ui-navbar.js +49 -24
- package/neural-interface/public/shared/ui-notifications.js +1 -0
- package/neural-interface/public/shared/ui-panels.js +96 -65
- package/neural-interface/public/shared/ui-resume.js +9 -7
- package/neural-interface/public/shared/ui-schedules-studio.js +546 -431
- package/neural-interface/public/shared/ui-scroll-follow.js +16 -5
- package/neural-interface/public/shared/ui-sessions.js +27 -15
- package/neural-interface/public/shared/ui-settings.js +683 -526
- package/neural-interface/public/shared/ui-sidepanel-session-entry.js +9 -2
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +4 -4
- package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
- package/neural-interface/public/shared/ui-sidepanel-tray.js +48 -7
- package/neural-interface/public/shared/ui-sidepanel-windows.js +18 -13
- package/neural-interface/public/shared/ui-skills.js +33 -2830
- package/neural-interface/public/shared/ui-styleguide.js +5 -2
- package/neural-interface/public/shared/ui-sync.js +15 -5
- package/neural-interface/public/shared/ui-terminal.js +270 -190
- package/neural-interface/public/shared/ui-tool-updates.js +6 -4
- package/neural-interface/public/shared/ui-tooltip.js +81 -51
- package/neural-interface/public/shared/ui-topright-overflow.js +233 -0
- package/neural-interface/public/shared/ui-trash.js +6 -4
- 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 -65
- package/neural-interface/public/shared/ui-tutorial.css +47 -100
- package/neural-interface/public/shared/ui-tutorial.js +285 -509
- package/neural-interface/public/shared/ui-update.js +38 -2
- package/neural-interface/public/shared/ui-whiteboard.js +312 -59
- package/neural-interface/public/shared/ui-worker-end.js +5 -1
- 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 +3 -3
- 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-questions.js +34 -0
- package/neural-interface/public/shared/worker-restart.js +12 -0
- package/neural-interface/public/sidepanel-session.html +2 -2
- 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 +32 -1
- package/neural-interface/public/variant/2d/minimap.js +11 -13
- package/neural-interface/public/variant/3d/main.js +69 -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/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 +32 -0
- package/neural-interface/server.js +4553 -2855
- package/neural-interface/templates/rulesets/README.md +1 -1
- package/neural-interface/templates/rulesets/core.md +1 -1
- package/neural-interface/templates/rulesets/legacy-hashes.json +23 -5
- package/neural-interface/templates/rulesets/manifest.json +8 -8
- package/npm-shrinkwrap.json +34 -12
- package/package.json +17 -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 +6 -6
- 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
- /package/neural-interface/templates/rulesets/{gemini.md → antigravity.md} +0 -0
|
@@ -74,6 +74,7 @@ import { PathConfineError, isInside } from './path-confine.js';
|
|
|
74
74
|
import { canonicalInside, openConfinedStream, removeConfinedEntry } from './confined-fs.js';
|
|
75
75
|
import { matchFileRoots, useFileRoot } from './editor-files-api.js';
|
|
76
76
|
import { runGit } from './editor-git-api.js';
|
|
77
|
+
import { gitStatusRunner, projectStatus, createIgnoredPrefixes, watchRelative, MAX_IGNORE_FOLDERS } from './git-status-runner.js';
|
|
77
78
|
import { TrashUnavailable, createRevealer, createTrash, renameExclusive } from './file-explorer-system.js';
|
|
78
79
|
import { isAllowedWebSocketOrigin } from './http-guards.js';
|
|
79
80
|
import { createTreeWatcher, watchStrategy } from './file-explorer-watch.js';
|
|
@@ -89,8 +90,6 @@ export const MAX_UPLOAD_BYTES = 512 * 1024 * 1024;
|
|
|
89
90
|
export const MAX_RAW_BYTES = 30 * 1024 * 1024;
|
|
90
91
|
export const MAX_BATCH = 500;
|
|
91
92
|
export const MAX_WATCH_SESSIONS = 16;
|
|
92
|
-
const GIT_MAX_BUFFER = 64 * 1024 * 1024;
|
|
93
|
-
const GIT_STATUS_TIMEOUT_MS = 15000;
|
|
94
93
|
const STAT_PARALLEL = 64;
|
|
95
94
|
const COPY_CHUNK_BYTES = 1024 * 1024;
|
|
96
95
|
const O_NOFOLLOW = constants.O_NOFOLLOW || 0;
|
|
@@ -273,7 +272,7 @@ async function mapLimit(items, limit, work) {
|
|
|
273
272
|
* @returns {import('express').Router & { close: () => void, sessions: () => number }}
|
|
274
273
|
*/
|
|
275
274
|
export function createFileExplorerRouter({
|
|
276
|
-
fileRoots, guard = null, origins = {}, git = runGit, trash = createTrash(), revealer = createRevealer(),
|
|
275
|
+
fileRoots, guard = null, origins = {}, git = runGit, statusRunner = gitStatusRunner, trash = createTrash(), revealer = createRevealer(),
|
|
277
276
|
platform = process.platform, watch = watchStrategy(), watchImpl = undefined, watchClock = {}, io = {}, log = (line) => console.warn(line),
|
|
278
277
|
maxUploadBytes = MAX_UPLOAD_BYTES, listingCap = LISTING_CAP,
|
|
279
278
|
} = {}) {
|
|
@@ -292,8 +291,7 @@ export function createFileExplorerRouter({
|
|
|
292
291
|
// The folders only: a single file the editor is allowed by name is not something the explorer manages.
|
|
293
292
|
const roots = () => ({ dirs: (fileRoots()?.dirs || []).filter(Boolean), files: [] });
|
|
294
293
|
const sessions = new Map(); // watch id → { watcher, root, real, res, written: Map<real dir, written dir> }
|
|
295
|
-
const gitKnown = new Map(); //
|
|
296
|
-
const gitFlights = new Map(); // canonical project folder → the status being read
|
|
294
|
+
const gitKnown = new Map(); // bounded visible-folder listings and ignore prefixes, independent of status revisions
|
|
297
295
|
|
|
298
296
|
function answer(res, error) {
|
|
299
297
|
const done = error?.midway || {};
|
|
@@ -438,6 +436,8 @@ export function createFileExplorerRouter({
|
|
|
438
436
|
dirents.sort((a, b) => (a.isDirectory() !== b.isDirectory() ? (a.isDirectory() ? -1 : 1) : compareNames(a.name, b.name)));
|
|
439
437
|
dirents = dirents.slice(0, listingCap);
|
|
440
438
|
}
|
|
439
|
+
const ignoreResult = await ignoredEntries(dir.real, dir.real, dirents, requestSignal(res), true);
|
|
440
|
+
const ignored = new Set(ignoreResult || []);
|
|
441
441
|
const realBase = realpathSync(dir.base);
|
|
442
442
|
const items = await mapLimit(dirents, STAT_PARALLEL, async (entry) => {
|
|
443
443
|
const full = join(dir.real, entry.name);
|
|
@@ -455,46 +455,67 @@ export function createFileExplorerRouter({
|
|
|
455
455
|
try { const st = await lstat(full); return { name: entry.name, type: 'file', size: st.size, mtime: st.mtimeMs }; }
|
|
456
456
|
catch { return { name: entry.name, type: 'file' }; }
|
|
457
457
|
});
|
|
458
|
+
for (const item of items) {
|
|
459
|
+
if (ignored.has(item.name) || ignored.has(item.name + '/')) item.ignored = true;
|
|
460
|
+
else if (ignoreResult === null) item.ignored = null; // still being checked, not known-normal
|
|
461
|
+
}
|
|
458
462
|
items.sort((a, b) => (a.type !== b.type ? (a.type === 'dir' ? -1 : 1) : compareNames(a.name, b.name)));
|
|
459
463
|
res.json({ ok: true, path: dir.written, items, total, truncated });
|
|
460
464
|
}));
|
|
461
465
|
|
|
462
466
|
// ── The git state of a project ──
|
|
463
|
-
|
|
464
|
-
const
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
if (named.ok) branch = named.stdout.toString('utf8').trim();
|
|
474
|
-
if (!branch) {
|
|
475
|
-
const short = await git(['rev-parse', '--short', 'HEAD'], { cwd: real, maxBuffer: 1024 * 1024 });
|
|
476
|
-
if (short.ok) branch = short.stdout.toString('utf8').trim();
|
|
467
|
+
function requestSignal(res) {
|
|
468
|
+
const controller = new AbortController();
|
|
469
|
+
res.once('close', () => controller.abort());
|
|
470
|
+
return controller.signal;
|
|
471
|
+
}
|
|
472
|
+
function ignoreData(real) {
|
|
473
|
+
let known = gitKnown.get(real);
|
|
474
|
+
if (!known) {
|
|
475
|
+
if (gitKnown.size >= 64) gitKnown.delete(gitKnown.keys().next().value);
|
|
476
|
+
gitKnown.set(real, known = { folders: new Map(), prefixes: createIgnoredPrefixes(platform), at: Date.now() });
|
|
477
477
|
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
478
|
+
if (Date.now() - known.at >= 30000) { known.prefixes.clear(); known.folders.clear(); known.at = Date.now(); }
|
|
479
|
+
return known;
|
|
480
|
+
}
|
|
481
|
+
async function ignoredEntries(real, folder, dirents, signal, cacheOnly = false) {
|
|
482
|
+
const known = ignoreData(real);
|
|
483
|
+
const rel = slashes(relative(real, folder));
|
|
484
|
+
const paths = dirents ? dirents.map(e => `${rel ? rel + '/' : ''}${e.name}${e.isDirectory() ? '/' : ''}`) : known.folders.get(folder)?.paths;
|
|
485
|
+
if (!paths) return null;
|
|
486
|
+
const ignored = await statusRunner.ignored(real, paths, { signal, cacheOnly });
|
|
487
|
+
if (known.folders.size >= MAX_IGNORE_FOLDERS && !known.folders.has(folder)) known.folders.delete(known.folders.keys().next().value);
|
|
488
|
+
known.folders.set(folder, { paths });
|
|
489
|
+
for (const path of ignored || []) known.prefixes.add(path);
|
|
490
|
+
return ignored;
|
|
491
|
+
}
|
|
492
|
+
async function readGit(real, signal) {
|
|
493
|
+
const result = projectStatus(await statusRunner.read(real, { signal }), real);
|
|
494
|
+
if (!result.isGit && result.ok) return { isGit: false };
|
|
495
|
+
const dirs = new Set([real]);
|
|
496
|
+
for (const session of sessions.values()) if (session.real === real) for (const folder of session.written.keys()) dirs.add(folder);
|
|
497
|
+
const known = ignoreData(real), ignored = [];
|
|
498
|
+
let ignoreUnknown = false;
|
|
499
|
+
for (const folder of dirs) {
|
|
500
|
+
try {
|
|
501
|
+
// Reuse the folder's entry names between listings/TTL; a tracked edit
|
|
502
|
+
// does not need readdir + check-ignore in every expanded folder.
|
|
503
|
+
const entries = known.folders.has(folder) ? null : (await readdir(folder, { withFileTypes: true })).slice(0, listingCap);
|
|
504
|
+
const checked = await ignoredEntries(real, folder, entries, signal);
|
|
505
|
+
if (checked === null) ignoreUnknown = true;
|
|
506
|
+
else ignored.push(...checked);
|
|
507
|
+
} catch { ignoreUnknown = true; }
|
|
508
|
+
}
|
|
509
|
+
const changes = Object.fromEntries((result.changes || []).map(c => [c.path, c.status]));
|
|
510
|
+
return { isGit: result.isGit, branch: result.branch, changes, ignored: [...new Set(ignored)], ignoreUnknown,
|
|
511
|
+
...(!result.ok || result.partial ? { partial: true, stale: !!result.stale, scanState: result.timedOut ? 'timed-out' : 'failed' } : {}) };
|
|
483
512
|
}
|
|
484
513
|
router.get('/git', route(async (req, res) => {
|
|
485
514
|
const dir = folderOf(req.query.root);
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
if (!flight) {
|
|
489
|
-
flight = readGit(dir.real).finally(() => gitFlights.delete(dir.real));
|
|
490
|
-
gitFlights.set(dir.real, flight);
|
|
491
|
-
}
|
|
492
|
-
res.json({ ok: true, root: dir.written, ...(await flight) });
|
|
515
|
+
if (req.query.refresh === '1') statusRunner.invalidate(dir.real, { refresh: true });
|
|
516
|
+
res.json({ ok: true, root: dir.written, ...(await readGit(dir.real, requestSignal(res))) });
|
|
493
517
|
}));
|
|
494
|
-
const ignoredByGit =
|
|
495
|
-
const known = gitKnown.get(real);
|
|
496
|
-
return !!known && (known.files.has(path) || known.dirs.some((dir) => path.startsWith(dir) || `${path}/` === dir));
|
|
497
|
-
};
|
|
518
|
+
const ignoredByGit = real => path => ignoreData(real).prefixes.has(watchRelative(real, path, platform));
|
|
498
519
|
|
|
499
520
|
// ── Entries by name ──
|
|
500
521
|
router.get('/search', route(async (req, res) => {
|
|
@@ -505,8 +526,9 @@ export function createFileExplorerRouter({
|
|
|
505
526
|
const words = query.split(/\s+/);
|
|
506
527
|
const byPath = query.includes('/');
|
|
507
528
|
// What git ignores as a whole folder (node_modules, build output) is not walked; without git, the usual two.
|
|
508
|
-
const
|
|
509
|
-
const
|
|
529
|
+
const signal = requestSignal(res);
|
|
530
|
+
const known = gitKnown.get(dir.real);
|
|
531
|
+
const skipped = (rel, name) => name === '.git' || (known ? known.prefixes.has(rel) : name === 'node_modules');
|
|
510
532
|
const items = [];
|
|
511
533
|
const queue = [''];
|
|
512
534
|
const started = Date.now();
|
|
@@ -515,8 +537,15 @@ export function createFileExplorerRouter({
|
|
|
515
537
|
const rel = queue[at];
|
|
516
538
|
let dirents;
|
|
517
539
|
try { dirents = await readdir(rel ? join(dir.real, rel) : dir.real, { withFileTypes: true }); } catch { continue; }
|
|
540
|
+
if (dirents.length > listingCap) { dirents = dirents.slice(0, listingCap); partial = true; }
|
|
541
|
+
const checked = await ignoredEntries(dir.real, rel ? join(dir.real, rel) : dir.real, dirents, signal);
|
|
542
|
+
// Unknown ignore policy must not send name search into an enormous cache.
|
|
543
|
+
if (checked === null) { partial = true; break; }
|
|
544
|
+
const ignored = new Set(checked);
|
|
545
|
+
if (signal.aborted) break;
|
|
518
546
|
dirents.sort((a, b) => compareNames(a.name, b.name));
|
|
519
547
|
for (const entry of dirents) {
|
|
548
|
+
if (ignored.has(`${rel ? rel + '/' : ''}${entry.name}${entry.isDirectory() ? '/' : ''}`)) continue;
|
|
520
549
|
if (++visited > SEARCH_MAX_VISITED || Date.now() - started > SEARCH_BUDGET_MS) { partial = true; break walk; }
|
|
521
550
|
const path = rel ? `${rel}/${entry.name}` : entry.name;
|
|
522
551
|
const isDir = entry.isDirectory();
|
|
@@ -547,7 +576,12 @@ export function createFileExplorerRouter({
|
|
|
547
576
|
...(watchImpl ? { watch: watchImpl } : {}),
|
|
548
577
|
...watchClock,
|
|
549
578
|
ignoresGit: ignoredByGit(dir.real),
|
|
550
|
-
onChange: ({ dirs, git: gitChanged }) =>
|
|
579
|
+
onChange: ({ dirs, git: gitChanged, ignores }) => {
|
|
580
|
+
if (gitChanged || ignores) statusRunner.invalidate(dir.real, { ignores });
|
|
581
|
+
if (ignores) gitKnown.delete(dir.real);
|
|
582
|
+
else { const known = gitKnown.get(dir.real); for (const folder of dirs) known?.folders.delete(folder); }
|
|
583
|
+
send('change', { dirs: dirs.map((real) => session.written.get(real)).filter(Boolean), git: gitChanged });
|
|
584
|
+
},
|
|
551
585
|
onError: () => send('resync', {}),
|
|
552
586
|
// A stream is not a watch that works: what is really watched is said, and said again when it changes.
|
|
553
587
|
onState: (state) => send('state', state),
|
|
@@ -557,7 +591,7 @@ export function createFileExplorerRouter({
|
|
|
557
591
|
const beat = setInterval(() => { try { res.write(': still here\n\n'); } catch { /* closing */ } }, 25000);
|
|
558
592
|
beat.unref?.();
|
|
559
593
|
// The response's end is the page leaving (a request's own `close` only says its body was read).
|
|
560
|
-
res.on('close', () => { clearInterval(beat); session.watcher.close(); sessions.delete(id); });
|
|
594
|
+
res.on('close', () => { clearInterval(beat); session.watcher.close(); sessions.delete(id); if (![...sessions.values()].some(s => s.real === dir.real)) statusRunner.release(dir.real); });
|
|
561
595
|
}));
|
|
562
596
|
|
|
563
597
|
router.post('/watch/:id', route(async (req, res) => {
|
|
@@ -1033,7 +1067,7 @@ export function createFileExplorerRouter({
|
|
|
1033
1067
|
router.use((req, res) => res.status(404).json({ ok: false, code: 'bad-request', error: 'Unknown explorer route.' }));
|
|
1034
1068
|
|
|
1035
1069
|
/** Release every watch (the server is going down). */
|
|
1036
|
-
router.close = () => { for (const session of sessions.values()) { session.watcher.close(); try { session.res.end(); } catch { /* gone */ } } sessions.clear(); };
|
|
1070
|
+
router.close = () => { for (const session of sessions.values()) { session.watcher.close(); statusRunner.release(session.real); try { session.res.end(); } catch { /* gone */ } } sessions.clear(); };
|
|
1037
1071
|
router.sessions = () => sessions.size;
|
|
1038
1072
|
return router;
|
|
1039
1073
|
}
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
// that is not there, whatever folders the page shows meanwhile.
|
|
27
27
|
|
|
28
28
|
import { existsSync, watch as fsWatch } from 'node:fs';
|
|
29
|
-
import { dirname, join, relative, sep } from 'node:path';
|
|
29
|
+
import { dirname, isAbsolute, join, relative, sep } from 'node:path';
|
|
30
30
|
|
|
31
31
|
export const MAX_WATCHED_DIRS = 600;
|
|
32
32
|
/** A watch that failed is tried again after this long, then after twice that, and so on. */
|
|
@@ -80,6 +80,7 @@ export function createTreeWatcher({
|
|
|
80
80
|
let said = null; // the `live` that was last reported
|
|
81
81
|
const pending = new Set();
|
|
82
82
|
let pendingGit = false;
|
|
83
|
+
let pendingIgnores = false;
|
|
83
84
|
let timer = null;
|
|
84
85
|
let firstAt = 0;
|
|
85
86
|
|
|
@@ -88,9 +89,10 @@ export function createTreeWatcher({
|
|
|
88
89
|
function flush() {
|
|
89
90
|
timer = null;
|
|
90
91
|
if (closed || (!pending.size && !pendingGit)) return;
|
|
91
|
-
const change = { dirs: [...pending], git: pendingGit };
|
|
92
|
+
const change = { dirs: [...pending], git: pendingGit, ...(pendingIgnores ? { ignores: true } : {}) };
|
|
92
93
|
pending.clear();
|
|
93
94
|
pendingGit = false;
|
|
95
|
+
pendingIgnores = false;
|
|
94
96
|
try { onChange(change); } catch { /* a listener's failure is not the watcher's */ }
|
|
95
97
|
}
|
|
96
98
|
|
|
@@ -107,7 +109,11 @@ export function createTreeWatcher({
|
|
|
107
109
|
// One change, by its path below the root ('' is the root itself).
|
|
108
110
|
function note(rel) {
|
|
109
111
|
if (closed) return;
|
|
110
|
-
|
|
112
|
+
// Native watchers can report an absolute path, or no filename at all.
|
|
113
|
+
// Missing names conservatively dirty status, never all ignore data.
|
|
114
|
+
const path = slashed(isAbsolute(rel || '') ? relative(root, rel) : rel || '');
|
|
115
|
+
if (path === '..' || path.startsWith('../')) return;
|
|
116
|
+
if (path === '.git/info/exclude' || path === '.git/config' || path === '.git/config.worktree' || path === '.gitignore' || path.endsWith('/.gitignore')) { pendingIgnores = true; pendingGit = true; }
|
|
111
117
|
if (path === '.git' || path.startsWith('.git/')) {
|
|
112
118
|
const inside = path.slice(5);
|
|
113
119
|
if (GIT_STATE_FILES.test(inside) || inside.startsWith('refs/')) pendingGit = true;
|
|
@@ -11,7 +11,10 @@
|
|
|
11
11
|
// redirect too), stopped at the pinned size, compared with
|
|
12
12
|
// the pinned SHA-256 (cloudflared/install.js downloadPinned).
|
|
13
13
|
// A file that does not match is removed: nothing of it is
|
|
14
|
-
// ever unpacked
|
|
14
|
+
// ever unpacked. An installed app carries the release file
|
|
15
|
+
// itself (<resources>\git-archive): it is copied into the
|
|
16
|
+
// work folder and checked the same way (copyPinned), never
|
|
17
|
+
// unpacked from where it lies
|
|
15
18
|
// extractArchive the .tar.bz2 read by this application itself (bzip2.js,
|
|
16
19
|
// then the tar reader below), never by a tar program: what a
|
|
17
20
|
// Windows 10 tar.exe can read differs from build to build.
|
|
@@ -34,8 +37,10 @@
|
|
|
34
37
|
// Only node: modules and this application's own files here (see pins.js).
|
|
35
38
|
|
|
36
39
|
import { createHash, randomBytes } from 'node:crypto';
|
|
40
|
+
import { pipeline } from 'node:stream/promises';
|
|
41
|
+
import { Transform } from 'node:stream';
|
|
37
42
|
import {
|
|
38
|
-
closeSync, constants, copyFileSync, existsSync, linkSync, lstatSync, mkdirSync, openSync, readdirSync, readFileSync,
|
|
43
|
+
closeSync, constants, copyFileSync, createReadStream, createWriteStream, existsSync, linkSync, lstatSync, mkdirSync, openSync, readdirSync, readFileSync,
|
|
39
44
|
renameSync, rmSync, statSync, writeFileSync, writeSync,
|
|
40
45
|
} from 'node:fs';
|
|
41
46
|
import { dirname, join } from 'node:path';
|
|
@@ -463,6 +468,40 @@ export function unpackInWorker(data, { signal, onProgress = () => {}, onStep = (
|
|
|
463
468
|
});
|
|
464
469
|
}
|
|
465
470
|
|
|
471
|
+
/**
|
|
472
|
+
* The release file an installed app carries (`source`), copied into `destination` (the attempt's own work folder)
|
|
473
|
+
* and compared with the pin as it is copied: its size, then its SHA-256. What is unpacked is the copy, so nothing
|
|
474
|
+
* can change the file between the check and the read. A file that does not match is removed and named.
|
|
475
|
+
*/
|
|
476
|
+
export async function copyPinned({ source, pin, destination, signal, onProgress = () => {} }) {
|
|
477
|
+
const hash = createHash('sha256');
|
|
478
|
+
let size = 0;
|
|
479
|
+
const count = new Transform({
|
|
480
|
+
transform(chunk, encoding, done) {
|
|
481
|
+
size += chunk.length;
|
|
482
|
+
if (size > pin.size) { done(new GitBashError('checksum', `${source} is larger than the pinned ${pin.file}.`)); return; }
|
|
483
|
+
hash.update(chunk);
|
|
484
|
+
onProgress({ received: size, total: pin.size });
|
|
485
|
+
done(null, chunk);
|
|
486
|
+
},
|
|
487
|
+
});
|
|
488
|
+
try {
|
|
489
|
+
await pipeline(createReadStream(source, { signal }), count, createWriteStream(destination, { flags: 'wx', mode: 0o600 }), { signal });
|
|
490
|
+
} catch (error) {
|
|
491
|
+
// A file that was there already is not this attempt's to remove.
|
|
492
|
+
if (error?.code !== 'EEXIST') rmSync(destination, { force: true });
|
|
493
|
+
if (signal?.aborted) throw cancelled();
|
|
494
|
+
if (error instanceof GitBashError) throw error;
|
|
495
|
+
throw new GitBashError('disk', `The Git for Windows release file this app carries could not be read (${error?.code || error?.message || error}).`);
|
|
496
|
+
}
|
|
497
|
+
const sha256 = hash.digest('hex');
|
|
498
|
+
if (size !== pin.size || sha256 !== pin.sha256) {
|
|
499
|
+
rmSync(destination, { force: true });
|
|
500
|
+
throw new GitBashError('checksum', `${source} is not the pinned ${pin.file} (${size} bytes, sha256 ${sha256}).`, { detail: sha256 });
|
|
501
|
+
}
|
|
502
|
+
return { size, sha256 };
|
|
503
|
+
}
|
|
504
|
+
|
|
466
505
|
/** Renaming a folder on Windows can meet a file an antivirus scanner holds open for a moment: try again, briefly. */
|
|
467
506
|
async function renameWithRetry(from, to, { tries = 6, wait = (ms) => new Promise((done) => setTimeout(done, ms)) } = {}) {
|
|
468
507
|
for (let attempt = 1; ; attempt++) {
|
|
@@ -489,9 +528,10 @@ async function renameWithRetry(from, to, { tries = 6, wait = (ms) => new Promise
|
|
|
489
528
|
* @param {(p: { phase: string, received?: number, total: number, consumed?: number }) => void} [o.onProgress]
|
|
490
529
|
* @param {boolean} [o.worker] unpack in a worker thread (true for the server; tests and builds run it inline)
|
|
491
530
|
* @param {Function} [o.unpack] what unpacks (data, { signal, onProgress, onStep }): unpackInWorker or unpackAndCheck
|
|
531
|
+
* @param {string|null} [o.archive] the release file an installed app carries: used instead of a download
|
|
492
532
|
* @returns {Promise<{ path: string, version: string }>}
|
|
493
533
|
*/
|
|
494
|
-
export async function installGitBash({ pins, pin, root, fetchImpl, signal, onStep = () => {}, onProgress = () => {}, stallMs, worker = true, unpack = null, platform = process.platform, licensesDir }) {
|
|
534
|
+
export async function installGitBash({ pins, pin, root, fetchImpl, signal, onStep = () => {}, onProgress = () => {}, stallMs, worker = true, unpack = null, platform = process.platform, licensesDir, archive = null }) {
|
|
495
535
|
const directory = join(root, pin.version);
|
|
496
536
|
let work = null;
|
|
497
537
|
try {
|
|
@@ -501,12 +541,18 @@ export async function installGitBash({ pins, pin, root, fetchImpl, signal, onSte
|
|
|
501
541
|
work = join(root, `${WORK_PREFIX}${process.pid}-${randomBytes(6).toString('hex')}`);
|
|
502
542
|
mkdirSync(work, { mode: 0o700 });
|
|
503
543
|
const releaseFile = join(work, 'release.part');
|
|
504
|
-
|
|
505
|
-
|
|
544
|
+
if (archive) {
|
|
545
|
+
// The release file this app carries: copied and checked, then unpacked like a download.
|
|
546
|
+
onStep('extract');
|
|
547
|
+
await copyPinned({ source: archive, pin, destination: releaseFile, signal });
|
|
548
|
+
} else {
|
|
549
|
+
onStep('download');
|
|
550
|
+
await downloadPinned({ pin, destination: releaseFile, fetchImpl, signal, stallMs, onProgress: ({ received, total }) => onProgress({ phase: 'download', received, total }) });
|
|
551
|
+
}
|
|
506
552
|
if (signal?.aborted) throw cancelled();
|
|
507
553
|
const tree = join(work, 'tree');
|
|
508
554
|
mkdirSync(tree);
|
|
509
|
-
const options = { archive: releaseFile, destination: tree, pins, pin, source: 'downloaded', platform, licensesDir };
|
|
555
|
+
const options = { archive: releaseFile, destination: tree, pins, pin, source: archive ? 'archive' : 'downloaded', platform, licensesDir };
|
|
510
556
|
const report = ({ consumed, total }) => onProgress({ phase: 'extract', consumed, total });
|
|
511
557
|
const run = unpack || (worker
|
|
512
558
|
? unpackInWorker
|
|
@@ -7,23 +7,31 @@
|
|
|
7
7
|
// Claude Code started by SynaBun get" (resolver.js) and, on a PC that has
|
|
8
8
|
// none, puts SynaBun's own copy of Git for Windows in place:
|
|
9
9
|
//
|
|
10
|
-
// bundled inside an installed app: <resources>\git
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
10
|
+
// bundled inside an installed app of an earlier build: <resources>\git
|
|
11
|
+
// downloaded DATA_HOME/runtime/git/<version>/, outside data/ so no backup
|
|
12
|
+
// holds it: on an npm install fetched once from the pinned
|
|
13
|
+
// release (pins.js); in an installed app unpacked from the
|
|
14
|
+
// release file the Windows x64 installer carries
|
|
15
|
+
// (<resources>\git-archive\<file>; packaging/lib/git-bash.mjs),
|
|
16
|
+
// which keeps ~9,600 files out of every install and update. Either
|
|
17
|
+
// way checked and unpacked by install.js, and kept across updates
|
|
18
|
+
// (an update from a build with the tree moves that tree here:
|
|
19
|
+
// packaging/desktop/src/updater/git-adopt.mjs)
|
|
15
20
|
//
|
|
16
|
-
// It downloads
|
|
17
|
-
// and only when neither the PC nor the app
|
|
18
|
-
// start (ensure) or at the first Claude Code
|
|
19
|
-
// once per process.
|
|
21
|
+
// It downloads (or unpacks the carried release file) only on Windows, only for
|
|
22
|
+
// a processor a release is pinned for, and only when neither the PC nor the app
|
|
23
|
+
// has a Git Bash: at the server's start (ensure) or at the first Claude Code
|
|
24
|
+
// start that finds none (childEnv), once per process. Until it is in place a
|
|
25
|
+
// Claude Code started by SynaBun runs as it does on a PC without Git Bash (its
|
|
26
|
+
// PowerShell fallback); whenReady() waits for it, bounded. A failed download is not tried again by itself: Settings
|
|
20
27
|
// shows why and has the button that tries again. SYNABUN_GIT_BASH=off turns all
|
|
21
28
|
// of it off: no download, nothing added to any environment.
|
|
22
29
|
//
|
|
23
30
|
// resolution() the Git Bash in use (refreshed when older than half a minute)
|
|
24
31
|
// childEnv(env) the environment of a Claude Code process SynaBun starts
|
|
25
32
|
// gitExecutable() git.exe of SynaBun's copy when it is the one in use (SynaBun's own git features)
|
|
26
|
-
// ensure() download when there is no Git Bash at all; never rejects
|
|
33
|
+
// ensure() download (or unpack the carried file) when there is no Git Bash at all; never rejects
|
|
34
|
+
// whenReady(ms) resolves the status once no download or unpacking runs, or after `ms`
|
|
27
35
|
// status() what Settings shows: missing / downloading / ready / failed, and the rest
|
|
28
36
|
// onChange(fn) told when the Git Bash in use changes (the settings of the user's own `claude` follow it)
|
|
29
37
|
// housekeep() what an earlier run left in the data home: a stopped attempt's work folder, a version the
|
|
@@ -37,7 +45,7 @@ import { packagedRuntime } from '../../../lib/packaged-runtime.js';
|
|
|
37
45
|
import { pathEnvValue } from '../augmented-path.js';
|
|
38
46
|
import { sweepStaleWork } from '../cloudflared/install.js';
|
|
39
47
|
import { checkInstalled, GitBashError, installGitBash } from './install.js';
|
|
40
|
-
import { BUNDLED_FOLDER, gitBashPinFor, inTree, MARKER, readGitBashPins } from './pins.js';
|
|
48
|
+
import { ARCHIVE_FOLDER, BUNDLED_FOLDER, gitBashPinFor, inTree, MARKER, readGitBashPins } from './pins.js';
|
|
41
49
|
import { gitBashChildEnv, resolveGitBash } from './resolver.js';
|
|
42
50
|
|
|
43
51
|
const VERSION_FOLDER = /^\d+\.\d+\.\d+\.\d+$/;
|
|
@@ -100,6 +108,12 @@ export function createGitBashManager({
|
|
|
100
108
|
const root = gitRoot(dataHome);
|
|
101
109
|
const bundledRoot = platform === 'win32' && packaged?.resources ? join(packaged.resources, BUNDLED_FOLDER) : null;
|
|
102
110
|
const downloadedRoot = pin ? join(root, pin.version) : null;
|
|
111
|
+
// The release file an installed Windows app carries (packaging/lib/git-bash.mjs), unpacked instead of a download.
|
|
112
|
+
const carriedArchive = platform === 'win32' && pin && packaged?.resources ? join(packaged.resources, ARCHIVE_FOLDER, pin.file) : null;
|
|
113
|
+
const archiveThere = () => {
|
|
114
|
+
if (!carriedArchive) return false;
|
|
115
|
+
try { return statSync(carriedArchive).isFile(); } catch { return false; }
|
|
116
|
+
};
|
|
103
117
|
const ownRoots = [bundledRoot, root].filter(Boolean);
|
|
104
118
|
|
|
105
119
|
const verified = new Map(); // copy root → { sig, result }: a copy is hashed again only when its marker changes
|
|
@@ -110,6 +124,7 @@ export function createGitBashManager({
|
|
|
110
124
|
let lastError = null;
|
|
111
125
|
let attempted = false; // a download was started in this process (the first-need trigger fires once)
|
|
112
126
|
let settings = null; // what the last sync of the user's settings did (noteSettings)
|
|
127
|
+
let earlyStartLogged = false; // a Claude Code started while the copy was being put in place (said once)
|
|
113
128
|
|
|
114
129
|
/** SynaBun's own copies, checked: the app's, then the data home's. */
|
|
115
130
|
function copies() {
|
|
@@ -179,7 +194,7 @@ export function createGitBashManager({
|
|
|
179
194
|
return { swept, removed: await pruneOtherVersions() };
|
|
180
195
|
}
|
|
181
196
|
|
|
182
|
-
/** Download SynaBun's copy when there is no Git Bash at all. Resolves the status; never rejects. */
|
|
197
|
+
/** Download (or unpack the carried release file of) SynaBun's copy when there is no Git Bash at all. Resolves the status; never rejects. */
|
|
183
198
|
function ensure({ reason = 'start' } = {}) {
|
|
184
199
|
if (platform !== 'win32' || !pin || off) return Promise.resolve(status());
|
|
185
200
|
if (download) return download.promise.then(() => status());
|
|
@@ -187,12 +202,23 @@ export function createGitBashManager({
|
|
|
187
202
|
attempted = true;
|
|
188
203
|
lastError = null;
|
|
189
204
|
const stop = new AbortController();
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
pins, pin, root, fetchImpl, stallMs, worker, platform: host, signal: stop.signal,
|
|
205
|
+
const archive = archiveThere() ? carriedArchive : null;
|
|
206
|
+
const job = { stop, archive, step: archive ? 'extract' : 'download', progress: archive ? { phase: 'extract', consumed: 0, total: pin.size } : { phase: 'download', received: 0, total: pin.size } };
|
|
207
|
+
const run = (from) => install({
|
|
208
|
+
pins, pin, root, fetchImpl, stallMs, worker, platform: host, signal: stop.signal, archive: from,
|
|
194
209
|
onStep: (step) => { job.step = step; },
|
|
195
210
|
onProgress: (progress) => { job.progress = progress; },
|
|
211
|
+
});
|
|
212
|
+
if (archive) log(`[git-bash] No Git Bash on this PC (${reason}): unpacking Git for Windows ${pin.version} from the release file this app carries (${archive}) in the background`);
|
|
213
|
+
else log(`[git-bash] No Git Bash on this PC (${reason}): downloading Git for Windows ${pin.version} (${(pin.size / 1024 / 1024).toFixed(1)} MB) from ${pin.url}`);
|
|
214
|
+
job.promise = run(archive).catch((error) => {
|
|
215
|
+
// A carried file that cannot be used (damaged, unreadable): the pinned download is the same release.
|
|
216
|
+
if (!archive || stop.signal.aborted || error?.code === 'cancelled') throw error;
|
|
217
|
+
log(`[git-bash] The carried release file could not be used (${describe(error).code}): ${describe(error).message} Downloading it instead.`);
|
|
218
|
+
job.archive = null;
|
|
219
|
+
job.step = 'download';
|
|
220
|
+
job.progress = { phase: 'download', received: 0, total: pin.size };
|
|
221
|
+
return run(null);
|
|
196
222
|
}).then(async (placed) => {
|
|
197
223
|
log(`[git-bash] Git for Windows ${pin.version} is in place, release file and tree checked: ${placed.path}`);
|
|
198
224
|
verified.delete(downloadedRoot);
|
|
@@ -209,10 +235,24 @@ export function createGitBashManager({
|
|
|
209
235
|
return job.promise.then(() => status());
|
|
210
236
|
}
|
|
211
237
|
|
|
238
|
+
/** The status once nothing is being put in place, or after `timeoutMs`, whichever comes first. Never rejects. */
|
|
239
|
+
function whenReady(timeoutMs = 120_000) {
|
|
240
|
+
if (!download) return Promise.resolve(status());
|
|
241
|
+
const job = download;
|
|
242
|
+
return new Promise((done) => {
|
|
243
|
+
const timer = setTimeout(() => done(status()), Math.max(0, timeoutMs));
|
|
244
|
+
job.promise.then(() => { clearTimeout(timer); done(status()); }, () => { clearTimeout(timer); done(status()); });
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
212
248
|
function childEnv(base = env) {
|
|
213
249
|
const r = resolution();
|
|
214
250
|
// The first Claude Code start that finds no Git Bash at all fetches one (once per process; Settings retries).
|
|
215
251
|
if (platform === 'win32' && pin && !off && !r.source && !download && !attempted) ensure({ reason: 'first-need' });
|
|
252
|
+
if (platform === 'win32' && !r.source && download && !earlyStartLogged) {
|
|
253
|
+
earlyStartLogged = true;
|
|
254
|
+
log('[git-bash] A Claude Code started while Git for Windows is still being put in place: it runs without Git Bash (its PowerShell fallback) until the next start.');
|
|
255
|
+
}
|
|
216
256
|
return gitBashChildEnv(base, r, { platform });
|
|
217
257
|
}
|
|
218
258
|
|
|
@@ -240,7 +280,7 @@ export function createGitBashManager({
|
|
|
240
280
|
if (download) {
|
|
241
281
|
const { phase, received = 0, consumed = 0, total = 0 } = download.progress || {};
|
|
242
282
|
const done = phase === 'extract' ? consumed : received;
|
|
243
|
-
return { ...base, state: 'downloading', step: download.step, progress: { phase, done, total, percent: total ? Math.min(100, Math.floor((done / total) * 100)) : 0 } };
|
|
283
|
+
return { ...base, state: 'downloading', step: download.step, fromArchive: !!download.archive, progress: { phase, done, total, percent: total ? Math.min(100, Math.floor((done / total) * 100)) : 0 } };
|
|
244
284
|
}
|
|
245
285
|
if (r.source === 'bundled' || r.source === 'downloaded') return { ...base, state: 'ready' };
|
|
246
286
|
if (r.source) return { ...base, state: 'system' };
|
|
@@ -259,7 +299,9 @@ export function createGitBashManager({
|
|
|
259
299
|
childEnv,
|
|
260
300
|
gitExecutable,
|
|
261
301
|
ensure,
|
|
302
|
+
whenReady,
|
|
262
303
|
status,
|
|
304
|
+
carriedArchive,
|
|
263
305
|
housekeep,
|
|
264
306
|
cancel: () => { if (!download) return false; download.stop.abort(); return true; },
|
|
265
307
|
noteSettings: (result) => { settings = result || null; },
|
|
@@ -42,6 +42,13 @@ const SOURCE_TREE = 'https://github.com/git-for-windows/git/tree/';
|
|
|
42
42
|
/** The folder of a packaged application that holds its Git for Windows, beside `runtime/` and `app/`. */
|
|
43
43
|
export const BUNDLED_FOLDER = 'git';
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* The folder of a packaged Windows application that holds the pinned release file itself (`<file>` of the pin):
|
|
47
|
+
* one file in the installer instead of a tree of ten thousand, unpacked into the data home on first need
|
|
48
|
+
* (manager.js) and kept there across updates.
|
|
49
|
+
*/
|
|
50
|
+
export const ARCHIVE_FOLDER = 'git-archive';
|
|
51
|
+
|
|
45
52
|
/** What SynaBun puts at the top of its copy, beside what the release file holds (never part of the tree's digest). */
|
|
46
53
|
export const MARKER = 'synabun-git.json';
|
|
47
54
|
export const ADDED_FILES = Object.freeze(['LICENSE.txt', 'SOURCE.txt', MARKER]);
|
|
@@ -155,6 +162,11 @@ export function bundledGitRoot(resources, platform = process.platform) {
|
|
|
155
162
|
return (platform === 'win32' ? win32 : posix).join(resources, BUNDLED_FOLDER);
|
|
156
163
|
}
|
|
157
164
|
|
|
165
|
+
/** The release file a packaged application carries for `pin`: `<resources>/git-archive/<file>`. */
|
|
166
|
+
export function bundledGitArchive(resources, pin, platform = process.platform) {
|
|
167
|
+
return (platform === 'win32' ? win32 : posix).join(resources, ARCHIVE_FOLDER, pin.file);
|
|
168
|
+
}
|
|
169
|
+
|
|
158
170
|
/** LICENSE.txt of SynaBun's copy: Git's own license texts of the pinned tag, after a line that says what they cover. */
|
|
159
171
|
export function licenseText(dir = LICENSES_DIR) {
|
|
160
172
|
const copying = readFileSync(join(dir, 'COPYING'), 'utf8');
|
|
@@ -126,12 +126,12 @@ export function readsIndex(args) {
|
|
|
126
126
|
* @param {string[]} args the command and its arguments, the command first (['status', '--porcelain'])
|
|
127
127
|
* @param {{ monitor?: boolean }} [o] `monitor`: the repository has git's built-in file system monitor switched on
|
|
128
128
|
*/
|
|
129
|
-
export function gitReadArgs(args, { monitor = false } = {}) {
|
|
129
|
+
export function gitReadArgs(args, { monitor = false, nullDevice = devNull } = {}) {
|
|
130
130
|
const [command, ...rest] = Array.isArray(args) ? args : [];
|
|
131
131
|
const own = [
|
|
132
132
|
'--no-pager',
|
|
133
133
|
'-c', `core.fsmonitor=${monitor === true ? 'true' : 'false'}`,
|
|
134
|
-
'-c', `core.hooksPath=${
|
|
134
|
+
'-c', `core.hooksPath=${nullDevice}`,
|
|
135
135
|
'-c', 'log.showSignature=false',
|
|
136
136
|
];
|
|
137
137
|
if (command === undefined) return own;
|