@tomsun28/pizza 0.0.9 → 0.1.1
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/README.md +20 -143
- package/README.zh-CN.md +36 -0
- package/dist/bun/cli.d.ts +2 -0
- package/dist/bun/cli.js +6 -0
- package/dist/bun/register-bedrock.d.ts +1 -0
- package/dist/bun/register-bedrock.js +3 -0
- package/dist/cli/args.d.ts +48 -0
- package/dist/cli/args.js +330 -0
- package/dist/cli/config-selector.d.ts +13 -0
- package/dist/cli/config-selector.js +30 -0
- package/dist/cli/file-processor.d.ts +14 -0
- package/dist/cli/file-processor.js +82 -0
- package/dist/cli/initial-message.d.ts +17 -0
- package/dist/cli/initial-message.js +21 -0
- package/dist/cli/list-models.d.ts +8 -0
- package/dist/cli/list-models.js +96 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +68 -0
- package/dist/config.d.ts +79 -0
- package/dist/config.js +223 -0
- package/dist/core/agent/index.d.ts +6 -0
- package/dist/core/agent/index.js +6 -0
- package/dist/core/agent/types.d.ts +202 -0
- package/dist/core/agent/types.js +51 -0
- package/dist/core/auth-storage.d.ts +135 -0
- package/dist/core/auth-storage.js +417 -0
- package/dist/core/bash-executor.d.ts +31 -0
- package/dist/core/bash-executor.js +107 -0
- package/dist/core/compaction/branch-summarization.d.ts +87 -0
- package/dist/core/compaction/branch-summarization.js +242 -0
- package/dist/core/compaction/compaction-engine.d.ts +40 -0
- package/dist/core/compaction/compaction-engine.js +292 -0
- package/dist/core/compaction/compaction.d.ts +106 -0
- package/dist/core/compaction/compaction.js +295 -0
- package/dist/core/compaction/index.d.ts +7 -0
- package/dist/core/compaction/index.js +7 -0
- package/dist/core/compaction/utils.d.ts +37 -0
- package/dist/core/compaction/utils.js +152 -0
- package/dist/core/defaults.d.ts +2 -0
- package/dist/core/defaults.js +1 -0
- package/dist/core/diagnostics.d.ts +14 -0
- package/dist/core/diagnostics.js +1 -0
- package/dist/core/event-bus.d.ts +8 -0
- package/dist/core/event-bus.js +24 -0
- package/dist/core/event-store/events.d.ts +465 -0
- package/dist/core/event-store/events.js +6 -0
- package/dist/core/event-store/index.d.ts +10 -0
- package/dist/core/event-store/index.js +10 -0
- package/dist/core/event-store/sqlite-store.d.ts +27 -0
- package/dist/core/event-store/sqlite-store.js +275 -0
- package/dist/core/event-store/store.d.ts +91 -0
- package/dist/core/event-store/store.js +7 -0
- package/dist/core/event-store/thread-scoped-store.d.ts +29 -0
- package/dist/core/event-store/thread-scoped-store.js +50 -0
- package/dist/core/event-store/types.d.ts +70 -0
- package/dist/core/event-store/types.js +7 -0
- package/dist/core/event-store/workspace.d.ts +37 -0
- package/dist/core/event-store/workspace.js +71 -0
- package/dist/core/exec.d.ts +28 -0
- package/dist/core/exec.js +74 -0
- package/dist/core/export-html/ansi-to-html.d.ts +21 -0
- package/dist/core/export-html/ansi-to-html.js +248 -0
- package/dist/core/export-html/index.d.ts +29 -0
- package/dist/core/export-html/index.js +186 -0
- package/dist/core/export-html/template.css +1017 -0
- package/dist/core/export-html/template.html +55 -0
- package/dist/core/export-html/template.js +1742 -0
- package/dist/core/export-html/tool-renderer.d.ts +39 -0
- package/dist/core/export-html/tool-renderer.js +94 -0
- package/dist/core/export-html/vendor/highlight.min.js +1213 -0
- package/dist/core/export-html/vendor/marked.min.js +6 -0
- package/dist/core/extensions/index.d.ts +12 -0
- package/dist/core/extensions/index.js +9 -0
- package/dist/core/extensions/loader.d.ts +24 -0
- package/dist/core/extensions/loader.js +492 -0
- package/dist/core/extensions/runner.d.ts +167 -0
- package/dist/core/extensions/runner.js +898 -0
- package/dist/core/extensions/session-context.d.ts +67 -0
- package/dist/core/extensions/session-context.js +272 -0
- package/dist/core/extensions/types.d.ts +1147 -0
- package/dist/core/extensions/types.js +44 -0
- package/dist/core/extensions/wrapper.d.ts +19 -0
- package/dist/core/extensions/wrapper.js +21 -0
- package/dist/core/footer-data-provider.d.ts +47 -0
- package/dist/core/footer-data-provider.js +321 -0
- package/dist/core/goal/index.d.ts +3 -0
- package/dist/core/goal/index.js +1 -0
- package/dist/core/goal/scheduler.d.ts +83 -0
- package/dist/core/goal/scheduler.js +154 -0
- package/dist/core/goal/types.d.ts +182 -0
- package/dist/core/goal/types.js +11 -0
- package/dist/core/index.d.ts +8 -0
- package/dist/core/index.js +8 -0
- package/dist/core/intent/classifier.d.ts +53 -0
- package/dist/core/intent/classifier.js +353 -0
- package/dist/core/intent/index.d.ts +8 -0
- package/dist/core/intent/index.js +8 -0
- package/dist/core/intent/tool-adapter.d.ts +31 -0
- package/dist/core/intent/tool-adapter.js +168 -0
- package/dist/core/intent/types.d.ts +71 -0
- package/dist/core/intent/types.js +6 -0
- package/dist/core/keybindings.d.ts +297 -0
- package/dist/core/keybindings.js +250 -0
- package/dist/core/messages.d.ts +30 -0
- package/dist/core/messages.js +134 -0
- package/dist/core/model-registry.d.ts +131 -0
- package/dist/core/model-registry.js +634 -0
- package/dist/core/model-resolver.d.ts +109 -0
- package/dist/core/model-resolver.js +486 -0
- package/dist/core/output-guard.d.ts +5 -0
- package/dist/core/output-guard.js +58 -0
- package/dist/core/package-manager.d.ts +196 -0
- package/dist/core/package-manager.js +1873 -0
- package/dist/core/projection/boundary-inferrer.d.ts +58 -0
- package/dist/core/projection/boundary-inferrer.js +122 -0
- package/dist/core/projection/event-to-message.d.ts +35 -0
- package/dist/core/projection/event-to-message.js +253 -0
- package/dist/core/projection/goal-projection.d.ts +67 -0
- package/dist/core/projection/goal-projection.js +339 -0
- package/dist/core/projection/index.d.ts +13 -0
- package/dist/core/projection/index.js +13 -0
- package/dist/core/projection/session-manager.d.ts +100 -0
- package/dist/core/projection/session-manager.js +331 -0
- package/dist/core/projection/session-projection.d.ts +58 -0
- package/dist/core/projection/session-projection.js +257 -0
- package/dist/core/projection/task-history.d.ts +13 -0
- package/dist/core/projection/task-history.js +61 -0
- package/dist/core/projection/timeline-projection.d.ts +82 -0
- package/dist/core/projection/timeline-projection.js +276 -0
- package/dist/core/projection/types.d.ts +69 -0
- package/dist/core/projection/types.js +6 -0
- package/dist/core/prompt-templates.d.ts +51 -0
- package/dist/core/prompt-templates.js +249 -0
- package/dist/core/resolve-config-value.d.ts +22 -0
- package/dist/core/resolve-config-value.js +125 -0
- package/dist/core/resource-loader.d.ts +193 -0
- package/dist/core/resource-loader.js +685 -0
- package/dist/core/runtime/ai-client.d.ts +12 -0
- package/dist/core/runtime/ai-client.js +153 -0
- package/dist/core/runtime/checkpoint.d.ts +12 -0
- package/dist/core/runtime/checkpoint.js +78 -0
- package/dist/core/runtime/index.d.ts +12 -0
- package/dist/core/runtime/index.js +12 -0
- package/dist/core/runtime/llm-types.d.ts +99 -0
- package/dist/core/runtime/llm-types.js +6 -0
- package/dist/core/runtime/local-runtime.d.ts +23 -0
- package/dist/core/runtime/local-runtime.js +59 -0
- package/dist/core/runtime/policies.d.ts +72 -0
- package/dist/core/runtime/policies.js +37 -0
- package/dist/core/runtime/reactor.d.ts +213 -0
- package/dist/core/runtime/reactor.js +992 -0
- package/dist/core/runtime/runtime.d.ts +191 -0
- package/dist/core/runtime/runtime.js +449 -0
- package/dist/core/runtime/types.d.ts +42 -0
- package/dist/core/runtime/types.js +1 -0
- package/dist/core/sdk.d.ts +12 -0
- package/dist/core/sdk.js +10 -0
- package/dist/core/session-context-builder.d.ts +9 -0
- package/dist/core/session-context-builder.js +87 -0
- package/dist/core/session-cwd.d.ts +18 -0
- package/dist/core/session-cwd.js +36 -0
- package/dist/core/session-facade-factory.d.ts +79 -0
- package/dist/core/session-facade-factory.js +460 -0
- package/dist/core/session-facade.d.ts +57 -0
- package/dist/core/session-facade.js +91 -0
- package/dist/core/session-listing.d.ts +30 -0
- package/dist/core/session-listing.js +168 -0
- package/dist/core/session-reader.d.ts +19 -0
- package/dist/core/session-reader.js +101 -0
- package/dist/core/session-ref.d.ts +7 -0
- package/dist/core/session-ref.js +25 -0
- package/dist/core/session-services.d.ts +51 -0
- package/dist/core/session-services.js +90 -0
- package/dist/core/session-stats.d.ts +43 -0
- package/dist/core/session-stats.js +41 -0
- package/dist/core/settings-manager.d.ts +236 -0
- package/dist/core/settings-manager.js +706 -0
- package/dist/core/skill-block-parser.d.ts +12 -0
- package/dist/core/skill-block-parser.js +15 -0
- package/dist/core/skills.d.ts +59 -0
- package/dist/core/skills.js +408 -0
- package/dist/core/slash-commands.d.ts +13 -0
- package/dist/core/slash-commands.js +17 -0
- package/dist/core/source-info.d.ts +17 -0
- package/dist/core/source-info.js +18 -0
- package/dist/core/system-prompt.d.ts +27 -0
- package/dist/core/system-prompt.js +179 -0
- package/dist/core/timings.d.ts +7 -0
- package/dist/core/timings.js +30 -0
- package/dist/core/tools/bash.d.ts +95 -0
- package/dist/core/tools/bash.js +513 -0
- package/dist/core/tools/builtin-commands.d.ts +75 -0
- package/dist/core/tools/builtin-commands.js +503 -0
- package/dist/core/tools/compat-commands.d.ts +12 -0
- package/dist/core/tools/compat-commands.js +1280 -0
- package/dist/core/tools/edit-diff.d.ts +59 -0
- package/dist/core/tools/edit-diff.js +263 -0
- package/dist/core/tools/edit.d.ts +49 -0
- package/dist/core/tools/edit.js +340 -0
- package/dist/core/tools/file-mutation-queue.d.ts +5 -0
- package/dist/core/tools/file-mutation-queue.js +36 -0
- package/dist/core/tools/find.d.ts +34 -0
- package/dist/core/tools/find.js +459 -0
- package/dist/core/tools/grep.d.ts +36 -0
- package/dist/core/tools/grep.js +422 -0
- package/dist/core/tools/index.d.ts +70 -0
- package/dist/core/tools/index.js +129 -0
- package/dist/core/tools/line-anchors.d.ts +33 -0
- package/dist/core/tools/line-anchors.js +151 -0
- package/dist/core/tools/ls.d.ts +36 -0
- package/dist/core/tools/ls.js +268 -0
- package/dist/core/tools/path-utils.d.ts +7 -0
- package/dist/core/tools/path-utils.js +80 -0
- package/dist/core/tools/read.d.ts +35 -0
- package/dist/core/tools/read.js +244 -0
- package/dist/core/tools/render-utils.d.ts +20 -0
- package/dist/core/tools/render-utils.js +48 -0
- package/dist/core/tools/session-split.d.ts +9 -0
- package/dist/core/tools/session-split.js +77 -0
- package/dist/core/tools/tool-definition-wrapper.d.ts +13 -0
- package/dist/core/tools/tool-definition-wrapper.js +32 -0
- package/dist/core/tools/truncate.d.ts +69 -0
- package/dist/core/tools/truncate.js +204 -0
- package/dist/core/tools/write.d.ts +25 -0
- package/dist/core/tools/write.js +211 -0
- package/dist/core/types/session-types.d.ts +123 -0
- package/dist/core/types/session-types.js +8 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +38 -0
- package/dist/main.d.ts +11 -2
- package/dist/main.js +577 -149
- package/dist/modes/event-mapper.d.ts +115 -0
- package/dist/modes/event-mapper.js +200 -0
- package/dist/modes/gui/server.d.ts +14 -0
- package/dist/modes/gui/server.js +1032 -0
- package/dist/modes/index.d.ts +10 -0
- package/dist/modes/index.js +9 -0
- package/dist/modes/interactive/assets/clankolas.png +0 -0
- package/dist/modes/interactive/components/armin.d.ts +33 -0
- package/dist/modes/interactive/components/armin.js +328 -0
- package/dist/modes/interactive/components/assistant-message.d.ts +19 -0
- package/dist/modes/interactive/components/assistant-message.js +115 -0
- package/dist/modes/interactive/components/bash-execution.d.ts +33 -0
- package/dist/modes/interactive/components/bash-execution.js +169 -0
- package/dist/modes/interactive/components/bordered-loader.d.ts +15 -0
- package/dist/modes/interactive/components/bordered-loader.js +50 -0
- package/dist/modes/interactive/components/branch-summary-message.d.ts +15 -0
- package/dist/modes/interactive/components/branch-summary-message.js +41 -0
- package/dist/modes/interactive/components/brand-header.d.ts +33 -0
- package/dist/modes/interactive/components/brand-header.js +235 -0
- package/dist/modes/interactive/components/compaction-summary-message.d.ts +15 -0
- package/dist/modes/interactive/components/compaction-summary-message.js +42 -0
- package/dist/modes/interactive/components/config-selector.d.ts +70 -0
- package/dist/modes/interactive/components/config-selector.js +469 -0
- package/dist/modes/interactive/components/countdown-timer.d.ts +13 -0
- package/dist/modes/interactive/components/countdown-timer.js +27 -0
- package/dist/modes/interactive/components/custom-editor.d.ts +27 -0
- package/dist/modes/interactive/components/custom-editor.js +94 -0
- package/dist/modes/interactive/components/custom-message.d.ts +19 -0
- package/dist/modes/interactive/components/custom-message.js +73 -0
- package/dist/modes/interactive/components/daxnuts.d.ts +22 -0
- package/dist/modes/interactive/components/daxnuts.js +137 -0
- package/dist/modes/interactive/components/diff.d.ts +11 -0
- package/dist/modes/interactive/components/diff.js +132 -0
- package/dist/modes/interactive/components/dynamic-border.d.ts +14 -0
- package/dist/modes/interactive/components/dynamic-border.js +19 -0
- package/dist/modes/interactive/components/earendil-announcement.d.ts +4 -0
- package/dist/modes/interactive/components/earendil-announcement.js +39 -0
- package/dist/modes/interactive/components/extension-editor.d.ts +19 -0
- package/dist/modes/interactive/components/extension-editor.js +105 -0
- package/dist/modes/interactive/components/extension-input.d.ts +22 -0
- package/dist/modes/interactive/components/extension-input.js +54 -0
- package/dist/modes/interactive/components/extension-selector.d.ts +23 -0
- package/dist/modes/interactive/components/extension-selector.js +70 -0
- package/dist/modes/interactive/components/footer.d.ts +55 -0
- package/dist/modes/interactive/components/footer.js +207 -0
- package/dist/modes/interactive/components/index.d.ts +31 -0
- package/dist/modes/interactive/components/index.js +32 -0
- package/dist/modes/interactive/components/keybinding-hints.d.ts +7 -0
- package/dist/modes/interactive/components/keybinding-hints.js +21 -0
- package/dist/modes/interactive/components/login-dialog.d.ts +41 -0
- package/dist/modes/interactive/components/login-dialog.js +138 -0
- package/dist/modes/interactive/components/model-selector.d.ts +32 -0
- package/dist/modes/interactive/components/model-selector.js +209 -0
- package/dist/modes/interactive/components/oauth-selector.d.ts +18 -0
- package/dist/modes/interactive/components/oauth-selector.js +91 -0
- package/dist/modes/interactive/components/settings-selector.d.ts +57 -0
- package/dist/modes/interactive/components/settings-selector.js +299 -0
- package/dist/modes/interactive/components/show-images-selector.d.ts +9 -0
- package/dist/modes/interactive/components/show-images-selector.js +37 -0
- package/dist/modes/interactive/components/skill-invocation-message.d.ts +16 -0
- package/dist/modes/interactive/components/skill-invocation-message.js +44 -0
- package/dist/modes/interactive/components/theme-selector.d.ts +10 -0
- package/dist/modes/interactive/components/theme-selector.js +47 -0
- package/dist/modes/interactive/components/thinking-selector.d.ts +10 -0
- package/dist/modes/interactive/components/thinking-selector.js +50 -0
- package/dist/modes/interactive/components/timeline-view.d.ts +31 -0
- package/dist/modes/interactive/components/timeline-view.js +81 -0
- package/dist/modes/interactive/components/tool-execution.d.ts +64 -0
- package/dist/modes/interactive/components/tool-execution.js +305 -0
- package/dist/modes/interactive/components/tree-selector.d.ts +88 -0
- package/dist/modes/interactive/components/tree-selector.js +1072 -0
- package/dist/modes/interactive/components/user-message-selector.d.ts +29 -0
- package/dist/modes/interactive/components/user-message-selector.js +110 -0
- package/dist/modes/interactive/components/user-message.d.ts +9 -0
- package/dist/modes/interactive/components/user-message.js +27 -0
- package/dist/modes/interactive/components/visual-truncate.d.ts +23 -0
- package/dist/modes/interactive/components/visual-truncate.js +32 -0
- package/dist/modes/interactive/interactive-mode.d.ts +394 -0
- package/dist/modes/interactive/interactive-mode.js +4116 -0
- package/dist/modes/interactive/theme/dark.json +85 -0
- package/dist/modes/interactive/theme/light.json +84 -0
- package/dist/modes/interactive/theme/theme-schema.json +335 -0
- package/dist/modes/interactive/theme/theme.d.ts +80 -0
- package/dist/modes/interactive/theme/theme.js +973 -0
- package/dist/modes/print-mode.d.ts +27 -0
- package/dist/modes/print-mode.js +102 -0
- package/dist/modes/rpc/jsonl.d.ts +16 -0
- package/dist/modes/rpc/jsonl.js +48 -0
- package/dist/modes/rpc/rpc-client.d.ts +219 -0
- package/dist/modes/rpc/rpc-client.js +405 -0
- package/dist/modes/rpc/rpc-mode.d.ts +19 -0
- package/dist/modes/rpc/rpc-mode.js +381 -0
- package/dist/modes/rpc/rpc-types.d.ts +406 -0
- package/dist/modes/rpc/rpc-types.js +7 -0
- package/dist/package-manager-cli.d.ts +3 -0
- package/dist/package-manager-cli.js +233 -0
- package/dist/utils/changelog.d.ts +20 -0
- package/dist/utils/changelog.js +86 -0
- package/dist/utils/child-process.d.ts +10 -0
- package/dist/utils/child-process.js +77 -0
- package/dist/utils/clipboard-image.d.ts +10 -0
- package/dist/utils/clipboard-image.js +244 -0
- package/dist/utils/clipboard-native.d.ts +7 -0
- package/dist/utils/clipboard-native.js +13 -0
- package/dist/utils/clipboard.d.ts +1 -0
- package/dist/utils/clipboard.js +77 -0
- package/dist/utils/exif-orientation.d.ts +4 -0
- package/dist/utils/exif-orientation.js +157 -0
- package/dist/utils/frontmatter.d.ts +7 -0
- package/dist/utils/frontmatter.js +25 -0
- package/dist/utils/git.d.ts +25 -0
- package/dist/utils/git.js +162 -0
- package/dist/utils/image-convert.d.ts +8 -0
- package/dist/utils/image-convert.js +38 -0
- package/dist/utils/image-resize.d.ts +35 -0
- package/dist/utils/image-resize.js +136 -0
- package/dist/utils/mime.d.ts +1 -0
- package/dist/utils/mime.js +25 -0
- package/dist/utils/path-shims.d.ts +1 -0
- package/dist/utils/path-shims.js +90 -0
- package/dist/utils/paths.d.ts +6 -0
- package/dist/utils/paths.js +18 -0
- package/dist/utils/photon.d.ts +20 -0
- package/dist/utils/photon.js +120 -0
- package/dist/utils/shell.d.ts +29 -0
- package/dist/utils/shell.js +189 -0
- package/dist/utils/sleep.d.ts +4 -0
- package/dist/utils/sleep.js +16 -0
- package/dist/utils/tools-manager.d.ts +3 -0
- package/dist/utils/tools-manager.js +101 -0
- package/dist/vendor/bin/darwin-arm64/fd +0 -0
- package/dist/vendor/bin/darwin-arm64/rg +0 -0
- package/dist/vendor/bin/darwin-x64/fd +0 -0
- package/dist/vendor/bin/darwin-x64/rg +0 -0
- package/dist/vendor/bin/linux-arm64/fd +0 -0
- package/dist/vendor/bin/linux-arm64/rg +0 -0
- package/dist/vendor/bin/linux-x64/fd +0 -0
- package/dist/vendor/bin/linux-x64/rg +0 -0
- package/dist/vendor/bin/win32-arm64/fd.exe +0 -0
- package/dist/vendor/bin/win32-arm64/rg.exe +0 -0
- package/dist/vendor/bin/win32-x64/fd.exe +0 -0
- package/dist/vendor/bin/win32-x64/rg.exe +0 -0
- package/package.json +90 -26
- package/dist/extension/core.d.ts +0 -69
- package/dist/extension/core.js +0 -270
- package/dist/extension/core.js.map +0 -1
- package/dist/extension/index.d.ts +0 -25
- package/dist/extension/index.js +0 -303
- package/dist/extension/index.js.map +0 -1
- package/dist/main.js.map +0 -1
- package/themes/pizza.json +0 -87
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential storage for API keys and OAuth tokens.
|
|
3
|
+
* Handles loading, saving, and refreshing credentials from auth.json.
|
|
4
|
+
*
|
|
5
|
+
* Uses file locking to prevent race conditions when multiple Pizza instances
|
|
6
|
+
* try to refresh tokens simultaneously.
|
|
7
|
+
*/
|
|
8
|
+
import { getEnvApiKey, } from "@earendil-works/pi-ai/compat";
|
|
9
|
+
import { getOAuthApiKey, getOAuthProvider, getOAuthProviders } from "@earendil-works/pi-ai/oauth";
|
|
10
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
11
|
+
import { dirname, join } from "path";
|
|
12
|
+
import lockfile from "proper-lockfile";
|
|
13
|
+
import { getAgentDir } from "../config.js";
|
|
14
|
+
import { resolveConfigValue } from "./resolve-config-value.js";
|
|
15
|
+
export class FileAuthStorageBackend {
|
|
16
|
+
constructor(authPath = join(getAgentDir(), "auth.json")) {
|
|
17
|
+
this.authPath = authPath;
|
|
18
|
+
}
|
|
19
|
+
ensureParentDir() {
|
|
20
|
+
const dir = dirname(this.authPath);
|
|
21
|
+
if (!existsSync(dir)) {
|
|
22
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
ensureFileExists() {
|
|
26
|
+
if (!existsSync(this.authPath)) {
|
|
27
|
+
writeFileSync(this.authPath, "{}", "utf-8");
|
|
28
|
+
chmodSync(this.authPath, 0o600);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
acquireLockSyncWithRetry(path) {
|
|
32
|
+
const maxAttempts = 10;
|
|
33
|
+
const delayMs = 20;
|
|
34
|
+
let lastError;
|
|
35
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
36
|
+
try {
|
|
37
|
+
return lockfile.lockSync(path, { realpath: false });
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
const code = typeof error === "object" && error !== null && "code" in error
|
|
41
|
+
? String(error.code)
|
|
42
|
+
: undefined;
|
|
43
|
+
if (code !== "ELOCKED" || attempt === maxAttempts) {
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
lastError = error;
|
|
47
|
+
const start = Date.now();
|
|
48
|
+
while (Date.now() - start < delayMs) {
|
|
49
|
+
// Sleep synchronously to avoid changing callers to async.
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
throw lastError ?? new Error("Failed to acquire auth storage lock");
|
|
54
|
+
}
|
|
55
|
+
withLock(fn) {
|
|
56
|
+
this.ensureParentDir();
|
|
57
|
+
this.ensureFileExists();
|
|
58
|
+
let release;
|
|
59
|
+
try {
|
|
60
|
+
release = this.acquireLockSyncWithRetry(this.authPath);
|
|
61
|
+
const current = existsSync(this.authPath) ? readFileSync(this.authPath, "utf-8") : undefined;
|
|
62
|
+
const { result, next } = fn(current);
|
|
63
|
+
if (next !== undefined) {
|
|
64
|
+
writeFileSync(this.authPath, next, "utf-8");
|
|
65
|
+
chmodSync(this.authPath, 0o600);
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
if (release) {
|
|
71
|
+
release();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async withLockAsync(fn) {
|
|
76
|
+
this.ensureParentDir();
|
|
77
|
+
this.ensureFileExists();
|
|
78
|
+
let release;
|
|
79
|
+
let lockCompromised = false;
|
|
80
|
+
let lockCompromisedError;
|
|
81
|
+
const throwIfCompromised = () => {
|
|
82
|
+
if (lockCompromised) {
|
|
83
|
+
throw lockCompromisedError ?? new Error("Auth storage lock was compromised");
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
try {
|
|
87
|
+
release = await lockfile.lock(this.authPath, {
|
|
88
|
+
retries: {
|
|
89
|
+
retries: 10,
|
|
90
|
+
factor: 2,
|
|
91
|
+
minTimeout: 100,
|
|
92
|
+
maxTimeout: 10000,
|
|
93
|
+
randomize: true,
|
|
94
|
+
},
|
|
95
|
+
stale: 30000,
|
|
96
|
+
onCompromised: (err) => {
|
|
97
|
+
lockCompromised = true;
|
|
98
|
+
lockCompromisedError = err;
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
throwIfCompromised();
|
|
102
|
+
const current = existsSync(this.authPath) ? readFileSync(this.authPath, "utf-8") : undefined;
|
|
103
|
+
const { result, next } = await fn(current);
|
|
104
|
+
throwIfCompromised();
|
|
105
|
+
if (next !== undefined) {
|
|
106
|
+
writeFileSync(this.authPath, next, "utf-8");
|
|
107
|
+
chmodSync(this.authPath, 0o600);
|
|
108
|
+
}
|
|
109
|
+
throwIfCompromised();
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
if (release) {
|
|
114
|
+
try {
|
|
115
|
+
await release();
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// Ignore unlock errors when lock is compromised.
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export class InMemoryAuthStorageBackend {
|
|
125
|
+
withLock(fn) {
|
|
126
|
+
const { result, next } = fn(this.value);
|
|
127
|
+
if (next !== undefined) {
|
|
128
|
+
this.value = next;
|
|
129
|
+
}
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
async withLockAsync(fn) {
|
|
133
|
+
const { result, next } = await fn(this.value);
|
|
134
|
+
if (next !== undefined) {
|
|
135
|
+
this.value = next;
|
|
136
|
+
}
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Credential storage backed by a JSON file.
|
|
142
|
+
*/
|
|
143
|
+
export class AuthStorage {
|
|
144
|
+
constructor(storage) {
|
|
145
|
+
this.storage = storage;
|
|
146
|
+
this.data = {};
|
|
147
|
+
this.runtimeOverrides = new Map();
|
|
148
|
+
this.loadError = null;
|
|
149
|
+
this.errors = [];
|
|
150
|
+
this.reload();
|
|
151
|
+
}
|
|
152
|
+
static create(authPath) {
|
|
153
|
+
return new AuthStorage(new FileAuthStorageBackend(authPath ?? join(getAgentDir(), "auth.json")));
|
|
154
|
+
}
|
|
155
|
+
static fromStorage(storage) {
|
|
156
|
+
return new AuthStorage(storage);
|
|
157
|
+
}
|
|
158
|
+
static inMemory(data = {}) {
|
|
159
|
+
const storage = new InMemoryAuthStorageBackend();
|
|
160
|
+
storage.withLock(() => ({ result: undefined, next: JSON.stringify(data, null, 2) }));
|
|
161
|
+
return AuthStorage.fromStorage(storage);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Set a runtime API key override (not persisted to disk).
|
|
165
|
+
* Used for CLI --api-key flag.
|
|
166
|
+
*/
|
|
167
|
+
setRuntimeApiKey(provider, apiKey) {
|
|
168
|
+
this.runtimeOverrides.set(provider, apiKey);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Remove a runtime API key override.
|
|
172
|
+
*/
|
|
173
|
+
removeRuntimeApiKey(provider) {
|
|
174
|
+
this.runtimeOverrides.delete(provider);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Set a fallback resolver for API keys not found in auth.json or env vars.
|
|
178
|
+
* Used for custom provider keys from models.json.
|
|
179
|
+
*/
|
|
180
|
+
setFallbackResolver(resolver) {
|
|
181
|
+
this.fallbackResolver = resolver;
|
|
182
|
+
}
|
|
183
|
+
recordError(error) {
|
|
184
|
+
const normalizedError = error instanceof Error ? error : new Error(String(error));
|
|
185
|
+
this.errors.push(normalizedError);
|
|
186
|
+
}
|
|
187
|
+
parseStorageData(content) {
|
|
188
|
+
if (!content) {
|
|
189
|
+
return {};
|
|
190
|
+
}
|
|
191
|
+
return JSON.parse(content);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Reload credentials from storage.
|
|
195
|
+
*/
|
|
196
|
+
reload() {
|
|
197
|
+
let content;
|
|
198
|
+
try {
|
|
199
|
+
this.storage.withLock((current) => {
|
|
200
|
+
content = current;
|
|
201
|
+
return { result: undefined };
|
|
202
|
+
});
|
|
203
|
+
this.data = this.parseStorageData(content);
|
|
204
|
+
this.loadError = null;
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
this.loadError = error;
|
|
208
|
+
this.recordError(error);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
persistProviderChange(provider, credential) {
|
|
212
|
+
if (this.loadError) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
try {
|
|
216
|
+
this.storage.withLock((current) => {
|
|
217
|
+
const currentData = this.parseStorageData(current);
|
|
218
|
+
const merged = { ...currentData };
|
|
219
|
+
if (credential) {
|
|
220
|
+
merged[provider] = credential;
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
delete merged[provider];
|
|
224
|
+
}
|
|
225
|
+
return { result: undefined, next: JSON.stringify(merged, null, 2) };
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
this.recordError(error);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Get credential for a provider.
|
|
234
|
+
*/
|
|
235
|
+
get(provider) {
|
|
236
|
+
return this.data[provider] ?? undefined;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Set credential for a provider.
|
|
240
|
+
*/
|
|
241
|
+
set(provider, credential) {
|
|
242
|
+
this.data[provider] = credential;
|
|
243
|
+
this.persistProviderChange(provider, credential);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Remove credential for a provider.
|
|
247
|
+
*/
|
|
248
|
+
remove(provider) {
|
|
249
|
+
delete this.data[provider];
|
|
250
|
+
this.persistProviderChange(provider, undefined);
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* List all providers with credentials.
|
|
254
|
+
*/
|
|
255
|
+
list() {
|
|
256
|
+
return Object.keys(this.data);
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Check if credentials exist for a provider in auth.json.
|
|
260
|
+
*/
|
|
261
|
+
has(provider) {
|
|
262
|
+
return provider in this.data;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Check if any form of auth is configured for a provider.
|
|
266
|
+
* Unlike getApiKey(), this doesn't refresh OAuth tokens.
|
|
267
|
+
*/
|
|
268
|
+
hasAuth(provider) {
|
|
269
|
+
if (this.runtimeOverrides.has(provider))
|
|
270
|
+
return true;
|
|
271
|
+
if (this.data[provider])
|
|
272
|
+
return true;
|
|
273
|
+
if (getEnvApiKey(provider))
|
|
274
|
+
return true;
|
|
275
|
+
if (this.fallbackResolver?.(provider))
|
|
276
|
+
return true;
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Get all credentials (for passing to getOAuthApiKey).
|
|
281
|
+
*/
|
|
282
|
+
getAll() {
|
|
283
|
+
return { ...this.data };
|
|
284
|
+
}
|
|
285
|
+
drainErrors() {
|
|
286
|
+
const drained = [...this.errors];
|
|
287
|
+
this.errors = [];
|
|
288
|
+
return drained;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Login to an OAuth provider.
|
|
292
|
+
*/
|
|
293
|
+
async login(providerId, callbacks) {
|
|
294
|
+
const provider = getOAuthProvider(providerId);
|
|
295
|
+
if (!provider) {
|
|
296
|
+
throw new Error(`Unknown OAuth provider: ${providerId}`);
|
|
297
|
+
}
|
|
298
|
+
const credentials = await provider.login(callbacks);
|
|
299
|
+
this.set(providerId, { type: "oauth", ...credentials });
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Logout from a provider.
|
|
303
|
+
*/
|
|
304
|
+
logout(provider) {
|
|
305
|
+
this.remove(provider);
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Refresh OAuth token with backend locking to prevent race conditions.
|
|
309
|
+
* Multiple Pizza instances may try to refresh simultaneously when tokens expire.
|
|
310
|
+
*/
|
|
311
|
+
async refreshOAuthTokenWithLock(providerId) {
|
|
312
|
+
const provider = getOAuthProvider(providerId);
|
|
313
|
+
if (!provider) {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
const result = await this.storage.withLockAsync(async (current) => {
|
|
317
|
+
const currentData = this.parseStorageData(current);
|
|
318
|
+
this.data = currentData;
|
|
319
|
+
this.loadError = null;
|
|
320
|
+
const cred = currentData[providerId];
|
|
321
|
+
if (cred?.type !== "oauth") {
|
|
322
|
+
return { result: null };
|
|
323
|
+
}
|
|
324
|
+
if (Date.now() < cred.expires) {
|
|
325
|
+
return { result: { apiKey: provider.getApiKey(cred), newCredentials: cred } };
|
|
326
|
+
}
|
|
327
|
+
const oauthCreds = {};
|
|
328
|
+
for (const [key, value] of Object.entries(currentData)) {
|
|
329
|
+
if (value.type === "oauth") {
|
|
330
|
+
oauthCreds[key] = value;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
const refreshed = await getOAuthApiKey(providerId, oauthCreds);
|
|
334
|
+
if (!refreshed) {
|
|
335
|
+
return { result: null };
|
|
336
|
+
}
|
|
337
|
+
const merged = {
|
|
338
|
+
...currentData,
|
|
339
|
+
[providerId]: { type: "oauth", ...refreshed.newCredentials },
|
|
340
|
+
};
|
|
341
|
+
this.data = merged;
|
|
342
|
+
this.loadError = null;
|
|
343
|
+
return { result: refreshed, next: JSON.stringify(merged, null, 2) };
|
|
344
|
+
});
|
|
345
|
+
return result;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Get API key for a provider.
|
|
349
|
+
* Priority:
|
|
350
|
+
* 1. Runtime override (CLI --api-key)
|
|
351
|
+
* 2. API key from auth.json
|
|
352
|
+
* 3. OAuth token from auth.json (auto-refreshed with locking)
|
|
353
|
+
* 4. Environment variable
|
|
354
|
+
* 5. Fallback resolver (models.json custom providers)
|
|
355
|
+
*/
|
|
356
|
+
async getApiKey(providerId, options) {
|
|
357
|
+
// Runtime override takes highest priority
|
|
358
|
+
const runtimeKey = this.runtimeOverrides.get(providerId);
|
|
359
|
+
if (runtimeKey) {
|
|
360
|
+
return runtimeKey;
|
|
361
|
+
}
|
|
362
|
+
const cred = this.data[providerId];
|
|
363
|
+
if (cred?.type === "api_key") {
|
|
364
|
+
return resolveConfigValue(cred.key);
|
|
365
|
+
}
|
|
366
|
+
if (cred?.type === "oauth") {
|
|
367
|
+
const provider = getOAuthProvider(providerId);
|
|
368
|
+
if (!provider) {
|
|
369
|
+
// Unknown OAuth provider, can't get API key
|
|
370
|
+
return undefined;
|
|
371
|
+
}
|
|
372
|
+
// Check if token needs refresh
|
|
373
|
+
const needsRefresh = Date.now() >= cred.expires;
|
|
374
|
+
if (needsRefresh) {
|
|
375
|
+
// Use locked refresh to prevent race conditions
|
|
376
|
+
try {
|
|
377
|
+
const result = await this.refreshOAuthTokenWithLock(providerId);
|
|
378
|
+
if (result) {
|
|
379
|
+
return result.apiKey;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
catch (error) {
|
|
383
|
+
this.recordError(error);
|
|
384
|
+
// Refresh failed - re-read file to check if another instance succeeded
|
|
385
|
+
this.reload();
|
|
386
|
+
const updatedCred = this.data[providerId];
|
|
387
|
+
if (updatedCred?.type === "oauth" && Date.now() < updatedCred.expires) {
|
|
388
|
+
// Another instance refreshed successfully, use those credentials
|
|
389
|
+
return provider.getApiKey(updatedCred);
|
|
390
|
+
}
|
|
391
|
+
// Refresh truly failed - return undefined so model discovery skips this provider
|
|
392
|
+
// User can /login to re-authenticate (credentials preserved for retry)
|
|
393
|
+
return undefined;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
// Token not expired, use current access token
|
|
398
|
+
return provider.getApiKey(cred);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
// Fall back to environment variable
|
|
402
|
+
const envKey = getEnvApiKey(providerId);
|
|
403
|
+
if (envKey)
|
|
404
|
+
return envKey;
|
|
405
|
+
// Fall back to custom resolver (e.g., models.json custom providers)
|
|
406
|
+
if (options?.includeFallback !== false) {
|
|
407
|
+
return this.fallbackResolver?.(providerId) ?? undefined;
|
|
408
|
+
}
|
|
409
|
+
return undefined;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Get all registered OAuth providers
|
|
413
|
+
*/
|
|
414
|
+
getOAuthProviders() {
|
|
415
|
+
return getOAuthProviders();
|
|
416
|
+
}
|
|
417
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bash command execution with streaming support and cancellation.
|
|
3
|
+
*
|
|
4
|
+
* This module provides a unified bash execution implementation used by:
|
|
5
|
+
* - The reactor for interactive and RPC modes
|
|
6
|
+
* - Direct calls from modes that need bash execution
|
|
7
|
+
*/
|
|
8
|
+
import type { BashOperations } from "./tools/bash.js";
|
|
9
|
+
export interface BashExecutorOptions {
|
|
10
|
+
/** Callback for streaming output chunks (already sanitized) */
|
|
11
|
+
onChunk?: (chunk: string) => void;
|
|
12
|
+
/** AbortSignal for cancellation */
|
|
13
|
+
signal?: AbortSignal;
|
|
14
|
+
}
|
|
15
|
+
export interface BashResult {
|
|
16
|
+
/** Combined stdout + stderr output (sanitized, possibly truncated) */
|
|
17
|
+
output: string;
|
|
18
|
+
/** Process exit code (undefined if killed/cancelled) */
|
|
19
|
+
exitCode: number | undefined;
|
|
20
|
+
/** Whether the command was cancelled via signal */
|
|
21
|
+
cancelled: boolean;
|
|
22
|
+
/** Whether the output was truncated */
|
|
23
|
+
truncated: boolean;
|
|
24
|
+
/** Path to temp file containing full output (if output exceeded truncation threshold) */
|
|
25
|
+
fullOutputPath?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Execute a bash command using custom BashOperations.
|
|
29
|
+
* Used for remote execution (SSH, containers, etc.).
|
|
30
|
+
*/
|
|
31
|
+
export declare function executeBashWithOperations(command: string, cwd: string, operations: BashOperations, options?: BashExecutorOptions): Promise<BashResult>;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bash command execution with streaming support and cancellation.
|
|
3
|
+
*
|
|
4
|
+
* This module provides a unified bash execution implementation used by:
|
|
5
|
+
* - The reactor for interactive and RPC modes
|
|
6
|
+
* - Direct calls from modes that need bash execution
|
|
7
|
+
*/
|
|
8
|
+
import { randomBytes } from "node:crypto";
|
|
9
|
+
import { createWriteStream } from "node:fs";
|
|
10
|
+
import { tmpdir } from "node:os";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import stripAnsi from "strip-ansi";
|
|
13
|
+
import { sanitizeBinaryOutput } from "../utils/shell.js";
|
|
14
|
+
import { DEFAULT_MAX_BYTES, truncateTail } from "./tools/truncate.js";
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Implementation
|
|
17
|
+
// ============================================================================
|
|
18
|
+
/**
|
|
19
|
+
* Execute a bash command using custom BashOperations.
|
|
20
|
+
* Used for remote execution (SSH, containers, etc.).
|
|
21
|
+
*/
|
|
22
|
+
export async function executeBashWithOperations(command, cwd, operations, options) {
|
|
23
|
+
const outputChunks = [];
|
|
24
|
+
let outputBytes = 0;
|
|
25
|
+
const maxOutputBytes = DEFAULT_MAX_BYTES * 2;
|
|
26
|
+
let tempFilePath;
|
|
27
|
+
let tempFileStream;
|
|
28
|
+
let totalBytes = 0;
|
|
29
|
+
const ensureTempFile = () => {
|
|
30
|
+
if (tempFilePath) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const id = randomBytes(8).toString("hex");
|
|
34
|
+
tempFilePath = join(tmpdir(), `pizza-bash-${id}.log`);
|
|
35
|
+
tempFileStream = createWriteStream(tempFilePath);
|
|
36
|
+
for (const chunk of outputChunks) {
|
|
37
|
+
tempFileStream.write(chunk);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const decoder = new TextDecoder();
|
|
41
|
+
const onData = (data) => {
|
|
42
|
+
totalBytes += data.length;
|
|
43
|
+
// Sanitize: strip ANSI, replace binary garbage, normalize newlines
|
|
44
|
+
const text = sanitizeBinaryOutput(stripAnsi(decoder.decode(data, { stream: true }))).replace(/\r/g, "");
|
|
45
|
+
// Start writing to temp file if exceeds threshold
|
|
46
|
+
if (totalBytes > DEFAULT_MAX_BYTES) {
|
|
47
|
+
ensureTempFile();
|
|
48
|
+
}
|
|
49
|
+
if (tempFileStream) {
|
|
50
|
+
tempFileStream.write(text);
|
|
51
|
+
}
|
|
52
|
+
// Keep rolling buffer
|
|
53
|
+
outputChunks.push(text);
|
|
54
|
+
outputBytes += text.length;
|
|
55
|
+
while (outputBytes > maxOutputBytes && outputChunks.length > 1) {
|
|
56
|
+
const removed = outputChunks.shift();
|
|
57
|
+
outputBytes -= removed.length;
|
|
58
|
+
}
|
|
59
|
+
// Stream to callback
|
|
60
|
+
if (options?.onChunk) {
|
|
61
|
+
options.onChunk(text);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
try {
|
|
65
|
+
const result = await operations.exec(command, cwd, {
|
|
66
|
+
onData,
|
|
67
|
+
signal: options?.signal,
|
|
68
|
+
});
|
|
69
|
+
if (tempFileStream) {
|
|
70
|
+
tempFileStream.end();
|
|
71
|
+
}
|
|
72
|
+
const fullOutput = outputChunks.join("");
|
|
73
|
+
const truncationResult = truncateTail(fullOutput);
|
|
74
|
+
if (truncationResult.truncated) {
|
|
75
|
+
ensureTempFile();
|
|
76
|
+
}
|
|
77
|
+
const cancelled = options?.signal?.aborted ?? false;
|
|
78
|
+
return {
|
|
79
|
+
output: truncationResult.truncated ? truncationResult.content : fullOutput,
|
|
80
|
+
exitCode: cancelled ? undefined : (result.exitCode ?? undefined),
|
|
81
|
+
cancelled,
|
|
82
|
+
truncated: truncationResult.truncated,
|
|
83
|
+
fullOutputPath: tempFilePath,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
if (tempFileStream) {
|
|
88
|
+
tempFileStream.end();
|
|
89
|
+
}
|
|
90
|
+
// Check if it was an abort
|
|
91
|
+
if (options?.signal?.aborted) {
|
|
92
|
+
const fullOutput = outputChunks.join("");
|
|
93
|
+
const truncationResult = truncateTail(fullOutput);
|
|
94
|
+
if (truncationResult.truncated) {
|
|
95
|
+
ensureTempFile();
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
output: truncationResult.truncated ? truncationResult.content : fullOutput,
|
|
99
|
+
exitCode: undefined,
|
|
100
|
+
cancelled: true,
|
|
101
|
+
truncated: truncationResult.truncated,
|
|
102
|
+
fullOutputPath: tempFilePath,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
throw err;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Branch summarization for tree navigation.
|
|
3
|
+
*
|
|
4
|
+
* When navigating to a different point in the session tree, this generates
|
|
5
|
+
* a summary of the branch being left so context isn't lost.
|
|
6
|
+
*/
|
|
7
|
+
import type { AgentMessage } from "../agent/types.js";
|
|
8
|
+
import type { Model } from "@earendil-works/pi-ai/compat";
|
|
9
|
+
import type { ReadonlySessionManager, SessionEntry } from "../types/session-types.js";
|
|
10
|
+
import { type FileOperations } from "./utils.js";
|
|
11
|
+
export interface BranchSummaryResult {
|
|
12
|
+
summary?: string;
|
|
13
|
+
readFiles?: string[];
|
|
14
|
+
modifiedFiles?: string[];
|
|
15
|
+
aborted?: boolean;
|
|
16
|
+
error?: string;
|
|
17
|
+
}
|
|
18
|
+
/** Details stored in BranchSummaryEntry.details for file tracking */
|
|
19
|
+
export interface BranchSummaryDetails {
|
|
20
|
+
readFiles: string[];
|
|
21
|
+
modifiedFiles: string[];
|
|
22
|
+
}
|
|
23
|
+
export type { FileOperations } from "./utils.js";
|
|
24
|
+
export interface BranchPreparation {
|
|
25
|
+
/** Messages extracted for summarization, in chronological order */
|
|
26
|
+
messages: AgentMessage[];
|
|
27
|
+
/** File operations extracted from tool calls */
|
|
28
|
+
fileOps: FileOperations;
|
|
29
|
+
/** Total estimated tokens in messages */
|
|
30
|
+
totalTokens: number;
|
|
31
|
+
}
|
|
32
|
+
export interface CollectEntriesResult {
|
|
33
|
+
/** Entries to summarize, in chronological order */
|
|
34
|
+
entries: SessionEntry[];
|
|
35
|
+
/** Common ancestor between old and new position, if any */
|
|
36
|
+
commonAncestorId: string | null;
|
|
37
|
+
}
|
|
38
|
+
export interface GenerateBranchSummaryOptions {
|
|
39
|
+
/** Model to use for summarization */
|
|
40
|
+
model: Model<any>;
|
|
41
|
+
/** API key for the model */
|
|
42
|
+
apiKey: string;
|
|
43
|
+
/** Request headers for the model */
|
|
44
|
+
headers?: Record<string, string>;
|
|
45
|
+
/** Abort signal for cancellation */
|
|
46
|
+
signal: AbortSignal;
|
|
47
|
+
/** Optional custom instructions for summarization */
|
|
48
|
+
customInstructions?: string;
|
|
49
|
+
/** If true, customInstructions replaces the default prompt instead of being appended */
|
|
50
|
+
replaceInstructions?: boolean;
|
|
51
|
+
/** Tokens reserved for prompt + LLM response (default 16384) */
|
|
52
|
+
reserveTokens?: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Collect entries that should be summarized when navigating from one position to another.
|
|
56
|
+
*
|
|
57
|
+
* Walks from oldLeafId back to the common ancestor with targetId, collecting entries
|
|
58
|
+
* along the way. Does NOT stop at compaction boundaries - those are included and their
|
|
59
|
+
* summaries become context.
|
|
60
|
+
*
|
|
61
|
+
* @param session - Session manager (read-only access)
|
|
62
|
+
* @param oldLeafId - Current position (where we're navigating from)
|
|
63
|
+
* @param targetId - Target position (where we're navigating to)
|
|
64
|
+
* @returns Entries to summarize and the common ancestor
|
|
65
|
+
*/
|
|
66
|
+
export declare function collectEntriesForBranchSummary(session: ReadonlySessionManager, oldLeafId: string | null, targetId: string): CollectEntriesResult;
|
|
67
|
+
/**
|
|
68
|
+
* Prepare entries for summarization with token budget.
|
|
69
|
+
*
|
|
70
|
+
* Walks entries from NEWEST to OLDEST, adding messages until we hit the token budget.
|
|
71
|
+
* This ensures we keep the most recent context when the branch is too long.
|
|
72
|
+
*
|
|
73
|
+
* Also collects file operations from:
|
|
74
|
+
* - Tool calls in assistant messages
|
|
75
|
+
* - Existing branch_summary entries' details (for cumulative tracking)
|
|
76
|
+
*
|
|
77
|
+
* @param entries - Entries in chronological order
|
|
78
|
+
* @param tokenBudget - Maximum tokens to include (0 = no limit)
|
|
79
|
+
*/
|
|
80
|
+
export declare function prepareBranchEntries(entries: SessionEntry[], tokenBudget?: number): BranchPreparation;
|
|
81
|
+
/**
|
|
82
|
+
* Generate a summary of abandoned branch entries.
|
|
83
|
+
*
|
|
84
|
+
* @param entries - Session entries to summarize (chronological order)
|
|
85
|
+
* @param options - Generation options
|
|
86
|
+
*/
|
|
87
|
+
export declare function generateBranchSummary(entries: SessionEntry[], options: GenerateBranchSummaryOptions): Promise<BranchSummaryResult>;
|