@oh-my-pi/pi-coding-agent 6.9.69 → 8.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/CHANGELOG.md +219 -51
- package/README.md +1 -1
- package/docs/hooks.md +2 -2
- package/docs/sdk.md +1 -1
- package/examples/sdk/04-skills.ts +1 -1
- package/package.json +10 -10
- package/scripts/format-prompts.ts +143 -0
- package/scripts/generate-template.ts +1 -1
- package/src/cli/args.ts +3 -3
- package/src/cli/config-cli.ts +4 -4
- package/src/cli/file-processor.ts +3 -3
- package/src/cli/list-models.ts +2 -2
- package/src/cli/plugin-cli.ts +3 -3
- package/src/cli/session-picker.ts +2 -2
- package/src/cli/setup-cli.ts +2 -2
- package/src/cli/stats-cli.ts +1 -1
- package/src/cli/update-cli.ts +2 -2
- package/src/{core → config}/keybindings.ts +1 -1
- package/src/{core → config}/model-registry.ts +8 -1
- package/src/{core → config}/model-resolver.ts +3 -3
- package/src/{core → config}/prompt-templates.ts +2 -2
- package/src/{core → config}/settings-manager.ts +6 -6
- package/src/{core/cursor/exec-bridge.ts → cursor.ts} +4 -4
- package/src/discovery/agents-md.ts +4 -4
- package/src/discovery/builtin.ts +17 -17
- package/src/discovery/claude.ts +12 -12
- package/src/discovery/cline.ts +6 -6
- package/src/discovery/codex.ts +21 -21
- package/src/discovery/cursor.ts +9 -9
- package/src/discovery/gemini.ts +9 -9
- package/src/discovery/github.ts +6 -6
- package/src/discovery/helpers.ts +4 -4
- package/src/discovery/index.ts +16 -16
- package/src/discovery/mcp-json.ts +4 -4
- package/src/discovery/ssh.ts +4 -4
- package/src/discovery/vscode.ts +4 -4
- package/src/discovery/windsurf.ts +6 -6
- package/src/{core/tools/exa → exa}/company.ts +2 -3
- package/src/{core/tools/exa → exa}/index.ts +2 -2
- package/src/{core/tools/exa → exa}/linkedin.ts +2 -3
- package/src/{core/tools/exa → exa}/mcp-client.ts +2 -2
- package/src/{core/tools/exa → exa}/render.ts +3 -3
- package/src/{core/tools/exa → exa}/researcher.ts +1 -1
- package/src/{core/tools/exa → exa}/search.ts +2 -10
- package/src/{core/tools/exa → exa}/websets.ts +1 -1
- package/src/{core → exec}/bash-executor.ts +23 -7
- package/src/{core → export}/custom-share.ts +1 -1
- package/src/{core/export-html → export/html}/index.ts +3 -3
- package/src/{core → export}/ttsr.ts +2 -2
- package/src/{core → extensibility}/custom-commands/bundled/review/index.ts +4 -4
- package/src/{core → extensibility}/custom-commands/loader.ts +3 -3
- package/src/{core → extensibility}/custom-commands/types.ts +1 -1
- package/src/{core → extensibility}/custom-tools/loader.ts +9 -9
- package/src/{core → extensibility}/custom-tools/types.ts +6 -6
- package/src/{core → extensibility}/custom-tools/wrapper.ts +1 -1
- package/src/{core → extensibility}/extensions/loader.ts +8 -8
- package/src/{core → extensibility}/extensions/runner.ts +3 -3
- package/src/{core → extensibility}/extensions/types.ts +15 -15
- package/src/{core → extensibility}/extensions/wrapper.ts +1 -1
- package/src/{core → extensibility}/hooks/index.ts +1 -1
- package/src/{core → extensibility}/hooks/loader.ts +7 -7
- package/src/{core → extensibility}/hooks/runner.ts +4 -4
- package/src/{core → extensibility}/hooks/types.ts +9 -9
- package/src/{core → extensibility}/plugins/doctor.ts +1 -1
- package/src/{core → extensibility}/plugins/installer.ts +2 -3
- package/src/{core → extensibility}/plugins/paths.ts +1 -1
- package/src/{core → extensibility}/skills.ts +8 -48
- package/src/{core → extensibility}/slash-commands.ts +6 -6
- package/src/index.ts +127 -128
- package/src/internal-urls/agent-protocol.ts +126 -0
- package/src/internal-urls/artifact-protocol.ts +93 -0
- package/src/internal-urls/index.ts +28 -0
- package/src/internal-urls/json-query.ts +126 -0
- package/src/internal-urls/router.ts +69 -0
- package/src/internal-urls/rule-protocol.ts +56 -0
- package/src/internal-urls/skill-protocol.ts +112 -0
- package/src/internal-urls/types.ts +48 -0
- package/src/{core/python-executor.ts → ipy/executor.ts} +74 -13
- package/src/{core/python-gateway-coordinator.ts → ipy/gateway-coordinator.ts} +40 -270
- package/src/{core/python-kernel.ts → ipy/kernel.ts} +38 -10
- package/src/{core/python-prelude.py → ipy/prelude.py} +201 -8
- package/src/ipy/prelude.ts +3 -0
- package/src/{core/tools/lsp → lsp}/client.ts +7 -6
- package/src/{core/tools/lsp → lsp}/clients/biome-client.ts +1 -1
- package/src/{core/tools/lsp → lsp}/clients/index.ts +1 -1
- package/src/{core/tools/lsp → lsp}/clients/lsp-linter-client.ts +4 -4
- package/src/{core/tools/lsp → lsp}/config.ts +1 -1
- package/src/{core/tools/lsp → lsp}/index.ts +29 -17
- package/src/{core/tools/lsp → lsp}/render.ts +2 -2
- package/src/{core/tools/lsp → lsp}/types.ts +14 -16
- package/src/{core/tools/lsp → lsp}/utils.ts +1 -1
- package/src/main.ts +12 -12
- package/src/{core/mcp → mcp}/config.ts +8 -8
- package/src/{core/mcp → mcp}/loader.ts +5 -6
- package/src/{core/mcp → mcp}/manager.ts +2 -2
- package/src/{core/mcp → mcp}/tool-bridge.ts +35 -6
- package/src/{core/mcp → mcp}/tool-cache.ts +1 -1
- package/src/{core/mcp → mcp}/transports/http.ts +7 -1
- package/src/{core/mcp → mcp}/transports/stdio.ts +1 -1
- package/src/{core/mcp → mcp}/types.ts +1 -1
- package/src/migrations.ts +2 -2
- package/src/modes/{interactive/components → components}/armin.ts +1 -1
- package/src/modes/{interactive/components → components}/assistant-message.ts +1 -1
- package/src/modes/{interactive/components → components}/bash-execution.ts +37 -29
- package/src/modes/{interactive/components → components}/bordered-loader.ts +1 -1
- package/src/modes/{interactive/components → components}/branch-summary-message.ts +2 -2
- package/src/modes/{interactive/components → components}/compaction-summary-message.ts +2 -2
- package/src/modes/{interactive/components → components}/custom-message.ts +3 -3
- package/src/modes/{interactive/components → components}/diff.ts +1 -1
- package/src/modes/{interactive/components → components}/dynamic-border.ts +1 -1
- package/src/modes/{interactive/components → components}/extensions/extension-dashboard.ts +3 -3
- package/src/modes/{interactive/components → components}/extensions/extension-list.ts +2 -2
- package/src/modes/{interactive/components → components}/extensions/inspector-panel.ts +1 -1
- package/src/modes/{interactive/components → components}/extensions/state-manager.ts +11 -17
- package/src/modes/{interactive/components → components}/extensions/types.ts +1 -1
- package/src/modes/{interactive/components → components}/footer.ts +3 -3
- package/src/modes/{interactive/components → components}/history-search.ts +2 -2
- package/src/modes/{interactive/components → components}/hook-editor.ts +1 -1
- package/src/modes/{interactive/components → components}/hook-input.ts +1 -1
- package/src/modes/{interactive/components → components}/hook-message.ts +3 -3
- package/src/modes/{interactive/components → components}/hook-selector.ts +1 -1
- package/src/modes/{interactive/components → components}/keybinding-hints.ts +2 -2
- package/src/modes/{interactive/components → components}/login-dialog.ts +1 -1
- package/src/modes/{interactive/components → components}/model-selector.ts +5 -5
- package/src/modes/{interactive/components → components}/oauth-selector.ts +2 -2
- package/src/modes/{interactive/components → components}/plugin-settings.ts +3 -3
- package/src/modes/{interactive/components → components}/python-execution.ts +35 -24
- package/src/modes/{interactive/components → components}/queue-mode-selector.ts +1 -1
- package/src/modes/{interactive/components → components}/read-tool-group.ts +2 -2
- package/src/modes/{interactive/components → components}/session-selector.ts +3 -3
- package/src/modes/{interactive/components → components}/settings-defs.ts +2 -2
- package/src/modes/{interactive/components → components}/settings-selector.ts +2 -2
- package/src/modes/{interactive/components → components}/show-images-selector.ts +1 -1
- package/src/modes/{interactive/components → components}/status-line/segments.ts +2 -2
- package/src/modes/{interactive/components → components}/status-line/separators.ts +1 -1
- package/src/modes/{interactive/components → components}/status-line/types.ts +2 -2
- package/src/modes/{interactive/components → components}/status-line-segment-editor.ts +2 -2
- package/src/modes/{interactive/components → components}/status-line.ts +3 -3
- package/src/modes/{interactive/components → components}/theme-selector.ts +1 -1
- package/src/modes/{interactive/components → components}/thinking-selector.ts +1 -1
- package/src/modes/{interactive/components → components}/todo-display.ts +3 -4
- package/src/modes/{interactive/components → components}/todo-reminder.ts +2 -2
- package/src/modes/{interactive/components → components}/tool-execution.ts +8 -8
- package/src/modes/{interactive/components → components}/tree-selector.ts +3 -3
- package/src/modes/{interactive/components → components}/ttsr-notification.ts +2 -2
- package/src/modes/{interactive/components → components}/user-message-selector.ts +1 -1
- package/src/modes/{interactive/components → components}/user-message.ts +1 -1
- package/src/modes/{interactive/components → components}/welcome.ts +2 -2
- package/src/modes/{interactive/controllers → controllers}/command-controller.ts +381 -30
- package/src/modes/{interactive/controllers → controllers}/event-controller.ts +9 -9
- package/src/modes/{interactive/controllers → controllers}/extension-ui-controller.ts +8 -8
- package/src/modes/{interactive/controllers → controllers}/input-controller.ts +61 -13
- package/src/modes/{interactive/controllers → controllers}/selector-controller.ts +16 -16
- package/src/modes/index.ts +1 -1
- package/src/modes/{interactive/interactive-mode.ts → interactive-mode.ts} +20 -15
- package/src/modes/print-mode.ts +1 -1
- package/src/modes/rpc/rpc-client.ts +3 -3
- package/src/modes/rpc/rpc-mode.ts +3 -3
- package/src/modes/rpc/rpc-types.ts +3 -3
- package/src/modes/{interactive/theme → theme}/theme.ts +1 -1
- package/src/modes/{interactive/types.ts → types.ts} +10 -10
- package/src/modes/{interactive/utils → utils}/ui-helpers.ts +20 -27
- package/src/{core/tools/patch → patch}/applicator.ts +1 -1
- package/src/{core/tools/patch → patch}/diff.ts +1 -1
- package/src/{core/tools/patch → patch}/index.ts +31 -36
- package/src/{core/tools/patch → patch}/shared.ts +9 -6
- package/src/prompts/agents/explore.md +83 -46
- package/src/prompts/agents/init.md +9 -4
- package/src/prompts/agents/plan.md +8 -7
- package/src/prompts/agents/reviewer.md +36 -18
- package/src/prompts/agents/task.md +4 -4
- package/src/prompts/compaction/branch-summary-preamble.md +0 -1
- package/src/prompts/review-request.md +0 -1
- package/src/prompts/system/custom-system-prompt.md +2 -14
- package/src/prompts/system/file-operations.md +0 -2
- package/src/prompts/system/system-prompt.md +182 -171
- package/src/prompts/system/web-search.md +26 -0
- package/src/prompts/tools/ask.md +31 -24
- package/src/prompts/tools/bash.md +20 -17
- package/src/prompts/tools/calculator.md +9 -5
- package/src/prompts/tools/fetch.md +16 -0
- package/src/prompts/tools/find.md +15 -5
- package/src/prompts/tools/gemini-image.md +21 -6
- package/src/prompts/tools/grep.md +28 -12
- package/src/prompts/tools/lsp.md +35 -14
- package/src/prompts/tools/patch.md +39 -41
- package/src/prompts/tools/python.md +59 -77
- package/src/prompts/tools/read.md +23 -22
- package/src/prompts/tools/replace.md +19 -12
- package/src/prompts/tools/ssh.md +21 -28
- package/src/prompts/tools/task.md +54 -44
- package/src/prompts/tools/todo-write.md +52 -163
- package/src/prompts/tools/web-search.md +16 -9
- package/src/prompts/tools/write.md +13 -2
- package/src/{core/sdk.ts → sdk.ts} +65 -34
- package/src/{core → session}/agent-session.ts +157 -41
- package/src/{core → session}/agent-storage.ts +2 -2
- package/src/session/artifacts.ts +110 -0
- package/src/{core → session}/auth-storage.ts +525 -203
- package/src/{core → session}/compaction/branch-summarization.ts +5 -5
- package/src/{core → session}/compaction/compaction.ts +6 -6
- package/src/{core → session}/compaction/utils.ts +3 -3
- package/src/{core → session}/history-storage.ts +1 -1
- package/src/{core → session}/messages.ts +6 -8
- package/src/{core → session}/session-manager.ts +2 -2
- package/src/{core → session}/storage-migration.ts +2 -2
- package/src/session/streaming-output.ts +177 -0
- package/src/{core/ssh → ssh}/connection-manager.ts +1 -1
- package/src/{core/ssh → ssh}/ssh-executor.ts +19 -4
- package/src/{core/ssh → ssh}/sshfs-mount.ts +1 -1
- package/src/{core/system-prompt.ts → system-prompt.ts} +8 -37
- package/src/{core/tools/task → task}/agents.ts +8 -8
- package/src/{core/tools/task → task}/commands.ts +5 -6
- package/src/{core/tools/task → task}/discovery.ts +3 -3
- package/src/{core/tools/task → task}/executor.ts +34 -44
- package/src/{core/tools/task → task}/index.ts +206 -50
- package/src/{core/tools/task → task}/render.ts +80 -23
- package/src/{core/tools/task → task}/subprocess-tool-registry.ts +1 -1
- package/src/task/template.ts +47 -0
- package/src/{core/tools/task → task}/types.ts +19 -27
- package/src/{core/tools/task → task}/worker-protocol.ts +8 -4
- package/src/{core/tools/task → task}/worker.ts +34 -29
- package/src/task/worktree.ts +166 -0
- package/src/{core/tools → tools}/ask.ts +13 -21
- package/src/{core/tools → tools}/bash-interceptor.ts +1 -1
- package/src/{core/tools → tools}/bash.ts +61 -63
- package/src/{core/tools → tools}/calculator.ts +4 -4
- package/src/{core/tools → tools}/complete.ts +1 -1
- package/src/{core/tools → tools}/context.ts +2 -2
- package/src/{core/tools/web-fetch.ts → tools/fetch.ts} +97 -76
- package/src/{core/tools → tools}/find.ts +96 -107
- package/src/{core/tools → tools}/gemini-image.ts +420 -29
- package/src/{core/tools → tools}/grep.ts +155 -164
- package/src/{core/tools → tools}/index.ts +63 -56
- package/src/tools/list-limit.ts +40 -0
- package/src/{core/tools → tools}/ls.ts +44 -35
- package/src/{core/tools → tools}/notebook.ts +3 -3
- package/src/tools/output-meta.ts +443 -0
- package/src/tools/output-utils.ts +63 -0
- package/src/{core/tools → tools}/python.ts +106 -89
- package/src/tools/read.ts +882 -0
- package/src/{core/tools → tools}/render-utils.ts +1 -1
- package/src/{core/tools → tools}/renderers.ts +8 -10
- package/src/{core/tools → tools}/review.ts +2 -2
- package/src/{core/tools → tools}/ssh.ts +56 -59
- package/src/{core/tools → tools}/todo-write.ts +12 -23
- package/src/tools/tool-errors.ts +95 -0
- package/src/tools/tool-result.ts +92 -0
- package/src/{core/tools → tools}/truncate.ts +2 -2
- package/src/{core/tools → tools}/write.ts +15 -13
- package/src/utils/changelog.ts +1 -1
- package/src/{core → utils}/file-mentions.ts +4 -4
- package/src/utils/image-convert.ts +1 -1
- package/src/utils/image-resize.ts +1 -1
- package/src/utils/shell.ts +1 -1
- package/src/{core → utils}/title-generator.ts +4 -4
- package/src/utils/tools-manager.ts +1 -1
- package/src/{core/tools/web-scrapers → web/scrapers}/choosealicense.ts +1 -1
- package/src/{core/tools/web-scrapers → web/scrapers}/twitter.ts +3 -2
- package/src/{core/tools/web-scrapers → web/scrapers}/types.ts +4 -2
- package/src/{core/tools/web-scrapers → web/scrapers}/utils.ts +1 -1
- package/src/{core/tools/web-scrapers → web/scrapers}/youtube.ts +14 -13
- package/src/{core/tools/web-search → web/search}/auth.ts +4 -4
- package/src/{core/tools/web-search → web/search}/index.ts +22 -71
- package/src/{core/tools/web-search → web/search}/providers/anthropic.ts +7 -10
- package/src/{core/tools/web-search → web/search}/providers/exa.ts +2 -2
- package/src/{core/tools/web-search → web/search}/providers/perplexity.ts +4 -16
- package/src/{core/tools/web-search → web/search}/render.ts +3 -3
- package/scripts/migrate-sessions.sh +0 -93
- package/src/core/index.ts +0 -56
- package/src/core/python-prelude.ts +0 -3
- package/src/core/ssh-executor.ts +0 -5
- package/src/core/streaming-output.ts +0 -115
- package/src/core/tools/output.ts +0 -519
- package/src/core/tools/read.ts +0 -717
- package/src/core/tools/task/template.ts +0 -37
- package/src/prompts/tools/output.md +0 -47
- package/src/prompts/tools/web-fetch.md +0 -9
- /package/src/{core/tools/exa → exa}/types.ts +0 -0
- /package/src/{core → exec}/exec.ts +0 -0
- /package/src/{core/export-html → export/html}/template.css +0 -0
- /package/src/{core/export-html → export/html}/template.generated.ts +0 -0
- /package/src/{core/export-html → export/html}/template.html +0 -0
- /package/src/{core/export-html → export/html}/template.js +0 -0
- /package/src/{core/export-html → export/html}/template.macro.ts +0 -0
- /package/src/{core/export-html → export/html}/vendor/highlight.min.js +0 -0
- /package/src/{core/export-html → export/html}/vendor/marked.min.js +0 -0
- /package/src/{core → extensibility}/custom-commands/index.ts +0 -0
- /package/src/{core → extensibility}/custom-tools/index.ts +0 -0
- /package/src/{core → extensibility}/extensions/index.ts +0 -0
- /package/src/{core → extensibility}/hooks/tool-wrapper.ts +0 -0
- /package/src/{core → extensibility}/plugins/index.ts +0 -0
- /package/src/{core → extensibility}/plugins/loader.ts +0 -0
- /package/src/{core → extensibility}/plugins/manager.ts +0 -0
- /package/src/{core → extensibility}/plugins/parser.ts +0 -0
- /package/src/{core → extensibility}/plugins/types.ts +0 -0
- /package/src/{core/python-modules.ts → ipy/modules.ts} +0 -0
- /package/src/{core/tools/lsp → lsp}/defaults.json +0 -0
- /package/src/{core/tools/lsp → lsp}/edits.ts +0 -0
- /package/src/{core/tools/lsp → lsp}/lspmux.ts +0 -0
- /package/src/{core/tools/lsp → lsp}/rust-analyzer.ts +0 -0
- /package/src/{core/mcp → mcp}/client.ts +0 -0
- /package/src/{core/mcp → mcp}/index.ts +0 -0
- /package/src/{core/mcp → mcp}/json-rpc.ts +0 -0
- /package/src/{core/mcp → mcp}/transports/index.ts +0 -0
- /package/src/modes/{interactive/components → components}/countdown-timer.ts +0 -0
- /package/src/modes/{interactive/components → components}/custom-editor.ts +0 -0
- /package/src/modes/{interactive/components → components}/extensions/index.ts +0 -0
- /package/src/modes/{interactive/components → components}/index.ts +0 -0
- /package/src/modes/{interactive/components → components}/status-line/index.ts +0 -0
- /package/src/modes/{interactive/components → components}/status-line/presets.ts +0 -0
- /package/src/modes/{interactive/components → components}/visual-truncate.ts +0 -0
- /package/src/modes/{interactive/theme → theme}/dark.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/alabaster.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/amethyst.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/anthracite.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/basalt.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/birch.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-abyss.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-arctic.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-aurora.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-catppuccin.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-cavern.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-copper.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-cosmos.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-cyberpunk.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-dracula.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-eclipse.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-ember.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-equinox.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-forest.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-github.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-gruvbox.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-lavender.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-lunar.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-midnight.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-monochrome.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-monokai.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-nebula.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-nord.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-ocean.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-one.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-rainforest.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-reef.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-retro.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-rose-pine.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-sakura.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-slate.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-solarized.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-solstice.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-starfall.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-sunset.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-swamp.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-synthwave.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-taiga.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-terminal.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-tokyo-night.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-tundra.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-twilight.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/dark-volcanic.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/graphite.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/index.ts +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-arctic.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-aurora-day.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-canyon.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-catppuccin.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-cirrus.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-coral.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-cyberpunk.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-dawn.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-dunes.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-eucalyptus.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-forest.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-frost.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-github.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-glacier.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-gruvbox.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-haze.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-honeycomb.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-lagoon.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-lavender.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-meadow.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-mint.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-monochrome.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-ocean.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-one.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-opal.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-orchard.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-paper.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-prism.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-retro.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-sand.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-savanna.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-solarized.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-soleil.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-sunset.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-synthwave.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-tokyo-night.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-wetland.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/light-zenith.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/limestone.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/mahogany.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/marble.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/obsidian.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/onyx.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/pearl.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/porcelain.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/quartz.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/sandstone.json +0 -0
- /package/src/modes/{interactive/theme → theme}/defaults/titanium.json +0 -0
- /package/src/modes/{interactive/theme → theme}/light.json +0 -0
- /package/src/modes/{interactive/theme → theme}/theme-schema.json +0 -0
- /package/src/{core/tools/patch → patch}/fuzzy.ts +0 -0
- /package/src/{core/tools/patch → patch}/normalize.ts +0 -0
- /package/src/{core/tools/patch → patch}/normative.ts +0 -0
- /package/src/{core/tools/patch → patch}/parser.ts +0 -0
- /package/src/{core/tools/patch → patch}/types.ts +0 -0
- /package/src/{core → session}/compaction/index.ts +0 -0
- /package/src/{core → session}/session-storage.ts +0 -0
- /package/src/{core/tools/task → task}/name-generator.ts +0 -0
- /package/src/{core/tools/task → task}/omp-command.ts +0 -0
- /package/src/{core/tools/task → task}/parallel.ts +0 -0
- /package/src/{core/tools → tools}/jtd-to-json-schema.ts +0 -0
- /package/src/{core/tools → tools}/path-utils.ts +0 -0
- /package/src/{core → utils}/event-bus.ts +0 -0
- /package/src/{core → utils}/frontmatter.ts +0 -0
- /package/src/{core → utils}/terminal-notify.ts +0 -0
- /package/src/{core → utils}/timings.ts +0 -0
- /package/src/{core → utils}/utils.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/artifacthub.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/arxiv.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/aur.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/biorxiv.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/bluesky.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/brew.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/cheatsh.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/chocolatey.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/cisa-kev.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/clojars.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/coingecko.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/crates-io.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/crossref.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/devto.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/discogs.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/discourse.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/dockerhub.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/fdroid.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/firefox-addons.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/flathub.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/github-gist.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/github.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/gitlab.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/go-pkg.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/hackage.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/hackernews.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/hex.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/huggingface.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/iacr.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/index.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/jetbrains-marketplace.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/lemmy.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/lobsters.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/mastodon.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/maven.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/mdn.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/metacpan.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/musicbrainz.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/npm.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/nuget.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/nvd.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/ollama.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/open-vsx.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/opencorporates.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/openlibrary.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/orcid.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/osv.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/packagist.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/pub-dev.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/pubmed.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/pypi.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/rawg.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/readthedocs.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/reddit.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/repology.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/rfc.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/rubygems.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/searchcode.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/sec-edgar.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/semantic-scholar.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/snapcraft.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/sourcegraph.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/spdx.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/spotify.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/stackoverflow.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/terraform.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/tldr.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/vimeo.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/vscode-marketplace.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/w3c.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/wikidata.ts +0 -0
- /package/src/{core/tools/web-scrapers → web/scrapers}/wikipedia.ts +0 -0
- /package/src/{core/tools/web-search → web/search}/types.ts +0 -0
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
type KernelExecuteResult,
|
|
7
7
|
type PreludeHelper,
|
|
8
8
|
PythonKernel,
|
|
9
|
-
} from "
|
|
10
|
-
import { OutputSink } from "
|
|
9
|
+
} from "$c/ipy/kernel";
|
|
10
|
+
import { OutputSink } from "$c/session/streaming-output";
|
|
11
11
|
export type PythonKernelMode = "session" | "per-call";
|
|
12
12
|
|
|
13
13
|
export interface PythonExecutorOptions {
|
|
@@ -27,6 +27,13 @@ export interface PythonExecutorOptions {
|
|
|
27
27
|
reset?: boolean;
|
|
28
28
|
/** Use shared gateway across pi instances (default: true) */
|
|
29
29
|
useSharedGateway?: boolean;
|
|
30
|
+
/** Session file path for accessing task outputs */
|
|
31
|
+
sessionFile?: string;
|
|
32
|
+
/** Artifacts directory for $ARTIFACTS env var and artifact storage */
|
|
33
|
+
artifactsDir?: string;
|
|
34
|
+
/** Artifact path/id for full output storage */
|
|
35
|
+
artifactPath?: string;
|
|
36
|
+
artifactId?: string;
|
|
30
37
|
}
|
|
31
38
|
|
|
32
39
|
export interface PythonKernelExecutor {
|
|
@@ -42,8 +49,16 @@ export interface PythonResult {
|
|
|
42
49
|
cancelled: boolean;
|
|
43
50
|
/** Whether the output was truncated */
|
|
44
51
|
truncated: boolean;
|
|
45
|
-
/**
|
|
46
|
-
|
|
52
|
+
/** Artifact ID if full output was saved to artifact storage */
|
|
53
|
+
artifactId?: string;
|
|
54
|
+
/** Total number of lines in the output stream */
|
|
55
|
+
totalLines: number;
|
|
56
|
+
/** Total number of bytes in the output stream */
|
|
57
|
+
totalBytes: number;
|
|
58
|
+
/** Number of lines included in the output text */
|
|
59
|
+
outputLines: number;
|
|
60
|
+
/** Number of bytes included in the output text */
|
|
61
|
+
outputBytes: number;
|
|
47
62
|
/** Rich display outputs captured from display_data/execute_result */
|
|
48
63
|
displayOutputs: KernelDisplayOutput[];
|
|
49
64
|
/** Whether stdin was requested */
|
|
@@ -79,6 +94,7 @@ export async function warmPythonEnvironment(
|
|
|
79
94
|
cwd: string,
|
|
80
95
|
sessionId?: string,
|
|
81
96
|
useSharedGateway?: boolean,
|
|
97
|
+
sessionFile?: string,
|
|
82
98
|
): Promise<{ ok: boolean; reason?: string; docs: PreludeHelper[] }> {
|
|
83
99
|
try {
|
|
84
100
|
await ensureKernelAvailable(cwd);
|
|
@@ -97,6 +113,7 @@ export async function warmPythonEnvironment(
|
|
|
97
113
|
cwd,
|
|
98
114
|
async (kernel) => kernel.introspectPrelude(),
|
|
99
115
|
useSharedGateway,
|
|
116
|
+
sessionFile,
|
|
100
117
|
);
|
|
101
118
|
cachedPreludeDocs = docs;
|
|
102
119
|
return { ok: true, docs };
|
|
@@ -115,8 +132,21 @@ export function resetPreludeDocsCache(): void {
|
|
|
115
132
|
cachedPreludeDocs = null;
|
|
116
133
|
}
|
|
117
134
|
|
|
118
|
-
async function createKernelSession(
|
|
119
|
-
|
|
135
|
+
async function createKernelSession(
|
|
136
|
+
sessionId: string,
|
|
137
|
+
cwd: string,
|
|
138
|
+
useSharedGateway?: boolean,
|
|
139
|
+
sessionFile?: string,
|
|
140
|
+
artifactsDir?: string,
|
|
141
|
+
): Promise<KernelSession> {
|
|
142
|
+
const env: Record<string, string> | undefined =
|
|
143
|
+
sessionFile || artifactsDir
|
|
144
|
+
? {
|
|
145
|
+
...(sessionFile ? { OMP_SESSION_FILE: sessionFile } : {}),
|
|
146
|
+
...(artifactsDir ? { ARTIFACTS: artifactsDir } : {}),
|
|
147
|
+
}
|
|
148
|
+
: undefined;
|
|
149
|
+
const kernel = await PythonKernel.start({ cwd, useSharedGateway, env });
|
|
120
150
|
const session: KernelSession = {
|
|
121
151
|
id: sessionId,
|
|
122
152
|
kernel,
|
|
@@ -137,7 +167,13 @@ async function createKernelSession(sessionId: string, cwd: string, useSharedGate
|
|
|
137
167
|
return session;
|
|
138
168
|
}
|
|
139
169
|
|
|
140
|
-
async function restartKernelSession(
|
|
170
|
+
async function restartKernelSession(
|
|
171
|
+
session: KernelSession,
|
|
172
|
+
cwd: string,
|
|
173
|
+
useSharedGateway?: boolean,
|
|
174
|
+
sessionFile?: string,
|
|
175
|
+
artifactsDir?: string,
|
|
176
|
+
): Promise<void> {
|
|
141
177
|
session.restartCount += 1;
|
|
142
178
|
if (session.restartCount > 1) {
|
|
143
179
|
throw new Error("Python kernel restarted too many times in this session");
|
|
@@ -147,7 +183,14 @@ async function restartKernelSession(session: KernelSession, cwd: string, useShar
|
|
|
147
183
|
} catch (err) {
|
|
148
184
|
logger.warn("Failed to shutdown crashed kernel", { error: err instanceof Error ? err.message : String(err) });
|
|
149
185
|
}
|
|
150
|
-
const
|
|
186
|
+
const env: Record<string, string> | undefined =
|
|
187
|
+
sessionFile || artifactsDir
|
|
188
|
+
? {
|
|
189
|
+
...(sessionFile ? { OMP_SESSION_FILE: sessionFile } : {}),
|
|
190
|
+
...(artifactsDir ? { ARTIFACTS: artifactsDir } : {}),
|
|
191
|
+
}
|
|
192
|
+
: undefined;
|
|
193
|
+
const kernel = await PythonKernel.start({ cwd, useSharedGateway, env });
|
|
151
194
|
session.kernel = kernel;
|
|
152
195
|
session.dead = false;
|
|
153
196
|
session.lastUsedAt = Date.now();
|
|
@@ -170,17 +213,19 @@ async function withKernelSession<T>(
|
|
|
170
213
|
cwd: string,
|
|
171
214
|
handler: (kernel: PythonKernel) => Promise<T>,
|
|
172
215
|
useSharedGateway?: boolean,
|
|
216
|
+
sessionFile?: string,
|
|
217
|
+
artifactsDir?: string,
|
|
173
218
|
): Promise<T> {
|
|
174
219
|
let session = kernelSessions.get(sessionId);
|
|
175
220
|
if (!session) {
|
|
176
|
-
session = await createKernelSession(sessionId, cwd, useSharedGateway);
|
|
221
|
+
session = await createKernelSession(sessionId, cwd, useSharedGateway, sessionFile, artifactsDir);
|
|
177
222
|
kernelSessions.set(sessionId, session);
|
|
178
223
|
}
|
|
179
224
|
|
|
180
225
|
const run = async (): Promise<T> => {
|
|
181
226
|
session!.lastUsedAt = Date.now();
|
|
182
227
|
if (session!.dead || !session!.kernel.isAlive()) {
|
|
183
|
-
await restartKernelSession(session!, cwd, useSharedGateway);
|
|
228
|
+
await restartKernelSession(session!, cwd, useSharedGateway, sessionFile, artifactsDir);
|
|
184
229
|
}
|
|
185
230
|
try {
|
|
186
231
|
const result = await handler(session!.kernel);
|
|
@@ -190,7 +235,7 @@ async function withKernelSession<T>(
|
|
|
190
235
|
if (!session!.dead && session!.kernel.isAlive()) {
|
|
191
236
|
throw err;
|
|
192
237
|
}
|
|
193
|
-
await restartKernelSession(session!, cwd, useSharedGateway);
|
|
238
|
+
await restartKernelSession(session!, cwd, useSharedGateway, sessionFile, artifactsDir);
|
|
194
239
|
const result = await handler(session!.kernel);
|
|
195
240
|
session!.restartCount = 0;
|
|
196
241
|
return result;
|
|
@@ -210,7 +255,11 @@ async function executeWithKernel(
|
|
|
210
255
|
code: string,
|
|
211
256
|
options: PythonExecutorOptions | undefined,
|
|
212
257
|
): Promise<PythonResult> {
|
|
213
|
-
const sink = new OutputSink({
|
|
258
|
+
const sink = new OutputSink({
|
|
259
|
+
onChunk: options?.onChunk,
|
|
260
|
+
artifactPath: options?.artifactPath,
|
|
261
|
+
artifactId: options?.artifactId,
|
|
262
|
+
});
|
|
214
263
|
const displayOutputs: KernelDisplayOutput[] = [];
|
|
215
264
|
|
|
216
265
|
try {
|
|
@@ -273,8 +322,18 @@ export async function executePython(code: string, options?: PythonExecutorOption
|
|
|
273
322
|
|
|
274
323
|
const kernelMode = options?.kernelMode ?? "session";
|
|
275
324
|
const useSharedGateway = options?.useSharedGateway;
|
|
325
|
+
const sessionFile = options?.sessionFile;
|
|
326
|
+
const artifactsDir = options?.artifactsDir;
|
|
327
|
+
|
|
276
328
|
if (kernelMode === "per-call") {
|
|
277
|
-
const
|
|
329
|
+
const env: Record<string, string> | undefined =
|
|
330
|
+
sessionFile || artifactsDir
|
|
331
|
+
? {
|
|
332
|
+
...(sessionFile ? { OMP_SESSION_FILE: sessionFile } : {}),
|
|
333
|
+
...(artifactsDir ? { ARTIFACTS: artifactsDir } : {}),
|
|
334
|
+
}
|
|
335
|
+
: undefined;
|
|
336
|
+
const kernel = await PythonKernel.start({ cwd, useSharedGateway, env });
|
|
278
337
|
try {
|
|
279
338
|
return await executeWithKernel(kernel, code, options);
|
|
280
339
|
} finally {
|
|
@@ -294,5 +353,7 @@ export async function executePython(code: string, options?: PythonExecutorOption
|
|
|
294
353
|
cwd,
|
|
295
354
|
async (kernel) => executeWithKernel(kernel, code, options),
|
|
296
355
|
useSharedGateway,
|
|
356
|
+
sessionFile,
|
|
357
|
+
artifactsDir,
|
|
297
358
|
);
|
|
298
359
|
}
|
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
existsSync,
|
|
4
4
|
mkdirSync,
|
|
5
5
|
openSync,
|
|
6
|
-
readdirSync,
|
|
7
6
|
readFileSync,
|
|
8
7
|
renameSync,
|
|
9
8
|
statSync,
|
|
@@ -15,16 +14,14 @@ import { createServer } from "node:net";
|
|
|
15
14
|
import { delimiter, join } from "node:path";
|
|
16
15
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
17
16
|
import type { Subprocess } from "bun";
|
|
18
|
-
import { getAgentDir } from "
|
|
19
|
-
import { getShellConfig, killProcessTree } from "
|
|
20
|
-
import { getOrCreateSnapshot } from "
|
|
17
|
+
import { getAgentDir } from "$c/config";
|
|
18
|
+
import { getShellConfig, killProcessTree } from "$c/utils/shell";
|
|
19
|
+
import { getOrCreateSnapshot } from "$c/utils/shell-snapshot";
|
|
21
20
|
|
|
22
21
|
const GATEWAY_DIR_NAME = "python-gateway";
|
|
23
22
|
const GATEWAY_INFO_FILE = "gateway.json";
|
|
24
23
|
const GATEWAY_LOCK_FILE = "gateway.lock";
|
|
25
|
-
const GATEWAY_CLIENT_PREFIX = "client-";
|
|
26
24
|
const GATEWAY_STARTUP_TIMEOUT_MS = 30000;
|
|
27
|
-
const GATEWAY_IDLE_TIMEOUT_MS = 30000;
|
|
28
25
|
const GATEWAY_LOCK_TIMEOUT_MS = GATEWAY_STARTUP_TIMEOUT_MS + 5000;
|
|
29
26
|
const GATEWAY_LOCK_RETRY_MS = 50;
|
|
30
27
|
const GATEWAY_LOCK_STALE_MS = GATEWAY_STARTUP_TIMEOUT_MS * 2;
|
|
@@ -140,8 +137,6 @@ export interface GatewayInfo {
|
|
|
140
137
|
url: string;
|
|
141
138
|
pid: number;
|
|
142
139
|
startedAt: number;
|
|
143
|
-
refCount: number;
|
|
144
|
-
cwd: string;
|
|
145
140
|
pythonPath?: string;
|
|
146
141
|
venvPath?: string | null;
|
|
147
142
|
}
|
|
@@ -158,9 +153,7 @@ interface AcquireResult {
|
|
|
158
153
|
|
|
159
154
|
let localGatewayProcess: Subprocess | null = null;
|
|
160
155
|
let localGatewayUrl: string | null = null;
|
|
161
|
-
let idleShutdownTimer: ReturnType<typeof setTimeout> | null = null;
|
|
162
156
|
let isCoordinatorInitialized = false;
|
|
163
|
-
let localClientFile: string | null = null;
|
|
164
157
|
|
|
165
158
|
function filterEnv(env: Record<string, string | undefined>): Record<string, string | undefined> {
|
|
166
159
|
const filtered: Record<string, string | undefined> = {};
|
|
@@ -346,6 +339,7 @@ async function withGatewayLock<T>(handler: () => Promise<T>): Promise<T> {
|
|
|
346
339
|
function readGatewayInfo(): GatewayInfo | null {
|
|
347
340
|
const infoPath = getGatewayInfoPath();
|
|
348
341
|
if (!existsSync(infoPath)) return null;
|
|
342
|
+
|
|
349
343
|
try {
|
|
350
344
|
const content = readFileSync(infoPath, "utf-8");
|
|
351
345
|
const parsed = JSON.parse(content) as Partial<GatewayInfo>;
|
|
@@ -353,16 +347,10 @@ function readGatewayInfo(): GatewayInfo | null {
|
|
|
353
347
|
if (typeof parsed.url !== "string" || typeof parsed.pid !== "number" || typeof parsed.startedAt !== "number") {
|
|
354
348
|
return null;
|
|
355
349
|
}
|
|
356
|
-
if (typeof parsed.cwd !== "string") return null;
|
|
357
|
-
const clients = pruneStaleClientInfos(listClientInfos());
|
|
358
|
-
const totalRefCount = clients.reduce((sum, client) => sum + client.info.refCount, 0);
|
|
359
|
-
const recoveredRefCount = clients.length > 0 ? totalRefCount : 0;
|
|
360
350
|
return {
|
|
361
351
|
url: parsed.url,
|
|
362
352
|
pid: parsed.pid,
|
|
363
353
|
startedAt: parsed.startedAt,
|
|
364
|
-
refCount: recoveredRefCount,
|
|
365
|
-
cwd: parsed.cwd,
|
|
366
354
|
pythonPath: typeof parsed.pythonPath === "string" ? parsed.pythonPath : undefined,
|
|
367
355
|
venvPath: typeof parsed.venvPath === "string" || parsed.venvPath === null ? parsed.venvPath : undefined,
|
|
368
356
|
};
|
|
@@ -398,103 +386,6 @@ function isPidRunning(pid: number): boolean {
|
|
|
398
386
|
}
|
|
399
387
|
}
|
|
400
388
|
|
|
401
|
-
interface GatewayClientInfo {
|
|
402
|
-
pid: number;
|
|
403
|
-
refCount: number;
|
|
404
|
-
updatedAt?: number;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
function getClientFilePath(pid: number): string {
|
|
408
|
-
return join(getGatewayDir(), `${GATEWAY_CLIENT_PREFIX}${pid}.json`);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
function readClientInfo(path: string): GatewayClientInfo | null {
|
|
412
|
-
try {
|
|
413
|
-
const raw = readFileSync(path, "utf-8");
|
|
414
|
-
const parsed = JSON.parse(raw) as GatewayClientInfo;
|
|
415
|
-
if (typeof parsed.pid !== "number" || typeof parsed.refCount !== "number") return null;
|
|
416
|
-
return parsed;
|
|
417
|
-
} catch {
|
|
418
|
-
return null;
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
function listClientInfos(): Array<{ path: string; info: GatewayClientInfo }> {
|
|
423
|
-
const dir = getGatewayDir();
|
|
424
|
-
if (!existsSync(dir)) return [];
|
|
425
|
-
const entries = readdirSync(dir);
|
|
426
|
-
const results: Array<{ path: string; info: GatewayClientInfo }> = [];
|
|
427
|
-
for (const entry of entries) {
|
|
428
|
-
if (!entry.startsWith(GATEWAY_CLIENT_PREFIX)) continue;
|
|
429
|
-
const path = join(dir, entry);
|
|
430
|
-
const info = readClientInfo(path);
|
|
431
|
-
if (!info) continue;
|
|
432
|
-
results.push({ path, info });
|
|
433
|
-
}
|
|
434
|
-
return results;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
function pruneStaleClientInfos(
|
|
438
|
-
clients: Array<{ path: string; info: GatewayClientInfo }>,
|
|
439
|
-
): Array<{ path: string; info: GatewayClientInfo }> {
|
|
440
|
-
const active: Array<{ path: string; info: GatewayClientInfo }> = [];
|
|
441
|
-
for (const client of clients) {
|
|
442
|
-
if (!isPidRunning(client.info.pid)) {
|
|
443
|
-
try {
|
|
444
|
-
unlinkSync(client.path);
|
|
445
|
-
} catch {
|
|
446
|
-
// Ignore cleanup errors
|
|
447
|
-
}
|
|
448
|
-
continue;
|
|
449
|
-
}
|
|
450
|
-
active.push(client);
|
|
451
|
-
}
|
|
452
|
-
return active;
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
function updateLocalClientRefCount(delta: number): { totalRefCount: number; localRefCount: number } {
|
|
456
|
-
ensureGatewayDir();
|
|
457
|
-
const clients = pruneStaleClientInfos(listClientInfos());
|
|
458
|
-
const localPath = localClientFile ?? getClientFilePath(process.pid);
|
|
459
|
-
const localEntry = clients.find((client) => client.info.pid === process.pid);
|
|
460
|
-
const baseCount = localEntry?.info.refCount ?? 0;
|
|
461
|
-
const nextCount = Math.max(0, baseCount + delta);
|
|
462
|
-
const otherClients = clients.filter((client) => client.info.pid !== process.pid);
|
|
463
|
-
|
|
464
|
-
if (nextCount <= 0) {
|
|
465
|
-
if (localEntry) {
|
|
466
|
-
try {
|
|
467
|
-
unlinkSync(localEntry.path);
|
|
468
|
-
} catch {
|
|
469
|
-
// Ignore cleanup errors
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
if (localClientFile === localPath) {
|
|
473
|
-
localClientFile = null;
|
|
474
|
-
}
|
|
475
|
-
} else {
|
|
476
|
-
const payload: GatewayClientInfo = { pid: process.pid, refCount: nextCount, updatedAt: Date.now() };
|
|
477
|
-
writeFileSync(localPath, JSON.stringify(payload, null, 2));
|
|
478
|
-
localClientFile = localPath;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
const totalRefCount =
|
|
482
|
-
otherClients.reduce((sum, client) => sum + client.info.refCount, 0) + (nextCount > 0 ? nextCount : 0);
|
|
483
|
-
return { totalRefCount, localRefCount: nextCount };
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
function clearClientFiles(): void {
|
|
487
|
-
const clients = listClientInfos();
|
|
488
|
-
for (const client of clients) {
|
|
489
|
-
try {
|
|
490
|
-
unlinkSync(client.path);
|
|
491
|
-
} catch {
|
|
492
|
-
// Ignore cleanup errors
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
localClientFile = null;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
389
|
async function isGatewayHealthy(url: string): Promise<boolean> {
|
|
499
390
|
try {
|
|
500
391
|
const controller = new AbortController();
|
|
@@ -533,11 +424,6 @@ async function startGatewayProcess(
|
|
|
533
424
|
OMP_SHELL_SNAPSHOT: snapshotPath ?? undefined,
|
|
534
425
|
};
|
|
535
426
|
|
|
536
|
-
const pythonPathParts = [cwd, kernelEnv.PYTHONPATH].filter(Boolean).join(delimiter);
|
|
537
|
-
if (pythonPathParts) {
|
|
538
|
-
kernelEnv.PYTHONPATH = pythonPathParts;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
427
|
const gatewayPort = await allocatePort();
|
|
542
428
|
const gatewayUrl = `http://127.0.0.1:${gatewayPort}`;
|
|
543
429
|
|
|
@@ -570,7 +456,6 @@ async function startGatewayProcess(
|
|
|
570
456
|
exited = true;
|
|
571
457
|
});
|
|
572
458
|
|
|
573
|
-
// Wait for gateway to become healthy
|
|
574
459
|
const startTime = Date.now();
|
|
575
460
|
while (Date.now() - startTime < GATEWAY_STARTUP_TIMEOUT_MS) {
|
|
576
461
|
if (exited) {
|
|
@@ -593,70 +478,15 @@ async function startGatewayProcess(
|
|
|
593
478
|
throw new Error("Gateway startup timeout");
|
|
594
479
|
}
|
|
595
480
|
|
|
596
|
-
function
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
clearClientFiles();
|
|
606
|
-
return;
|
|
607
|
-
}
|
|
608
|
-
const clients = pruneStaleClientInfos(listClientInfos());
|
|
609
|
-
const totalRefCount = clients.reduce((sum, client) => sum + client.info.refCount, 0);
|
|
610
|
-
if (totalRefCount > 0) {
|
|
611
|
-
if (info.refCount !== totalRefCount) {
|
|
612
|
-
writeGatewayInfo({ ...info, refCount: totalRefCount });
|
|
613
|
-
}
|
|
614
|
-
return;
|
|
615
|
-
}
|
|
616
|
-
logger.debug("Shutting down idle shared gateway", { pid: info.pid });
|
|
617
|
-
if (localGatewayProcess) {
|
|
618
|
-
await shutdownLocalGateway();
|
|
619
|
-
} else if (isPidRunning(info.pid)) {
|
|
620
|
-
try {
|
|
621
|
-
await killProcessTree(info.pid);
|
|
622
|
-
} catch (err) {
|
|
623
|
-
logger.warn("Failed to kill idle shared gateway", {
|
|
624
|
-
error: err instanceof Error ? err.message : String(err),
|
|
625
|
-
pid: info.pid,
|
|
626
|
-
});
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
clearGatewayInfo();
|
|
630
|
-
clearClientFiles();
|
|
631
|
-
});
|
|
632
|
-
} catch (err) {
|
|
633
|
-
logger.warn("Failed to shutdown idle shared gateway", {
|
|
634
|
-
error: err instanceof Error ? err.message : String(err),
|
|
635
|
-
});
|
|
636
|
-
} finally {
|
|
637
|
-
idleShutdownTimer = null;
|
|
638
|
-
}
|
|
639
|
-
}, GATEWAY_IDLE_TIMEOUT_MS);
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
function cancelIdleShutdown(): void {
|
|
643
|
-
if (idleShutdownTimer) {
|
|
644
|
-
clearTimeout(idleShutdownTimer);
|
|
645
|
-
idleShutdownTimer = null;
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
async function shutdownLocalGateway(): Promise<void> {
|
|
650
|
-
if (localGatewayProcess) {
|
|
651
|
-
try {
|
|
652
|
-
await killProcessTree(localGatewayProcess.pid);
|
|
653
|
-
} catch (err) {
|
|
654
|
-
logger.warn("Failed to kill shared gateway process", {
|
|
655
|
-
error: err instanceof Error ? err.message : String(err),
|
|
656
|
-
});
|
|
657
|
-
}
|
|
658
|
-
localGatewayProcess = null;
|
|
659
|
-
localGatewayUrl = null;
|
|
481
|
+
async function killGateway(pid: number, context: string): Promise<void> {
|
|
482
|
+
try {
|
|
483
|
+
await killProcessTree(pid);
|
|
484
|
+
} catch (err) {
|
|
485
|
+
logger.warn("Failed to kill shared gateway process", {
|
|
486
|
+
error: err instanceof Error ? err.message : String(err),
|
|
487
|
+
pid,
|
|
488
|
+
context,
|
|
489
|
+
});
|
|
660
490
|
}
|
|
661
491
|
}
|
|
662
492
|
|
|
@@ -668,67 +498,32 @@ export async function acquireSharedGateway(cwd: string): Promise<AcquireResult |
|
|
|
668
498
|
try {
|
|
669
499
|
return await withGatewayLock(async () => {
|
|
670
500
|
const existingInfo = readGatewayInfo();
|
|
671
|
-
if (existingInfo
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
if (
|
|
678
|
-
existingInfo.cwd !== cwd ||
|
|
679
|
-
!existingInfo.pythonPath ||
|
|
680
|
-
existingInfo.pythonPath !== runtime.pythonPath ||
|
|
681
|
-
existingVenv !== runtimeVenv
|
|
682
|
-
) {
|
|
683
|
-
logger.debug("Shared gateway metadata mismatch", {
|
|
684
|
-
existingCwd: existingInfo.cwd,
|
|
685
|
-
requestedCwd: cwd,
|
|
686
|
-
existingPython: existingInfo.pythonPath,
|
|
687
|
-
runtimePython: runtime.pythonPath,
|
|
688
|
-
existingVenv,
|
|
689
|
-
runtimeVenv,
|
|
690
|
-
});
|
|
691
|
-
return null;
|
|
501
|
+
if (existingInfo) {
|
|
502
|
+
if (await isGatewayAlive(existingInfo)) {
|
|
503
|
+
localGatewayUrl = existingInfo.url;
|
|
504
|
+
isCoordinatorInitialized = true;
|
|
505
|
+
logger.debug("Reusing global Python gateway", { url: existingInfo.url });
|
|
506
|
+
return { url: existingInfo.url, isShared: true };
|
|
692
507
|
}
|
|
693
|
-
const { totalRefCount } = updateLocalClientRefCount(1);
|
|
694
|
-
const updatedInfo = { ...existingInfo, refCount: totalRefCount };
|
|
695
|
-
writeGatewayInfo(updatedInfo);
|
|
696
|
-
cancelIdleShutdown();
|
|
697
|
-
logger.debug("Reusing shared gateway", { url: existingInfo.url, refCount: updatedInfo.refCount });
|
|
698
|
-
isCoordinatorInitialized = true;
|
|
699
|
-
return { url: existingInfo.url, isShared: true };
|
|
700
|
-
}
|
|
701
508
|
|
|
702
|
-
if (existingInfo) {
|
|
703
509
|
logger.debug("Cleaning up stale gateway info", { pid: existingInfo.pid });
|
|
704
510
|
if (isPidRunning(existingInfo.pid)) {
|
|
705
|
-
|
|
706
|
-
await killProcessTree(existingInfo.pid);
|
|
707
|
-
} catch (err) {
|
|
708
|
-
logger.warn("Failed to kill stale shared gateway process", {
|
|
709
|
-
error: err instanceof Error ? err.message : String(err),
|
|
710
|
-
pid: existingInfo.pid,
|
|
711
|
-
});
|
|
712
|
-
}
|
|
511
|
+
await killGateway(existingInfo.pid, "stale");
|
|
713
512
|
}
|
|
714
513
|
clearGatewayInfo();
|
|
715
|
-
clearClientFiles();
|
|
716
514
|
}
|
|
717
515
|
|
|
718
516
|
const { url, pid, pythonPath, venvPath } = await startGatewayProcess(cwd);
|
|
719
|
-
const { totalRefCount } = updateLocalClientRefCount(1);
|
|
720
517
|
const info: GatewayInfo = {
|
|
721
518
|
url,
|
|
722
519
|
pid,
|
|
723
520
|
startedAt: Date.now(),
|
|
724
|
-
refCount: totalRefCount,
|
|
725
|
-
cwd,
|
|
726
521
|
pythonPath,
|
|
727
522
|
venvPath,
|
|
728
523
|
};
|
|
729
524
|
writeGatewayInfo(info);
|
|
730
525
|
isCoordinatorInitialized = true;
|
|
731
|
-
logger.debug("Started
|
|
526
|
+
logger.debug("Started global Python gateway", { url, pid });
|
|
732
527
|
return { url, isShared: true };
|
|
733
528
|
});
|
|
734
529
|
} catch (err) {
|
|
@@ -741,48 +536,24 @@ export async function acquireSharedGateway(cwd: string): Promise<AcquireResult |
|
|
|
741
536
|
|
|
742
537
|
export async function releaseSharedGateway(): Promise<void> {
|
|
743
538
|
if (!isCoordinatorInitialized) return;
|
|
744
|
-
|
|
745
|
-
try {
|
|
746
|
-
await withGatewayLock(async () => {
|
|
747
|
-
const { totalRefCount } = updateLocalClientRefCount(-1);
|
|
748
|
-
const info = readGatewayInfo();
|
|
749
|
-
if (!info) return;
|
|
750
|
-
|
|
751
|
-
const newRefCount = Math.max(0, totalRefCount);
|
|
752
|
-
if (newRefCount === 0) {
|
|
753
|
-
const updatedInfo = { ...info, refCount: 0 };
|
|
754
|
-
writeGatewayInfo(updatedInfo);
|
|
755
|
-
scheduleIdleShutdown();
|
|
756
|
-
logger.debug("Scheduled idle shutdown for shared gateway", { pid: info.pid });
|
|
757
|
-
return;
|
|
758
|
-
}
|
|
759
|
-
const updatedInfo = { ...info, refCount: newRefCount };
|
|
760
|
-
writeGatewayInfo(updatedInfo);
|
|
761
|
-
logger.debug("Released shared gateway reference", { url: info.url, refCount: newRefCount });
|
|
762
|
-
});
|
|
763
|
-
} catch (err) {
|
|
764
|
-
logger.warn("Failed to release shared gateway", {
|
|
765
|
-
error: err instanceof Error ? err.message : String(err),
|
|
766
|
-
});
|
|
767
|
-
}
|
|
768
539
|
}
|
|
769
540
|
|
|
770
541
|
export function getSharedGatewayUrl(): string | null {
|
|
771
|
-
return localGatewayUrl;
|
|
542
|
+
if (localGatewayUrl) return localGatewayUrl;
|
|
543
|
+
return readGatewayInfo()?.url ?? null;
|
|
772
544
|
}
|
|
773
545
|
|
|
774
546
|
export function isSharedGatewayActive(): boolean {
|
|
775
|
-
return
|
|
547
|
+
return getGatewayStatus().active;
|
|
776
548
|
}
|
|
777
549
|
|
|
778
550
|
export interface GatewayStatus {
|
|
779
551
|
active: boolean;
|
|
780
|
-
shared: boolean;
|
|
781
552
|
url: string | null;
|
|
782
553
|
pid: number | null;
|
|
783
|
-
refCount: number;
|
|
784
|
-
cwd: string | null;
|
|
785
554
|
uptime: number | null;
|
|
555
|
+
pythonPath: string | null;
|
|
556
|
+
venvPath: string | null;
|
|
786
557
|
}
|
|
787
558
|
|
|
788
559
|
export function getGatewayStatus(): GatewayStatus {
|
|
@@ -790,45 +561,44 @@ export function getGatewayStatus(): GatewayStatus {
|
|
|
790
561
|
if (!info) {
|
|
791
562
|
return {
|
|
792
563
|
active: false,
|
|
793
|
-
shared: false,
|
|
794
564
|
url: null,
|
|
795
565
|
pid: null,
|
|
796
|
-
refCount: 0,
|
|
797
|
-
cwd: null,
|
|
798
566
|
uptime: null,
|
|
567
|
+
pythonPath: null,
|
|
568
|
+
venvPath: null,
|
|
799
569
|
};
|
|
800
570
|
}
|
|
801
571
|
const active = isPidRunning(info.pid);
|
|
802
|
-
const clients = pruneStaleClientInfos(listClientInfos());
|
|
803
|
-
const clientRefCount = clients.reduce((sum, client) => sum + client.info.refCount, 0);
|
|
804
|
-
const refCount = clientRefCount > 0 ? clientRefCount : info.refCount;
|
|
805
572
|
return {
|
|
806
573
|
active,
|
|
807
|
-
shared: active && refCount > 1,
|
|
808
574
|
url: info.url,
|
|
809
575
|
pid: info.pid,
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
576
|
+
uptime: active ? Date.now() - info.startedAt : null,
|
|
577
|
+
pythonPath: info.pythonPath ?? null,
|
|
578
|
+
venvPath: info.venvPath ?? null,
|
|
813
579
|
};
|
|
814
580
|
}
|
|
815
581
|
|
|
816
582
|
export async function shutdownSharedGateway(): Promise<void> {
|
|
817
|
-
cancelIdleShutdown();
|
|
818
583
|
try {
|
|
819
584
|
await withGatewayLock(async () => {
|
|
820
585
|
const info = readGatewayInfo();
|
|
821
|
-
if (info)
|
|
822
|
-
|
|
586
|
+
if (!info) return;
|
|
587
|
+
if (isPidRunning(info.pid)) {
|
|
588
|
+
await killGateway(info.pid, "shutdown");
|
|
823
589
|
}
|
|
824
|
-
|
|
590
|
+
clearGatewayInfo();
|
|
825
591
|
});
|
|
826
592
|
} catch (err) {
|
|
827
593
|
logger.warn("Failed to shutdown shared gateway", {
|
|
828
594
|
error: err instanceof Error ? err.message : String(err),
|
|
829
595
|
});
|
|
830
596
|
} finally {
|
|
831
|
-
|
|
597
|
+
if (localGatewayProcess) {
|
|
598
|
+
await killGateway(localGatewayProcess.pid, "shutdown-local");
|
|
599
|
+
}
|
|
600
|
+
localGatewayProcess = null;
|
|
601
|
+
localGatewayUrl = null;
|
|
832
602
|
isCoordinatorInitialized = false;
|
|
833
603
|
}
|
|
834
604
|
}
|