@maudecode/talaria-web 0.0.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/contract_versions.json +18 -0
- package/dist/api/auth-raw.d.ts +9 -0
- package/dist/api/auth-raw.js +134 -0
- package/dist/api/auth-raw.js.map +1 -0
- package/dist/api/automation-raw.d.ts +16 -0
- package/dist/api/automation-raw.js +340 -0
- package/dist/api/automation-raw.js.map +1 -0
- package/dist/api/automation-router.d.ts +788 -0
- package/dist/api/automation-router.js +248 -0
- package/dist/api/automation-router.js.map +1 -0
- package/dist/api/chat-router.d.ts +375 -0
- package/dist/api/chat-router.js +446 -0
- package/dist/api/chat-router.js.map +1 -0
- package/dist/api/git-router.d.ts +681 -0
- package/dist/api/git-router.js +275 -0
- package/dist/api/git-router.js.map +1 -0
- package/dist/api/raw-routes.d.ts +10 -0
- package/dist/api/raw-routes.js +466 -0
- package/dist/api/raw-routes.js.map +1 -0
- package/dist/api/router.d.ts +226 -0
- package/dist/api/router.js +331 -0
- package/dist/api/router.js.map +1 -0
- package/dist/api/session-visibility.d.ts +12 -0
- package/dist/api/session-visibility.js +51 -0
- package/dist/api/session-visibility.js.map +1 -0
- package/dist/api/sessions-router.d.ts +1728 -0
- package/dist/api/sessions-router.js +815 -0
- package/dist/api/sessions-router.js.map +1 -0
- package/dist/api/settings-router.d.ts +592 -0
- package/dist/api/settings-router.js +474 -0
- package/dist/api/settings-router.js.map +1 -0
- package/dist/api/sse-routes.d.ts +51 -0
- package/dist/api/sse-routes.js +602 -0
- package/dist/api/sse-routes.js.map +1 -0
- package/dist/api/tools-raw.d.ts +5 -0
- package/dist/api/tools-raw.js +291 -0
- package/dist/api/tools-raw.js.map +1 -0
- package/dist/api/tools-router.d.ts +494 -0
- package/dist/api/tools-router.js +349 -0
- package/dist/api/tools-router.js.map +1 -0
- package/dist/app.d.ts +4787 -0
- package/dist/app.js +453 -0
- package/dist/app.js.map +1 -0
- package/dist/auth/cookies.d.ts +17 -0
- package/dist/auth/cookies.js +42 -0
- package/dist/auth/cookies.js.map +1 -0
- package/dist/auth/crypto.d.ts +9 -0
- package/dist/auth/crypto.js +38 -0
- package/dist/auth/crypto.js.map +1 -0
- package/dist/auth/gate.d.ts +39 -0
- package/dist/auth/gate.js +406 -0
- package/dist/auth/gate.js.map +1 -0
- package/dist/auth/oidc.d.ts +138 -0
- package/dist/auth/oidc.js +836 -0
- package/dist/auth/oidc.js.map +1 -0
- package/dist/auth/passkeys.d.ts +49 -0
- package/dist/auth/passkeys.js +310 -0
- package/dist/auth/passkeys.js.map +1 -0
- package/dist/auth/store.d.ts +116 -0
- package/dist/auth/store.js +485 -0
- package/dist/auth/store.js.map +1 -0
- package/dist/bin/talaria-web-mcp.d.ts +2 -0
- package/dist/bin/talaria-web-mcp.js +12 -0
- package/dist/bin/talaria-web-mcp.js.map +1 -0
- package/dist/bin/talaria-web.d.ts +2 -0
- package/dist/bin/talaria-web.js +87 -0
- package/dist/bin/talaria-web.js.map +1 -0
- package/dist/cli/ctl.d.ts +58 -0
- package/dist/cli/ctl.js +643 -0
- package/dist/cli/ctl.js.map +1 -0
- package/dist/cli/dotenv.d.ts +30 -0
- package/dist/cli/dotenv.js +115 -0
- package/dist/cli/dotenv.js.map +1 -0
- package/dist/cli/launcher.d.ts +49 -0
- package/dist/cli/launcher.js +213 -0
- package/dist/cli/launcher.js.map +1 -0
- package/dist/cli/supervise.d.ts +10 -0
- package/dist/cli/supervise.js +33 -0
- package/dist/cli/supervise.js.map +1 -0
- package/dist/cli/web-root.d.ts +2 -0
- package/dist/cli/web-root.js +23 -0
- package/dist/cli/web-root.js.map +1 -0
- package/dist/config/agent-config.d.ts +109 -0
- package/dist/config/agent-config.js +632 -0
- package/dist/config/agent-config.js.map +1 -0
- package/dist/config.d.ts +36 -0
- package/dist/config.js +105 -0
- package/dist/config.js.map +1 -0
- package/dist/fs/atomic.d.ts +7 -0
- package/dist/fs/atomic.js +68 -0
- package/dist/fs/atomic.js.map +1 -0
- package/dist/http/addresses.d.ts +5 -0
- package/dist/http/addresses.js +68 -0
- package/dist/http/addresses.js.map +1 -0
- package/dist/http/capped.d.ts +2 -0
- package/dist/http/capped.js +22 -0
- package/dist/http/capped.js.map +1 -0
- package/dist/http/context.d.ts +281 -0
- package/dist/http/context.js +327 -0
- package/dist/http/context.js.map +1 -0
- package/dist/http/csp.d.ts +11 -0
- package/dist/http/csp.js +51 -0
- package/dist/http/csp.js.map +1 -0
- package/dist/http/origin.d.ts +39 -0
- package/dist/http/origin.js +183 -0
- package/dist/http/origin.js.map +1 -0
- package/dist/http/pinned.d.ts +18 -0
- package/dist/http/pinned.js +62 -0
- package/dist/http/pinned.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/server.d.ts +29 -0
- package/dist/mcp/server.js +221 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/onboarding.d.ts +57 -0
- package/dist/onboarding.js +381 -0
- package/dist/onboarding.js.map +1 -0
- package/dist/profiles/profiles.d.ts +96 -0
- package/dist/profiles/profiles.js +476 -0
- package/dist/profiles/profiles.js.map +1 -0
- package/dist/projects.d.ts +36 -0
- package/dist/projects.js +102 -0
- package/dist/projects.js.map +1 -0
- package/dist/providers/catalog.d.ts +118 -0
- package/dist/providers/catalog.js +868 -0
- package/dist/providers/catalog.js.map +1 -0
- package/dist/providers/env-file.d.ts +6 -0
- package/dist/providers/env-file.js +106 -0
- package/dist/providers/env-file.js.map +1 -0
- package/dist/providers/runtime-env.d.ts +20 -0
- package/dist/providers/runtime-env.js +85 -0
- package/dist/providers/runtime-env.js.map +1 -0
- package/dist/providers/tables.d.ts +54 -0
- package/dist/providers/tables.js +54 -0
- package/dist/providers/tables.js.map +1 -0
- package/dist/redact.d.ts +23 -0
- package/dist/redact.js +337 -0
- package/dist/redact.js.map +1 -0
- package/dist/release.d.ts +16 -0
- package/dist/release.js +114 -0
- package/dist/release.js.map +1 -0
- package/dist/runtime.d.ts +21 -0
- package/dist/runtime.js +680 -0
- package/dist/runtime.js.map +1 -0
- package/dist/server.d.ts +20 -0
- package/dist/server.js +90 -0
- package/dist/server.js.map +1 -0
- package/dist/sessions/anchor.d.ts +43 -0
- package/dist/sessions/anchor.js +311 -0
- package/dist/sessions/anchor.js.map +1 -0
- package/dist/sessions/cli-sessions.d.ts +27 -0
- package/dist/sessions/cli-sessions.js +158 -0
- package/dist/sessions/cli-sessions.js.map +1 -0
- package/dist/sessions/completions.d.ts +62 -0
- package/dist/sessions/completions.js +332 -0
- package/dist/sessions/completions.js.map +1 -0
- package/dist/sessions/drafts.d.ts +26 -0
- package/dist/sessions/drafts.js +117 -0
- package/dist/sessions/drafts.js.map +1 -0
- package/dist/sessions/events.d.ts +32 -0
- package/dist/sessions/events.js +80 -0
- package/dist/sessions/events.js.map +1 -0
- package/dist/sessions/export.d.ts +4 -0
- package/dist/sessions/export.js +186 -0
- package/dist/sessions/export.js.map +1 -0
- package/dist/sessions/gateway-watcher.d.ts +58 -0
- package/dist/sessions/gateway-watcher.js +199 -0
- package/dist/sessions/gateway-watcher.js.map +1 -0
- package/dist/sessions/journal.d.ts +103 -0
- package/dist/sessions/journal.js +543 -0
- package/dist/sessions/journal.js.map +1 -0
- package/dist/sessions/list.d.ts +130 -0
- package/dist/sessions/list.js +846 -0
- package/dist/sessions/list.js.map +1 -0
- package/dist/sessions/merge.d.ts +57 -0
- package/dist/sessions/merge.js +517 -0
- package/dist/sessions/merge.js.map +1 -0
- package/dist/sessions/pending.d.ts +69 -0
- package/dist/sessions/pending.js +271 -0
- package/dist/sessions/pending.js.map +1 -0
- package/dist/sessions/reasoning-titles.d.ts +31 -0
- package/dist/sessions/reasoning-titles.js +143 -0
- package/dist/sessions/reasoning-titles.js.map +1 -0
- package/dist/sessions/relay.d.ts +131 -0
- package/dist/sessions/relay.js +577 -0
- package/dist/sessions/relay.js.map +1 -0
- package/dist/sessions/service.d.ts +189 -0
- package/dist/sessions/service.js +1344 -0
- package/dist/sessions/service.js.map +1 -0
- package/dist/sessions/session.d.ts +131 -0
- package/dist/sessions/session.js +505 -0
- package/dist/sessions/session.js.map +1 -0
- package/dist/sessions/shares.d.ts +31 -0
- package/dist/sessions/shares.js +267 -0
- package/dist/sessions/shares.js.map +1 -0
- package/dist/sessions/state-db.d.ts +52 -0
- package/dist/sessions/state-db.js +641 -0
- package/dist/sessions/state-db.js.map +1 -0
- package/dist/sessions/state-saved.d.ts +13 -0
- package/dist/sessions/state-saved.js +47 -0
- package/dist/sessions/state-saved.js.map +1 -0
- package/dist/sessions/store.d.ts +124 -0
- package/dist/sessions/store.js +790 -0
- package/dist/sessions/store.js.map +1 -0
- package/dist/sessions/streams.d.ts +90 -0
- package/dist/sessions/streams.js +200 -0
- package/dist/sessions/streams.js.map +1 -0
- package/dist/sessions/titles.d.ts +17 -0
- package/dist/sessions/titles.js +231 -0
- package/dist/sessions/titles.js.map +1 -0
- package/dist/sessions/todo.d.ts +8 -0
- package/dist/sessions/todo.js +72 -0
- package/dist/sessions/todo.js.map +1 -0
- package/dist/sessions/turn-context.d.ts +27 -0
- package/dist/sessions/turn-context.js +136 -0
- package/dist/sessions/turn-context.js.map +1 -0
- package/dist/sessions/turn.d.ts +178 -0
- package/dist/sessions/turn.js +1357 -0
- package/dist/sessions/turn.js.map +1 -0
- package/dist/sessions/window.d.ts +10 -0
- package/dist/sessions/window.js +151 -0
- package/dist/sessions/window.js.map +1 -0
- package/dist/settings.d.ts +57 -0
- package/dist/settings.js +481 -0
- package/dist/settings.js.map +1 -0
- package/dist/sidecar/client.d.ts +75 -0
- package/dist/sidecar/client.js +252 -0
- package/dist/sidecar/client.js.map +1 -0
- package/dist/sidecar/discover.d.ts +22 -0
- package/dist/sidecar/discover.js +146 -0
- package/dist/sidecar/discover.js.map +1 -0
- package/dist/sidecar/fake.d.ts +37 -0
- package/dist/sidecar/fake.js +79 -0
- package/dist/sidecar/fake.js.map +1 -0
- package/dist/spa.d.ts +42 -0
- package/dist/spa.js +145 -0
- package/dist/spa.js.map +1 -0
- package/dist/startup.d.ts +19 -0
- package/dist/startup.js +44 -0
- package/dist/startup.js.map +1 -0
- package/dist/test/harness.d.ts +41 -0
- package/dist/test/harness.js +123 -0
- package/dist/test/harness.js.map +1 -0
- package/dist/text/diff.d.ts +16 -0
- package/dist/text/diff.js +122 -0
- package/dist/text/diff.js.map +1 -0
- package/dist/text/markdown.d.ts +8 -0
- package/dist/text/markdown.js +185 -0
- package/dist/text/markdown.js.map +1 -0
- package/dist/tools/crons.d.ts +51 -0
- package/dist/tools/crons.js +317 -0
- package/dist/tools/crons.js.map +1 -0
- package/dist/tools/extensions.d.ts +92 -0
- package/dist/tools/extensions.js +1096 -0
- package/dist/tools/extensions.js.map +1 -0
- package/dist/tools/health.d.ts +21 -0
- package/dist/tools/health.js +334 -0
- package/dist/tools/health.js.map +1 -0
- package/dist/tools/hygiene.d.ts +29 -0
- package/dist/tools/hygiene.js +112 -0
- package/dist/tools/hygiene.js.map +1 -0
- package/dist/tools/insights.d.ts +3 -0
- package/dist/tools/insights.js +92 -0
- package/dist/tools/insights.js.map +1 -0
- package/dist/tools/kanban.d.ts +25 -0
- package/dist/tools/kanban.js +79 -0
- package/dist/tools/kanban.js.map +1 -0
- package/dist/tools/mcp-health.d.ts +28 -0
- package/dist/tools/mcp-health.js +207 -0
- package/dist/tools/mcp-health.js.map +1 -0
- package/dist/tools/mcp.d.ts +34 -0
- package/dist/tools/mcp.js +269 -0
- package/dist/tools/mcp.js.map +1 -0
- package/dist/tools/memory.d.ts +20 -0
- package/dist/tools/memory.js +216 -0
- package/dist/tools/memory.js.map +1 -0
- package/dist/tools/skills.d.ts +43 -0
- package/dist/tools/skills.js +193 -0
- package/dist/tools/skills.js.map +1 -0
- package/dist/tools/terminal.d.ts +96 -0
- package/dist/tools/terminal.js +324 -0
- package/dist/tools/terminal.js.map +1 -0
- package/dist/tools/updates.d.ts +151 -0
- package/dist/tools/updates.js +1151 -0
- package/dist/tools/updates.js.map +1 -0
- package/dist/util.d.ts +6 -0
- package/dist/util.js +40 -0
- package/dist/util.js.map +1 -0
- package/dist/workspace/fs.d.ts +61 -0
- package/dist/workspace/fs.js +555 -0
- package/dist/workspace/fs.js.map +1 -0
- package/dist/workspace/git.d.ts +204 -0
- package/dist/workspace/git.js +1236 -0
- package/dist/workspace/git.js.map +1 -0
- package/dist/workspace/media.d.ts +47 -0
- package/dist/workspace/media.js +448 -0
- package/dist/workspace/media.js.map +1 -0
- package/dist/workspace/paths.d.ts +6 -0
- package/dist/workspace/paths.js +41 -0
- package/dist/workspace/paths.js.map +1 -0
- package/dist/workspace/rollback.d.ts +29 -0
- package/dist/workspace/rollback.js +337 -0
- package/dist/workspace/rollback.js.map +1 -0
- package/dist/workspace/unzip.d.ts +9 -0
- package/dist/workspace/unzip.js +61 -0
- package/dist/workspace/unzip.js.map +1 -0
- package/dist/workspace/upload.d.ts +40 -0
- package/dist/workspace/upload.js +215 -0
- package/dist/workspace/upload.js.map +1 -0
- package/dist/workspace/workspaces.d.ts +84 -0
- package/dist/workspace/workspaces.js +663 -0
- package/dist/workspace/workspaces.js.map +1 -0
- package/dist/workspace/worktrees.d.ts +14 -0
- package/dist/workspace/worktrees.js +171 -0
- package/dist/workspace/worktrees.js.map +1 -0
- package/dist/workspace/zip.d.ts +16 -0
- package/dist/workspace/zip.js +205 -0
- package/dist/workspace/zip.js.map +1 -0
- package/package.json +54 -0
- package/sidecar/agent_dependency.json +11 -0
- package/sidecar/scripts/record-fixtures.py +72 -0
- package/sidecar/scripts/replay_sidecar.py +113 -0
- package/sidecar/scripts/test.sh +22 -0
- package/sidecar/talaria_sidecar/__init__.py +10 -0
- package/sidecar/talaria_sidecar/__main__.py +46 -0
- package/sidecar/talaria_sidecar/errors.py +41 -0
- package/sidecar/talaria_sidecar/home.py +103 -0
- package/sidecar/talaria_sidecar/methods/__init__.py +37 -0
- package/sidecar/talaria_sidecar/methods/aux.py +160 -0
- package/sidecar/talaria_sidecar/methods/chat.py +791 -0
- package/sidecar/talaria_sidecar/methods/commands.py +340 -0
- package/sidecar/talaria_sidecar/methods/config.py +131 -0
- package/sidecar/talaria_sidecar/methods/cron.py +558 -0
- package/sidecar/talaria_sidecar/methods/gateway.py +106 -0
- package/sidecar/talaria_sidecar/methods/goals.py +225 -0
- package/sidecar/talaria_sidecar/methods/kanban.py +723 -0
- package/sidecar/talaria_sidecar/methods/mcp.py +65 -0
- package/sidecar/talaria_sidecar/methods/process.py +155 -0
- package/sidecar/talaria_sidecar/methods/profiles.py +254 -0
- package/sidecar/talaria_sidecar/methods/providers.py +241 -0
- package/sidecar/talaria_sidecar/methods/runtime.py +68 -0
- package/sidecar/talaria_sidecar/methods/skills.py +262 -0
- package/sidecar/talaria_sidecar/methods/state_db.py +418 -0
- package/sidecar/talaria_sidecar/methods/stt.py +131 -0
- package/sidecar/talaria_sidecar/methods/text.py +85 -0
- package/sidecar/talaria_sidecar/methods/usage.py +59 -0
- package/sidecar/talaria_sidecar/methods/worktree.py +71 -0
- package/sidecar/talaria_sidecar/rpc.py +218 -0
- package/sidecar/talaria_sidecar/runtime.py +330 -0
- package/static/brand/apple-touch-icon.png +0 -0
- package/static/brand/brandmark.svg +12 -0
- package/static/brand/favicon-192.png +0 -0
- package/static/brand/favicon-32.png +0 -0
- package/static/brand/favicon-512.png +0 -0
- package/static/brand/favicon-512.svg +14 -0
- package/static/brand/favicon-dark.svg +14 -0
- package/static/brand/favicon-light.svg +14 -0
- package/static/brand/favicon.ico +0 -0
- package/static/brand/favicon.svg +14 -0
- package/static/dist/FILES.txt +515 -0
- package/static/dist/assets/AppShell-gs0grlbs.js +1 -0
- package/static/dist/assets/ChatPage-gi1xlz52.js +18 -0
- package/static/dist/assets/ChatPage-gmu5c7lf.css +1 -0
- package/static/dist/assets/HubRoute-qs9vzuh0.js +2 -0
- package/static/dist/assets/InterVariable-nonokl6i.woff2 +0 -0
- package/static/dist/assets/KaTeX_AMS-Regular-e1why8ff.woff2 +0 -0
- package/static/dist/assets/KaTeX_AMS-Regular-mec5w1mt.woff +0 -0
- package/static/dist/assets/KaTeX_AMS-Regular-mos1wgtv.ttf +0 -0
- package/static/dist/assets/KaTeX_Caligraphic-Bold-axibvb5b.ttf +0 -0
- package/static/dist/assets/KaTeX_Caligraphic-Bold-eccvbn5c.woff +0 -0
- package/static/dist/assets/KaTeX_Caligraphic-Bold-n63xiolk.woff2 +0 -0
- package/static/dist/assets/KaTeX_Caligraphic-Regular-c5dvswgu.ttf +0 -0
- package/static/dist/assets/KaTeX_Caligraphic-Regular-iz5gexw5.woff +0 -0
- package/static/dist/assets/KaTeX_Caligraphic-Regular-npwmqylf.woff2 +0 -0
- package/static/dist/assets/KaTeX_Fraktur-Bold-ftsn4fxp.ttf +0 -0
- package/static/dist/assets/KaTeX_Fraktur-Bold-gokmhdpe.woff +0 -0
- package/static/dist/assets/KaTeX_Fraktur-Bold-ikhebgtj.woff2 +0 -0
- package/static/dist/assets/KaTeX_Fraktur-Regular-hzce8q9o.ttf +0 -0
- package/static/dist/assets/KaTeX_Fraktur-Regular-i0egury6.woff2 +0 -0
- package/static/dist/assets/KaTeX_Fraktur-Regular-okwm110b.woff +0 -0
- package/static/dist/assets/KaTeX_Main-Bold-c5g2m5kh.ttf +0 -0
- package/static/dist/assets/KaTeX_Main-Bold-ktk38ybk.woff2 +0 -0
- package/static/dist/assets/KaTeX_Main-Bold-ureqgmzo.woff +0 -0
- package/static/dist/assets/KaTeX_Main-BoldItalic-bd0upj71.woff +0 -0
- package/static/dist/assets/KaTeX_Main-BoldItalic-oj033t4i.woff2 +0 -0
- package/static/dist/assets/KaTeX_Main-BoldItalic-opttanob.ttf +0 -0
- package/static/dist/assets/KaTeX_Main-Italic-2p4bq1jf.woff2 +0 -0
- package/static/dist/assets/KaTeX_Main-Italic-dj86j9xo.ttf +0 -0
- package/static/dist/assets/KaTeX_Main-Italic-esmxqs8d.woff +0 -0
- package/static/dist/assets/KaTeX_Main-Regular-c978jrkj.ttf +0 -0
- package/static/dist/assets/KaTeX_Main-Regular-hbkzldb8.woff2 +0 -0
- package/static/dist/assets/KaTeX_Main-Regular-n865mmt0.woff +0 -0
- package/static/dist/assets/KaTeX_Math-BoldItalic-cbkxiib1.woff +0 -0
- package/static/dist/assets/KaTeX_Math-BoldItalic-hcom2j4y.ttf +0 -0
- package/static/dist/assets/KaTeX_Math-BoldItalic-jdo1yxu8.woff2 +0 -0
- package/static/dist/assets/KaTeX_Math-Italic-b5lfnctb.ttf +0 -0
- package/static/dist/assets/KaTeX_Math-Italic-cz4b2ure.woff2 +0 -0
- package/static/dist/assets/KaTeX_Math-Italic-lo894bvj.woff +0 -0
- package/static/dist/assets/KaTeX_SansSerif-Bold-h55uj19s.ttf +0 -0
- package/static/dist/assets/KaTeX_SansSerif-Bold-m9bkcp3c.woff +0 -0
- package/static/dist/assets/KaTeX_SansSerif-Bold-otxc8itm.woff2 +0 -0
- package/static/dist/assets/KaTeX_SansSerif-Italic-bp85cocg.ttf +0 -0
- package/static/dist/assets/KaTeX_SansSerif-Italic-g42k16nn.woff +0 -0
- package/static/dist/assets/KaTeX_SansSerif-Italic-k4kksncm.woff2 +0 -0
- package/static/dist/assets/KaTeX_SansSerif-Regular-evq9548m.ttf +0 -0
- package/static/dist/assets/KaTeX_SansSerif-Regular-izeggh93.woff +0 -0
- package/static/dist/assets/KaTeX_SansSerif-Regular-ltw53ck4.woff2 +0 -0
- package/static/dist/assets/KaTeX_Script-Regular-k8v4ghws.ttf +0 -0
- package/static/dist/assets/KaTeX_Script-Regular-o2nd302f.woff +0 -0
- package/static/dist/assets/KaTeX_Script-Regular-oybd33cp.woff2 +0 -0
- package/static/dist/assets/KaTeX_Size1-Regular-cjccv44r.woff2 +0 -0
- package/static/dist/assets/KaTeX_Size1-Regular-k13yopgz.woff +0 -0
- package/static/dist/assets/KaTeX_Size1-Regular-naiuhmx7.ttf +0 -0
- package/static/dist/assets/KaTeX_Size2-Regular-cnnxwlt7.woff +0 -0
- package/static/dist/assets/KaTeX_Size2-Regular-hlh6jazj.ttf +0 -0
- package/static/dist/assets/KaTeX_Size2-Regular-onxq3bzc.woff2 +0 -0
- package/static/dist/assets/KaTeX_Size3-Regular-i00haf83.woff +0 -0
- package/static/dist/assets/KaTeX_Size3-Regular-nk2kx36d.ttf +0 -0
- package/static/dist/assets/KaTeX_Size4-Regular-effsfy3y.woff +0 -0
- package/static/dist/assets/KaTeX_Size4-Regular-myjqyche.ttf +0 -0
- package/static/dist/assets/KaTeX_Size4-Regular-nv9nppzf.woff2 +0 -0
- package/static/dist/assets/KaTeX_Typewriter-Regular-iqvr3vwu.woff2 +0 -0
- package/static/dist/assets/KaTeX_Typewriter-Regular-kzj4h457.woff +0 -0
- package/static/dist/assets/KaTeX_Typewriter-Regular-owzsy8p5.ttf +0 -0
- package/static/dist/assets/Markdown-bt9bqd6a.js +598 -0
- package/static/dist/assets/Markdown-lwviulmg.css +1 -0
- package/static/dist/assets/RightPanel-dn19xu42.js +1 -0
- package/static/dist/assets/Select-y7wtf85g.js +1 -0
- package/static/dist/assets/SessionListPanel-igwdj9xr.js +1 -0
- package/static/dist/assets/SettingsLayout-n6mu0ghg.js +1 -0
- package/static/dist/assets/States-oil2issf.js +1 -0
- package/static/dist/assets/Toaster-jtn59pjm.js +1 -0
- package/static/dist/assets/_app-k4jrkgfa.js +1 -0
- package/static/dist/assets/_app.ext._extensionId-mmf1mepm.js +1 -0
- package/static/dist/assets/_app.index-mb41nf00.js +1 -0
- package/static/dist/assets/_app.insights-mz5cep8m.js +1 -0
- package/static/dist/assets/_app.kanban-ocgw506i.js +1 -0
- package/static/dist/assets/_app.logs-lstmoju6.js +1 -0
- package/static/dist/assets/_app.memory-dishj9ra.js +1 -0
- package/static/dist/assets/_app.profiles-dynpvsio.js +1 -0
- package/static/dist/assets/_app.session._sessionId-kevi4qd2.js +1 -0
- package/static/dist/assets/_app.settings-c2czm9v4.js +1 -0
- package/static/dist/assets/_app.settings._section-exwflld7.js +1 -0
- package/static/dist/assets/_app.skills-j5jh2nug.js +1 -0
- package/static/dist/assets/_app.tasks-b99d98fo.js +2 -0
- package/static/dist/assets/_app.todos-ma8urvn3.js +1 -0
- package/static/dist/assets/_app.workspaces-l86cxeod.js +1 -0
- package/static/dist/assets/abap-ij37pki6.js +1 -0
- package/static/dist/assets/abnfDiagram-VCTEODGH-hvrr25lb.js +1 -0
- package/static/dist/assets/actionscript-3-dz7y3j1x.js +1 -0
- package/static/dist/assets/ada-k9zgv987.js +1 -0
- package/static/dist/assets/andromeeda-c2n0n86o.js +1 -0
- package/static/dist/assets/angular-html-nu6l31w3.js +1 -0
- package/static/dist/assets/angular-ts-fbx4f8mm.js +1 -0
- package/static/dist/assets/apache-binppq6z.js +1 -0
- package/static/dist/assets/apex-h8illjly.js +1 -0
- package/static/dist/assets/apl-bx2xr7dh.js +1 -0
- package/static/dist/assets/applescript-h0ok5sww.js +1 -0
- package/static/dist/assets/ara-dlpjioua.js +1 -0
- package/static/dist/assets/arc-7popfhbk.js +1 -0
- package/static/dist/assets/architecture-7GRP2DOG-mqft9w2p.js +1 -0
- package/static/dist/assets/architectureDiagram-5GKGNRK7-b3r0z8sq.js +36 -0
- package/static/dist/assets/array-f36850hm.js +1 -0
- package/static/dist/assets/asciidoc-lx0cuujr.js +1 -0
- package/static/dist/assets/asm-j5diz3sz.js +1 -0
- package/static/dist/assets/astro-g77i97cb.js +1 -0
- package/static/dist/assets/aurora-x-h2hngdsl.js +1 -0
- package/static/dist/assets/awk-fecghegf.js +1 -0
- package/static/dist/assets/ayu-dark-nvvuggz9.js +1 -0
- package/static/dist/assets/ayu-light-k5fxgplt.js +1 -0
- package/static/dist/assets/ayu-mirage-gltrvhcx.js +1 -0
- package/static/dist/assets/ballerina-hk9oga18.js +1 -0
- package/static/dist/assets/bat-ghs7lag5.js +1 -0
- package/static/dist/assets/beancount-pc6q2dmq.js +1 -0
- package/static/dist/assets/berry-l96jp8n4.js +1 -0
- package/static/dist/assets/bibtex-jxn7maz2.js +1 -0
- package/static/dist/assets/bicep-i1yv3ngd.js +1 -0
- package/static/dist/assets/bird2-k3aagqn0.js +1 -0
- package/static/dist/assets/blade-boo0u92y.js +1 -0
- package/static/dist/assets/blockDiagram-I7D4REHJ-hd3gw0cj.js +129 -0
- package/static/dist/assets/bsl-f8m1ja7w.js +1 -0
- package/static/dist/assets/c-cat0w3sc.js +1 -0
- package/static/dist/assets/c3-j6ile59o.js +1 -0
- package/static/dist/assets/c4Diagram-7LVT6UL2-jlmbjiji.js +38 -0
- package/static/dist/assets/cadence-e3x2pgsu.js +1 -0
- package/static/dist/assets/cairo-mblac9dt.js +1 -0
- package/static/dist/assets/catppuccin-frappe-dj7qsd4s.js +1 -0
- package/static/dist/assets/catppuccin-latte-oh2bx7qn.js +1 -0
- package/static/dist/assets/catppuccin-macchiato-m3xyazph.js +1 -0
- package/static/dist/assets/catppuccin-mocha-m3rjj89j.js +1 -0
- package/static/dist/assets/channel-ix8cxdld.js +1 -0
- package/static/dist/assets/chunk-2Q5K7J3B-k08c29qw.js +1 -0
- package/static/dist/assets/chunk-4HAMMTFA-oydbpsin.js +62 -0
- package/static/dist/assets/chunk-5VM5RSS4-br0zxsux.js +15 -0
- package/static/dist/assets/chunk-75Z2AOVW-j633otj1.js +2 -0
- package/static/dist/assets/chunk-DU6HZSFF-ods2l027.js +128 -0
- package/static/dist/assets/chunk-F27PBJKO-ln4n2cwt.js +1 -0
- package/static/dist/assets/chunk-FOHPRMQF-opuflp73.js +161 -0
- package/static/dist/assets/chunk-GMAD6QVW-fpz133eh.js +72 -0
- package/static/dist/assets/chunk-GVQU2GXP-abs9ixiy.js +1 -0
- package/static/dist/assets/chunk-IMKFNOWR-j9vukdy1.js +231 -0
- package/static/dist/assets/chunk-JWPE2WC7-mw03m5s9.js +1 -0
- package/static/dist/assets/chunk-L3NEJ4N5-cfyw87r3.js +1 -0
- package/static/dist/assets/chunk-OSK3NFVY-baja78wr.js +10 -0
- package/static/dist/assets/chunk-P2QGCYS3-g66xby8h.js +1 -0
- package/static/dist/assets/chunk-POPQ4Y6H-osqu864t.js +1 -0
- package/static/dist/assets/chunk-PWAF6VOD-egejasv1.js +1 -0
- package/static/dist/assets/chunk-SHT3W25Y-l014h8zz.js +168 -0
- package/static/dist/assets/chunk-SVP7TREG-n3z10bp3.js +88 -0
- package/static/dist/assets/chunk-TICWLB2K-bzozkjc0.js +206 -0
- package/static/dist/assets/chunk-XXDRQBXY-n40pf9vy.js +1 -0
- package/static/dist/assets/chunk-Y2CYZVJY-n9gs7k4d.js +1 -0
- package/static/dist/assets/clarity-bdn1d8fx.js +1 -0
- package/static/dist/assets/classDiagram-ZZMXUADV-c210y3il.js +1 -0
- package/static/dist/assets/classDiagram-v2-VYDZK3BY-c210y3il.js +1 -0
- package/static/dist/assets/clojure-n5b9o4lq.js +1 -0
- package/static/dist/assets/cmake-f68p68h5.js +1 -0
- package/static/dist/assets/cobol-go622aep.js +1 -0
- package/static/dist/assets/codeowners-lgfjmw2s.js +1 -0
- package/static/dist/assets/codeql-cojlysyb.js +1 -0
- package/static/dist/assets/coffee-jwo1xjhq.js +1 -0
- package/static/dist/assets/common-lisp-ko58h8pe.js +1 -0
- package/static/dist/assets/copied-cjsttec1.js +1 -0
- package/static/dist/assets/coq-gns70ukp.js +1 -0
- package/static/dist/assets/cose-bilkent-JH36ORCC-eclcy6ja.js +1 -0
- package/static/dist/assets/cpp-nre7j0kq.js +1 -0
- package/static/dist/assets/crystal-lviso2je.js +1 -0
- package/static/dist/assets/csharp-kkz9hcxp.js +1 -0
- package/static/dist/assets/css-ywz9j4hb.js +1 -0
- package/static/dist/assets/csv-ol0sxk26.js +1 -0
- package/static/dist/assets/cue-h5naoluy.js +1 -0
- package/static/dist/assets/cynefin-OW5HDTMX-elpt7h1j.js +1 -0
- package/static/dist/assets/cynefinDiagram-5FMLGOSQ-5mzl5pi2.js +62 -0
- package/static/dist/assets/cypher-j2an42no.js +1 -0
- package/static/dist/assets/cytoscape.esm-bqu53u12.js +321 -0
- package/static/dist/assets/d-crxkca3p.js +1 -0
- package/static/dist/assets/dagre-GXQ25YYZ-ihwlhmfl.js +4 -0
- package/static/dist/assets/dagre-lkoknbjn.js +1 -0
- package/static/dist/assets/dark-plus-kin19s9x.js +1 -0
- package/static/dist/assets/dart-nsg0xvqh.js +1 -0
- package/static/dist/assets/dax-f82777dv.js +1 -0
- package/static/dist/assets/defaultLocale-eeoev87l.js +1 -0
- package/static/dist/assets/desktop-nvg88bea.js +1 -0
- package/static/dist/assets/diagram-S7CK7UJ4-4z90agaz.js +30 -0
- package/static/dist/assets/diagram-UQ7AKVKN-eifw0lg7.js +41 -0
- package/static/dist/assets/diagram-VSXAHHWV-ksh3sjqv.js +3 -0
- package/static/dist/assets/diagram-VX7I27RA-ett72f71.js +24 -0
- package/static/dist/assets/diagram-Z3DM3KII-l8scp8nu.js +24 -0
- package/static/dist/assets/diff-c5xjoml2.js +1 -0
- package/static/dist/assets/dist-c4c2m3kv.js +1 -0
- package/static/dist/assets/docker-s0pf2u92.js +1 -0
- package/static/dist/assets/dotenv-d2gvqxc1.js +1 -0
- package/static/dist/assets/dracula-eicfho73.js +1 -0
- package/static/dist/assets/dracula-soft-doslejd2.js +1 -0
- package/static/dist/assets/dream-maker-mz8d3pqb.js +1 -0
- package/static/dist/assets/ebnfDiagram-PWID7BFC-f0pxcmit.js +1 -0
- package/static/dist/assets/edge-dvv35byi.js +1 -0
- package/static/dist/assets/elixir-i2f1ki1g.js +1 -0
- package/static/dist/assets/elm-cpepyjm4.js +1 -0
- package/static/dist/assets/emacs-lisp-lhl63kmy.js +1 -0
- package/static/dist/assets/endpoints-ipnv1hd4.js +64 -0
- package/static/dist/assets/erDiagram-RLTQ6QDP-f47m5xh0.js +99 -0
- package/static/dist/assets/erb-idlyfheg.js +1 -0
- package/static/dist/assets/erlang-kbk8e1kh.js +1 -0
- package/static/dist/assets/eventmodeling-NTZA5JFV-d9b5a0ia.js +1 -0
- package/static/dist/assets/everforest-dark-cv4maom7.js +1 -0
- package/static/dist/assets/everforest-light-njdw7fx6.js +1 -0
- package/static/dist/assets/fennel-mm8r79co.js +1 -0
- package/static/dist/assets/fish-em0xdaat.js +1 -0
- package/static/dist/assets/flowDiagram-HODETNUW-kfxxsyrk.js +1 -0
- package/static/dist/assets/fluent-kzq1mil2.js +1 -0
- package/static/dist/assets/fortran-fixed-form-lwddxd99.js +1 -0
- package/static/dist/assets/fortran-free-form-jao3bzvo.js +1 -0
- package/static/dist/assets/fsharp-ouam7w4k.js +1 -0
- package/static/dist/assets/ganttDiagram-EL5Y4UJY-bmktjdi0.js +292 -0
- package/static/dist/assets/gdresource-gawivel9.js +1 -0
- package/static/dist/assets/gdscript-kcamxf4c.js +1 -0
- package/static/dist/assets/gdshader-hx54new5.js +1 -0
- package/static/dist/assets/genie-i5n3m91v.js +1 -0
- package/static/dist/assets/gherkin-lxn181dy.js +1 -0
- package/static/dist/assets/git-commit-e6qgtuh5.js +1 -0
- package/static/dist/assets/git-rebase-d9lpfpbn.js +1 -0
- package/static/dist/assets/gitGraph-4MIJSDKK-j2ueg258.js +1 -0
- package/static/dist/assets/gitGraphDiagram-WWUBYQGX-ms6586v9.js +106 -0
- package/static/dist/assets/github-dark-default-m0qs6qki.js +1 -0
- package/static/dist/assets/github-dark-dimmed-g0vz4qee.js +1 -0
- package/static/dist/assets/github-dark-high-contrast-hugubo52.js +1 -0
- package/static/dist/assets/github-dark-ljlyim8s.js +1 -0
- package/static/dist/assets/github-light-default-fgfak5mv.js +1 -0
- package/static/dist/assets/github-light-high-contrast-hka6wb3r.js +1 -0
- package/static/dist/assets/github-light-jhyi45t4.js +1 -0
- package/static/dist/assets/gleam-ix1dym3g.js +1 -0
- package/static/dist/assets/glimmer-js-39i0urfb.js +1 -0
- package/static/dist/assets/glimmer-ts-gdtd6n73.js +1 -0
- package/static/dist/assets/glsl-o1djm5pp.js +1 -0
- package/static/dist/assets/gn-cbzhsgl9.js +1 -0
- package/static/dist/assets/gnuplot-dr8f9gvm.js +1 -0
- package/static/dist/assets/go-enhtrrw8.js +1 -0
- package/static/dist/assets/graphlib-mrng1xrk.js +1 -0
- package/static/dist/assets/graphql-kj6rz55t.js +1 -0
- package/static/dist/assets/groovy-jff59t9e.js +1 -0
- package/static/dist/assets/gruvbox-dark-hard-lgsj7y5r.js +1 -0
- package/static/dist/assets/gruvbox-dark-medium-ezt86wrc.js +1 -0
- package/static/dist/assets/gruvbox-dark-soft-1a3jfdlm.js +1 -0
- package/static/dist/assets/gruvbox-light-hard-d82lddjc.js +1 -0
- package/static/dist/assets/gruvbox-light-medium-d3ffitv3.js +1 -0
- package/static/dist/assets/gruvbox-light-soft-e5gf7i2q.js +1 -0
- package/static/dist/assets/hack-pe1qqr2i.js +1 -0
- package/static/dist/assets/haml-cknnbcqs.js +1 -0
- package/static/dist/assets/handlebars-hwq64orh.js +1 -0
- package/static/dist/assets/haskell-o7nor2z2.js +1 -0
- package/static/dist/assets/haxe-4rwdh42h.js +1 -0
- package/static/dist/assets/hcl-nnng763h.js +1 -0
- package/static/dist/assets/highlighted-body-KPVGNVTW-ev52d2si.js +1 -0
- package/static/dist/assets/hjson-kscwmuzk.js +1 -0
- package/static/dist/assets/hlsl-kopkvwox.js +1 -0
- package/static/dist/assets/horizon-bright-gn4aqinx.js +1 -0
- package/static/dist/assets/horizon-h5nzrwlb.js +1 -0
- package/static/dist/assets/houston-kifsbmbx.js +1 -0
- package/static/dist/assets/html-derivative-h2i2pyjc.js +1 -0
- package/static/dist/assets/html-ha05xtvt.js +1 -0
- package/static/dist/assets/http-cf94zu27.js +1 -0
- package/static/dist/assets/hurl-lslax568.js +1 -0
- package/static/dist/assets/hxml-g52gxet6.js +1 -0
- package/static/dist/assets/hy-jc9www1l.js +1 -0
- package/static/dist/assets/imba-n9x1ej2f.js +1 -0
- package/static/dist/assets/index-bz7g7rhx.css +2 -0
- package/static/dist/assets/index-hywfjw88.js +11 -0
- package/static/dist/assets/info-A6RAGUB7-j717vyt7.js +1 -0
- package/static/dist/assets/infoDiagram-27XIBGKW-dsbpib36.js +2 -0
- package/static/dist/assets/ini-hg6eosim.js +1 -0
- package/static/dist/assets/init-ljpdkjra.js +1 -0
- package/static/dist/assets/ishikawaDiagram-5VMMS53U-n0sz3i2d.js +70 -0
- package/static/dist/assets/java-ndjf2o89.js +1 -0
- package/static/dist/assets/javascript-coftlwy3.js +1 -0
- package/static/dist/assets/jinja-etei9wy7.js +1 -0
- package/static/dist/assets/jison-hyekgft1.js +1 -0
- package/static/dist/assets/journeyDiagram-3NMN7TZE-mcxz1mme.js +139 -0
- package/static/dist/assets/json-lok0yaho.js +1 -0
- package/static/dist/assets/json5-e4tr07tg.js +1 -0
- package/static/dist/assets/jsonc-jbmkhoep.js +1 -0
- package/static/dist/assets/jsonl-j35ke6at.js +1 -0
- package/static/dist/assets/jsonnet-iewuy86o.js +1 -0
- package/static/dist/assets/jssm-m1449zlc.js +1 -0
- package/static/dist/assets/jsx-b64w0nbx.js +1 -0
- package/static/dist/assets/jsx-runtime-eu92a5pu.js +1 -0
- package/static/dist/assets/julia-jxxzz2yn.js +1 -0
- package/static/dist/assets/just-mc29iw9t.js +1 -0
- package/static/dist/assets/kanagawa-dragon-i9mmmgud.js +1 -0
- package/static/dist/assets/kanagawa-lotus-ev5oro17.js +1 -0
- package/static/dist/assets/kanagawa-wave-i06560uw.js +1 -0
- package/static/dist/assets/kanban-definition-UXKFOSKX-n6hq2lyc.js +89 -0
- package/static/dist/assets/kdl-kgzv1x9e.js +1 -0
- package/static/dist/assets/kotlin-nmx05b3f.js +1 -0
- package/static/dist/assets/kusto-fawmdms4.js +1 -0
- package/static/dist/assets/laserwave-goebpayr.js +1 -0
- package/static/dist/assets/latex-pafa03ft.js +1 -0
- package/static/dist/assets/lazyRouteComponent-faquioun.js +1 -0
- package/static/dist/assets/lean-jom31nm3.js +1 -0
- package/static/dist/assets/less-mwvscr3d.js +1 -0
- package/static/dist/assets/light-plus-mws1g85f.js +1 -0
- package/static/dist/assets/line-h5xq20s4.js +1 -0
- package/static/dist/assets/linear-becwlnhk.js +1 -0
- package/static/dist/assets/link-ib4prsn5.js +1 -0
- package/static/dist/assets/liquid-hz5c2pov.js +1 -0
- package/static/dist/assets/llvm-j5wmr3g9.js +1 -0
- package/static/dist/assets/log-eur4gurm.js +1 -0
- package/static/dist/assets/login-mvjys1ol.js +1 -0
- package/static/dist/assets/logo-j3h8r0pu.js +1 -0
- package/static/dist/assets/lua-leyf8mlb.js +1 -0
- package/static/dist/assets/luau-im5d6q8k.js +1 -0
- package/static/dist/assets/make-npl27xqy.js +1 -0
- package/static/dist/assets/markdown-fibv6lrm.js +1 -0
- package/static/dist/assets/marko-cn1fzpdv.js +1 -0
- package/static/dist/assets/matchContext-f8oy3lo7.js +1 -0
- package/static/dist/assets/material-theme-darker-dhm8zmok.js +1 -0
- package/static/dist/assets/material-theme-gdimc61s.js +1 -0
- package/static/dist/assets/material-theme-lighter-c1fszbb2.js +1 -0
- package/static/dist/assets/material-theme-ocean-iakojpmy.js +1 -0
- package/static/dist/assets/material-theme-palenight-hgxmykmi.js +1 -0
- package/static/dist/assets/matlab-o6nvvbf2.js +1 -0
- package/static/dist/assets/mdc-huhy88df.js +1 -0
- package/static/dist/assets/mdx-mmgeh26h.js +1 -0
- package/static/dist/assets/mermaid-HWGCJPDP-gmnkgf1n.js +1 -0
- package/static/dist/assets/mermaid-f9ad87lb.js +1 -0
- package/static/dist/assets/mermaid-parser.core-n9cuu5ue.js +7 -0
- package/static/dist/assets/min-dark-e5sicuw2.js +1 -0
- package/static/dist/assets/min-light-gst87fvf.js +1 -0
- package/static/dist/assets/mindmap-definition-YA3MSWOX-gta3u7z4.js +96 -0
- package/static/dist/assets/mipsasm-etoqqos6.js +1 -0
- package/static/dist/assets/mojo-fyyk0b1z.js +1 -0
- package/static/dist/assets/monokai-jl389a9o.js +1 -0
- package/static/dist/assets/moonbit-je86hbgx.js +1 -0
- package/static/dist/assets/move-g7w7k042.js +1 -0
- package/static/dist/assets/narrat-d1cszt6i.js +1 -0
- package/static/dist/assets/nextflow-fpd3eqgm.js +1 -0
- package/static/dist/assets/nextflow-groovy-nc95p6pm.js +1 -0
- package/static/dist/assets/nginx-e7lvhsmn.js +1 -0
- package/static/dist/assets/night-owl-light-b2j1bc8t.js +1 -0
- package/static/dist/assets/night-owl-nm3cgqfv.js +1 -0
- package/static/dist/assets/nim-dun5d8mk.js +1 -0
- package/static/dist/assets/nix-sxazvl0w.js +1 -0
- package/static/dist/assets/nord-jiiendyo.js +1 -0
- package/static/dist/assets/not-found-ccnnac39.js +1 -0
- package/static/dist/assets/nushell-nbi9xmdg.js +1 -0
- package/static/dist/assets/objective-c-oshe4ycx.js +1 -0
- package/static/dist/assets/objective-cpp-go29ofq1.js +1 -0
- package/static/dist/assets/ocaml-56kivtnr.js +1 -0
- package/static/dist/assets/odin-g772bhi5.js +1 -0
- package/static/dist/assets/onboarding-l6quy6gz.js +1 -0
- package/static/dist/assets/one-dark-pro-ext74l87.js +1 -0
- package/static/dist/assets/one-light-o5mlsn8i.js +1 -0
- package/static/dist/assets/openscad-e9e9mfyg.js +1 -0
- package/static/dist/assets/ordinal-d88pfdla.js +1 -0
- package/static/dist/assets/packet-AYTQ26CC-bpzuq5n6.js +1 -0
- package/static/dist/assets/pascal-dmg2rs6d.js +1 -0
- package/static/dist/assets/passkeys-ktie3sr0.js +1 -0
- package/static/dist/assets/path-b509aej0.js +1 -0
- package/static/dist/assets/pegDiagram-XKGWAZYB-juye13e5.js +1 -0
- package/static/dist/assets/perl-bj3oqtet.js +1 -0
- package/static/dist/assets/php-buzp12r0.js +1 -0
- package/static/dist/assets/pie-WAS4IAKB-odhb8buq.js +1 -0
- package/static/dist/assets/pieDiagram-E7YTZNPT-mgb14ksh.js +39 -0
- package/static/dist/assets/pkl-co2grztk.js +1 -0
- package/static/dist/assets/plastic-mm7c7uq7.js +1 -0
- package/static/dist/assets/plsql-l0i9f33n.js +1 -0
- package/static/dist/assets/po-f3ga9osn.js +1 -0
- package/static/dist/assets/poimandres-mnwq5v4d.js +1 -0
- package/static/dist/assets/polar-ldicpe6g.js +1 -0
- package/static/dist/assets/postcss-fgpvnt75.js +1 -0
- package/static/dist/assets/powerquery-mflxo46x.js +1 -0
- package/static/dist/assets/powershell-oado4mxi.js +1 -0
- package/static/dist/assets/prepaint-f14kbd49.js +21 -0
- package/static/dist/assets/prisma-go1e7wq8.js +1 -0
- package/static/dist/assets/prolog-cbjal8aj.js +1 -0
- package/static/dist/assets/proto-lrhov7xc.js +1 -0
- package/static/dist/assets/pug-mjad7he9.js +1 -0
- package/static/dist/assets/puppet-h22szrmn.js +1 -0
- package/static/dist/assets/purescript-dwpj7hdb.js +1 -0
- package/static/dist/assets/python-b767fgue.js +1 -0
- package/static/dist/assets/qml-d1gdp10s.js +1 -0
- package/static/dist/assets/qmldir-lsawched.js +1 -0
- package/static/dist/assets/qss-lyvmg4f4.js +1 -0
- package/static/dist/assets/quadrantDiagram-AXDQQJYC-cg36naog.js +7 -0
- package/static/dist/assets/r-o3r9c7ao.js +1 -0
- package/static/dist/assets/racket-nbfqjw5h.js +1 -0
- package/static/dist/assets/radar-RG4KPBEZ-lgzfpnef.js +1 -0
- package/static/dist/assets/railroad-74A4TZTK-gmglc3ua.js +1 -0
- package/static/dist/assets/railroad-abnf-HS5TGJTU-5mjl5wpv.js +1 -0
- package/static/dist/assets/railroad-ebnf-LZEXJU2U-bfram8v8.js +1 -0
- package/static/dist/assets/railroad-peg-WCYAUIDC-nwfczn88.js +1 -0
- package/static/dist/assets/railroadDiagram-O6MQD6OU-cmxwjt73.js +1 -0
- package/static/dist/assets/raku-hcy6uwm6.js +1 -0
- package/static/dist/assets/razor-ilc585yr.js +1 -0
- package/static/dist/assets/red-if4jt303.js +1 -0
- package/static/dist/assets/refresh-cw-mf192q3f.js +1 -0
- package/static/dist/assets/reg-ivi9d1my.js +1 -0
- package/static/dist/assets/regexp-mpjtcvwm.js +1 -0
- package/static/dist/assets/rel-gqpkg3ta.js +1 -0
- package/static/dist/assets/requirementDiagram-BXWQKSXE-ir0s2mek.js +84 -0
- package/static/dist/assets/riscv-j1f2yjp2.js +1 -0
- package/static/dist/assets/rolldown-runtime-b9miacsj.js +1 -0
- package/static/dist/assets/ron-bjqasg78.js +1 -0
- package/static/dist/assets/root-mbklpuv4.js +1 -0
- package/static/dist/assets/rose-pine-dawn-nlpzqzob.js +1 -0
- package/static/dist/assets/rose-pine-grpwpg67.js +1 -0
- package/static/dist/assets/rose-pine-moon-b9yyevxr.js +1 -0
- package/static/dist/assets/rosmsg-hv3wcot6.js +1 -0
- package/static/dist/assets/rough.esm-on4k4je3.js +1 -0
- package/static/dist/assets/rst-jrkwjdyi.js +1 -0
- package/static/dist/assets/ruby-n4tjcbpz.js +1 -0
- package/static/dist/assets/rust-jqduclx1.js +1 -0
- package/static/dist/assets/sankeyDiagram-P5KCCOFB-jo7jcdq0.js +40 -0
- package/static/dist/assets/sas-hjo6y93k.js +1 -0
- package/static/dist/assets/sass-m1yncbwu.js +1 -0
- package/static/dist/assets/saved-g777hp1t.js +1 -0
- package/static/dist/assets/scala-l9ahg4e9.js +1 -0
- package/static/dist/assets/scheme-mlodf39l.js +1 -0
- package/static/dist/assets/scss-ncidjv48.js +1 -0
- package/static/dist/assets/sdbl-bwg2t5pt.js +1 -0
- package/static/dist/assets/search-i2kfogyx.js +1 -0
- package/static/dist/assets/sequenceDiagram-WJ2MYXX4-n0pf6xk5.js +162 -0
- package/static/dist/assets/settings_section_extensions_title-hgvh507l.js +1 -0
- package/static/dist/assets/shaderlab-be89ch6g.js +1 -0
- package/static/dist/assets/share._token-il3y6js2.js +2 -0
- package/static/dist/assets/shellscript-m8whjpqd.js +1 -0
- package/static/dist/assets/shellsession-i5bj6syz.js +1 -0
- package/static/dist/assets/sizeCapture-INFHLROL-g612l48j.js +1 -0
- package/static/dist/assets/slack-dark-ny9l0r7b.js +1 -0
- package/static/dist/assets/slack-ochin-g98102hs.js +1 -0
- package/static/dist/assets/sliders-horizontal-kd99ffvl.js +1 -0
- package/static/dist/assets/smalltalk-g9lpwisn.js +1 -0
- package/static/dist/assets/snazzy-light-dlu9q2m5.js +1 -0
- package/static/dist/assets/solarized-dark-mx1nd4sm.js +1 -0
- package/static/dist/assets/solarized-light-mqzd33fz.js +1 -0
- package/static/dist/assets/solidity-ih313ji4.js +1 -0
- package/static/dist/assets/soy-hhh36ysq.js +1 -0
- package/static/dist/assets/sparql-pewjbauq.js +1 -0
- package/static/dist/assets/splunk-d8q89juf.js +1 -0
- package/static/dist/assets/sql-j21bzcl6.js +1 -0
- package/static/dist/assets/src-hjyib4mu.js +1 -0
- package/static/dist/assets/ssh-config-fzxlymn8.js +1 -0
- package/static/dist/assets/stata-bbu2onh1.js +1 -0
- package/static/dist/assets/stateDiagram-D77RDMKH-olbrwyv9.js +1 -0
- package/static/dist/assets/stateDiagram-v2-MP3YSRHH-dnzlmdpu.js +1 -0
- package/static/dist/assets/stylus-hifjg0ye.js +1 -0
- package/static/dist/assets/surrealql-l5vczuco.js +1 -0
- package/static/dist/assets/svelte-idn422g6.js +1 -0
- package/static/dist/assets/swift-n11rc3v3.js +1 -0
- package/static/dist/assets/swimlanes-42K2YHIH-od14zrw6.js +1 -0
- package/static/dist/assets/swimlanesDiagram-VR7AAH4N-ck0eqqdp.js +8 -0
- package/static/dist/assets/synthwave-84-clkt7319.js +1 -0
- package/static/dist/assets/system-verilog-l8k1po2l.js +1 -0
- package/static/dist/assets/systemd-d1zoxedb.js +1 -0
- package/static/dist/assets/talonscript-j9gt5qb1.js +1 -0
- package/static/dist/assets/tasl-meer3lyk.js +1 -0
- package/static/dist/assets/tcl-jdc54rsg.js +1 -0
- package/static/dist/assets/templ-e22qou3c.js +1 -0
- package/static/dist/assets/terraform-oav3wzsy.js +1 -0
- package/static/dist/assets/tex-deqpc5ee.js +1 -0
- package/static/dist/assets/timeline-definition-24CTP7MA-l0qokmnb.js +120 -0
- package/static/dist/assets/todoStore-la6uyqzj.js +1 -0
- package/static/dist/assets/tokyo-night-cnyqlmc2.js +1 -0
- package/static/dist/assets/toml-jj1ee1k1.js +1 -0
- package/static/dist/assets/trash-bxz1k9cw.js +1 -0
- package/static/dist/assets/treeView-Q6P3EWNA-hma3ip8m.js +1 -0
- package/static/dist/assets/treemap-WGGIJYW6-bghjng5m.js +1 -0
- package/static/dist/assets/ts-tags-ctnibc27.js +1 -0
- package/static/dist/assets/tsv-cxbgj4wh.js +1 -0
- package/static/dist/assets/tsx-i163n32g.js +1 -0
- package/static/dist/assets/turtle-g1kfgpwv.js +1 -0
- package/static/dist/assets/twig-x0fi7wm8.js +1 -0
- package/static/dist/assets/typescript-g18c34xz.js +1 -0
- package/static/dist/assets/typespec-hokgy7ic.js +1 -0
- package/static/dist/assets/typst-l6lt2vta.js +1 -0
- package/static/dist/assets/untitled-nfaw0q7m.js +1 -0
- package/static/dist/assets/useForm-k7fj2zsr.js +1 -0
- package/static/dist/assets/useLocale-evhacmv2.js +1 -0
- package/static/dist/assets/useParams-gpbjxj8s.js +1 -0
- package/static/dist/assets/useSearch-bhdptos7.js +1 -0
- package/static/dist/assets/useSelector-f2aak1ol.js +1 -0
- package/static/dist/assets/v-lwnsi8rl.js +1 -0
- package/static/dist/assets/vala-db1h2kp7.js +1 -0
- package/static/dist/assets/vb-nrezymby.js +1 -0
- package/static/dist/assets/vennDiagram-4TSXK5OY-bo97zf3z.js +34 -0
- package/static/dist/assets/verilog-jworpiw4.js +1 -0
- package/static/dist/assets/vesper-o1vwtlg2.js +1 -0
- package/static/dist/assets/vhdl-gnn4vfnm.js +1 -0
- package/static/dist/assets/viml-ogfqutxv.js +1 -0
- package/static/dist/assets/vitesse-black-b5y62t71.js +1 -0
- package/static/dist/assets/vitesse-dark-fj1ju380.js +1 -0
- package/static/dist/assets/vitesse-light-bjycd3ha.js +1 -0
- package/static/dist/assets/vue-html-nskj3du0.js +1 -0
- package/static/dist/assets/vue-mwu5l1bb.js +1 -0
- package/static/dist/assets/vue-vine-cqe77smq.js +1 -0
- package/static/dist/assets/vyper-jsmo764l.js +1 -0
- package/static/dist/assets/wardley-WFR3VGLG-pbjl6jav.js +1 -0
- package/static/dist/assets/wardleyDiagram-VM6X3IG4-bmy9i09l.js +78 -0
- package/static/dist/assets/wasm-g1zrybde.js +1 -0
- package/static/dist/assets/wasm-gezzbmz0.js +1 -0
- package/static/dist/assets/wenyan-lgcd7t1n.js +1 -0
- package/static/dist/assets/wgsl-gotoh48n.js +1 -0
- package/static/dist/assets/wikitext-j1379ez7.js +1 -0
- package/static/dist/assets/wit-neu67rwu.js +1 -0
- package/static/dist/assets/wolfram-mbb1p6oi.js +1 -0
- package/static/dist/assets/xml-gau7gvrv.js +1 -0
- package/static/dist/assets/xsl-ew09zq8b.js +1 -0
- package/static/dist/assets/xychartDiagram-S5SC5T6Z-eercu0mc.js +7 -0
- package/static/dist/assets/yaml-ofnrxtvf.js +1 -0
- package/static/dist/assets/zenscript-ge1h0r83.js +1 -0
- package/static/dist/assets/zig-ik06evde.js +1 -0
- package/static/dist/extension-sdk.js +1 -0
- package/static/dist/index.html +1 -0
- package/static/dist/manifest.webmanifest +29 -0
- package/static/dist/sw.js +944 -0
|
@@ -0,0 +1,1236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git helpers for the workspace panel (Python `api/workspace_git.py`).
|
|
3
|
+
*
|
|
4
|
+
* The browser only sends session ids and workspace-relative paths. This module
|
|
5
|
+
* resolves the workspace server-side, scopes paths before they become Git
|
|
6
|
+
* pathspecs, and keeps every Git subprocess shell-free, env-scrubbed, hardened
|
|
7
|
+
* against repo-local config, and bounded by a timeout.
|
|
8
|
+
*/
|
|
9
|
+
import { spawnSync } from 'node:child_process';
|
|
10
|
+
import { closeSync, existsSync, fstatSync, lstatSync, mkdtempSync, mkdirSync, readFileSync, realpathSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
11
|
+
import { tmpdir } from 'node:os';
|
|
12
|
+
import { dirname, join, relative, resolve, sep } from 'node:path';
|
|
13
|
+
import { isWithin, resolvePathLikePython } from './paths.js';
|
|
14
|
+
import { openAnchoredFd, rmtreeAnchored, safeResolveWs, unlinkAnchored } from './fs.js';
|
|
15
|
+
import { str } from '../util.js';
|
|
16
|
+
export const GIT_TIMEOUT_MS = 5_000;
|
|
17
|
+
export const GIT_REMOTE_TIMEOUT_MS = 60_000;
|
|
18
|
+
export const STATUS_FILE_LIMIT = 500;
|
|
19
|
+
export const STATUS_CACHE_TTL_MS = 2_000;
|
|
20
|
+
export const STATUS_CACHE_LIMIT = 32;
|
|
21
|
+
export const DIFF_SIZE_LIMIT = 512 * 1024;
|
|
22
|
+
export const COMMIT_MESSAGE_DIFF_LIMIT = 64 * 1024;
|
|
23
|
+
export const WORKSPACE_GIT_DESTRUCTIVE_ENV = 'HERMES_WEBUI_WORKSPACE_GIT_DESTRUCTIVE';
|
|
24
|
+
const GIT_ENV_SCRUB_KEYS = ['GIT_DIR', 'GIT_WORK_TREE', 'GIT_CONFIG_GLOBAL', 'GIT_CONFIG_SYSTEM', 'GIT_CONFIG_COUNT', 'GIT_CONFIG_PARAMETERS', 'GIT_ASKPASS', 'SSH_ASKPASS', 'GIT_SSH', 'GIT_SSH_COMMAND'];
|
|
25
|
+
const GIT_ENV_SCRUB_PREFIXES = ['GIT_CONFIG_KEY_', 'GIT_CONFIG_VALUE_'];
|
|
26
|
+
const BRANCH_SWITCH_STASH_PREFIX = 'hermes-webui branch switch';
|
|
27
|
+
const GIT_HARDENED_CONFIG = [
|
|
28
|
+
['core.fsmonitor', 'false'],
|
|
29
|
+
['core.sshCommand', 'ssh'],
|
|
30
|
+
['core.askPass', ''],
|
|
31
|
+
['credential.helper', ''],
|
|
32
|
+
['protocol.ext.allow', 'never'],
|
|
33
|
+
['core.gitProxy', ''],
|
|
34
|
+
['submodule.recurse', 'false'],
|
|
35
|
+
['fetch.recurseSubmodules', 'false'],
|
|
36
|
+
];
|
|
37
|
+
const GIT_DESTRUCTIVE_HARDENED_CONFIG = [
|
|
38
|
+
['commit.gpgSign', 'false'],
|
|
39
|
+
['push.gpgSign', 'false'],
|
|
40
|
+
['gpg.program', ''],
|
|
41
|
+
['gpg.ssh.program', ''],
|
|
42
|
+
['gpg.x509.program', ''],
|
|
43
|
+
['core.alternateRefsCommand', ''],
|
|
44
|
+
];
|
|
45
|
+
const CONFLICT_CODES = new Set(['DD', 'AU', 'UD', 'UA', 'DU', 'AA', 'UU']);
|
|
46
|
+
export class GitWorkspaceError extends Error {
|
|
47
|
+
code;
|
|
48
|
+
constructor(message, code = 'git_failed') {
|
|
49
|
+
super(message);
|
|
50
|
+
this.code = code;
|
|
51
|
+
this.name = 'GitWorkspaceError';
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export function classifyGitError(message, args = []) {
|
|
55
|
+
const text = message.toLowerCase();
|
|
56
|
+
const joined = args.join(' ').toLowerCase();
|
|
57
|
+
if (text.includes('timed out'))
|
|
58
|
+
return 'timeout';
|
|
59
|
+
if (text.includes('not installed') || text.includes("no such file or directory: 'git'"))
|
|
60
|
+
return 'missing_git';
|
|
61
|
+
if (text.includes('not a git repository'))
|
|
62
|
+
return 'not_a_repo';
|
|
63
|
+
if (text.includes('outside the workspace') || text.includes('outside the git repository'))
|
|
64
|
+
return 'path_outside_workspace';
|
|
65
|
+
if (text.includes('authentication failed') || text.includes('permission denied') || text.includes('could not read username'))
|
|
66
|
+
return 'auth_failed';
|
|
67
|
+
if (text.includes('no upstream') || text.includes('no configured push destination') || text.includes('has no upstream branch'))
|
|
68
|
+
return 'no_upstream';
|
|
69
|
+
if (text.includes('non-fast-forward') || text.includes('fetch first') || (text.includes('rejected') && joined.includes('push')))
|
|
70
|
+
return 'non_fast_forward';
|
|
71
|
+
if (text.includes('conflict') || text.includes('unmerged') || (text.includes('merge') && text.includes('needs')))
|
|
72
|
+
return 'conflict';
|
|
73
|
+
if (text.includes('working tree') && (text.includes('clean') || text.includes('dirty')))
|
|
74
|
+
return 'dirty_worktree';
|
|
75
|
+
if (text.includes('local changes') || text.includes('would be overwritten by checkout'))
|
|
76
|
+
return 'dirty_worktree';
|
|
77
|
+
if (text.includes('invalid reference') || text.includes('not a valid') || text.includes('unknown revision'))
|
|
78
|
+
return 'invalid_ref';
|
|
79
|
+
if (text.includes('hook'))
|
|
80
|
+
return 'hook_failed';
|
|
81
|
+
return 'git_failed';
|
|
82
|
+
}
|
|
83
|
+
/** Unified diff of `[]` → `lines` (Python `difflib.unified_diff` for an empty left side). */
|
|
84
|
+
function unifiedDiffFromEmpty(lines, toFile) {
|
|
85
|
+
if (!lines.length)
|
|
86
|
+
return [];
|
|
87
|
+
// `difflib` writes `+1` for a single-line range and `+1,N` otherwise.
|
|
88
|
+
return [`--- /dev/null`, `+++ ${toFile}`, `@@ -0,0 +${lines.length === 1 ? '1' : `1,${String(lines.length)}`} @@`, ...lines.map((l) => `+${l}`)];
|
|
89
|
+
}
|
|
90
|
+
function diffStats(diff) {
|
|
91
|
+
let additions = 0;
|
|
92
|
+
let deletions = 0;
|
|
93
|
+
for (const line of diff.split('\n')) {
|
|
94
|
+
if (line.startsWith('+++') || line.startsWith('---'))
|
|
95
|
+
continue;
|
|
96
|
+
if (line.startsWith('+'))
|
|
97
|
+
additions += 1;
|
|
98
|
+
else if (line.startsWith('-'))
|
|
99
|
+
deletions += 1;
|
|
100
|
+
}
|
|
101
|
+
return [additions, deletions];
|
|
102
|
+
}
|
|
103
|
+
function splitLines(text) {
|
|
104
|
+
// Python `str.splitlines()`: no trailing empty element for a final newline.
|
|
105
|
+
const lines = text.split(/\r\n|\r|\n/);
|
|
106
|
+
if (lines.length && lines[lines.length - 1] === '')
|
|
107
|
+
lines.pop();
|
|
108
|
+
return lines;
|
|
109
|
+
}
|
|
110
|
+
export class GitRunner {
|
|
111
|
+
deps;
|
|
112
|
+
locks = new Map();
|
|
113
|
+
statusCache = new Map();
|
|
114
|
+
generations = new Map();
|
|
115
|
+
constructor(deps) {
|
|
116
|
+
this.deps = deps;
|
|
117
|
+
}
|
|
118
|
+
destructiveEnabled() {
|
|
119
|
+
return ['1', 'true', 'yes', 'on'].includes((this.deps.env[WORKSPACE_GIT_DESTRUCTIVE_ENV] ?? '').trim().toLowerCase());
|
|
120
|
+
}
|
|
121
|
+
cleanEnv(extra) {
|
|
122
|
+
const env = {};
|
|
123
|
+
for (const [k, v] of Object.entries(this.deps.env))
|
|
124
|
+
if (v !== undefined)
|
|
125
|
+
env[k] = v;
|
|
126
|
+
Object.assign(env, extra ?? {});
|
|
127
|
+
for (const key of GIT_ENV_SCRUB_KEYS)
|
|
128
|
+
Reflect.deleteProperty(env, key);
|
|
129
|
+
for (const key of Object.keys(env))
|
|
130
|
+
if (GIT_ENV_SCRUB_PREFIXES.some((p) => key.startsWith(p)))
|
|
131
|
+
Reflect.deleteProperty(env, key);
|
|
132
|
+
env.GIT_TERMINAL_PROMPT = '0';
|
|
133
|
+
return env;
|
|
134
|
+
}
|
|
135
|
+
now() {
|
|
136
|
+
return this.deps.now ? this.deps.now() * 1000 : Date.now();
|
|
137
|
+
}
|
|
138
|
+
// ── subprocess ───────────────────────────────────────────────────────────
|
|
139
|
+
rawGit(cwd, argv, env, timeoutMs, input) {
|
|
140
|
+
return spawnSync('git', argv, { cwd, env, encoding: 'utf8', timeout: timeoutMs, maxBuffer: 64 * 1024 * 1024, windowsHide: true, ...(input !== undefined ? { input } : {}) });
|
|
141
|
+
}
|
|
142
|
+
configNamesForScope(scope, cwd, env, pattern, nameRe, ignoreUnsupported = false) {
|
|
143
|
+
const result = this.rawGit(cwd, ['config', '--includes', scope, '--name-only', '--get-regexp', pattern], env, GIT_TIMEOUT_MS);
|
|
144
|
+
const names = new Set();
|
|
145
|
+
if (result.status !== 0 && result.status !== 1) {
|
|
146
|
+
if (ignoreUnsupported)
|
|
147
|
+
return names;
|
|
148
|
+
const message = (result.stderr || result.stdout || 'Git command failed').trim();
|
|
149
|
+
throw new GitWorkspaceError(message, classifyGitError(message, ['config']));
|
|
150
|
+
}
|
|
151
|
+
for (const line of (result.stdout || '').split('\n')) {
|
|
152
|
+
const m = nameRe.exec(line.trim());
|
|
153
|
+
if (m?.[1])
|
|
154
|
+
names.add(m[1]);
|
|
155
|
+
}
|
|
156
|
+
return names;
|
|
157
|
+
}
|
|
158
|
+
namesLocalAndWorktree(cwd, env, pattern, nameRe) {
|
|
159
|
+
const names = this.configNamesForScope('--local', cwd, env, pattern, nameRe);
|
|
160
|
+
for (const n of this.configNamesForScope('--worktree', cwd, env, pattern, nameRe, true))
|
|
161
|
+
names.add(n);
|
|
162
|
+
return names;
|
|
163
|
+
}
|
|
164
|
+
filterNames(cwd, env) {
|
|
165
|
+
return this.namesLocalAndWorktree(cwd, env, String.raw `^filter\..*\.(clean|smudge|process|required)$`, /^filter\.(.+)\.(clean|smudge|process|required)$/);
|
|
166
|
+
}
|
|
167
|
+
mergeDriverNames(cwd, env) {
|
|
168
|
+
return this.namesLocalAndWorktree(cwd, env, String.raw `^merge\..*\.driver$`, /^merge\.(.+)\.driver$/);
|
|
169
|
+
}
|
|
170
|
+
remoteHelperNames(cwd, env) {
|
|
171
|
+
return this.namesLocalAndWorktree(cwd, env, String.raw `^remote\..*\.(uploadpack|receivepack)$`, /^remote\.(.+)\.(uploadpack|receivepack)$/);
|
|
172
|
+
}
|
|
173
|
+
static legal(name) {
|
|
174
|
+
return !name.includes('\n') && !name.includes('\0');
|
|
175
|
+
}
|
|
176
|
+
hasRepoLocalFilters(cwd) {
|
|
177
|
+
return this.filterNames(cwd, this.cleanEnv()).size > 0;
|
|
178
|
+
}
|
|
179
|
+
run(ctxOrCwd, args, opts = {}) {
|
|
180
|
+
const cwd = typeof ctxOrCwd === 'string' ? ctxOrCwd : ctxOrCwd.repoRoot;
|
|
181
|
+
const runEnv = this.cleanEnv(opts.env);
|
|
182
|
+
const effectiveDestructive = Boolean(opts.destructive) && this.destructiveEnabled();
|
|
183
|
+
const hardenedDestructive = effectiveDestructive || Boolean(opts.forceDestructiveHardening);
|
|
184
|
+
let attributesFile = null;
|
|
185
|
+
let hooksPath = null;
|
|
186
|
+
const extraConfigs = [];
|
|
187
|
+
let argv = [...args];
|
|
188
|
+
try {
|
|
189
|
+
if (opts.disableFilterAttributes) {
|
|
190
|
+
attributesFile = join(mkdtempSync(join(tmpdir(), 'hermes-webui-git-attrs-')), 'attributes');
|
|
191
|
+
writeFileSync(attributesFile, '');
|
|
192
|
+
}
|
|
193
|
+
if (opts.disableFilterAttributes || opts.neutralizeFilterPrograms) {
|
|
194
|
+
for (const name of [...this.filterNames(cwd, runEnv)].sort()) {
|
|
195
|
+
if (!GitRunner.legal(name))
|
|
196
|
+
continue;
|
|
197
|
+
extraConfigs.push([`filter.${name}.clean`, 'cat'], [`filter.${name}.smudge`, 'cat'], [`filter.${name}.process`, ''], [`filter.${name}.required`, 'false']);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (effectiveDestructive) {
|
|
201
|
+
for (const name of [...this.mergeDriverNames(cwd, runEnv)].sort()) {
|
|
202
|
+
if (!GitRunner.legal(name))
|
|
203
|
+
continue;
|
|
204
|
+
extraConfigs.push([`merge.${name}.driver`, 'git merge-file "%A" "%O" "%B"']);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (effectiveDestructive || opts.neutralizeRemoteHelpers) {
|
|
208
|
+
const names = [...this.remoteHelperNames(cwd, runEnv)].sort();
|
|
209
|
+
for (const name of names) {
|
|
210
|
+
if (!GitRunner.legal(name))
|
|
211
|
+
continue;
|
|
212
|
+
extraConfigs.push([`remote.${name}.uploadpack`, 'git-upload-pack'], [`remote.${name}.receivepack`, 'git-receive-pack']);
|
|
213
|
+
}
|
|
214
|
+
if (names.length && argv.length) {
|
|
215
|
+
const command = argv[0];
|
|
216
|
+
if (command === 'fetch' || command === 'pull')
|
|
217
|
+
argv = [command, '--upload-pack=git-upload-pack', ...argv.slice(1)];
|
|
218
|
+
else if (command === 'push')
|
|
219
|
+
argv = [command, '--receive-pack=git-receive-pack', ...argv.slice(1)];
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (hardenedDestructive)
|
|
223
|
+
hooksPath = mkdtempSync(join(tmpdir(), 'hermes-webui-git-hooks-'));
|
|
224
|
+
if (extraConfigs.length) {
|
|
225
|
+
runEnv.GIT_CONFIG_COUNT = String(extraConfigs.length);
|
|
226
|
+
extraConfigs.forEach(([key, value], i) => {
|
|
227
|
+
runEnv[`GIT_CONFIG_KEY_${i}`] = key;
|
|
228
|
+
runEnv[`GIT_CONFIG_VALUE_${i}`] = value;
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
const full = [];
|
|
232
|
+
for (const [key, value] of GIT_HARDENED_CONFIG)
|
|
233
|
+
full.push('-c', `${key}=${value}`);
|
|
234
|
+
if (hardenedDestructive) {
|
|
235
|
+
for (const [key, value] of GIT_DESTRUCTIVE_HARDENED_CONFIG)
|
|
236
|
+
full.push('-c', `${key}=${value}`);
|
|
237
|
+
if (hooksPath)
|
|
238
|
+
full.push('-c', `core.hooksPath=${hooksPath}`);
|
|
239
|
+
}
|
|
240
|
+
if (attributesFile)
|
|
241
|
+
full.push('-c', `core.attributesFile=${attributesFile}`);
|
|
242
|
+
full.push(...argv);
|
|
243
|
+
const result = this.rawGit(cwd, full, runEnv, opts.timeoutMs ?? GIT_TIMEOUT_MS, opts.input);
|
|
244
|
+
if (result.error) {
|
|
245
|
+
const err = result.error;
|
|
246
|
+
if (err.code === 'ETIMEDOUT')
|
|
247
|
+
throw new GitWorkspaceError('Git command timed out', 'timeout');
|
|
248
|
+
if (err.code === 'ENOENT')
|
|
249
|
+
throw new GitWorkspaceError('Git is not installed or not available on PATH', 'missing_git');
|
|
250
|
+
throw new GitWorkspaceError(err.message, classifyGitError(err.message, args));
|
|
251
|
+
}
|
|
252
|
+
const out = { status: result.status ?? 1, stdout: result.stdout || '', stderr: result.stderr || '' };
|
|
253
|
+
if (opts.check && out.status !== 0) {
|
|
254
|
+
const message = (out.stderr || out.stdout || 'Git command failed').trim();
|
|
255
|
+
throw new GitWorkspaceError(message, classifyGitError(message, args));
|
|
256
|
+
}
|
|
257
|
+
return out;
|
|
258
|
+
}
|
|
259
|
+
finally {
|
|
260
|
+
if (attributesFile)
|
|
261
|
+
rmSync(dirname(attributesFile), { recursive: true, force: true });
|
|
262
|
+
if (hooksPath)
|
|
263
|
+
rmSync(hooksPath, { recursive: true, force: true });
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// ── context and paths ────────────────────────────────────────────────────
|
|
267
|
+
resolveContext(workspace) {
|
|
268
|
+
const ws = resolvePathLikePython(workspace);
|
|
269
|
+
// Python ran `git rev-parse` with `cwd=<workspace>`; a vanished directory raised `FileNotFoundError`, which `_run_git`
|
|
270
|
+
// reported as `missing_git` — every route answered 400 rather than "not a repo".
|
|
271
|
+
if (!existsSync(ws))
|
|
272
|
+
throw new GitWorkspaceError('Git is not installed or not available on PATH', 'missing_git');
|
|
273
|
+
let result;
|
|
274
|
+
try {
|
|
275
|
+
result = this.run(ws, ['rev-parse', '--show-toplevel']);
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
278
|
+
if (error instanceof GitWorkspaceError && (error.code === 'missing_git' || error.code === 'timeout'))
|
|
279
|
+
throw error;
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
if (result.status !== 0)
|
|
283
|
+
return null;
|
|
284
|
+
const repoRoot = resolvePathLikePython(result.stdout.trim());
|
|
285
|
+
if (ws !== repoRoot && !isWithin(ws, repoRoot))
|
|
286
|
+
return null;
|
|
287
|
+
const prefix = ws === repoRoot ? '' : relative(repoRoot, ws).split(sep).join('/');
|
|
288
|
+
return { workspace: ws, repoRoot, workspacePrefix: prefix };
|
|
289
|
+
}
|
|
290
|
+
static workspacePathspec(ctx) {
|
|
291
|
+
return ctx.workspacePrefix || '.';
|
|
292
|
+
}
|
|
293
|
+
static repoRel(ctx, workspaceRel) {
|
|
294
|
+
let target;
|
|
295
|
+
try {
|
|
296
|
+
target = safeResolveWs(ctx.workspace, workspaceRel || '.');
|
|
297
|
+
}
|
|
298
|
+
catch (error) {
|
|
299
|
+
throw new GitWorkspaceError(error.message, 'path_outside_workspace');
|
|
300
|
+
}
|
|
301
|
+
if (target !== ctx.repoRoot && !isWithin(target, ctx.repoRoot))
|
|
302
|
+
throw new GitWorkspaceError('Path is outside the Git repository', 'path_outside_workspace');
|
|
303
|
+
if (ctx.workspacePrefix && target !== ctx.workspace && !isWithin(target, ctx.workspace))
|
|
304
|
+
throw new GitWorkspaceError('Path is outside the workspace', 'path_outside_workspace');
|
|
305
|
+
const rel = relative(ctx.repoRoot, target).split(sep).join('/');
|
|
306
|
+
return rel || '.';
|
|
307
|
+
}
|
|
308
|
+
static workspaceRel(ctx, repoRel) {
|
|
309
|
+
const normalized = repoRel.replace(/\\/g, '/');
|
|
310
|
+
if (!ctx.workspacePrefix)
|
|
311
|
+
return normalized;
|
|
312
|
+
const prefix = `${ctx.workspacePrefix.replace(/\/+$/, '')}/`;
|
|
313
|
+
if (normalized === ctx.workspacePrefix)
|
|
314
|
+
return '.';
|
|
315
|
+
if (normalized.startsWith(prefix))
|
|
316
|
+
return normalized.slice(prefix.length);
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
// ── mutation lock ────────────────────────────────────────────────────────
|
|
320
|
+
async withMutationLock(ctx, fn) {
|
|
321
|
+
const key = ctx.repoRoot;
|
|
322
|
+
const previous = this.locks.get(key) ?? Promise.resolve();
|
|
323
|
+
let release;
|
|
324
|
+
const current = new Promise((r) => { release = r; });
|
|
325
|
+
const chained = previous.then(() => current);
|
|
326
|
+
this.locks.set(key, chained);
|
|
327
|
+
let timer;
|
|
328
|
+
const acquired = await Promise.race([previous.then(() => true), new Promise((r) => { timer = setTimeout(() => { r(false); }, GIT_REMOTE_TIMEOUT_MS); })]);
|
|
329
|
+
clearTimeout(timer);
|
|
330
|
+
if (!acquired) {
|
|
331
|
+
release();
|
|
332
|
+
throw new GitWorkspaceError('Another Git operation is still running', 'operation_in_progress');
|
|
333
|
+
}
|
|
334
|
+
try {
|
|
335
|
+
return fn();
|
|
336
|
+
}
|
|
337
|
+
finally {
|
|
338
|
+
this.invalidateStatusCache(ctx.repoRoot);
|
|
339
|
+
release();
|
|
340
|
+
if (this.locks.get(key) === chained)
|
|
341
|
+
this.locks.delete(key);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
// ── status cache ─────────────────────────────────────────────────────────
|
|
345
|
+
static resolveGitDir(repoRoot) {
|
|
346
|
+
const dotGit = join(repoRoot, '.git');
|
|
347
|
+
try {
|
|
348
|
+
if (statSync(dotGit).isFile()) {
|
|
349
|
+
const text = readFileSync(dotGit, 'utf8').trim();
|
|
350
|
+
if (text.startsWith('gitdir:')) {
|
|
351
|
+
const target = text.slice('gitdir:'.length).trim();
|
|
352
|
+
return resolve(repoRoot, target);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
catch { /* fall through */ }
|
|
357
|
+
return dotGit;
|
|
358
|
+
}
|
|
359
|
+
statusFingerprint(repoRoot) {
|
|
360
|
+
const gitDir = GitRunner.resolveGitDir(repoRoot);
|
|
361
|
+
const parts = [String(this.destructiveEnabled())];
|
|
362
|
+
for (const name of ['index', 'HEAD']) {
|
|
363
|
+
try {
|
|
364
|
+
const st = statSync(join(gitDir, name), { bigint: true });
|
|
365
|
+
parts.push(`${st.mtimeNs}:${st.size}:${st.ino}`);
|
|
366
|
+
}
|
|
367
|
+
catch {
|
|
368
|
+
parts.push('null');
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return parts.join('|');
|
|
372
|
+
}
|
|
373
|
+
cachedStatus(workspace) {
|
|
374
|
+
const entry = this.statusCache.get(workspace);
|
|
375
|
+
if (!entry)
|
|
376
|
+
return null;
|
|
377
|
+
if (this.now() - entry.storedAt >= STATUS_CACHE_TTL_MS || this.statusFingerprint(entry.repoRoot) !== entry.fingerprint) {
|
|
378
|
+
this.statusCache.delete(workspace);
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
return structuredClone(entry.payload);
|
|
382
|
+
}
|
|
383
|
+
storeStatus(ctx, generation, fingerprint, payload) {
|
|
384
|
+
if ((this.generations.get(ctx.repoRoot) ?? 0) !== generation)
|
|
385
|
+
return;
|
|
386
|
+
if (!this.statusCache.has(ctx.workspace) && this.statusCache.size >= STATUS_CACHE_LIMIT) {
|
|
387
|
+
const oldest = this.statusCache.keys().next();
|
|
388
|
+
if (!oldest.done)
|
|
389
|
+
this.statusCache.delete(oldest.value);
|
|
390
|
+
}
|
|
391
|
+
this.statusCache.set(ctx.workspace, { storedAt: this.now(), repoRoot: ctx.repoRoot, fingerprint, payload: structuredClone(payload) });
|
|
392
|
+
}
|
|
393
|
+
invalidateStatusCache(repoRoot) {
|
|
394
|
+
this.generations.set(repoRoot, (this.generations.get(repoRoot) ?? 0) + 1);
|
|
395
|
+
for (const [key, entry] of this.statusCache)
|
|
396
|
+
if (entry.repoRoot === repoRoot)
|
|
397
|
+
this.statusCache.delete(key);
|
|
398
|
+
}
|
|
399
|
+
// ── status ───────────────────────────────────────────────────────────────
|
|
400
|
+
static statusCode(xy, untracked, renamed) {
|
|
401
|
+
if (untracked)
|
|
402
|
+
return '??';
|
|
403
|
+
if (CONFLICT_CODES.has(xy))
|
|
404
|
+
return xy;
|
|
405
|
+
if (renamed)
|
|
406
|
+
return 'R';
|
|
407
|
+
for (const ch of xy)
|
|
408
|
+
if ('MADRCUT'.includes(ch))
|
|
409
|
+
return ch;
|
|
410
|
+
return xy.replace(/^\.+|\.+$/g, '') || 'M';
|
|
411
|
+
}
|
|
412
|
+
parseNumstat(text, ctx) {
|
|
413
|
+
const stats = new Map();
|
|
414
|
+
const tokens = text.split('\0');
|
|
415
|
+
let i = 0;
|
|
416
|
+
while (i < tokens.length) {
|
|
417
|
+
const record = tokens[i] ?? '';
|
|
418
|
+
i += 1;
|
|
419
|
+
if (!record)
|
|
420
|
+
continue;
|
|
421
|
+
const first = record.indexOf('\t');
|
|
422
|
+
const second = first >= 0 ? record.indexOf('\t', first + 1) : -1;
|
|
423
|
+
if (first < 0 || second < 0)
|
|
424
|
+
continue;
|
|
425
|
+
const rawAdd = record.slice(0, first);
|
|
426
|
+
const rawDel = record.slice(first + 1, second);
|
|
427
|
+
let rawPath = record.slice(second + 1);
|
|
428
|
+
if (!rawPath) {
|
|
429
|
+
if (i + 1 >= tokens.length)
|
|
430
|
+
break;
|
|
431
|
+
rawPath = tokens[i + 1] ?? '';
|
|
432
|
+
i += 2;
|
|
433
|
+
}
|
|
434
|
+
const binary = rawAdd === '-' || rawDel === '-';
|
|
435
|
+
const additions = binary ? 0 : Number.parseInt(rawAdd || '0', 10) || 0;
|
|
436
|
+
const deletions = binary ? 0 : Number.parseInt(rawDel || '0', 10) || 0;
|
|
437
|
+
const workspacePath = GitRunner.workspaceRel(ctx, rawPath);
|
|
438
|
+
if (workspacePath === null)
|
|
439
|
+
continue;
|
|
440
|
+
stats.set(workspacePath, [additions, deletions, binary]);
|
|
441
|
+
}
|
|
442
|
+
return stats;
|
|
443
|
+
}
|
|
444
|
+
collectNumstat(ctx, cached) {
|
|
445
|
+
const args = ['diff', '--numstat', '-z', '--no-textconv', '--ignore-cr-at-eol'];
|
|
446
|
+
if (cached)
|
|
447
|
+
args.push('--cached');
|
|
448
|
+
args.push('--', GitRunner.workspacePathspec(ctx));
|
|
449
|
+
const result = this.run(ctx, args, { disableFilterAttributes: this.destructiveEnabled(), neutralizeFilterPrograms: true });
|
|
450
|
+
if (result.status !== 0)
|
|
451
|
+
return null;
|
|
452
|
+
return this.parseNumstat(result.stdout, ctx);
|
|
453
|
+
}
|
|
454
|
+
/** An untracked file's bytes through the anchored walk (no symlinked component), or null when unreadable/too large. */
|
|
455
|
+
static readUntracked(workspace, path) {
|
|
456
|
+
let fd;
|
|
457
|
+
// Python read an untracked symlink through its target; that is kept for links that stay inside the workspace, so
|
|
458
|
+
// a link pointing outside can never pull foreign bytes into a diff.
|
|
459
|
+
let source = path;
|
|
460
|
+
try {
|
|
461
|
+
if (lstatSync(path).isSymbolicLink()) {
|
|
462
|
+
const real = realpathSync(path);
|
|
463
|
+
const root = realpathSync(workspace);
|
|
464
|
+
if (real !== root && !isWithin(real, root))
|
|
465
|
+
return null;
|
|
466
|
+
source = real;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
catch {
|
|
470
|
+
return null;
|
|
471
|
+
}
|
|
472
|
+
try {
|
|
473
|
+
fd = openAnchoredFd(workspace, source, { wantDir: false });
|
|
474
|
+
}
|
|
475
|
+
catch {
|
|
476
|
+
return null;
|
|
477
|
+
}
|
|
478
|
+
try {
|
|
479
|
+
const st = fstatSync(fd);
|
|
480
|
+
if (!st.isFile())
|
|
481
|
+
return null;
|
|
482
|
+
if (st.size > DIFF_SIZE_LIMIT)
|
|
483
|
+
return { tooLarge: true };
|
|
484
|
+
return { data: readFileSync(fd) };
|
|
485
|
+
}
|
|
486
|
+
catch {
|
|
487
|
+
return null;
|
|
488
|
+
}
|
|
489
|
+
finally {
|
|
490
|
+
closeSync(fd);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
static countUntrackedFile(workspace, path) {
|
|
494
|
+
const read = GitRunner.readUntracked(workspace, path);
|
|
495
|
+
if (!read || 'tooLarge' in read)
|
|
496
|
+
return [0, 0, false];
|
|
497
|
+
const data = read.data;
|
|
498
|
+
if (data.includes(0))
|
|
499
|
+
return [0, 0, true];
|
|
500
|
+
const text = data.toString('utf8');
|
|
501
|
+
if (text.includes('�') && !data.equals(Buffer.from(text, 'utf8')))
|
|
502
|
+
return [0, 0, true];
|
|
503
|
+
return [splitLines(text).length || (text ? 1 : 0), 0, false];
|
|
504
|
+
}
|
|
505
|
+
status(workspace, opts = {}) {
|
|
506
|
+
const resolved = resolvePathLikePython(workspace);
|
|
507
|
+
if (opts.useCache) {
|
|
508
|
+
const cached = this.cachedStatus(resolved);
|
|
509
|
+
if (cached)
|
|
510
|
+
return cached;
|
|
511
|
+
}
|
|
512
|
+
const ctx = this.resolveContext(resolved);
|
|
513
|
+
if (!ctx)
|
|
514
|
+
return { is_git: false };
|
|
515
|
+
const generation = this.generations.get(ctx.repoRoot) ?? 0;
|
|
516
|
+
const result = this.run(ctx, ['status', '--porcelain=v2', '-z', '--branch', '--untracked-files=all', '--', GitRunner.workspacePathspec(ctx)], { check: true, disableFilterAttributes: this.destructiveEnabled(), neutralizeFilterPrograms: true });
|
|
517
|
+
const stagedStats = this.collectNumstat(ctx, true);
|
|
518
|
+
const unstagedStats = this.collectNumstat(ctx, false);
|
|
519
|
+
const statSources = [stagedStats ?? new Map(), unstagedStats ?? new Map()];
|
|
520
|
+
let branch = '';
|
|
521
|
+
let upstream = '';
|
|
522
|
+
let ahead = 0;
|
|
523
|
+
let behind = 0;
|
|
524
|
+
const files = new Map();
|
|
525
|
+
const noise = { filemode_only: 0, crlf_only: 0 };
|
|
526
|
+
const tokens = result.stdout.split('\0');
|
|
527
|
+
let i = 0;
|
|
528
|
+
let truncated = false;
|
|
529
|
+
while (i < tokens.length) {
|
|
530
|
+
const rec = tokens[i] ?? '';
|
|
531
|
+
i += 1;
|
|
532
|
+
if (!rec)
|
|
533
|
+
continue;
|
|
534
|
+
if (rec.startsWith('# ')) {
|
|
535
|
+
const parts = rec.split(' ');
|
|
536
|
+
const key = parts[1];
|
|
537
|
+
const value = parts.slice(2).join(' ');
|
|
538
|
+
if (key === 'branch.head')
|
|
539
|
+
branch = value === '(detached)' ? '' : value;
|
|
540
|
+
else if (key === 'branch.upstream')
|
|
541
|
+
upstream = value;
|
|
542
|
+
else if (key === 'branch.ab') {
|
|
543
|
+
for (const bit of value.split(/\s+/)) {
|
|
544
|
+
if (bit.startsWith('+') && /^\d+$/.test(bit.slice(1)))
|
|
545
|
+
ahead = Number.parseInt(bit.slice(1), 10);
|
|
546
|
+
else if (bit.startsWith('-') && /^\d+$/.test(bit.slice(1)))
|
|
547
|
+
behind = Number.parseInt(bit.slice(1), 10);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
let oldPath = null;
|
|
553
|
+
let renamed = false;
|
|
554
|
+
let modeHead = '';
|
|
555
|
+
let modeIndex = '';
|
|
556
|
+
let modeWorktree = '';
|
|
557
|
+
let xy;
|
|
558
|
+
let repoPath;
|
|
559
|
+
let untracked;
|
|
560
|
+
if (rec.startsWith('? ')) {
|
|
561
|
+
xy = '??';
|
|
562
|
+
repoPath = rec.slice(2);
|
|
563
|
+
untracked = true;
|
|
564
|
+
}
|
|
565
|
+
else if (rec.startsWith('1 ')) {
|
|
566
|
+
const parts = rec.split(' ');
|
|
567
|
+
if (parts.length < 9)
|
|
568
|
+
continue;
|
|
569
|
+
xy = parts[1] ?? '';
|
|
570
|
+
modeHead = parts[3] ?? '';
|
|
571
|
+
modeIndex = parts[4] ?? '';
|
|
572
|
+
modeWorktree = parts[5] ?? '';
|
|
573
|
+
repoPath = parts.slice(8).join(' ');
|
|
574
|
+
untracked = false;
|
|
575
|
+
}
|
|
576
|
+
else if (rec.startsWith('2 ')) {
|
|
577
|
+
const parts = rec.split(' ');
|
|
578
|
+
if (parts.length < 10)
|
|
579
|
+
continue;
|
|
580
|
+
xy = parts[1] ?? '';
|
|
581
|
+
modeHead = parts[3] ?? '';
|
|
582
|
+
modeIndex = parts[4] ?? '';
|
|
583
|
+
modeWorktree = parts[5] ?? '';
|
|
584
|
+
repoPath = parts.slice(9).join(' ');
|
|
585
|
+
if (i < tokens.length) {
|
|
586
|
+
oldPath = tokens[i] ?? null;
|
|
587
|
+
i += 1;
|
|
588
|
+
}
|
|
589
|
+
renamed = true;
|
|
590
|
+
untracked = false;
|
|
591
|
+
}
|
|
592
|
+
else if (rec.startsWith('u ')) {
|
|
593
|
+
const parts = rec.split(' ');
|
|
594
|
+
if (parts.length < 11)
|
|
595
|
+
continue;
|
|
596
|
+
xy = parts[1] ?? '';
|
|
597
|
+
repoPath = parts.slice(10).join(' ');
|
|
598
|
+
untracked = false;
|
|
599
|
+
}
|
|
600
|
+
else
|
|
601
|
+
continue;
|
|
602
|
+
const workspacePath = GitRunner.workspaceRel(ctx, repoPath);
|
|
603
|
+
if (workspacePath === null)
|
|
604
|
+
continue;
|
|
605
|
+
const oldWorkspacePath = oldPath ? GitRunner.workspaceRel(ctx, oldPath) : null;
|
|
606
|
+
const x = xy[0] ?? '.';
|
|
607
|
+
const y = xy[1] ?? '.';
|
|
608
|
+
const conflict = CONFLICT_CODES.has(xy) || rec.startsWith('u ');
|
|
609
|
+
let additions = 0;
|
|
610
|
+
let deletions = 0;
|
|
611
|
+
let binary = false;
|
|
612
|
+
for (const source of statSources) {
|
|
613
|
+
const entry = source.get(workspacePath);
|
|
614
|
+
if (entry) {
|
|
615
|
+
additions += entry[0];
|
|
616
|
+
deletions += entry[1];
|
|
617
|
+
binary = binary || entry[2];
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
if (untracked)
|
|
621
|
+
[additions, deletions, binary] = GitRunner.countUntrackedFile(ctx.workspace, join(ctx.workspace, workspacePath));
|
|
622
|
+
let staged = x !== '.' && x !== '?' && !untracked;
|
|
623
|
+
let unstaged = y !== '.' && y !== ' ' && !untracked;
|
|
624
|
+
if (staged && stagedStats && !renamed) {
|
|
625
|
+
staged = stagedStats.has(workspacePath) || (oldWorkspacePath !== null && stagedStats.has(oldWorkspacePath));
|
|
626
|
+
if (!staged)
|
|
627
|
+
noise.crlf_only += 1;
|
|
628
|
+
}
|
|
629
|
+
if (unstaged && unstagedStats && !renamed) {
|
|
630
|
+
unstaged = unstagedStats.has(workspacePath) || (oldWorkspacePath !== null && unstagedStats.has(oldWorkspacePath));
|
|
631
|
+
if (!unstaged)
|
|
632
|
+
noise.crlf_only += 1;
|
|
633
|
+
}
|
|
634
|
+
if (!(staged || unstaged || untracked || conflict || renamed))
|
|
635
|
+
continue;
|
|
636
|
+
if (!(untracked || conflict || renamed || binary) && additions === 0 && deletions === 0) {
|
|
637
|
+
const modeOnly = (staged && modeHead !== modeIndex) || (unstaged && modeIndex !== modeWorktree);
|
|
638
|
+
noise[modeOnly ? 'filemode_only' : 'crlf_only'] += 1;
|
|
639
|
+
continue;
|
|
640
|
+
}
|
|
641
|
+
files.set(workspacePath, {
|
|
642
|
+
path: workspacePath, old_path: oldWorkspacePath, workspace_path: workspacePath, status: GitRunner.statusCode(xy, untracked, renamed),
|
|
643
|
+
staged, unstaged, untracked, ignored: false, conflict, additions, deletions, binary,
|
|
644
|
+
});
|
|
645
|
+
if (files.size >= STATUS_FILE_LIMIT) {
|
|
646
|
+
truncated = true;
|
|
647
|
+
break;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
const fileList = [...files.values()].sort((a, b) => (a.path.toLowerCase() < b.path.toLowerCase() ? -1 : a.path.toLowerCase() > b.path.toLowerCase() ? 1 : 0));
|
|
651
|
+
const totals = { changed: fileList.length, staged: 0, unstaged: 0, untracked: 0, conflicts: 0 };
|
|
652
|
+
for (const item of fileList) {
|
|
653
|
+
if (item.staged)
|
|
654
|
+
totals.staged += 1;
|
|
655
|
+
if (item.unstaged)
|
|
656
|
+
totals.unstaged += 1;
|
|
657
|
+
if (item.untracked)
|
|
658
|
+
totals.untracked += 1;
|
|
659
|
+
if (item.conflict)
|
|
660
|
+
totals.conflicts += 1;
|
|
661
|
+
}
|
|
662
|
+
if (!branch)
|
|
663
|
+
branch = this.run(ctx, ['rev-parse', '--short', 'HEAD']).stdout.trim();
|
|
664
|
+
const payload = {
|
|
665
|
+
is_git: true, branch: branch || 'HEAD', upstream, ahead, behind, totals, files: fileList, truncated,
|
|
666
|
+
noise_filtering: { ...noise, active: noise.filemode_only > 0 || noise.crlf_only > 0 },
|
|
667
|
+
};
|
|
668
|
+
this.storeStatus(ctx, generation, this.statusFingerprint(ctx.repoRoot), payload);
|
|
669
|
+
return payload;
|
|
670
|
+
}
|
|
671
|
+
// ── branches ─────────────────────────────────────────────────────────────
|
|
672
|
+
branchAheadBehind(ctx, branch, upstream) {
|
|
673
|
+
if (!upstream)
|
|
674
|
+
return [0, 0];
|
|
675
|
+
const result = this.run(ctx, ['rev-list', '--left-right', '--count', `${branch}...${upstream}`]);
|
|
676
|
+
if (result.status !== 0)
|
|
677
|
+
return [0, 0];
|
|
678
|
+
const parts = result.stdout.trim().split(/\s+/);
|
|
679
|
+
if (parts.length !== 2)
|
|
680
|
+
return [0, 0];
|
|
681
|
+
const a = Number.parseInt(parts[0] ?? '', 10);
|
|
682
|
+
const b = Number.parseInt(parts[1] ?? '', 10);
|
|
683
|
+
return Number.isFinite(a) && Number.isFinite(b) ? [a, b] : [0, 0];
|
|
684
|
+
}
|
|
685
|
+
forEachRef(ctx, refPrefix) {
|
|
686
|
+
const fmt = '%(refname)%00%(refname:short)%00%(upstream:short)%00%(objectname:short)%00%(committerdate:unix)%00%(committerdate:relative)%00%(authorname)%00%(subject)';
|
|
687
|
+
const result = this.run(ctx, ['for-each-ref', `--format=${fmt}`, refPrefix], { check: true });
|
|
688
|
+
const refs = [];
|
|
689
|
+
for (const line of result.stdout.split('\n')) {
|
|
690
|
+
const [fullName = '', name = '', upstream = '', sha = '', updated = '', updatedRelative = '', author = '', subject = ''] = line.split('\0');
|
|
691
|
+
if (!name || fullName.endsWith('/HEAD') || name.endsWith('/HEAD'))
|
|
692
|
+
continue;
|
|
693
|
+
if (refPrefix === 'refs/remotes' && !name.includes('/'))
|
|
694
|
+
continue;
|
|
695
|
+
const item = { name, sha, updated: /^\d+$/.test(updated) ? Number.parseInt(updated, 10) : 0, updated_relative: updatedRelative, author, subject, upstream: '', ahead: 0, behind: 0 };
|
|
696
|
+
if (upstream) {
|
|
697
|
+
const [ahead, behind] = this.branchAheadBehind(ctx, name, upstream);
|
|
698
|
+
Object.assign(item, { upstream, ahead, behind });
|
|
699
|
+
}
|
|
700
|
+
refs.push(item);
|
|
701
|
+
}
|
|
702
|
+
return refs.sort((a, b) => (a.name.toLowerCase() < b.name.toLowerCase() ? -1 : a.name.toLowerCase() > b.name.toLowerCase() ? 1 : 0));
|
|
703
|
+
}
|
|
704
|
+
branches(workspace) {
|
|
705
|
+
const ctx = this.resolveContext(workspace);
|
|
706
|
+
if (!ctx)
|
|
707
|
+
throw new GitWorkspaceError('Workspace is not a Git repository', 'not_a_repo');
|
|
708
|
+
const headName = this.run(ctx, ['branch', '--show-current'], { check: true }).stdout.trim();
|
|
709
|
+
const headSha = this.run(ctx, ['rev-parse', '--short', 'HEAD'], { check: true }).stdout.trim();
|
|
710
|
+
const status = this.status(workspace);
|
|
711
|
+
return {
|
|
712
|
+
is_git: true, current: headName || headSha || 'HEAD', detached: !headName, head: headSha,
|
|
713
|
+
local: this.forEachRef(ctx, 'refs/heads'), remote: this.forEachRef(ctx, 'refs/remotes'),
|
|
714
|
+
upstream: status.upstream ?? '', ahead: status.ahead ?? 0, behind: status.behind ?? 0,
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
// ── checkout ─────────────────────────────────────────────────────────────
|
|
718
|
+
validateLocalBranch(ctx, ref) {
|
|
719
|
+
const r = ref.trim();
|
|
720
|
+
if (!r)
|
|
721
|
+
throw new GitWorkspaceError('Branch name is required', 'invalid_ref');
|
|
722
|
+
this.run(ctx, ['show-ref', '--verify', `refs/heads/${r}`], { check: true });
|
|
723
|
+
return r;
|
|
724
|
+
}
|
|
725
|
+
validateRemoteBranch(ctx, ref) {
|
|
726
|
+
const r = ref.trim();
|
|
727
|
+
if (!r)
|
|
728
|
+
throw new GitWorkspaceError('Remote branch name is required', 'invalid_ref');
|
|
729
|
+
this.run(ctx, ['show-ref', '--verify', `refs/remotes/${r}`], { check: true });
|
|
730
|
+
return r;
|
|
731
|
+
}
|
|
732
|
+
validateCheckoutStart(ctx, ref) {
|
|
733
|
+
const r = (ref || 'HEAD').trim() || 'HEAD';
|
|
734
|
+
if (this.run(ctx, ['rev-parse', '--verify', `${r}^{commit}`]).status !== 0)
|
|
735
|
+
throw new GitWorkspaceError('Invalid checkout reference', 'invalid_ref');
|
|
736
|
+
return r;
|
|
737
|
+
}
|
|
738
|
+
validateNewBranchName(ctx, name) {
|
|
739
|
+
const n = name.trim();
|
|
740
|
+
if (!n)
|
|
741
|
+
throw new GitWorkspaceError('New branch name is required', 'invalid_ref');
|
|
742
|
+
if (this.run(ctx, ['check-ref-format', '--branch', n]).status !== 0)
|
|
743
|
+
throw new GitWorkspaceError('Invalid branch name', 'invalid_ref');
|
|
744
|
+
if (this.run(ctx, ['show-ref', '--verify', `refs/heads/${n}`]).status === 0)
|
|
745
|
+
throw new GitWorkspaceError('A local branch with that name already exists', 'invalid_ref');
|
|
746
|
+
return n;
|
|
747
|
+
}
|
|
748
|
+
dirtyWorktree(ctx) {
|
|
749
|
+
return this.run(ctx, ['status', '--porcelain=v2', '--untracked-files=all'], { check: true, neutralizeFilterPrograms: true }).stdout.trim() !== '';
|
|
750
|
+
}
|
|
751
|
+
currentCheckoutLabel(ctx) {
|
|
752
|
+
const branch = this.run(ctx, ['branch', '--show-current']).stdout.trim();
|
|
753
|
+
if (branch)
|
|
754
|
+
return branch;
|
|
755
|
+
return this.run(ctx, ['rev-parse', '--short', 'HEAD'], { check: true }).stdout.trim() || 'HEAD';
|
|
756
|
+
}
|
|
757
|
+
static stashSubjectParts(subject) {
|
|
758
|
+
const s = subject.trim();
|
|
759
|
+
if (!s.startsWith('On ') || !s.includes(': '))
|
|
760
|
+
return null;
|
|
761
|
+
const idx = s.indexOf(': ');
|
|
762
|
+
const branch = s.slice(3, idx).trim();
|
|
763
|
+
const message = s.slice(idx + 2).trim();
|
|
764
|
+
if (!branch || !message.startsWith(BRANCH_SWITCH_STASH_PREFIX))
|
|
765
|
+
return null;
|
|
766
|
+
return [branch, message];
|
|
767
|
+
}
|
|
768
|
+
branchSwitchStashes(ctx) {
|
|
769
|
+
const result = this.run(ctx, ['stash', 'list', '--format=%gd%x00%gs']);
|
|
770
|
+
if (result.status !== 0)
|
|
771
|
+
return [];
|
|
772
|
+
const out = [];
|
|
773
|
+
for (const line of result.stdout.split('\n')) {
|
|
774
|
+
const idx = line.indexOf('\0');
|
|
775
|
+
if (idx < 0)
|
|
776
|
+
continue;
|
|
777
|
+
const parts = GitRunner.stashSubjectParts(line.slice(idx + 1));
|
|
778
|
+
if (!parts)
|
|
779
|
+
continue;
|
|
780
|
+
out.push({ ref: line.slice(0, idx), branch: parts[0], message: parts[1] });
|
|
781
|
+
}
|
|
782
|
+
return out;
|
|
783
|
+
}
|
|
784
|
+
restoreBranchSwitchStashLocked(ctx, branch) {
|
|
785
|
+
if (this.destructiveEnabled() && this.hasRepoLocalFilters(ctx.repoRoot))
|
|
786
|
+
return { restore_blocked: true, restore_reason: 'Repository defines local filter programs' };
|
|
787
|
+
if (this.dirtyWorktree(ctx))
|
|
788
|
+
return {};
|
|
789
|
+
for (const item of this.branchSwitchStashes(ctx)) {
|
|
790
|
+
if (item.branch !== branch)
|
|
791
|
+
continue;
|
|
792
|
+
const result = this.run(ctx, ['stash', 'pop', '--index', item.ref], { destructive: true, disableFilterAttributes: true });
|
|
793
|
+
if (result.status === 0)
|
|
794
|
+
return { restored_stash: item };
|
|
795
|
+
return { restore_failed: true, restore_error: (result.stderr || result.stdout || 'Git stash restore failed').trim(), restore_stash: item };
|
|
796
|
+
}
|
|
797
|
+
return {};
|
|
798
|
+
}
|
|
799
|
+
validateCheckoutRequestLocked(ctx, ref, mode, newBranch) {
|
|
800
|
+
if (mode === 'local') {
|
|
801
|
+
this.validateLocalBranch(ctx, ref);
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
if (mode === 'new' || mode === 'create') {
|
|
805
|
+
this.validateNewBranchName(ctx, newBranch || ref);
|
|
806
|
+
this.validateCheckoutStart(ctx, newBranch && ref && ref !== newBranch ? ref : 'HEAD');
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
if (mode === 'remote') {
|
|
810
|
+
const remoteRef = this.validateRemoteBranch(ctx, ref);
|
|
811
|
+
const branchName = (newBranch || remoteRef.split('/').slice(1).join('/')).trim();
|
|
812
|
+
if (this.run(ctx, ['show-ref', '--verify', `refs/heads/${branchName}`]).status !== 0)
|
|
813
|
+
this.validateNewBranchName(ctx, branchName);
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
if (mode === 'detached' || mode === 'detach') {
|
|
817
|
+
this.validateCheckoutStart(ctx, ref);
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
throw new GitWorkspaceError('Unsupported checkout mode', 'invalid_ref');
|
|
821
|
+
}
|
|
822
|
+
performCheckoutLocked(ctx, ref, mode, newBranch, track) {
|
|
823
|
+
if (this.destructiveEnabled() && this.hasRepoLocalFilters(ctx.repoRoot)) {
|
|
824
|
+
throw new GitWorkspaceError('Cannot checkout: repository defines local filter programs that would alter file content', 'filtered_path');
|
|
825
|
+
}
|
|
826
|
+
const destructive = { check: true, destructive: true, disableFilterAttributes: true };
|
|
827
|
+
if (mode === 'local')
|
|
828
|
+
return this.run(ctx, ['switch', '--recurse-submodules=no', this.validateLocalBranch(ctx, ref)], destructive);
|
|
829
|
+
if (mode === 'new' || mode === 'create') {
|
|
830
|
+
const branch = this.validateNewBranchName(ctx, newBranch || ref);
|
|
831
|
+
const startRef = this.validateCheckoutStart(ctx, newBranch && ref && ref !== newBranch ? ref : 'HEAD');
|
|
832
|
+
return this.run(ctx, ['switch', '--recurse-submodules=no', '-c', branch, startRef], destructive);
|
|
833
|
+
}
|
|
834
|
+
if (mode === 'remote') {
|
|
835
|
+
const remoteRef = this.validateRemoteBranch(ctx, ref);
|
|
836
|
+
const branchName = (newBranch || remoteRef.split('/').slice(1).join('/')).trim();
|
|
837
|
+
if (this.run(ctx, ['show-ref', '--verify', `refs/heads/${branchName}`]).status === 0) {
|
|
838
|
+
const result = this.run(ctx, ['switch', '--recurse-submodules=no', branchName], destructive);
|
|
839
|
+
if (track)
|
|
840
|
+
this.run(ctx, ['branch', '--set-upstream-to', remoteRef, branchName]);
|
|
841
|
+
return result;
|
|
842
|
+
}
|
|
843
|
+
const branch = this.validateNewBranchName(ctx, branchName);
|
|
844
|
+
const args = ['switch', '--recurse-submodules=no', '-c', branch];
|
|
845
|
+
if (track)
|
|
846
|
+
args.push('--track');
|
|
847
|
+
args.push(remoteRef);
|
|
848
|
+
return this.run(ctx, args, destructive);
|
|
849
|
+
}
|
|
850
|
+
if (mode === 'detached' || mode === 'detach')
|
|
851
|
+
return this.run(ctx, ['switch', '--recurse-submodules=no', '--detach', this.validateCheckoutStart(ctx, ref)], destructive);
|
|
852
|
+
throw new GitWorkspaceError('Unsupported checkout mode', 'invalid_ref');
|
|
853
|
+
}
|
|
854
|
+
static remoteMessage(result) {
|
|
855
|
+
return (result.stdout || result.stderr || '').trim();
|
|
856
|
+
}
|
|
857
|
+
async checkout(workspace, ref, mode, opts = {}) {
|
|
858
|
+
const ctx = this.resolveContext(workspace);
|
|
859
|
+
if (!ctx)
|
|
860
|
+
throw new GitWorkspaceError('Workspace is not a Git repository', 'not_a_repo');
|
|
861
|
+
const m = (mode || 'local').trim().toLowerCase();
|
|
862
|
+
const dirtyMode = (opts.dirtyMode || 'block').trim().toLowerCase();
|
|
863
|
+
if (dirtyMode !== 'block')
|
|
864
|
+
throw new GitWorkspaceError('Only dirty_mode=block is supported for branch checkout', 'dirty_worktree');
|
|
865
|
+
const result = await this.withMutationLock(ctx, () => {
|
|
866
|
+
this.validateCheckoutRequestLocked(ctx, ref, m, opts.newBranch ?? null);
|
|
867
|
+
if (this.dirtyWorktree(ctx))
|
|
868
|
+
throw new GitWorkspaceError('Checkout blocked because the Git worktree has uncommitted changes', 'dirty_worktree');
|
|
869
|
+
return this.performCheckoutLocked(ctx, ref, m, opts.newBranch ?? null, Boolean(opts.track));
|
|
870
|
+
});
|
|
871
|
+
const status = this.status(workspace);
|
|
872
|
+
const branches = this.branches(workspace);
|
|
873
|
+
return { ok: true, message: GitRunner.remoteMessage(result), current_branch: branches.current, status, branches };
|
|
874
|
+
}
|
|
875
|
+
async stashAndCheckout(workspace, ref, mode, opts = {}) {
|
|
876
|
+
const ctx = this.resolveContext(workspace);
|
|
877
|
+
if (!ctx)
|
|
878
|
+
throw new GitWorkspaceError('Workspace is not a Git repository', 'not_a_repo');
|
|
879
|
+
const m = (mode || 'local').trim().toLowerCase();
|
|
880
|
+
const targetLabel = (opts.newBranch || ref || 'HEAD').trim() || 'HEAD';
|
|
881
|
+
const stashName = `${BRANCH_SWITCH_STASH_PREFIX} to ${targetLabel}`.trim();
|
|
882
|
+
const outcome = await this.withMutationLock(ctx, () => {
|
|
883
|
+
this.validateCheckoutRequestLocked(ctx, ref, m, opts.newBranch ?? null);
|
|
884
|
+
if (this.destructiveEnabled() && this.hasRepoLocalFilters(ctx.repoRoot)) {
|
|
885
|
+
throw new GitWorkspaceError('Cannot stash: repository defines local filter programs that would alter file content', 'filtered_path');
|
|
886
|
+
}
|
|
887
|
+
let stashed = false;
|
|
888
|
+
if (this.dirtyWorktree(ctx)) {
|
|
889
|
+
const stashResult = this.run(ctx, ['stash', 'push', '-u', '-m', stashName], { check: true, destructive: true, disableFilterAttributes: true });
|
|
890
|
+
stashed = !GitRunner.remoteMessage(stashResult).includes('No local changes to save');
|
|
891
|
+
}
|
|
892
|
+
let result;
|
|
893
|
+
try {
|
|
894
|
+
result = this.performCheckoutLocked(ctx, ref, m, opts.newBranch ?? null, Boolean(opts.track));
|
|
895
|
+
}
|
|
896
|
+
catch (error) {
|
|
897
|
+
if (stashed)
|
|
898
|
+
this.run(ctx, ['stash', 'pop', '--index', 'stash@{0}'], { destructive: true, disableFilterAttributes: true });
|
|
899
|
+
throw error;
|
|
900
|
+
}
|
|
901
|
+
const restored = this.restoreBranchSwitchStashLocked(ctx, this.currentCheckoutLabel(ctx));
|
|
902
|
+
return { result, stashed, restored };
|
|
903
|
+
});
|
|
904
|
+
const status = this.status(workspace);
|
|
905
|
+
const branches = this.branches(workspace);
|
|
906
|
+
const { restored } = outcome;
|
|
907
|
+
return {
|
|
908
|
+
ok: true, message: GitRunner.remoteMessage(outcome.result), stash_name: outcome.stashed ? stashName : '', stashed: outcome.stashed,
|
|
909
|
+
restored_stash: restored.restored_stash ?? null, restore_failed: Boolean(restored.restore_failed), restore_error: str(restored.restore_error),
|
|
910
|
+
restore_stash: restored.restore_stash ?? null, current_branch: branches.current, status, branches,
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
// ── diff ─────────────────────────────────────────────────────────────────
|
|
914
|
+
static syntheticUntrackedDiff(workspace, path, label) {
|
|
915
|
+
const read = GitRunner.readUntracked(workspace, path);
|
|
916
|
+
if (!read)
|
|
917
|
+
throw new GitWorkspaceError('Path is not a file');
|
|
918
|
+
if ('tooLarge' in read)
|
|
919
|
+
return { binary: false, too_large: true, diff: '', additions: 0, deletions: 0 };
|
|
920
|
+
const data = read.data;
|
|
921
|
+
const text = data.toString('utf8');
|
|
922
|
+
if (data.includes(0) || (text.includes('�') && !data.equals(Buffer.from(text, 'utf8'))))
|
|
923
|
+
return { binary: true, too_large: false, diff: '', additions: 0, deletions: 0 };
|
|
924
|
+
const diffLines = unifiedDiffFromEmpty(splitLines(text), `b/${label}`);
|
|
925
|
+
let diff = diffLines.join('\n') + (diffLines.length ? '\n' : '');
|
|
926
|
+
const tooLarge = Buffer.byteLength(diff, 'utf8') > DIFF_SIZE_LIMIT;
|
|
927
|
+
if (tooLarge)
|
|
928
|
+
diff = diff.slice(0, DIFF_SIZE_LIMIT);
|
|
929
|
+
const [additions, deletions] = diffStats(diff);
|
|
930
|
+
return { binary: false, too_large: tooLarge, diff, additions, deletions };
|
|
931
|
+
}
|
|
932
|
+
diff(workspace, path, kind = 'unstaged') {
|
|
933
|
+
const ctx = this.resolveContext(workspace);
|
|
934
|
+
if (!ctx)
|
|
935
|
+
throw new GitWorkspaceError('Workspace is not a Git repository');
|
|
936
|
+
if (kind !== 'unstaged' && kind !== 'staged')
|
|
937
|
+
throw new GitWorkspaceError('kind must be staged or unstaged');
|
|
938
|
+
const repoRel = GitRunner.repoRel(ctx, path);
|
|
939
|
+
const workspaceRel = GitRunner.workspaceRel(ctx, repoRel) ?? path;
|
|
940
|
+
const status = this.status(workspace);
|
|
941
|
+
const fileState = (status.files ?? []).find((f) => f.path === workspaceRel);
|
|
942
|
+
if (kind === 'unstaged' && fileState?.untracked)
|
|
943
|
+
return { path: workspaceRel, kind, ...GitRunner.syntheticUntrackedDiff(ctx.workspace, join(ctx.workspace, workspaceRel), workspaceRel) };
|
|
944
|
+
const args = ['diff', '--no-ext-diff', '--no-textconv', '--unified=3'];
|
|
945
|
+
if (kind === 'staged')
|
|
946
|
+
args.push('--cached');
|
|
947
|
+
args.push('--', repoRel);
|
|
948
|
+
let diff = this.run(ctx, args, { check: true, neutralizeFilterPrograms: true }).stdout;
|
|
949
|
+
const binary = diff.includes('Binary files ') || diff.includes('GIT binary patch');
|
|
950
|
+
const tooLarge = Buffer.byteLength(diff, 'utf8') > DIFF_SIZE_LIMIT;
|
|
951
|
+
if (tooLarge)
|
|
952
|
+
diff = diff.slice(0, DIFF_SIZE_LIMIT);
|
|
953
|
+
const [additions, deletions] = diffStats(diff);
|
|
954
|
+
return { path: workspaceRel, kind, binary, too_large: tooLarge, additions, deletions, diff: binary ? '' : diff };
|
|
955
|
+
}
|
|
956
|
+
// ── stage / unstage / discard ────────────────────────────────────────────
|
|
957
|
+
static cleanPaths(paths) {
|
|
958
|
+
const cleaned = [];
|
|
959
|
+
for (const p of paths) {
|
|
960
|
+
const value = str(p).trim();
|
|
961
|
+
if (value && !cleaned.includes(value))
|
|
962
|
+
cleaned.push(value);
|
|
963
|
+
}
|
|
964
|
+
if (!cleaned.length)
|
|
965
|
+
throw new GitWorkspaceError('At least one path is required');
|
|
966
|
+
return cleaned;
|
|
967
|
+
}
|
|
968
|
+
static pathspecs(ctx, paths) {
|
|
969
|
+
return GitRunner.cleanPaths(paths).map((p) => GitRunner.repoRel(ctx, p));
|
|
970
|
+
}
|
|
971
|
+
blockFilteredDestructiveWrite(ctx, message) {
|
|
972
|
+
if (this.destructiveEnabled() && this.hasRepoLocalFilters(ctx.repoRoot))
|
|
973
|
+
throw new GitWorkspaceError(message, 'filtered_path');
|
|
974
|
+
}
|
|
975
|
+
requireContext(workspace) {
|
|
976
|
+
const ctx = this.resolveContext(workspace);
|
|
977
|
+
if (!ctx)
|
|
978
|
+
throw new GitWorkspaceError('Workspace is not a Git repository', 'not_a_repo');
|
|
979
|
+
return ctx;
|
|
980
|
+
}
|
|
981
|
+
async stage(workspace, paths) {
|
|
982
|
+
const ctx = this.requireContext(workspace);
|
|
983
|
+
await this.withMutationLock(ctx, () => {
|
|
984
|
+
this.blockFilteredDestructiveWrite(ctx, 'Repository uses local Git filters; stage may corrupt index content. Use the terminal to stage manually.');
|
|
985
|
+
this.run(ctx, ['add', '--', ...GitRunner.pathspecs(ctx, paths)], { check: true, destructive: true, disableFilterAttributes: true });
|
|
986
|
+
});
|
|
987
|
+
return this.status(workspace);
|
|
988
|
+
}
|
|
989
|
+
async unstage(workspace, paths) {
|
|
990
|
+
const ctx = this.requireContext(workspace);
|
|
991
|
+
const specs = GitRunner.pathspecs(ctx, paths);
|
|
992
|
+
await this.withMutationLock(ctx, () => {
|
|
993
|
+
const result = this.run(ctx, ['restore', '--staged', '--', ...specs], { destructive: true });
|
|
994
|
+
if (result.status !== 0)
|
|
995
|
+
this.run(ctx, ['reset', 'HEAD', '--', ...specs], { check: true, destructive: true });
|
|
996
|
+
});
|
|
997
|
+
return this.status(workspace);
|
|
998
|
+
}
|
|
999
|
+
async discard(workspace, paths, opts = {}) {
|
|
1000
|
+
const ctx = this.requireContext(workspace);
|
|
1001
|
+
await this.withMutationLock(ctx, () => {
|
|
1002
|
+
this.blockFilteredDestructiveWrite(ctx, 'Repository uses local Git filters; discard may corrupt working-tree content. Use the terminal to discard manually.');
|
|
1003
|
+
const status = this.status(workspace);
|
|
1004
|
+
const byPath = new Map((status.files ?? []).map((f) => [f.path, f]));
|
|
1005
|
+
for (const path of GitRunner.cleanPaths(paths)) {
|
|
1006
|
+
const repoRel = GitRunner.repoRel(ctx, path);
|
|
1007
|
+
const workspaceRel = GitRunner.workspaceRel(ctx, repoRel) ?? path;
|
|
1008
|
+
const state = byPath.get(workspaceRel) ?? byPath.get(`${workspaceRel.replace(/\/+$/, '')}/`);
|
|
1009
|
+
if (state?.conflict)
|
|
1010
|
+
throw new GitWorkspaceError('Conflicted files cannot be discarded from this panel', 'conflict');
|
|
1011
|
+
if (state?.untracked) {
|
|
1012
|
+
if (!opts.deleteUntracked)
|
|
1013
|
+
throw new GitWorkspaceError('Untracked files require delete_untracked=true');
|
|
1014
|
+
const target = safeResolveWs(ctx.workspace, workspaceRel);
|
|
1015
|
+
let isDir = false;
|
|
1016
|
+
try {
|
|
1017
|
+
isDir = lstatSync(target).isDirectory();
|
|
1018
|
+
}
|
|
1019
|
+
catch {
|
|
1020
|
+
isDir = false;
|
|
1021
|
+
}
|
|
1022
|
+
if (isDir)
|
|
1023
|
+
rmtreeAnchored(ctx.workspace, target);
|
|
1024
|
+
else {
|
|
1025
|
+
try {
|
|
1026
|
+
unlinkAnchored(ctx.workspace, target);
|
|
1027
|
+
}
|
|
1028
|
+
catch (error) {
|
|
1029
|
+
if (error.code !== 'ENOENT')
|
|
1030
|
+
throw error;
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
continue;
|
|
1034
|
+
}
|
|
1035
|
+
this.run(ctx, ['restore', '--worktree', '--', repoRel], { check: true, destructive: true, disableFilterAttributes: true });
|
|
1036
|
+
}
|
|
1037
|
+
});
|
|
1038
|
+
return this.status(workspace);
|
|
1039
|
+
}
|
|
1040
|
+
// ── commit messages ──────────────────────────────────────────────────────
|
|
1041
|
+
stagedDiffText(ctx) {
|
|
1042
|
+
const diff = this.run(ctx, ['diff', '--cached', '--no-ext-diff', '--no-textconv', '--unified=3', '--', GitRunner.workspacePathspec(ctx)], { check: true, neutralizeFilterPrograms: true }).stdout;
|
|
1043
|
+
return GitRunner.capDiff(diff);
|
|
1044
|
+
}
|
|
1045
|
+
static capDiff(diff) {
|
|
1046
|
+
const encoded = Buffer.from(diff, 'utf8');
|
|
1047
|
+
if (encoded.length <= COMMIT_MESSAGE_DIFF_LIMIT)
|
|
1048
|
+
return [diff, false];
|
|
1049
|
+
return [encoded.subarray(0, COMMIT_MESSAGE_DIFF_LIMIT).toString('utf8'), true];
|
|
1050
|
+
}
|
|
1051
|
+
selectedTempIndexEnv(ctx, specs) {
|
|
1052
|
+
this.blockFilteredDestructiveWrite(ctx, 'Repository uses local Git filters; selected commit staging may corrupt index content. Use the terminal to commit manually.');
|
|
1053
|
+
const dir = mkdtempSync(join(tmpdir(), 'hermes-webui-git-index-'));
|
|
1054
|
+
const indexPath = join(dir, 'index');
|
|
1055
|
+
const env = { GIT_INDEX_FILE: indexPath };
|
|
1056
|
+
try {
|
|
1057
|
+
const head = this.run(ctx, ['rev-parse', '--verify', 'HEAD'], { env, destructive: true });
|
|
1058
|
+
if (head.status === 0)
|
|
1059
|
+
this.run(ctx, ['read-tree', 'HEAD'], { check: true, env, destructive: true });
|
|
1060
|
+
else
|
|
1061
|
+
this.run(ctx, ['read-tree', '--empty'], { check: true, env, destructive: true });
|
|
1062
|
+
this.run(ctx, ['add', '-A', '--', ...specs], { check: true, env, destructive: true, disableFilterAttributes: true });
|
|
1063
|
+
return [env, dir];
|
|
1064
|
+
}
|
|
1065
|
+
catch (error) {
|
|
1066
|
+
rmSync(dir, { recursive: true, force: true });
|
|
1067
|
+
throw error;
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
selectedFiles(ctx, paths) {
|
|
1071
|
+
const requested = GitRunner.cleanPaths(paths);
|
|
1072
|
+
const requestedSpecs = requested.map((p) => GitRunner.repoRel(ctx, p));
|
|
1073
|
+
const workspacePaths = requestedSpecs.map((spec, i) => GitRunner.workspaceRel(ctx, spec) ?? requested[i] ?? spec);
|
|
1074
|
+
const status = this.status(ctx.workspace);
|
|
1075
|
+
const byPath = new Map((status.files ?? []).map((f) => [f.path, f]));
|
|
1076
|
+
const specs = [];
|
|
1077
|
+
const selected = [];
|
|
1078
|
+
workspacePaths.forEach((path, i) => {
|
|
1079
|
+
const state = byPath.get(path);
|
|
1080
|
+
if (!state)
|
|
1081
|
+
return;
|
|
1082
|
+
if (state.conflict)
|
|
1083
|
+
throw new GitWorkspaceError('Resolve conflicts before committing selected files', 'conflict');
|
|
1084
|
+
if (state.staged || state.unstaged || state.untracked) {
|
|
1085
|
+
selected.push(state);
|
|
1086
|
+
for (const spec of [requestedSpecs[i] ?? '', state.old_path ? GitRunner.repoRel(ctx, state.old_path) : ''])
|
|
1087
|
+
if (spec && !specs.includes(spec))
|
|
1088
|
+
specs.push(spec);
|
|
1089
|
+
}
|
|
1090
|
+
});
|
|
1091
|
+
if (selected.length !== workspacePaths.length)
|
|
1092
|
+
throw new GitWorkspaceError('Selected paths have no committable changes');
|
|
1093
|
+
return [specs, workspacePaths, selected];
|
|
1094
|
+
}
|
|
1095
|
+
selectedDiffText(ctx, specs) {
|
|
1096
|
+
const [env, dir] = this.selectedTempIndexEnv(ctx, specs);
|
|
1097
|
+
try {
|
|
1098
|
+
return GitRunner.capDiff(this.run(ctx, ['diff', '--cached', '--no-ext-diff', '--no-textconv', '--unified=3', '--', ...specs], { check: true, env, destructive: true, disableFilterAttributes: true }).stdout);
|
|
1099
|
+
}
|
|
1100
|
+
finally {
|
|
1101
|
+
rmSync(dir, { recursive: true, force: true });
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
static fileLines(files, label) {
|
|
1105
|
+
const lines = files.slice(0, 80).map((item) => `- ${item.status || 'M'} ${item.path} (${item.binary ? 'binary' : `+${item.additions || 0} -${item.deletions || 0}`})`);
|
|
1106
|
+
if (files.length > 80)
|
|
1107
|
+
lines.push(`- ... ${files.length - 80} more ${label} file(s)`);
|
|
1108
|
+
return lines;
|
|
1109
|
+
}
|
|
1110
|
+
selectedCommitMessagePrompt(workspace, paths) {
|
|
1111
|
+
const ctx = this.requireContext(workspace);
|
|
1112
|
+
const [specs, , selected] = this.selectedFiles(ctx, paths);
|
|
1113
|
+
const [diff, truncated] = this.selectedDiffText(ctx, specs);
|
|
1114
|
+
if (!diff.trim())
|
|
1115
|
+
throw new GitWorkspaceError('No selected diff is available');
|
|
1116
|
+
const status = this.status(workspace);
|
|
1117
|
+
const userPrompt = `Write a commit message for the selected Git diff below.\n\nBranch: ${status.branch || 'HEAD'}\nSelected files (${selected.length}):\n${GitRunner.fileLines(selected, 'selected').join('\n')}${truncated ? '\n\nDiff was truncated for size; summarize only what is visible.\n' : '\n'}\nSelected diff:\n\`\`\`diff\n${diff}\n\`\`\``;
|
|
1118
|
+
return { system_prompt: COMMIT_MESSAGE_SYSTEM_PROMPT, user_prompt: userPrompt, truncated, status };
|
|
1119
|
+
}
|
|
1120
|
+
stagedCommitMessagePrompt(workspace) {
|
|
1121
|
+
const ctx = this.resolveContext(workspace);
|
|
1122
|
+
if (!ctx)
|
|
1123
|
+
throw new GitWorkspaceError('Workspace is not a Git repository');
|
|
1124
|
+
const status = this.status(workspace);
|
|
1125
|
+
if ((status.totals?.staged ?? 0) <= 0)
|
|
1126
|
+
throw new GitWorkspaceError('Stage changes before generating a commit message');
|
|
1127
|
+
const [diff, truncated] = this.stagedDiffText(ctx);
|
|
1128
|
+
if (!diff.trim())
|
|
1129
|
+
throw new GitWorkspaceError('No staged diff is available');
|
|
1130
|
+
const stagedFiles = (status.files ?? []).filter((f) => f.staged);
|
|
1131
|
+
const userPrompt = `Write a commit message for the staged Git diff below.\n\nBranch: ${status.branch || 'HEAD'}\nStaged files (${stagedFiles.length}):\n${GitRunner.fileLines(stagedFiles, 'staged').join('\n')}${truncated ? '\n\nDiff was truncated for size; summarize only what is visible.\n' : '\n'}\nStaged diff:\n\`\`\`diff\n${diff}\n\`\`\``;
|
|
1132
|
+
return { system_prompt: COMMIT_MESSAGE_SYSTEM_PROMPT, user_prompt: userPrompt, truncated, status };
|
|
1133
|
+
}
|
|
1134
|
+
// ── commit / remote ──────────────────────────────────────────────────────
|
|
1135
|
+
async commit(workspace, message) {
|
|
1136
|
+
const msg = str(message).trim();
|
|
1137
|
+
if (!msg)
|
|
1138
|
+
throw new GitWorkspaceError('Commit message is required');
|
|
1139
|
+
const ctx = this.requireContext(workspace);
|
|
1140
|
+
await this.withMutationLock(ctx, () => {
|
|
1141
|
+
this.run(ctx, ['commit', '-m', msg], { timeoutMs: 10_000, check: true, destructive: true, disableFilterAttributes: true });
|
|
1142
|
+
});
|
|
1143
|
+
const sha = this.run(ctx, ['rev-parse', '--short', 'HEAD'], { check: true }).stdout.trim();
|
|
1144
|
+
return { ok: true, commit: sha, status: this.status(workspace) };
|
|
1145
|
+
}
|
|
1146
|
+
async commitSelected(workspace, message, paths) {
|
|
1147
|
+
const msg = str(message).trim();
|
|
1148
|
+
if (!msg)
|
|
1149
|
+
throw new GitWorkspaceError('Commit message is required');
|
|
1150
|
+
const ctx = this.requireContext(workspace);
|
|
1151
|
+
const workspacePaths = await this.withMutationLock(ctx, () => {
|
|
1152
|
+
const [specs, wsPaths] = this.selectedFiles(ctx, paths);
|
|
1153
|
+
const [env, dir] = this.selectedTempIndexEnv(ctx, specs);
|
|
1154
|
+
try {
|
|
1155
|
+
const quiet = this.run(ctx, ['diff', '--cached', '--quiet', '--no-textconv', '--', ...specs], { env, destructive: true, disableFilterAttributes: true });
|
|
1156
|
+
if (quiet.status === 0)
|
|
1157
|
+
throw new GitWorkspaceError('Selected paths have no committable changes');
|
|
1158
|
+
this.run(ctx, ['commit', '-m', msg], { timeoutMs: 10_000, check: true, env, destructive: true, disableFilterAttributes: true });
|
|
1159
|
+
this.run(ctx, ['reset', '-q', 'HEAD', '--', ...specs], { check: true, destructive: true });
|
|
1160
|
+
}
|
|
1161
|
+
finally {
|
|
1162
|
+
rmSync(dir, { recursive: true, force: true });
|
|
1163
|
+
}
|
|
1164
|
+
return wsPaths;
|
|
1165
|
+
});
|
|
1166
|
+
const sha = this.run(ctx, ['rev-parse', '--short', 'HEAD'], { check: true }).stdout.trim();
|
|
1167
|
+
return { ok: true, commit: sha, paths: workspacePaths, status: this.status(workspace) };
|
|
1168
|
+
}
|
|
1169
|
+
branchName(ctx) {
|
|
1170
|
+
const branch = this.run(ctx, ['branch', '--show-current'], { check: true }).stdout.trim();
|
|
1171
|
+
if (!branch)
|
|
1172
|
+
throw new GitWorkspaceError('Cannot push from a detached HEAD');
|
|
1173
|
+
return branch;
|
|
1174
|
+
}
|
|
1175
|
+
async fetch(workspace) {
|
|
1176
|
+
const ctx = this.requireContext(workspace);
|
|
1177
|
+
const result = await this.withMutationLock(ctx, () => this.run(ctx, ['fetch', '--prune', '--no-recurse-submodules'], {
|
|
1178
|
+
timeoutMs: GIT_REMOTE_TIMEOUT_MS, check: true, forceDestructiveHardening: true, disableFilterAttributes: this.destructiveEnabled(), neutralizeFilterPrograms: true, neutralizeRemoteHelpers: true,
|
|
1179
|
+
}));
|
|
1180
|
+
return { ok: true, message: GitRunner.remoteMessage(result), status: this.status(workspace) };
|
|
1181
|
+
}
|
|
1182
|
+
async pull(workspace) {
|
|
1183
|
+
const ctx = this.requireContext(workspace);
|
|
1184
|
+
const result = await this.withMutationLock(ctx, () => {
|
|
1185
|
+
this.blockFilteredDestructiveWrite(ctx, 'Repository uses local Git filters; pull may corrupt working-tree content. Use the terminal to pull manually.');
|
|
1186
|
+
return this.run(ctx, ['pull', '--ff-only', '--no-recurse-submodules'], { timeoutMs: GIT_REMOTE_TIMEOUT_MS, check: true, destructive: true, disableFilterAttributes: true, neutralizeFilterPrograms: true, neutralizeRemoteHelpers: true });
|
|
1187
|
+
});
|
|
1188
|
+
return { ok: true, message: GitRunner.remoteMessage(result), status: this.status(workspace) };
|
|
1189
|
+
}
|
|
1190
|
+
async push(workspace) {
|
|
1191
|
+
const ctx = this.requireContext(workspace);
|
|
1192
|
+
const result = await this.withMutationLock(ctx, () => {
|
|
1193
|
+
const status = this.status(workspace);
|
|
1194
|
+
const args = ['push'];
|
|
1195
|
+
if (!status.upstream) {
|
|
1196
|
+
const branch = this.branchName(ctx);
|
|
1197
|
+
const remotes = this.run(ctx, ['remote'], { check: true }).stdout.split(/\s+/).filter(Boolean);
|
|
1198
|
+
if (!remotes.includes('origin'))
|
|
1199
|
+
throw new GitWorkspaceError('No upstream branch or origin remote is configured', 'no_upstream');
|
|
1200
|
+
args.push('-u', 'origin', branch);
|
|
1201
|
+
}
|
|
1202
|
+
return this.run(ctx, args, { timeoutMs: GIT_REMOTE_TIMEOUT_MS, check: true, destructive: true, disableFilterAttributes: true, neutralizeFilterPrograms: true, neutralizeRemoteHelpers: true });
|
|
1203
|
+
});
|
|
1204
|
+
return { ok: true, message: GitRunner.remoteMessage(result), status: this.status(workspace) };
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
export const COMMIT_MESSAGE_SYSTEM_PROMPT = `When writing commit messages, PR titles, or PR descriptions:
|
|
1208
|
+
|
|
1209
|
+
- Inspect the staged diff before suggesting a commit message.
|
|
1210
|
+
- Do not use vague subjects like "update", "improve", "refine", "misc changes", "fix stuff", or "various changes".
|
|
1211
|
+
- For large commits, write a concise subject plus a short body with 2-5 bullets summarizing the main areas changed.
|
|
1212
|
+
- The subject should describe the actual user-facing result or bug fixed, not just broad implementation activity.
|
|
1213
|
+
- Keep wording short, clear, and natural.
|
|
1214
|
+
- Never mention AI, Cursor, Zed, agents, or similar tooling in commits, branch names, PR titles, or PR descriptions.
|
|
1215
|
+
- Never add your own thoughts or questions into the commit message, the commit message is definitive in nature.
|
|
1216
|
+
|
|
1217
|
+
Return only the commit message text. Do not wrap it in Markdown fences.`;
|
|
1218
|
+
export function cleanGeneratedCommitMessage(message) {
|
|
1219
|
+
let text = str(message).trim();
|
|
1220
|
+
if (text.startsWith('```')) {
|
|
1221
|
+
let lines = text.split('\n');
|
|
1222
|
+
if (lines[0]?.startsWith('```'))
|
|
1223
|
+
lines = lines.slice(1);
|
|
1224
|
+
if (lines.length && lines[lines.length - 1]?.trim() === '```')
|
|
1225
|
+
lines = lines.slice(0, -1);
|
|
1226
|
+
text = lines.join('\n').trim();
|
|
1227
|
+
}
|
|
1228
|
+
if ((text.startsWith('"') && text.endsWith('"')) || (text.startsWith("'") && text.endsWith("'")))
|
|
1229
|
+
text = text.slice(1, -1).trim();
|
|
1230
|
+
return text;
|
|
1231
|
+
}
|
|
1232
|
+
/** Ensure a git repository exists at `dir` (tests and the worktree helper). */
|
|
1233
|
+
export function ensureDir(dir) {
|
|
1234
|
+
mkdirSync(dir, { recursive: true });
|
|
1235
|
+
}
|
|
1236
|
+
//# sourceMappingURL=git.js.map
|