Merge remote-tracking branch 'origin/main' into pr-586-r2
# Conflicts: # packages/coding-agent/CHANGELOG.md
This commit is contained in:
@@ -63,6 +63,7 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
key: ${{ matrix.target }}
|
||||
cache-workspace-crates: true
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: "1.3"
|
||||
@@ -120,6 +121,8 @@ jobs:
|
||||
with:
|
||||
toolchain: nightly-2026-03-27
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-workspace-crates: true
|
||||
- uses: mlugg/setup-zig@v2
|
||||
with:
|
||||
version: 0.15.2
|
||||
@@ -156,6 +159,8 @@ jobs:
|
||||
with:
|
||||
toolchain: nightly-2026-03-27
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-workspace-crates: true
|
||||
- uses: mlugg/setup-zig@v2
|
||||
with:
|
||||
version: 0.15.2
|
||||
@@ -252,7 +257,7 @@ jobs:
|
||||
env:
|
||||
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
run: |
|
||||
for pkg in packages/*/; do
|
||||
for pkg in packages/utils packages/ai packages/natives packages/tui packages/stats packages/agent packages/coding-agent; do
|
||||
pkg_name=$(basename "$pkg")
|
||||
# Skip private packages
|
||||
if jq -e '.private == true' "$pkg/package.json" > /dev/null 2>&1; then
|
||||
|
||||
@@ -25,6 +25,7 @@ target/
|
||||
*~
|
||||
|
||||
# OS
|
||||
*.tmp
|
||||
.DS_Store
|
||||
|
||||
# Logs & profiling
|
||||
|
||||
Generated
+43
-31
@@ -291,9 +291,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "blake3"
|
||||
version = "1.8.3"
|
||||
version = "1.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d"
|
||||
checksum = "4d2d5991425dfd0785aed03aedcf0b321d61975c9b5b3689c774a2610ae0b51e"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
@@ -676,9 +676,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.17"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -1864,9 +1864,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.92"
|
||||
version = "0.3.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc4c90f45aa2e6eacbe8645f77fdea542ac97a494bcd117a67df9ff4d611f995"
|
||||
checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
@@ -2165,9 +2165,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "neo_frizbee"
|
||||
version = "0.8.3"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "638d4811ea05dee397475493065555838c35ed43c2d7d1bdeb5ff674d1a3bd67"
|
||||
checksum = "34a8a11f48357cecd5ada0751f1490d8da4dd935e595d34cc2727567d5288ead"
|
||||
dependencies = [
|
||||
"itertools 0.14.0",
|
||||
"raw-cpuid",
|
||||
@@ -2679,7 +2679,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pi-natives"
|
||||
version = "13.17.0"
|
||||
version = "13.17.1"
|
||||
dependencies = [
|
||||
"arboard",
|
||||
"ast-grep-core",
|
||||
@@ -2716,6 +2716,7 @@ dependencies = [
|
||||
"tree-sitter-bash",
|
||||
"tree-sitter-c",
|
||||
"tree-sitter-c-sharp",
|
||||
"tree-sitter-clojure",
|
||||
"tree-sitter-cpp",
|
||||
"tree-sitter-css",
|
||||
"tree-sitter-diff",
|
||||
@@ -3829,6 +3830,17 @@ dependencies = [
|
||||
"tree-sitter-language",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-clojure"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4004884cc509449a1d78fa3e1f02b4e953d0a8065984445304795e72e885338c"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter",
|
||||
"tree-sitter-language",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-cpp"
|
||||
version = "0.23.4"
|
||||
@@ -4390,9 +4402,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.115"
|
||||
version = "0.2.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6523d69017b7633e396a89c5efab138161ed5aafcbc8d3e5c5a42ae38f50495a"
|
||||
checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
@@ -4403,9 +4415,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.115"
|
||||
version = "0.2.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e3a6c758eb2f701ed3d052ff5737f5bfe6614326ea7f3bbac7156192dc32e67"
|
||||
checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -4413,9 +4425,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.115"
|
||||
version = "0.2.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "921de2737904886b52bcbb237301552d05969a6f9c40d261eb0533c8b055fedf"
|
||||
checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
@@ -4426,18 +4438,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.115"
|
||||
version = "0.2.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a93e946af942b58934c604527337bad9ae33ba1d5c6900bbb41c2c07c2364a93"
|
||||
checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-backend"
|
||||
version = "0.3.14"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa75f400b7f719bcd68b3f47cd939ba654cedeef690f486db71331eec4c6a406"
|
||||
checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"downcast-rs",
|
||||
@@ -4448,9 +4460,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wayland-client"
|
||||
version = "0.31.13"
|
||||
version = "0.31.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab51d9f7c071abeee76007e2b742499e535148035bb835f97aaed1338cf516c3"
|
||||
checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"rustix",
|
||||
@@ -4460,9 +4472,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wayland-protocols"
|
||||
version = "0.32.11"
|
||||
version = "0.32.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b23b5df31ceff1328f06ac607591d5ba360cf58f90c8fad4ac8d3a55a3c4aec7"
|
||||
checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"wayland-backend",
|
||||
@@ -4472,9 +4484,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wayland-protocols-wlr"
|
||||
version = "0.3.11"
|
||||
version = "0.3.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78248e4cc0eff8163370ba5c158630dcae1f3497a586b826eca2ef5f348d6235"
|
||||
checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"wayland-backend",
|
||||
@@ -4485,9 +4497,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wayland-scanner"
|
||||
version = "0.31.9"
|
||||
version = "0.31.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c86287151a309799b821ca709b7345a048a2956af05957c89cb824ab919fa4e3"
|
||||
checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quick-xml",
|
||||
@@ -4496,18 +4508,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wayland-sys"
|
||||
version = "0.31.10"
|
||||
version = "0.31.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "374f6b70e8e0d6bf9461a32988fd553b59ff630964924dad6e4a4eb6bd538d17"
|
||||
checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be"
|
||||
dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.92"
|
||||
version = "0.3.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84cde8507f4d7cfcb1185b8cb5890c494ffea65edbe1ba82cfd63661c805ed94"
|
||||
checksum = "cd70027e39b12f0849461e08ffc50b9cd7688d942c1c8e3c7b22273236b4dd0a"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ exclude = ["crates/brush-core-vendored", "crates/brush-builtins-vendored"]
|
||||
resolver = "3"
|
||||
|
||||
[workspace.package]
|
||||
version = "13.17.0"
|
||||
version = "13.17.1"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
authors = ["Can Boluk"]
|
||||
|
||||
@@ -313,7 +313,7 @@ Create images directly from the agent:
|
||||
|
||||
Modern terminal interface with smart session management:
|
||||
|
||||
- **Auto session titles**: Sessions automatically titled based on first message using smol model
|
||||
- **Auto session titles**: Sessions automatically titled based on first message using commit model, fallback to smol
|
||||
- **Welcome screen**: Logo, tips, recent sessions with selection
|
||||
- **Powerline footer**: Model, cwd, git branch/status, token usage, context %
|
||||
- **LSP status**: Shows which language servers are active and ready
|
||||
@@ -507,6 +507,7 @@ return config
|
||||
| Qwen Portal (`qwen-portal`) | `QWEN_OAUTH_TOKEN` or `QWEN_PORTAL_API_KEY` |
|
||||
| vLLM (`vllm`) | `VLLM_API_KEY` |
|
||||
| Cloudflare AI Gateway (`cloudflare-ai-gateway`) | `CLOUDFLARE_AI_GATEWAY_API_KEY` |
|
||||
| Vercel AI Gateway (`vercel-ai-gateway`) | `AI_GATEWAY_API_KEY` |
|
||||
| Qianfan (`qianfan`) | `QIANFAN_API_KEY` |
|
||||
|
||||
See [Environment Variables](docs/environment-variables.md) for the full list.
|
||||
@@ -544,6 +545,7 @@ Use `/login` with supported providers:
|
||||
- MiniMax Coding Plan (International / China)
|
||||
- Qwen Portal (`qwen-portal`)
|
||||
- Cloudflare AI Gateway (`cloudflare-ai-gateway`)
|
||||
- Vercel AI Gateway (`vercel-ai-gateway`)
|
||||
|
||||
For `ollama`, API key is optional. Leave it unset for local no-auth instances, or set `OLLAMA_API_KEY` for authenticated hosts.
|
||||
For `llama.cpp`, API key is optional. Leave it unset for local no-auth instances, or set `LLAMA_CPP_API_KEY` for authenticated hosts.
|
||||
@@ -1238,8 +1240,6 @@ Use `--tools <list>` to restrict available built-in tools.
|
||||
| `todo_write` | Phased task tracking with progress management |
|
||||
| `fetch` | Fetch and extract URL content |
|
||||
| `web_search` | Multi-provider web search |
|
||||
| `deep_search` | AI-powered deep research with synthesized results (Exa) |
|
||||
| `code_search` | Search code snippets and technical documentation (Exa) |
|
||||
| `write` | Create/overwrite files |
|
||||
| `generate_image` | Generate or edit images using Gemini image models |
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
},
|
||||
"packages/agent": {
|
||||
"name": "@oh-my-pi/pi-agent-core",
|
||||
"version": "13.17.0",
|
||||
"version": "13.17.1",
|
||||
"dependencies": {
|
||||
"@oh-my-pi/pi-ai": "workspace:*",
|
||||
"@oh-my-pi/pi-utils": "workspace:*",
|
||||
@@ -27,7 +27,7 @@
|
||||
},
|
||||
"packages/ai": {
|
||||
"name": "@oh-my-pi/pi-ai",
|
||||
"version": "13.17.0",
|
||||
"version": "13.17.1",
|
||||
"bin": {
|
||||
"pi-ai": "./src/cli.ts",
|
||||
},
|
||||
@@ -51,7 +51,7 @@
|
||||
},
|
||||
"packages/coding-agent": {
|
||||
"name": "@oh-my-pi/pi-coding-agent",
|
||||
"version": "13.17.0",
|
||||
"version": "13.17.1",
|
||||
"bin": {
|
||||
"omp": "src/cli.ts",
|
||||
},
|
||||
@@ -80,7 +80,7 @@
|
||||
},
|
||||
"packages/natives": {
|
||||
"name": "@oh-my-pi/pi-natives",
|
||||
"version": "13.17.0",
|
||||
"version": "13.17.1",
|
||||
"dependencies": {
|
||||
"@oh-my-pi/pi-utils": "workspace:*",
|
||||
},
|
||||
@@ -114,7 +114,7 @@
|
||||
},
|
||||
"packages/stats": {
|
||||
"name": "@oh-my-pi/omp-stats",
|
||||
"version": "13.17.0",
|
||||
"version": "13.17.1",
|
||||
"bin": {
|
||||
"omp-stats": "./src/index.ts",
|
||||
},
|
||||
@@ -139,7 +139,7 @@
|
||||
},
|
||||
"packages/swarm-extension": {
|
||||
"name": "@oh-my-pi/swarm-extension",
|
||||
"version": "13.17.0",
|
||||
"version": "13.17.1",
|
||||
"bin": {
|
||||
"omp-swarm": "src/cli.ts",
|
||||
},
|
||||
@@ -152,7 +152,7 @@
|
||||
},
|
||||
"packages/tui": {
|
||||
"name": "@oh-my-pi/pi-tui",
|
||||
"version": "13.17.0",
|
||||
"version": "13.17.1",
|
||||
"dependencies": {
|
||||
"@oh-my-pi/pi-natives": "workspace:*",
|
||||
"@oh-my-pi/pi-utils": "workspace:*",
|
||||
@@ -165,7 +165,7 @@
|
||||
},
|
||||
"packages/utils": {
|
||||
"name": "@oh-my-pi/pi-utils",
|
||||
"version": "13.17.0",
|
||||
"version": "13.17.1",
|
||||
"dependencies": {
|
||||
"beautiful-mermaid": "^1.1",
|
||||
"winston": "^3.19",
|
||||
@@ -191,25 +191,25 @@
|
||||
|
||||
"@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="],
|
||||
|
||||
"@aws-sdk/client-bedrock-runtime": ["@aws-sdk/client-bedrock-runtime@3.1019.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.25", "@aws-sdk/credential-provider-node": "^3.972.27", "@aws-sdk/eventstream-handler-node": "^3.972.12", "@aws-sdk/middleware-eventstream": "^3.972.8", "@aws-sdk/middleware-host-header": "^3.972.8", "@aws-sdk/middleware-logger": "^3.972.8", "@aws-sdk/middleware-recursion-detection": "^3.972.9", "@aws-sdk/middleware-user-agent": "^3.972.26", "@aws-sdk/middleware-websocket": "^3.972.14", "@aws-sdk/region-config-resolver": "^3.972.10", "@aws-sdk/token-providers": "3.1019.0", "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-endpoints": "^3.996.5", "@aws-sdk/util-user-agent-browser": "^3.972.8", "@aws-sdk/util-user-agent-node": "^3.973.12", "@smithy/config-resolver": "^4.4.13", "@smithy/core": "^3.23.12", "@smithy/eventstream-serde-browser": "^4.2.12", "@smithy/eventstream-serde-config-resolver": "^4.3.12", "@smithy/eventstream-serde-node": "^4.2.12", "@smithy/fetch-http-handler": "^5.3.15", "@smithy/hash-node": "^4.2.12", "@smithy/invalid-dependency": "^4.2.12", "@smithy/middleware-content-length": "^4.2.12", "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-retry": "^4.4.44", "@smithy/middleware-serde": "^4.2.15", "@smithy/middleware-stack": "^4.2.12", "@smithy/node-config-provider": "^4.3.12", "@smithy/node-http-handler": "^4.5.0", "@smithy/protocol-http": "^5.3.12", "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.43", "@smithy/util-defaults-mode-node": "^4.2.47", "@smithy/util-endpoints": "^3.3.3", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.12", "@smithy/util-stream": "^4.5.20", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-Wq1uMAZfySYofuwkFMaMM+k7epsGBRcJGE+ZosGB+8jC8Xs1lycbjSEFMt0Mo3z1qhkgEKGCQyjCbPTICMkkVw=="],
|
||||
"@aws-sdk/client-bedrock-runtime": ["@aws-sdk/client-bedrock-runtime@3.1021.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.26", "@aws-sdk/credential-provider-node": "^3.972.29", "@aws-sdk/eventstream-handler-node": "^3.972.12", "@aws-sdk/middleware-eventstream": "^3.972.8", "@aws-sdk/middleware-host-header": "^3.972.8", "@aws-sdk/middleware-logger": "^3.972.8", "@aws-sdk/middleware-recursion-detection": "^3.972.9", "@aws-sdk/middleware-user-agent": "^3.972.28", "@aws-sdk/middleware-websocket": "^3.972.14", "@aws-sdk/region-config-resolver": "^3.972.10", "@aws-sdk/token-providers": "3.1021.0", "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-endpoints": "^3.996.5", "@aws-sdk/util-user-agent-browser": "^3.972.8", "@aws-sdk/util-user-agent-node": "^3.973.14", "@smithy/config-resolver": "^4.4.13", "@smithy/core": "^3.23.13", "@smithy/eventstream-serde-browser": "^4.2.12", "@smithy/eventstream-serde-config-resolver": "^4.3.12", "@smithy/eventstream-serde-node": "^4.2.12", "@smithy/fetch-http-handler": "^5.3.15", "@smithy/hash-node": "^4.2.12", "@smithy/invalid-dependency": "^4.2.12", "@smithy/middleware-content-length": "^4.2.12", "@smithy/middleware-endpoint": "^4.4.28", "@smithy/middleware-retry": "^4.4.46", "@smithy/middleware-serde": "^4.2.16", "@smithy/middleware-stack": "^4.2.12", "@smithy/node-config-provider": "^4.3.12", "@smithy/node-http-handler": "^4.5.1", "@smithy/protocol-http": "^5.3.12", "@smithy/smithy-client": "^4.12.8", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.44", "@smithy/util-defaults-mode-node": "^4.2.48", "@smithy/util-endpoints": "^3.3.3", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.13", "@smithy/util-stream": "^4.5.21", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-FqAZq9ewaprev+BAUgUNeQKACVVEbGw8V3SZTKv0hEy5Ed5yhJhNuOL9G0QiUtdG6rX6NujGZjO5xBgKcvMLxQ=="],
|
||||
|
||||
"@aws-sdk/core": ["@aws-sdk/core@3.973.25", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@aws-sdk/xml-builder": "^3.972.16", "@smithy/core": "^3.23.12", "@smithy/node-config-provider": "^4.3.12", "@smithy/property-provider": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/signature-v4": "^5.3.12", "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-middleware": "^4.2.12", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-TNrx7eq6nKNOO62HWPqoBqPLXEkW6nLZQGwjL6lq1jZtigWYbK1NbCnT7mKDzbLMHZfuOECUt3n6CzxjUW9HWQ=="],
|
||||
"@aws-sdk/core": ["@aws-sdk/core@3.973.26", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@aws-sdk/xml-builder": "^3.972.16", "@smithy/core": "^3.23.13", "@smithy/node-config-provider": "^4.3.12", "@smithy/property-provider": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/signature-v4": "^5.3.12", "@smithy/smithy-client": "^4.12.8", "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-middleware": "^4.2.12", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-A/E6n2W42ruU+sfWk+mMUOyVXbsSgGrY3MJ9/0Az5qUdG67y8I6HYzzoAa+e/lzxxl1uCYmEL6BTMi9ZiZnplQ=="],
|
||||
|
||||
"@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.23", "", { "dependencies": { "@aws-sdk/core": "^3.973.25", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-EamaclJcCEaPHp6wiVknNMM2RlsPMjAHSsYSFLNENBM8Wz92QPc6cOn3dif6vPDQt0Oo4IEghDy3NMDCzY/IvA=="],
|
||||
"@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.24", "", { "dependencies": { "@aws-sdk/core": "^3.973.26", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-FWg8uFmT6vQM7VuzELzwVo5bzExGaKHdubn0StjgrcU5FvuLExUe+k06kn/40uKv59rYzhez8eFNM4yYE/Yb/w=="],
|
||||
|
||||
"@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.25", "", { "dependencies": { "@aws-sdk/core": "^3.973.25", "@aws-sdk/types": "^3.973.6", "@smithy/fetch-http-handler": "^5.3.15", "@smithy/node-http-handler": "^4.5.0", "@smithy/property-provider": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.20", "tslib": "^2.6.2" } }, "sha512-qPymamdPcLp6ugoVocG1y5r69ScNiRzb0hogX25/ij+Wz7c7WnsgjLTaz7+eB5BfRxeyUwuw5hgULMuwOGOpcw=="],
|
||||
"@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.26", "", { "dependencies": { "@aws-sdk/core": "^3.973.26", "@aws-sdk/types": "^3.973.6", "@smithy/fetch-http-handler": "^5.3.15", "@smithy/node-http-handler": "^4.5.1", "@smithy/property-provider": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/smithy-client": "^4.12.8", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.21", "tslib": "^2.6.2" } }, "sha512-CY4ppZ+qHYqcXqBVi//sdHST1QK3KzOEiLtpLsc9W2k2vfZPKExGaQIsOwcyvjpjUEolotitmd3mUNY56IwDEA=="],
|
||||
|
||||
"@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.972.26", "", { "dependencies": { "@aws-sdk/core": "^3.973.25", "@aws-sdk/credential-provider-env": "^3.972.23", "@aws-sdk/credential-provider-http": "^3.972.25", "@aws-sdk/credential-provider-login": "^3.972.26", "@aws-sdk/credential-provider-process": "^3.972.23", "@aws-sdk/credential-provider-sso": "^3.972.26", "@aws-sdk/credential-provider-web-identity": "^3.972.26", "@aws-sdk/nested-clients": "^3.996.16", "@aws-sdk/types": "^3.973.6", "@smithy/credential-provider-imds": "^4.2.12", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-xKxEAMuP6GYx2y5GET+d3aGEroax3AgGfwBE65EQAUe090lzyJ/RzxPX9s8v7Z6qAk0XwfQl+LrmH05X7YvTeg=="],
|
||||
"@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.972.28", "", { "dependencies": { "@aws-sdk/core": "^3.973.26", "@aws-sdk/credential-provider-env": "^3.972.24", "@aws-sdk/credential-provider-http": "^3.972.26", "@aws-sdk/credential-provider-login": "^3.972.28", "@aws-sdk/credential-provider-process": "^3.972.24", "@aws-sdk/credential-provider-sso": "^3.972.28", "@aws-sdk/credential-provider-web-identity": "^3.972.28", "@aws-sdk/nested-clients": "^3.996.18", "@aws-sdk/types": "^3.973.6", "@smithy/credential-provider-imds": "^4.2.12", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-wXYvq3+uQcZV7k+bE4yDXCTBdzWTU9x/nMiKBfzInmv6yYK1veMK0AKvRfRBd72nGWYKcL6AxwiPg9z/pYlgpw=="],
|
||||
|
||||
"@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.26", "", { "dependencies": { "@aws-sdk/core": "^3.973.25", "@aws-sdk/nested-clients": "^3.996.16", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-EFcM8RM3TUxnZOfMJo++3PnyxFu1fL/huzmn3Vh+8IWRgqZawUD3cRwwOr+/4bE9DpyHaLOWFAjY0lfK5X9ZkQ=="],
|
||||
"@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.28", "", { "dependencies": { "@aws-sdk/core": "^3.973.26", "@aws-sdk/nested-clients": "^3.996.18", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ZSTfO6jqUTCysbdBPtEX5OUR//3rbD0lN7jO3sQeS2Gjr/Y+DT6SbIJ0oT2cemNw3UzKu97sNONd1CwNMthuZQ=="],
|
||||
|
||||
"@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.27", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.23", "@aws-sdk/credential-provider-http": "^3.972.25", "@aws-sdk/credential-provider-ini": "^3.972.26", "@aws-sdk/credential-provider-process": "^3.972.23", "@aws-sdk/credential-provider-sso": "^3.972.26", "@aws-sdk/credential-provider-web-identity": "^3.972.26", "@aws-sdk/types": "^3.973.6", "@smithy/credential-provider-imds": "^4.2.12", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-jXpxSolfFnPVj6GCTtx3xIdWNoDR7hYC/0SbetGZxOC9UnNmipHeX1k6spVstf7eWJrMhXNQEgXC0pD1r5tXIg=="],
|
||||
"@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.29", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.24", "@aws-sdk/credential-provider-http": "^3.972.26", "@aws-sdk/credential-provider-ini": "^3.972.28", "@aws-sdk/credential-provider-process": "^3.972.24", "@aws-sdk/credential-provider-sso": "^3.972.28", "@aws-sdk/credential-provider-web-identity": "^3.972.28", "@aws-sdk/types": "^3.973.6", "@smithy/credential-provider-imds": "^4.2.12", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-clSzDcvndpFJAggLDnDb36sPdlZYyEs5Zm6zgZjjUhwsJgSWiWKwFIXUVBcbruidNyBdbpOv2tNDL9sX8y3/0g=="],
|
||||
|
||||
"@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.23", "", { "dependencies": { "@aws-sdk/core": "^3.973.25", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-IL/TFW59++b7MpHserjUblGrdP5UXy5Ekqqx1XQkERXBFJcZr74I7VaSrQT5dxdRMU16xGK4L0RQ5fQG1pMgnA=="],
|
||||
"@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.24", "", { "dependencies": { "@aws-sdk/core": "^3.973.26", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Q2k/XLrFXhEztPHqj4SLCNID3hEPdlhh1CDLBpNnM+1L8fq7P+yON9/9M1IGN/dA5W45v44ylERfXtDAlmMNmw=="],
|
||||
|
||||
"@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.972.26", "", { "dependencies": { "@aws-sdk/core": "^3.973.25", "@aws-sdk/nested-clients": "^3.996.16", "@aws-sdk/token-providers": "3.1019.0", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-c6ghvRb6gTlMznWhGxn/bpVCcp0HRaz4DobGVD9kI4vwHq186nU2xN/S7QGkm0lo0H2jQU8+dgpUFLxfTcwCOg=="],
|
||||
"@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.972.28", "", { "dependencies": { "@aws-sdk/core": "^3.973.26", "@aws-sdk/nested-clients": "^3.996.18", "@aws-sdk/token-providers": "3.1021.0", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-IoUlmKMLEITFn1SiCTjPfR6KrE799FBo5baWyk/5Ppar2yXZoUdaRqZzJzK6TcJxx450M8m8DbpddRVYlp5R/A=="],
|
||||
|
||||
"@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.26", "", { "dependencies": { "@aws-sdk/core": "^3.973.25", "@aws-sdk/nested-clients": "^3.996.16", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-cXcS3+XD3iwhoXkM44AmxjmbcKueoLCINr1e+IceMmCySda5ysNIfiGBGe9qn5EMiQ9Jd7pP0AGFtcd6OV3Lvg=="],
|
||||
"@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.28", "", { "dependencies": { "@aws-sdk/core": "^3.973.26", "@aws-sdk/nested-clients": "^3.996.18", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-d+6h0SD8GGERzKe27v5rOzNGKOl0D+l0bWJdqrxH8WSQzHzjsQFIAPgIeOTUwBHVsKKwtSxc91K/SWax6XgswQ=="],
|
||||
|
||||
"@aws-sdk/eventstream-handler-node": ["@aws-sdk/eventstream-handler-node@3.972.12", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@smithy/eventstream-codec": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ruyc/MNR6e+cUrGCth7fLQ12RXBZDy/bV06tgqB9Z5n/0SN/C0m6bsQEV8FF9zPI6VSAOaRd0rNgmpYVnGawrQ=="],
|
||||
|
||||
@@ -221,15 +221,15 @@
|
||||
|
||||
"@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.972.9", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-/Wt5+CT8dpTFQxEJ9iGy/UGrXr7p2wlIOEHvIr/YcHYByzoLjrqkYqXdJjd9UIgWjv7eqV2HnFJen93UTuwfTQ=="],
|
||||
|
||||
"@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.972.26", "", { "dependencies": { "@aws-sdk/core": "^3.973.25", "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-endpoints": "^3.996.5", "@smithy/core": "^3.23.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-retry": "^4.2.12", "tslib": "^2.6.2" } }, "sha512-AilFIh4rI/2hKyyGN6XrB0yN96W2o7e7wyrPWCM6QjZM1mcC/pVkW3IWWRvuBWMpVP8Fg+rMpbzeLQ6dTM4gig=="],
|
||||
"@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.972.28", "", { "dependencies": { "@aws-sdk/core": "^3.973.26", "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-endpoints": "^3.996.5", "@smithy/core": "^3.23.13", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-retry": "^4.2.13", "tslib": "^2.6.2" } }, "sha512-cfWZFlVh7Va9lRay4PN2A9ARFzaBYcA097InT5M2CdRS05ECF5yaz86jET8Wsl2WcyKYEvVr/QNmKtYtafUHtQ=="],
|
||||
|
||||
"@aws-sdk/middleware-websocket": ["@aws-sdk/middleware-websocket@3.972.14", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-format-url": "^3.972.8", "@smithy/eventstream-codec": "^4.2.12", "@smithy/eventstream-serde-browser": "^4.2.12", "@smithy/fetch-http-handler": "^5.3.15", "@smithy/protocol-http": "^5.3.12", "@smithy/signature-v4": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-qnfDlIHjm6DrTYNvWOUbnZdVKgtoKbO/Qzj+C0Wp5Y7VUrsvBRQtGKxD+hc+mRTS4N0kBJ6iZ3+zxm4N1OSyjg=="],
|
||||
|
||||
"@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.996.16", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.25", "@aws-sdk/middleware-host-header": "^3.972.8", "@aws-sdk/middleware-logger": "^3.972.8", "@aws-sdk/middleware-recursion-detection": "^3.972.9", "@aws-sdk/middleware-user-agent": "^3.972.26", "@aws-sdk/region-config-resolver": "^3.972.10", "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-endpoints": "^3.996.5", "@aws-sdk/util-user-agent-browser": "^3.972.8", "@aws-sdk/util-user-agent-node": "^3.973.12", "@smithy/config-resolver": "^4.4.13", "@smithy/core": "^3.23.12", "@smithy/fetch-http-handler": "^5.3.15", "@smithy/hash-node": "^4.2.12", "@smithy/invalid-dependency": "^4.2.12", "@smithy/middleware-content-length": "^4.2.12", "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-retry": "^4.4.44", "@smithy/middleware-serde": "^4.2.15", "@smithy/middleware-stack": "^4.2.12", "@smithy/node-config-provider": "^4.3.12", "@smithy/node-http-handler": "^4.5.0", "@smithy/protocol-http": "^5.3.12", "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.43", "@smithy/util-defaults-mode-node": "^4.2.47", "@smithy/util-endpoints": "^3.3.3", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.12", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-L7Qzoj/qQU1cL5GnYLQP5LbI+wlLCLoINvcykR3htKcQ4tzrPf2DOs72x933BM7oArYj1SKrkb2lGlsJHIic3g=="],
|
||||
"@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.996.18", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.26", "@aws-sdk/middleware-host-header": "^3.972.8", "@aws-sdk/middleware-logger": "^3.972.8", "@aws-sdk/middleware-recursion-detection": "^3.972.9", "@aws-sdk/middleware-user-agent": "^3.972.28", "@aws-sdk/region-config-resolver": "^3.972.10", "@aws-sdk/types": "^3.973.6", "@aws-sdk/util-endpoints": "^3.996.5", "@aws-sdk/util-user-agent-browser": "^3.972.8", "@aws-sdk/util-user-agent-node": "^3.973.14", "@smithy/config-resolver": "^4.4.13", "@smithy/core": "^3.23.13", "@smithy/fetch-http-handler": "^5.3.15", "@smithy/hash-node": "^4.2.12", "@smithy/invalid-dependency": "^4.2.12", "@smithy/middleware-content-length": "^4.2.12", "@smithy/middleware-endpoint": "^4.4.28", "@smithy/middleware-retry": "^4.4.46", "@smithy/middleware-serde": "^4.2.16", "@smithy/middleware-stack": "^4.2.12", "@smithy/node-config-provider": "^4.3.12", "@smithy/node-http-handler": "^4.5.1", "@smithy/protocol-http": "^5.3.12", "@smithy/smithy-client": "^4.12.8", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.44", "@smithy/util-defaults-mode-node": "^4.2.48", "@smithy/util-endpoints": "^3.3.3", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.13", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-c7ZSIXrESxHKx2Mcopgd8AlzZgoXMr20fkx5ViPWPOLBvmyhw9VwJx/Govg8Ef/IhEon5R9l53Z8fdYSEmp6VA=="],
|
||||
|
||||
"@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.972.10", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@smithy/config-resolver": "^4.4.13", "@smithy/node-config-provider": "^4.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-1dq9ToC6e070QvnVhhbAs3bb5r6cQ10gTVc6cyRV5uvQe7P138TV2uG2i6+Yok4bAkVAcx5AqkTEBUvWEtBlsQ=="],
|
||||
|
||||
"@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1019.0", "", { "dependencies": { "@aws-sdk/core": "^3.973.25", "@aws-sdk/nested-clients": "^3.996.16", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-OF+2RfRmUKyjzrRWlDcyju3RBsuqcrYDQ8TwrJg8efcOotMzuZN4U9mpVTIdATpmEc4lWNZBMSjPzrGm6JPnAQ=="],
|
||||
"@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1021.0", "", { "dependencies": { "@aws-sdk/core": "^3.973.26", "@aws-sdk/nested-clients": "^3.996.18", "@aws-sdk/types": "^3.973.6", "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-TKY6h9spUk3OLs5v1oAgW9mAeBE3LAGNBwJokLy96wwmd4W2v/tYlXseProyed9ValDj2u1jK/4Rg1T+1NXyJA=="],
|
||||
|
||||
"@aws-sdk/types": ["@aws-sdk/types@3.973.6", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw=="],
|
||||
|
||||
@@ -241,7 +241,7 @@
|
||||
|
||||
"@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.972.8", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@smithy/types": "^4.13.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA=="],
|
||||
|
||||
"@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.973.12", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "^3.972.26", "@aws-sdk/types": "^3.973.6", "@smithy/node-config-provider": "^4.3.12", "@smithy/types": "^4.13.1", "@smithy/util-config-provider": "^4.2.2", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-8phW0TS8ntENJgDcFewYT/Q8dOmarpvSxEjATu2GUBAutiHr++oEGCiBUwxslCMNvwW2cAPZNT53S/ym8zm/gg=="],
|
||||
"@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.973.14", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "^3.972.28", "@aws-sdk/types": "^3.973.6", "@smithy/node-config-provider": "^4.3.12", "@smithy/types": "^4.13.1", "@smithy/util-config-provider": "^4.2.2", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-vNSB/DYaPOyujVZBg/zUznH9QC142MaTHVmaFlF7uzzfg3CgT9f/l4C0Yi+vU/tbBhxVcXVB90Oohk5+o+ZbWw=="],
|
||||
|
||||
"@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.16", "", { "dependencies": { "@smithy/types": "^4.13.1", "fast-xml-parser": "5.5.8", "tslib": "^2.6.2" } }, "sha512-iu2pyvaqmeatIJLURLqx9D+4jKAdTH20ntzB6BFwjyN7V960r4jK32mx0Zf7YbtOYAbmbtQfDNuL60ONinyw7A=="],
|
||||
|
||||
@@ -267,23 +267,23 @@
|
||||
|
||||
"@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="],
|
||||
|
||||
"@biomejs/biome": ["@biomejs/biome@2.4.9", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.9", "@biomejs/cli-darwin-x64": "2.4.9", "@biomejs/cli-linux-arm64": "2.4.9", "@biomejs/cli-linux-arm64-musl": "2.4.9", "@biomejs/cli-linux-x64": "2.4.9", "@biomejs/cli-linux-x64-musl": "2.4.9", "@biomejs/cli-win32-arm64": "2.4.9", "@biomejs/cli-win32-x64": "2.4.9" }, "bin": { "biome": "bin/biome" } }, "sha512-wvZW92FrwitTcacvCBT8xdAbfbxWfDLwjYMmU3djjqQTh7Ni4ZdiWIT/x5VcZ+RQuxiKzIOzi5D+dcyJDFZMsA=="],
|
||||
"@biomejs/biome": ["@biomejs/biome@2.4.10", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.10", "@biomejs/cli-darwin-x64": "2.4.10", "@biomejs/cli-linux-arm64": "2.4.10", "@biomejs/cli-linux-arm64-musl": "2.4.10", "@biomejs/cli-linux-x64": "2.4.10", "@biomejs/cli-linux-x64-musl": "2.4.10", "@biomejs/cli-win32-arm64": "2.4.10", "@biomejs/cli-win32-x64": "2.4.10" }, "bin": { "biome": "bin/biome" } }, "sha512-xxA3AphFQ1geij4JTHXv4EeSTda1IFn22ye9LdyVPoJU19fNVl0uzfEuhsfQ4Yue/0FaLs2/ccVi4UDiE7R30w=="],
|
||||
|
||||
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.9", "", { "os": "darwin", "cpu": "arm64" }, "sha512-d5G8Gf2RpH5pYwiHLPA+UpG3G9TLQu4WM+VK6sfL7K68AmhcEQ9r+nkj/DvR/GYhYox6twsHUtmWWWIKfcfQQA=="],
|
||||
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-vuzzI1cWqDVzOMIkYyHbKqp+AkQq4K7k+UCXWpkYcY/HDn1UxdsbsfgtVpa40shem8Kax4TLDLlx8kMAecgqiw=="],
|
||||
|
||||
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.9", "", { "os": "darwin", "cpu": "x64" }, "sha512-LNCLNgqDMG7BLdc3a8aY/dwKPK7+R8/JXJoXjCvZh2gx8KseqBdFDKbhrr7HCWF8SzNhbTaALhTBoh/I6rf9lA=="],
|
||||
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-14fzASRo+BPotwp7nWULy2W5xeUyFnTaq1V13Etrrxkrih+ez/2QfgFm5Ehtf5vSjtgx/IJycMMpn5kPd5ZNaA=="],
|
||||
|
||||
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-4adnkAUi6K4C/emPRgYznMOcLlUqZdXWM6aIui4VP4LraE764g6Q4YguygnAUoxKjKIXIWPteKMgRbN0wsgwcg=="],
|
||||
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-7MH1CMW5uuxQ/s7FLST63qF8B3Hgu2HRdZ7tA1X1+mk+St4JOuIrqdhIBnnyqeyWJNI+Bww7Es5QZ0wIc1Cmkw=="],
|
||||
|
||||
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-8RCww5xnPn2wpK4L/QDGDOW0dq80uVWfppPxHIUg6mOs9B6gRmqPp32h1Ls3T8GnW8Wo5A8u7vpTwz4fExN+sw=="],
|
||||
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-WrJY6UuiSD/Dh+nwK2qOTu8kdMDlLV3dLMmychIghHPAysWFq1/DGC1pVZx8POE3ZkzKR3PUUnVrtZfMfaJjyQ=="],
|
||||
|
||||
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.9", "", { "os": "linux", "cpu": "x64" }, "sha512-L10na7POF0Ks/cgLFNF1ZvIe+X4onLkTi5oP9hY+Rh60Q+7fWzKDDCeGyiHUFf1nGIa9dQOOUPGe2MyYg8nMSQ=="],
|
||||
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.10", "", { "os": "linux", "cpu": "x64" }, "sha512-tZLvEEi2u9Xu1zAqRjTcpIDGVtldigVvzug2fTuPG0ME/g8/mXpRPcNgLB22bGn6FvLJpHHnqLnwliOu8xjYrg=="],
|
||||
|
||||
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.9", "", { "os": "linux", "cpu": "x64" }, "sha512-5TD+WS9v5vzXKzjetF0hgoaNFHMcpQeBUwKKVi3JbG1e9UCrFuUK3Gt185fyTzvRdwYkJJEMqglRPjmesmVv4A=="],
|
||||
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.10", "", { "os": "linux", "cpu": "x64" }, "sha512-kDTi3pI6PBN6CiczsWYOyP2zk0IJI08EWEQyDMQWW221rPaaEz6FvjLhnU07KMzLv8q3qSuoB93ua6inSQ55Tw=="],
|
||||
|
||||
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.9", "", { "os": "win32", "cpu": "arm64" }, "sha512-aDZr0RBC3sMGJOU10BvG7eZIlWLK/i51HRIfScE2lVhfts2dQTreowLiJJd+UYg/tHKxS470IbzpuKmd0MiD6g=="],
|
||||
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-umwQU6qPzH+ISTf/eHyJ/QoQnJs3V9Vpjz2OjZXe9MVBZ7prgGafMy7yYeRGnlmDAn87AKTF3Q6weLoMGpeqdQ=="],
|
||||
|
||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.9", "", { "os": "win32", "cpu": "x64" }, "sha512-NS4g/2G9SoQ4ktKtz31pvyc/rmgzlcIDCGU/zWbmHJAqx6gcRj2gj5Q/guXhoWTzCUaQZDIqiCQXHS7BcGYc0w=="],
|
||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.10", "", { "os": "win32", "cpu": "x64" }, "sha512-aW/JU5GuyH4uxMrNYpoC2kjaHlyJGLgIa3XkhPEZI0uKhZhJZU8BuEyJmvgzSPQNGozBwWjC972RaNdcJ9KyJg=="],
|
||||
|
||||
"@bufbuild/protobuf": ["@bufbuild/protobuf@2.11.0", "", {}, "sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ=="],
|
||||
|
||||
@@ -353,11 +353,9 @@
|
||||
|
||||
"@sinclair/typebox": ["@sinclair/typebox@0.34.49", "", {}, "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A=="],
|
||||
|
||||
"@smithy/abort-controller": ["@smithy/abort-controller@4.2.12", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-xolrFw6b+2iYGl6EcOL7IJY71vvyZ0DJ3mcKtpykqPe2uscwtzDZJa1uVQXyP7w9Dd+kGwYnPbMsJrGISKiY/Q=="],
|
||||
|
||||
"@smithy/config-resolver": ["@smithy/config-resolver@4.4.13", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.12", "@smithy/types": "^4.13.1", "@smithy/util-config-provider": "^4.2.2", "@smithy/util-endpoints": "^3.3.3", "@smithy/util-middleware": "^4.2.12", "tslib": "^2.6.2" } }, "sha512-iIzMC5NmOUP6WL6o8iPBjFhUhBZ9pPjpUpQYWMUFQqKyXXzOftbfK8zcQCz/jFV1Psmf05BK5ypx4K2r4Tnwdg=="],
|
||||
|
||||
"@smithy/core": ["@smithy/core@3.23.12", "", { "dependencies": { "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-middleware": "^4.2.12", "@smithy/util-stream": "^4.5.20", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w=="],
|
||||
"@smithy/core": ["@smithy/core@3.23.13", "", { "dependencies": { "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-middleware": "^4.2.12", "@smithy/util-stream": "^4.5.21", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-J+2TT9D6oGsUVXVEMvz8h2EmdVnkBiy2auCie4aSJMvKlzUtO5hqjEzXhoCUkIMo7gAYjbQcN0g/MMSXEhDs1Q=="],
|
||||
|
||||
"@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.12", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.12", "@smithy/property-provider": "^4.2.12", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "tslib": "^2.6.2" } }, "sha512-cr2lR792vNZcYMriSIj+Um3x9KWrjcu98kn234xA6reOAFMmbRpQMOv8KPgEmLLtx3eldU6c5wALKFqNOhugmg=="],
|
||||
|
||||
@@ -381,17 +379,17 @@
|
||||
|
||||
"@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.12", "", { "dependencies": { "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-YE58Yz+cvFInWI/wOTrB+DbvUVz/pLn5mC5MvOV4fdRUc6qGwygyngcucRQjAhiCEbmfLOXX0gntSIcgMvAjmA=="],
|
||||
|
||||
"@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.27", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-serde": "^4.2.15", "@smithy/node-config-provider": "^4.3.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "@smithy/util-middleware": "^4.2.12", "tslib": "^2.6.2" } }, "sha512-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA=="],
|
||||
"@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.28", "", { "dependencies": { "@smithy/core": "^3.23.13", "@smithy/middleware-serde": "^4.2.16", "@smithy/node-config-provider": "^4.3.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "@smithy/util-middleware": "^4.2.12", "tslib": "^2.6.2" } }, "sha512-p1gfYpi91CHcs5cBq982UlGlDrxoYUX6XdHSo91cQ2KFuz6QloHosO7Jc60pJiVmkWrKOV8kFYlGFFbQ2WUKKQ=="],
|
||||
|
||||
"@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.44", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.12", "@smithy/protocol-http": "^5.3.12", "@smithy/service-error-classification": "^4.2.12", "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.12", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-Y1Rav7m5CFRPQyM4CI0koD/bXjyjJu3EQxZZhtLGD88WIrBrQ7kqXM96ncd6rYnojwOo/u9MXu57JrEvu/nLrA=="],
|
||||
"@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.46", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.12", "@smithy/protocol-http": "^5.3.12", "@smithy/service-error-classification": "^4.2.12", "@smithy/smithy-client": "^4.12.8", "@smithy/types": "^4.13.1", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.13", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-SpvWNNOPOrKQGUqZbEPO+es+FRXMWvIyzUKUOYdDgdlA6BdZj/R58p4umoQ76c2oJC44PiM7mKizyyex1IJzow=="],
|
||||
|
||||
"@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.15", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg=="],
|
||||
"@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.16", "", { "dependencies": { "@smithy/core": "^3.23.13", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-beqfV+RZ9RSv+sQqor3xroUUYgRFCGRw6niGstPG8zO9LgTl0B0MCucxjmrH/2WwksQN7UUgI7KNANoZv+KALA=="],
|
||||
|
||||
"@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.12", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-kruC5gRHwsCOuyCd4ouQxYjgRAym2uDlCvQ5acuMtRrcdfg7mFBg6blaxcJ09STpt3ziEkis6bhg1uwrWU7txw=="],
|
||||
|
||||
"@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.12", "", { "dependencies": { "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-tr2oKX2xMcO+rBOjobSwVAkV05SIfUKz8iI53rzxEmgW3GOOPOv0UioSDk+J8OpRQnpnhsO3Af6IEBabQBVmiw=="],
|
||||
|
||||
"@smithy/node-http-handler": ["@smithy/node-http-handler@4.5.0", "", { "dependencies": { "@smithy/abort-controller": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/querystring-builder": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A=="],
|
||||
"@smithy/node-http-handler": ["@smithy/node-http-handler@4.5.1", "", { "dependencies": { "@smithy/protocol-http": "^5.3.12", "@smithy/querystring-builder": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-ejjxdAXjkPIs9lyYyVutOGNOraqUE9v/NjGMKwwFrfOM354wfSD8lmlj8hVwUzQmlLLF4+udhfCX9Exnbmvfzw=="],
|
||||
|
||||
"@smithy/property-provider": ["@smithy/property-provider@4.2.12", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-jqve46eYU1v7pZ5BM+fmkbq3DerkSluPr5EhvOcHxygxzD05ByDRppRwRPPpFrsFo5yDtCYLKu+kreHKVrvc7A=="],
|
||||
|
||||
@@ -407,7 +405,7 @@
|
||||
|
||||
"@smithy/signature-v4": ["@smithy/signature-v4@5.3.12", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.2", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-middleware": "^4.2.12", "@smithy/util-uri-escape": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-B/FBwO3MVOL00DaRSXfXfa/TRXRheagt/q5A2NM13u7q+sHS59EOVGQNfG7DkmVtdQm5m3vOosoKAXSqn/OEgw=="],
|
||||
|
||||
"@smithy/smithy-client": ["@smithy/smithy-client@4.12.7", "", { "dependencies": { "@smithy/core": "^3.23.12", "@smithy/middleware-endpoint": "^4.4.27", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.20", "tslib": "^2.6.2" } }, "sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ=="],
|
||||
"@smithy/smithy-client": ["@smithy/smithy-client@4.12.8", "", { "dependencies": { "@smithy/core": "^3.23.13", "@smithy/middleware-endpoint": "^4.4.28", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.21", "tslib": "^2.6.2" } }, "sha512-aJaAX7vHe5i66smoSSID7t4rKY08PbD8EBU7DOloixvhOozfYWdcSYE4l6/tjkZ0vBZhGjheWzB2mh31sLgCMA=="],
|
||||
|
||||
"@smithy/types": ["@smithy/types@4.13.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-787F3yzE2UiJIQ+wYW1CVg2odHjmaWLGksnKQHUrK/lYZSEcy1msuLVvxaR/sI2/aDe9U+TBuLsXnr3vod1g0g=="],
|
||||
|
||||
@@ -423,9 +421,9 @@
|
||||
|
||||
"@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ=="],
|
||||
|
||||
"@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.43", "", { "dependencies": { "@smithy/property-provider": "^4.2.12", "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Qd/0wCKMaXxev/z00TvNzGCH2jlKKKxXP1aDxB6oKwSQthe3Og2dMhSayGCnsma1bK/kQX1+X7SMP99t6FgiiQ=="],
|
||||
"@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.44", "", { "dependencies": { "@smithy/property-provider": "^4.2.12", "@smithy/smithy-client": "^4.12.8", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-eZg6XzaCbVr2S5cAErU5eGBDaOVTuTo1I65i4tQcHENRcZ8rMWhQy1DaIYUSLyZjsfXvmCqZrstSMYyGFocvHA=="],
|
||||
|
||||
"@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.47", "", { "dependencies": { "@smithy/config-resolver": "^4.4.13", "@smithy/credential-provider-imds": "^4.2.12", "@smithy/node-config-provider": "^4.3.12", "@smithy/property-provider": "^4.2.12", "@smithy/smithy-client": "^4.12.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-qSRbYp1EQ7th+sPFuVcVO05AE0QH635hycdEXlpzIahqHHf2Fyd/Zl+8v0XYMJ3cgDVPa0lkMefU7oNUjAP+DQ=="],
|
||||
"@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.48", "", { "dependencies": { "@smithy/config-resolver": "^4.4.13", "@smithy/credential-provider-imds": "^4.2.12", "@smithy/node-config-provider": "^4.3.12", "@smithy/property-provider": "^4.2.12", "@smithy/smithy-client": "^4.12.8", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-FqOKTlqSaoV3nzO55pMs5NBnZX8EhoI0DGmn9kbYeXWppgHD6dchyuj2HLqp4INJDJbSrj6OFYJkAh/WhSzZPg=="],
|
||||
|
||||
"@smithy/util-endpoints": ["@smithy/util-endpoints@3.3.3", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-VACQVe50j0HZPjpwWcjyT51KUQ4AnsvEaQ2lKHOSL4mNLD0G9BjEniQ+yCt1qqfKfiAHRAts26ud7hBjamrwig=="],
|
||||
|
||||
@@ -433,9 +431,9 @@
|
||||
|
||||
"@smithy/util-middleware": ["@smithy/util-middleware@4.2.12", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Er805uFUOvgc0l8nv0e0su0VFISoxhJ/AwOn3gL2NWNY2LUEldP5WtVcRYSQBcjg0y9NfG8JYrCJaYDpupBHJQ=="],
|
||||
|
||||
"@smithy/util-retry": ["@smithy/util-retry@4.2.12", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ=="],
|
||||
"@smithy/util-retry": ["@smithy/util-retry@4.2.13", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-qQQsIvL0MGIbUjeSrg0/VlQ3jGNKyM3/2iU3FPNgy01z+Sp4OvcaxbgIoFOTvB61ZoohtutuOvOcgmhbD0katQ=="],
|
||||
|
||||
"@smithy/util-stream": ["@smithy/util-stream@4.5.20", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.15", "@smithy/node-http-handler": "^4.5.0", "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-4yXLm5n/B5SRBR2p8cZ90Sbv4zL4NKsgxdzCzp/83cXw2KxLEumt5p+GAVyRNZgQOSrzXn9ARpO0lUe8XSlSDw=="],
|
||||
"@smithy/util-stream": ["@smithy/util-stream@4.5.21", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.15", "@smithy/node-http-handler": "^4.5.1", "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-KzSg+7KKywLnkoKejRtIBXDmwBfjGvg1U1i/etkC7XSWUyFCoLno1IohV2c74IzQqdhX5y3uE44r/8/wuK+A7Q=="],
|
||||
|
||||
"@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw=="],
|
||||
|
||||
@@ -467,21 +465,21 @@
|
||||
|
||||
"@types/yauzl": ["@types/yauzl@2.10.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="],
|
||||
|
||||
"@typescript/native-preview": ["@typescript/native-preview@7.0.0-dev.20260330.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260330.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260330.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20260330.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260330.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20260330.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260330.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20260330.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-tyRn6/PAYpkN35bKz2RPfzV/QAACvle9EYlYyywn/p/qPI5yOBLka0zM/2dGKx+o5vHVQ1gr/iVrgW8w7Wqc8Q=="],
|
||||
"@typescript/native-preview": ["@typescript/native-preview@7.0.0-dev.20260331.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260331.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260331.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20260331.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260331.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20260331.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260331.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20260331.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-Gfy2J/LhydkOHOw+ZWRw0M8Xl3O2bzQXLXIYITdMz2N4GpMm8misAvvCzhqMacOGvazKr1FsL9LIIW2kxk6kzw=="],
|
||||
|
||||
"@typescript/native-preview-darwin-arm64": ["@typescript/native-preview-darwin-arm64@7.0.0-dev.20260330.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-jX47OX4svh3/CzSDYd81+kELLP5r0+G7lFGiMAVUNpD+ZLmyh/nrkuuI643pGe8D0i9dTfg/aiZQq6d/00Feog=="],
|
||||
"@typescript/native-preview-darwin-arm64": ["@typescript/native-preview-darwin-arm64@7.0.0-dev.20260331.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1PRnBCN2csiCzj76YaSBtP4jPLEGBUmVhXHplC+yHOKaxx9nf3HFiFCg/19raInvN/lJ8+Bp1fZ/qIsWAAHiBw=="],
|
||||
|
||||
"@typescript/native-preview-darwin-x64": ["@typescript/native-preview-darwin-x64@7.0.0-dev.20260330.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-JysPTOMnyje+LkdB3LR+Emnl7hJCvMzUVRcOVJyzmxWOMFNCYPWNK3wDdwuoS6jE5Hii9vInt/xtn3pc+kgb+A=="],
|
||||
"@typescript/native-preview-darwin-x64": ["@typescript/native-preview-darwin-x64@7.0.0-dev.20260331.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-llXnfLGjl+gXsANLD7UI/gSb3lj7aZW13Rf8sVXQnHJ3/dkJRAm/MgLqdjuuyvYq3pFaleiep+zoLd96rLRqUw=="],
|
||||
|
||||
"@typescript/native-preview-linux-arm": ["@typescript/native-preview-linux-arm@7.0.0-dev.20260330.1", "", { "os": "linux", "cpu": "arm" }, "sha512-rVwd9ZgjR8Le6ItUD/p59LCOYdZbi4DZYavXAMzPPpaJCnmdHwOcJFWc61vricsQljRjVBnunQknHfYABZDq9Q=="],
|
||||
"@typescript/native-preview-linux-arm": ["@typescript/native-preview-linux-arm@7.0.0-dev.20260331.1", "", { "os": "linux", "cpu": "arm" }, "sha512-+8AZzA0BRjMkLDvdQKZOMuheRxNGpSWn7sOtoKqo70R915D0TyEynEXX6B7/aw3+Jfn1H5hLRiBjxoVsmdKENw=="],
|
||||
|
||||
"@typescript/native-preview-linux-arm64": ["@typescript/native-preview-linux-arm64@7.0.0-dev.20260330.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-9zlw4ENr6HAUNO+OiWuoQpWUGk4lkqb8NUyXDvTqtlgZGvjyePNVy8OW7KlPuPtCaQS3btV7w4h5X1wlTm2bFA=="],
|
||||
"@typescript/native-preview-linux-arm64": ["@typescript/native-preview-linux-arm64@7.0.0-dev.20260331.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-sH5gALi89jl5ZjAL/UsLDPsjT/nCLRfHl/pw86ablRX10tYsJhJ/RD6J/cl3g39kJ18tIISSbsuIBn+ncanfSA=="],
|
||||
|
||||
"@typescript/native-preview-linux-x64": ["@typescript/native-preview-linux-x64@7.0.0-dev.20260330.1", "", { "os": "linux", "cpu": "x64" }, "sha512-7cB4e12e61xaXgJu6N9rMP+6qdkODVsW8zGjVS5a+cobBCsJSdMnlshfitlL9a+BYWe9HseMqPnLcsaE+t22CQ=="],
|
||||
"@typescript/native-preview-linux-x64": ["@typescript/native-preview-linux-x64@7.0.0-dev.20260331.1", "", { "os": "linux", "cpu": "x64" }, "sha512-Yic6MYfX7Uit5jLLENzWFIi6tjp4LTLF37KBiVaHZSvEFyX1kqVwu4j9WNeaz81O6fcB/1dZ1MrILgfcqalNBg=="],
|
||||
|
||||
"@typescript/native-preview-win32-arm64": ["@typescript/native-preview-win32-arm64@7.0.0-dev.20260330.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-4wv/FX9F5oAQhzjqwL8KN4O5xgciBy5pT2yLP45xcQahxFUoPnQeF9Pz2x/jwXZSmuh8E1DQWX9evSVGA1MV2A=="],
|
||||
"@typescript/native-preview-win32-arm64": ["@typescript/native-preview-win32-arm64@7.0.0-dev.20260331.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-vGxK6gtGF97zSx9wOpiVME3h9v0tbZbrHHdKA+fLFNvDV0Df8ud89DEePL7l2yKnVVmf0OnjJy6sYoVyj+LIPA=="],
|
||||
|
||||
"@typescript/native-preview-win32-x64": ["@typescript/native-preview-win32-x64@7.0.0-dev.20260330.1", "", { "os": "win32", "cpu": "x64" }, "sha512-zKDOG/nq1yTx3cjfc19VFO+TYK2+03Wc17pGdD+X+dWTj15LZ2PKk3s4OtT2b8siTcAUn7zmwLsMCWPYO7xXQw=="],
|
||||
"@typescript/native-preview-win32-x64": ["@typescript/native-preview-win32-x64@7.0.0-dev.20260331.1", "", { "os": "win32", "cpu": "x64" }, "sha512-oJnNiU9UTDPJp6dOmOUW+/Wzt3MQZXIHsDaU4qM0RiAjFE6S+PIX8s5z/ID0orr4MMroUMiLdolL4OVZolNDSw=="],
|
||||
|
||||
"@typescript/vfs": ["@typescript/vfs@1.6.4", "", { "dependencies": { "debug": "^4.4.3" }, "peerDependencies": { "typescript": "*" } }, "sha512-PJFXFS4ZJKiJ9Qiuix6Dz/OwEIqHD7Dme1UwZhTK11vR+5dqW2ACbdndWQexBzCx+CPuMe5WBYQWCsFyGlQLlQ=="],
|
||||
|
||||
@@ -515,7 +513,7 @@
|
||||
|
||||
"bare-path": ["bare-path@3.0.0", "", { "dependencies": { "bare-os": "^3.0.1" } }, "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw=="],
|
||||
|
||||
"bare-stream": ["bare-stream@2.11.0", "", { "dependencies": { "streamx": "^2.25.0", "teex": "^1.0.1" }, "peerDependencies": { "bare-abort-controller": "*", "bare-buffer": "*", "bare-events": "*" }, "optionalPeers": ["bare-abort-controller", "bare-buffer", "bare-events"] }, "sha512-Y/+iQ49fL3rIn6w/AVxI/2+BRrpmzJvdWt5Jv8Za6Ngqc6V227c+pYjYYgLdpR3MwQ9ObVXD0ZrqoBztakM0rw=="],
|
||||
"bare-stream": ["bare-stream@2.12.0", "", { "dependencies": { "streamx": "^2.25.0", "teex": "^1.0.1" }, "peerDependencies": { "bare-abort-controller": "*", "bare-buffer": "*", "bare-events": "*" }, "optionalPeers": ["bare-abort-controller", "bare-buffer", "bare-events"] }, "sha512-w28i8lkBgREV3rPXGbgK+BO66q+ZpKqRWrZLiCdmmUlLPrQ45CzkvRhN+7lnv00Gpi2zy5naRxnUFAxCECDm9g=="],
|
||||
|
||||
"bare-url": ["bare-url@2.4.0", "", { "dependencies": { "bare-path": "^3.0.0" } }, "sha512-NSTU5WN+fy/L0DDenfE8SXQna4voXuW0FHM7wH8i3/q9khUSchfPbPezO4zSFMnDGIf9YE+mt/RWhZgNRKRIXA=="],
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ ast-grep-core = { version = "0.39", default-features = false, features = [
|
||||
tree-sitter = "0.25"
|
||||
tree-sitter-bash = "0.25"
|
||||
tree-sitter-c = "0.24"
|
||||
tree-sitter-clojure = "0.1"
|
||||
tree-sitter-c-sharp = "0.23"
|
||||
tree-sitter-cpp = "0.23"
|
||||
tree-sitter-css = "0.25"
|
||||
|
||||
@@ -162,6 +162,12 @@ static LANG_ALIASES: phf::Map<&'static str, SupportLang> = phf::phf_map! {
|
||||
"c#" => SupportLang::CSharp,
|
||||
"cs" => SupportLang::CSharp,
|
||||
"css" => SupportLang::Css,
|
||||
"clj" => SupportLang::Clojure,
|
||||
"cljc" => SupportLang::Clojure,
|
||||
"cljs" => SupportLang::Clojure,
|
||||
"clojure" => SupportLang::Clojure,
|
||||
"clojurescript" => SupportLang::Clojure,
|
||||
"edn" => SupportLang::Clojure,
|
||||
"diff" => SupportLang::Diff,
|
||||
"patch" => SupportLang::Diff,
|
||||
"elixir" => SupportLang::Elixir,
|
||||
|
||||
@@ -131,6 +131,7 @@ impl_lang_expando!(Zig, language_zig, 'µ');
|
||||
// ── Stub languages ($ accepted in grammar) ──────────────────────────────
|
||||
|
||||
impl_lang!(Bash, language_bash);
|
||||
impl_lang!(Clojure, language_clojure);
|
||||
impl_lang!(Java, language_java);
|
||||
impl_lang!(JavaScript, language_javascript);
|
||||
impl_lang!(Json, language_json);
|
||||
@@ -252,6 +253,7 @@ pub enum SupportLang {
|
||||
C,
|
||||
Cpp,
|
||||
CSharp,
|
||||
Clojure,
|
||||
Css,
|
||||
Diff,
|
||||
Elixir,
|
||||
@@ -292,9 +294,10 @@ impl SupportLang {
|
||||
pub const fn all_langs() -> &'static [Self] {
|
||||
use SupportLang::*;
|
||||
&[
|
||||
Bash, C, Cpp, CSharp, Css, Diff, Elixir, Go, Haskell, Hcl, Html, Java, JavaScript, Json,
|
||||
Julia, Kotlin, Lua, Make, Markdown, Nix, ObjC, Odin, Php, Python, Regex, Ruby, Rust,
|
||||
Scala, Solidity, Starlark, Swift, Toml, Tsx, TypeScript, Verilog, Xml, Yaml, Zig,
|
||||
Bash, C, Cpp, CSharp, Clojure, Css, Diff, Elixir, Go, Haskell, Hcl, Html, Java,
|
||||
JavaScript, Json, Julia, Kotlin, Lua, Make, Markdown, Nix, ObjC, Odin, Php, Python, Regex,
|
||||
Ruby, Rust, Scala, Solidity, Starlark, Swift, Toml, Tsx, TypeScript, Verilog, Xml, Yaml,
|
||||
Zig,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -306,6 +309,7 @@ impl SupportLang {
|
||||
Self::C => "c",
|
||||
Self::Cpp => "cpp",
|
||||
Self::CSharp => "csharp",
|
||||
Self::Clojure => "clojure",
|
||||
Self::Css => "css",
|
||||
Self::Diff => "diff",
|
||||
Self::Elixir => "elixir",
|
||||
@@ -360,6 +364,7 @@ macro_rules! execute_lang_method {
|
||||
S::C => C.$method($($pname,)*),
|
||||
S::Cpp => Cpp.$method($($pname,)*),
|
||||
S::CSharp => CSharp.$method($($pname,)*),
|
||||
S::Clojure => Clojure.$method($($pname,)*),
|
||||
S::Css => Css.$method($($pname,)*),
|
||||
S::Diff => Diff.$method($($pname,)*),
|
||||
S::Elixir => Elixir.$method($($pname,)*),
|
||||
@@ -456,6 +461,7 @@ const fn extensions(lang: SupportLang) -> &'static [&'static str] {
|
||||
C => &["c", "h"],
|
||||
Cpp => &["cc", "hpp", "cpp", "c++", "hh", "cxx", "cu", "ino"],
|
||||
CSharp => &["cs"],
|
||||
Clojure => &["clj", "cljs", "cljc", "edn"],
|
||||
Css => &["css", "scss"],
|
||||
Diff => &["diff", "patch"],
|
||||
Elixir => &["ex", "exs"],
|
||||
|
||||
@@ -8,6 +8,9 @@ pub fn language_bash() -> TSLanguage {
|
||||
pub fn language_c() -> TSLanguage {
|
||||
tree_sitter_c::LANGUAGE.into()
|
||||
}
|
||||
pub fn language_clojure() -> TSLanguage {
|
||||
tree_sitter_clojure::LANGUAGE.into()
|
||||
}
|
||||
pub fn language_cpp() -> TSLanguage {
|
||||
tree_sitter_cpp::LANGUAGE.into()
|
||||
}
|
||||
|
||||
+2
-1
@@ -34,7 +34,8 @@
|
||||
"generate-models": "bun --cwd=packages/ai scripts/generate-models.ts",
|
||||
"generate-docs-index": "bun --cwd=packages/coding-agent run generate-docs-index",
|
||||
"generate-template": "bun --cwd=packages/coding-agent run generate-template",
|
||||
"sync-exports": "bun scripts/sync-exports.ts"
|
||||
"sync-exports": "bun scripts/sync-exports.ts",
|
||||
"check-spoofed-versions": "bun scripts/check-spoofed-versions.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^2.4",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"type": "module",
|
||||
"name": "@oh-my-pi/pi-agent-core",
|
||||
"version": "13.17.0",
|
||||
"version": "13.17.1",
|
||||
"description": "General-purpose agent with transport abstraction, state management, and attachment support",
|
||||
"homepage": "https://github.com/can1357/oh-my-pi",
|
||||
"author": "Can Boluk",
|
||||
|
||||
@@ -2,6 +2,30 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [13.17.1] - 2026-04-01
|
||||
|
||||
### Added
|
||||
|
||||
- Added `thinkingSignature` field to thinking content blocks to preserve the original reasoning field name (e.g., `reasoning_text`, `reasoning_content`) for accurate follow-up requests
|
||||
- Added first-event timeout detection for streaming responses to abort stuck requests before user-visible content arrives
|
||||
- Added `PI_STREAM_FIRST_EVENT_TIMEOUT_MS` environment variable to configure first-event timeout (defaults to 15 seconds or idle timeout, whichever is lower)
|
||||
|
||||
### Changed
|
||||
|
||||
- Changed thinking block handling to track and distinguish between different reasoning field types, enabling proper field name preservation across multiple turns
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed Anthropic stream timeout errors to be properly retried by recognizing first-event timeout messages
|
||||
- Fixed stream stall detection to distinguish between first-event timeouts and idle timeouts, enabling faster recovery for stuck connections
|
||||
|
||||
### Added
|
||||
|
||||
- Added Vercel AI Gateway to `/login` providers for interactive API key setup
|
||||
|
||||
### Fixed
|
||||
- Fixed `omp commit` failing with HTTP 400 errors when using reasoning-enabled models on OpenAI-compatible endpoints that don't support the `developer` role (e.g., GitHub Copilot, custom proxies). Now falls back to `system` role when `developer` is unsupported.
|
||||
|
||||
## [13.17.0] - 2026-03-30
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"type": "module",
|
||||
"name": "@oh-my-pi/pi-ai",
|
||||
"version": "13.17.0",
|
||||
"version": "13.17.1",
|
||||
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
||||
"homepage": "https://github.com/can1357/oh-my-pi",
|
||||
"author": "Can Boluk",
|
||||
|
||||
@@ -62,6 +62,7 @@ import { loginTavily } from "./utils/oauth/tavily";
|
||||
import { loginTogether } from "./utils/oauth/together";
|
||||
import type { OAuthController, OAuthCredentials, OAuthProvider, OAuthProviderId } from "./utils/oauth/types";
|
||||
import { loginVenice } from "./utils/oauth/venice";
|
||||
import { loginVercelAiGateway } from "./utils/oauth/vercel-ai-gateway";
|
||||
import { loginVllm } from "./utils/oauth/vllm";
|
||||
import { loginXiaomi } from "./utils/oauth/xiaomi";
|
||||
import { loginZai } from "./utils/oauth/zai";
|
||||
@@ -903,6 +904,11 @@ export class AuthStorage {
|
||||
await saveApiKeyCredential(apiKey);
|
||||
return;
|
||||
}
|
||||
case "vercel-ai-gateway": {
|
||||
const apiKey = await loginVercelAiGateway(ctrl);
|
||||
await saveApiKeyCredential(apiKey);
|
||||
return;
|
||||
}
|
||||
case "vllm": {
|
||||
const apiKey = await loginVllm(ctrl);
|
||||
await saveApiKeyCredential(apiKey);
|
||||
|
||||
@@ -33,6 +33,7 @@ import type {
|
||||
import { isAnthropicOAuthToken, normalizeToolCallId, resolveCacheRetention } from "../utils";
|
||||
import { AssistantMessageEventStream } from "../utils/event-stream";
|
||||
import { finalizeErrorMessage, type RawHttpRequestDump } from "../utils/http-inspector";
|
||||
import { getStreamFirstEventTimeoutMs, iterateWithIdleTimeout } from "../utils/idle-iterator";
|
||||
import { parseStreamingJson } from "../utils/json-parse";
|
||||
import {
|
||||
buildCopilotDynamicHeaders,
|
||||
@@ -572,9 +573,9 @@ function isTransientStreamParseError(error: unknown): boolean {
|
||||
|
||||
export function isProviderRetryableError(error: unknown): boolean {
|
||||
if (!(error instanceof Error)) return false;
|
||||
const msg = error.message;
|
||||
const msg = error.message.toLowerCase();
|
||||
return (
|
||||
/rate.?limit|too many requests|overloaded|service.?unavailable|internal_error|stream error.*received from peer|1302/i.test(
|
||||
/rate.?limit|too many requests|overloaded|service.?unavailable|internal_error|stream error.*received from peer|1302|timed?\s*out while waiting for the first event|timeout waiting for first/i.test(
|
||||
msg,
|
||||
) || isTransientStreamParseError(error)
|
||||
);
|
||||
@@ -674,13 +675,22 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
||||
let providerRetryAttempt = 0;
|
||||
let started = false;
|
||||
do {
|
||||
const anthropicStream = client.messages.stream({ ...params, stream: true }, { signal: options?.signal });
|
||||
const requestAbortController = new AbortController();
|
||||
const requestSignal = options?.signal
|
||||
? AbortSignal.any([options.signal, requestAbortController.signal])
|
||||
: requestAbortController.signal;
|
||||
const anthropicStream = client.messages.stream({ ...params, stream: true }, { signal: requestSignal });
|
||||
if (copilotDynamicHeaders && output.usage.premiumRequests === undefined) {
|
||||
output.usage.premiumRequests = copilotDynamicHeaders.premiumRequests;
|
||||
}
|
||||
|
||||
try {
|
||||
for await (const event of anthropicStream) {
|
||||
for await (const event of iterateWithIdleTimeout(anthropicStream, {
|
||||
firstItemTimeoutMs: getStreamFirstEventTimeoutMs(),
|
||||
errorMessage: "Anthropic stream stalled while waiting for the next event",
|
||||
firstItemErrorMessage: "Anthropic stream timed out while waiting for the first event",
|
||||
onFirstItemTimeout: () => requestAbortController.abort(),
|
||||
})) {
|
||||
started = true;
|
||||
if (event.type === "message_start") {
|
||||
output.responseId = event.message.id;
|
||||
|
||||
@@ -20,8 +20,15 @@ import {
|
||||
} from "../types";
|
||||
import { AssistantMessageEventStream } from "../utils/event-stream";
|
||||
import { finalizeErrorMessage, type RawHttpRequestDump } from "../utils/http-inspector";
|
||||
import { getOpenAIStreamIdleTimeoutMs, iterateWithIdleTimeout } from "../utils/idle-iterator";
|
||||
import {
|
||||
createFirstEventWatchdog,
|
||||
getOpenAIStreamIdleTimeoutMs,
|
||||
getStreamFirstEventTimeoutMs,
|
||||
iterateWithIdleTimeout,
|
||||
markFirstStreamEvent,
|
||||
} from "../utils/idle-iterator";
|
||||
import { mapToOpenAIResponsesToolChoice } from "../utils/tool-choice";
|
||||
import { supportsDeveloperRole } from "./openai-responses";
|
||||
import {
|
||||
appendResponsesToolResultMessages,
|
||||
convertResponsesAssistantMessage,
|
||||
@@ -113,26 +120,31 @@ export const streamAzureOpenAIResponses: StreamFunction<"azure-openai-responses"
|
||||
// Create Azure OpenAI client
|
||||
const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
|
||||
const client = createClient(model, apiKey, options);
|
||||
const params = buildParams(model, context, options, deploymentName);
|
||||
const { baseUrl } = resolveAzureConfig(model, options);
|
||||
const params = buildParams(model, context, options, deploymentName, baseUrl);
|
||||
const requestAbortController = new AbortController();
|
||||
const requestSignal = options?.signal
|
||||
? AbortSignal.any([options.signal, requestAbortController.signal])
|
||||
: requestAbortController.signal;
|
||||
const idleTimeoutMs = getOpenAIStreamIdleTimeoutMs();
|
||||
const firstEventWatchdog = createFirstEventWatchdog(getStreamFirstEventTimeoutMs(idleTimeoutMs), () =>
|
||||
requestAbortController.abort(),
|
||||
);
|
||||
options?.onPayload?.(params);
|
||||
rawRequestDump = {
|
||||
provider: model.provider,
|
||||
api: output.api,
|
||||
model: model.id,
|
||||
method: "POST",
|
||||
url: `${resolveAzureConfig(model, options).baseUrl}/responses`,
|
||||
url: `${baseUrl}/responses`,
|
||||
body: params,
|
||||
};
|
||||
const openaiStream = await client.responses.create(params, { signal: requestSignal });
|
||||
stream.push({ type: "start", partial: output });
|
||||
|
||||
await processResponsesStream(
|
||||
iterateWithIdleTimeout(openaiStream, {
|
||||
idleTimeoutMs: getOpenAIStreamIdleTimeoutMs(),
|
||||
iterateWithIdleTimeout(markFirstStreamEvent(openaiStream, firstEventWatchdog), {
|
||||
idleTimeoutMs,
|
||||
errorMessage: "Azure OpenAI responses stream stalled while waiting for the next event",
|
||||
onIdle: () => requestAbortController.abort(),
|
||||
}),
|
||||
@@ -245,8 +257,9 @@ function buildParams(
|
||||
context: Context,
|
||||
options: AzureOpenAIResponsesOptions | undefined,
|
||||
deploymentName: string,
|
||||
resolvedBaseUrl?: string,
|
||||
) {
|
||||
const messages = convertMessages(model, context, true);
|
||||
const messages = convertMessages(model, context, true, resolvedBaseUrl);
|
||||
|
||||
const params: AzureOpenAIResponsesSamplingParams = {
|
||||
model: deploymentName,
|
||||
@@ -323,13 +336,14 @@ function convertMessages(
|
||||
model: Model<"azure-openai-responses">,
|
||||
context: Context,
|
||||
strictResponsesPairing: boolean,
|
||||
resolvedBaseUrl?: string,
|
||||
): ResponseInput {
|
||||
const messages: ResponseInput = [];
|
||||
const transformedMessages = transformMessages(context.messages, model, normalizeResponsesToolCallIdForTransform);
|
||||
const knownCallIds = new Set<string>();
|
||||
|
||||
if (context.systemPrompt) {
|
||||
const role = model.reasoning ? "developer" : "system";
|
||||
const role = model.reasoning && supportsDeveloperRole(resolvedBaseUrl ?? model) ? "developer" : "system";
|
||||
messages.push({
|
||||
role,
|
||||
content: context.systemPrompt.toWellFormed(),
|
||||
|
||||
@@ -65,14 +65,14 @@ const ANTIGRAVITY_ENDPOINT_FALLBACKS = [ANTIGRAVITY_DAILY_ENDPOINT, ANTIGRAVITY_
|
||||
|
||||
/**
|
||||
* Build a User-Agent string that identifies as Gemini CLI to unlock higher rate limits.
|
||||
* Uses the same format as the official Gemini CLI:
|
||||
* GeminiCLI/VERSION/MODEL (PLATFORM; ARCH) google-api-nodejs-client/10.5.0
|
||||
* Uses the same format as the official Gemini CLI (v0.35+):
|
||||
* GeminiCLI/VERSION/MODEL (PLATFORM; ARCH; SURFACE)
|
||||
*/
|
||||
export function getGeminiCliUserAgent(modelId = "gemini-3.1-pro-preview"): string {
|
||||
const version = process.env.PI_AI_GEMINI_CLI_VERSION || "0.34.0";
|
||||
const version = process.env.PI_AI_GEMINI_CLI_VERSION || "0.35.3";
|
||||
const platform = process.platform === "win32" ? "win32" : process.platform;
|
||||
const arch = process.arch === "x64" ? "x64" : process.arch;
|
||||
return `GeminiCLI/${version}/${modelId} (${platform}; ${arch}) google-api-nodejs-client/10.5.0`;
|
||||
return `GeminiCLI/${version}/${modelId} (${platform}; ${arch}; terminal)`;
|
||||
}
|
||||
|
||||
const ANTIGRAVITY_USER_AGENT = (() => {
|
||||
|
||||
@@ -36,10 +36,14 @@ function detectStrictModeSupport(provider: string, baseUrl: string): boolean {
|
||||
/**
|
||||
* Detect compatibility settings from provider and baseUrl for known providers.
|
||||
* Provider takes precedence over URL-based detection since it's explicitly configured.
|
||||
* @param model - The model configuration
|
||||
* @param resolvedBaseUrl - Optional resolved base URL (e.g., after GitHub Copilot proxy-ep resolution).
|
||||
* If provided, this takes precedence over model.baseUrl for URL-based checks.
|
||||
*/
|
||||
export function detectOpenAICompat(model: Model<"openai-completions">): ResolvedOpenAICompat {
|
||||
export function detectOpenAICompat(model: Model<"openai-completions">, resolvedBaseUrl?: string): ResolvedOpenAICompat {
|
||||
const provider = model.provider;
|
||||
const baseUrl = model.baseUrl;
|
||||
// Use resolvedBaseUrl if provided (e.g., after GitHub Copilot proxy-ep resolution)
|
||||
const baseUrl = resolvedBaseUrl ?? model.baseUrl;
|
||||
|
||||
const isCerebras = provider === "cerebras" || baseUrl.includes("cerebras.ai");
|
||||
const isZai = provider === "zai" || baseUrl.includes("api.z.ai");
|
||||
@@ -109,9 +113,15 @@ export function detectOpenAICompat(model: Model<"openai-completions">): Resolved
|
||||
/**
|
||||
* Resolve compatibility settings by layering explicit model.compat overrides onto
|
||||
* the detected defaults. This is the canonical compat view for both metadata and transport.
|
||||
* @param model - The model configuration
|
||||
* @param resolvedBaseUrl - Optional resolved base URL (e.g., after GitHub Copilot proxy-ep resolution).
|
||||
* If provided, this takes precedence over model.baseUrl for URL-based checks.
|
||||
*/
|
||||
export function resolveOpenAICompat(model: Model<"openai-completions">): ResolvedOpenAICompat {
|
||||
const detected = detectOpenAICompat(model);
|
||||
export function resolveOpenAICompat(
|
||||
model: Model<"openai-completions">,
|
||||
resolvedBaseUrl?: string,
|
||||
): ResolvedOpenAICompat {
|
||||
const detected = detectOpenAICompat(model, resolvedBaseUrl);
|
||||
if (!model.compat) {
|
||||
return detected;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,13 @@ import {
|
||||
} from "../types";
|
||||
import { AssistantMessageEventStream } from "../utils/event-stream";
|
||||
import { finalizeErrorMessage, type RawHttpRequestDump } from "../utils/http-inspector";
|
||||
import { getOpenAIStreamIdleTimeoutMs, iterateWithIdleTimeout } from "../utils/idle-iterator";
|
||||
import {
|
||||
createFirstEventWatchdog,
|
||||
getOpenAIStreamIdleTimeoutMs,
|
||||
getStreamFirstEventTimeoutMs,
|
||||
iterateWithIdleTimeout,
|
||||
markFirstStreamEvent,
|
||||
} from "../utils/idle-iterator";
|
||||
import { parseStreamingJson } from "../utils/json-parse";
|
||||
import { getKimiCommonHeaders } from "../utils/oauth/kimi";
|
||||
import { adaptSchemaForStrict, NO_STRICT } from "../utils/schema";
|
||||
@@ -192,6 +198,10 @@ export const streamOpenAICompletions: StreamFunction<"openai-completions"> = (
|
||||
const requestSignal = options?.signal
|
||||
? AbortSignal.any([options.signal, requestAbortController.signal])
|
||||
: requestAbortController.signal;
|
||||
const idleTimeoutMs = getOpenAIStreamIdleTimeoutMs();
|
||||
const firstEventWatchdog = createFirstEventWatchdog(getStreamFirstEventTimeoutMs(idleTimeoutMs), () =>
|
||||
requestAbortController.abort(),
|
||||
);
|
||||
const { client, copilotPremiumRequests, baseUrl } = await createClient(
|
||||
model,
|
||||
context,
|
||||
@@ -199,103 +209,105 @@ export const streamOpenAICompletions: StreamFunction<"openai-completions"> = (
|
||||
options?.headers,
|
||||
options?.initiatorOverride,
|
||||
);
|
||||
const params = buildParams(model, context, options);
|
||||
const params = buildParams(model, context, options, baseUrl);
|
||||
options?.onPayload?.(params);
|
||||
rawRequestDump = {
|
||||
provider: model.provider,
|
||||
api: output.api,
|
||||
model: model.id,
|
||||
method: "POST",
|
||||
url: `${baseUrl ?? "https://api.openai.com/v1"}/chat/completions`,
|
||||
url: `${baseUrl}/chat/completions`,
|
||||
body: params,
|
||||
};
|
||||
const openaiStream = await client.chat.completions.create(params, { signal: requestSignal });
|
||||
if (copilotPremiumRequests !== undefined) output.usage.premiumRequests = copilotPremiumRequests;
|
||||
stream.push({ type: "start", partial: output });
|
||||
|
||||
let currentBlock: TextContent | ThinkingContent | (ToolCall & { partialArgs?: string }) | null = null;
|
||||
const blocks = output.content;
|
||||
const blockIndex = () => blocks.length - 1;
|
||||
const finishCurrentBlock = (block?: typeof currentBlock) => {
|
||||
if (block) {
|
||||
if (block.type === "text") {
|
||||
stream.push({
|
||||
type: "text_end",
|
||||
contentIndex: blockIndex(),
|
||||
content: block.text,
|
||||
partial: output,
|
||||
});
|
||||
} else if (block.type === "thinking") {
|
||||
stream.push({
|
||||
type: "thinking_end",
|
||||
contentIndex: blockIndex(),
|
||||
content: block.thinking,
|
||||
partial: output,
|
||||
});
|
||||
} else if (block.type === "toolCall") {
|
||||
block.arguments = parseStreamingJson(block.partialArgs);
|
||||
delete block.partialArgs;
|
||||
stream.push({
|
||||
type: "toolcall_end",
|
||||
contentIndex: blockIndex(),
|
||||
toolCall: block,
|
||||
partial: output,
|
||||
});
|
||||
}
|
||||
}
|
||||
const parseMiniMaxThinkTags = model.provider === "minimax-code";
|
||||
type OpenAIStreamBlock = TextContent | ThinkingContent | (ToolCall & { partialArgs: string });
|
||||
let currentBlock: OpenAIStreamBlock | undefined;
|
||||
const blockIndex = (block: OpenAIStreamBlock | undefined): number => {
|
||||
if (!block) return Math.max(0, output.content.length - 1);
|
||||
return output.content.indexOf(block);
|
||||
};
|
||||
|
||||
const parseMiniMaxThinkTags = model.provider === "minimax-code" || model.provider === "minimax-code-cn";
|
||||
let taggedTextBuffer = "";
|
||||
let insideTaggedThinking = false;
|
||||
|
||||
const appendTextDelta = (delta: string) => {
|
||||
if (delta.length === 0) return;
|
||||
const finishCurrentBlock = (block: OpenAIStreamBlock | undefined): void => {
|
||||
if (!block) return;
|
||||
const contentIndex = blockIndex(block);
|
||||
if (contentIndex < 0) return;
|
||||
if (block.type === "text") {
|
||||
stream.push({ type: "text_end", contentIndex, content: block.text, partial: output });
|
||||
return;
|
||||
}
|
||||
if (block.type === "thinking") {
|
||||
stream.push({ type: "thinking_end", contentIndex, content: block.thinking, partial: output });
|
||||
return;
|
||||
}
|
||||
block.arguments = parseStreamingJson(block.partialArgs);
|
||||
delete (block as { partialArgs?: string }).partialArgs;
|
||||
stream.push({ type: "toolcall_end", contentIndex, toolCall: block, partial: output });
|
||||
};
|
||||
const appendText = (
|
||||
message: AssistantMessage,
|
||||
eventStream: AssistantMessageEventStream,
|
||||
text: string,
|
||||
): void => {
|
||||
if (!currentBlock || currentBlock.type !== "text") {
|
||||
finishCurrentBlock(currentBlock);
|
||||
currentBlock = { type: "text", text: "" };
|
||||
output.content.push(currentBlock);
|
||||
stream.push({ type: "text_start", contentIndex: blockIndex(), partial: output });
|
||||
}
|
||||
if (currentBlock.type === "text") {
|
||||
currentBlock.text += delta;
|
||||
stream.push({
|
||||
type: "text_delta",
|
||||
contentIndex: blockIndex(),
|
||||
delta,
|
||||
partial: output,
|
||||
});
|
||||
message.content.push(currentBlock);
|
||||
eventStream.push({ type: "text_start", contentIndex: blockIndex(currentBlock), partial: message });
|
||||
}
|
||||
currentBlock.text += text;
|
||||
eventStream.push({
|
||||
type: "text_delta",
|
||||
contentIndex: blockIndex(currentBlock),
|
||||
delta: text,
|
||||
partial: message,
|
||||
});
|
||||
};
|
||||
|
||||
const appendThinkingDelta = (delta: string, signature?: string) => {
|
||||
if (delta.length === 0) return;
|
||||
const appendThinking = (
|
||||
message: AssistantMessage,
|
||||
eventStream: AssistantMessageEventStream,
|
||||
thinking: string,
|
||||
signature?: string,
|
||||
): void => {
|
||||
if (
|
||||
!currentBlock ||
|
||||
currentBlock.type !== "thinking" ||
|
||||
(signature !== undefined && currentBlock.thinkingSignature !== signature)
|
||||
) {
|
||||
finishCurrentBlock(currentBlock);
|
||||
currentBlock = {
|
||||
type: "thinking",
|
||||
thinking: "",
|
||||
thinkingSignature: signature,
|
||||
};
|
||||
output.content.push(currentBlock);
|
||||
stream.push({ type: "thinking_start", contentIndex: blockIndex(), partial: output });
|
||||
}
|
||||
if (currentBlock.type === "thinking") {
|
||||
if (signature !== undefined && !currentBlock.thinkingSignature) {
|
||||
currentBlock.thinkingSignature = signature;
|
||||
}
|
||||
currentBlock.thinking += delta;
|
||||
stream.push({
|
||||
type: "thinking_delta",
|
||||
contentIndex: blockIndex(),
|
||||
delta,
|
||||
partial: output,
|
||||
currentBlock = { type: "thinking", thinking: "", thinkingSignature: signature };
|
||||
message.content.push(currentBlock);
|
||||
eventStream.push({
|
||||
type: "thinking_start",
|
||||
contentIndex: blockIndex(currentBlock),
|
||||
partial: message,
|
||||
});
|
||||
}
|
||||
if (signature !== undefined && !currentBlock.thinkingSignature) {
|
||||
currentBlock.thinkingSignature = signature;
|
||||
}
|
||||
currentBlock.thinking += thinking;
|
||||
eventStream.push({
|
||||
type: "thinking_delta",
|
||||
contentIndex: blockIndex(currentBlock),
|
||||
delta: thinking,
|
||||
partial: message,
|
||||
});
|
||||
};
|
||||
|
||||
let taggedTextBuffer = "";
|
||||
let insideTaggedThinking = false;
|
||||
const appendTextDelta = (text: string) => {
|
||||
if (!text) return;
|
||||
if (!firstTokenTime) firstTokenTime = Date.now();
|
||||
appendText(output, stream, text);
|
||||
};
|
||||
const appendThinkingDelta = (thinking: string, signature?: string) => {
|
||||
if (!thinking) return;
|
||||
if (!firstTokenTime) firstTokenTime = Date.now();
|
||||
appendThinking(output, stream, thinking, signature);
|
||||
};
|
||||
|
||||
const flushTaggedTextBuffer = () => {
|
||||
@@ -332,8 +344,8 @@ export const streamOpenAICompletions: StreamFunction<"openai-completions"> = (
|
||||
}
|
||||
};
|
||||
|
||||
for await (const chunk of iterateWithIdleTimeout(openaiStream, {
|
||||
idleTimeoutMs: getOpenAIStreamIdleTimeoutMs(),
|
||||
for await (const chunk of iterateWithIdleTimeout(markFirstStreamEvent(openaiStream, firstEventWatchdog), {
|
||||
idleTimeoutMs,
|
||||
errorMessage: "OpenAI completions stream stalled while waiting for the next event",
|
||||
onIdle: () => requestAbortController.abort(),
|
||||
})) {
|
||||
@@ -420,7 +432,11 @@ export const streamOpenAICompletions: StreamFunction<"openai-completions"> = (
|
||||
partialArgs: "",
|
||||
};
|
||||
output.content.push(currentBlock);
|
||||
stream.push({ type: "toolcall_start", contentIndex: blockIndex(), partial: output });
|
||||
stream.push({
|
||||
type: "toolcall_start",
|
||||
contentIndex: blockIndex(currentBlock),
|
||||
partial: output,
|
||||
});
|
||||
}
|
||||
|
||||
if (currentBlock.type === "toolCall") {
|
||||
@@ -434,7 +450,7 @@ export const streamOpenAICompletions: StreamFunction<"openai-completions"> = (
|
||||
}
|
||||
stream.push({
|
||||
type: "toolcall_delta",
|
||||
contentIndex: blockIndex(),
|
||||
contentIndex: blockIndex(currentBlock),
|
||||
delta,
|
||||
partial: output,
|
||||
});
|
||||
@@ -554,8 +570,13 @@ async function createClient(
|
||||
};
|
||||
}
|
||||
|
||||
function buildParams(model: Model<"openai-completions">, context: Context, options?: OpenAICompletionsOptions) {
|
||||
const compat = getCompat(model);
|
||||
function buildParams(
|
||||
model: Model<"openai-completions">,
|
||||
context: Context,
|
||||
options: OpenAICompletionsOptions | undefined,
|
||||
resolvedBaseUrl?: string,
|
||||
) {
|
||||
const compat = getCompat(model, resolvedBaseUrl);
|
||||
const messages = convertMessages(model, context, compat);
|
||||
maybeAddOpenRouterAnthropicCacheControl(model, messages);
|
||||
|
||||
@@ -906,7 +927,13 @@ export function convertMessages(
|
||||
}
|
||||
|
||||
if (compat.thinkingFormat === "openai") {
|
||||
const reasoningField = compat.reasoningContentField ?? "reasoning_content";
|
||||
const streamedReasoningField = nonEmptyThinkingBlocks[0]?.thinkingSignature;
|
||||
const reasoningField =
|
||||
streamedReasoningField === "reasoning_content" ||
|
||||
streamedReasoningField === "reasoning" ||
|
||||
streamedReasoningField === "reasoning_text"
|
||||
? streamedReasoningField
|
||||
: (compat.reasoningContentField ?? "reasoning_content");
|
||||
const reasoningContent = (assistantMsg as any)[reasoningField];
|
||||
if (!reasoningContent) {
|
||||
const reasoning = (assistantMsg as any).reasoning;
|
||||
@@ -962,10 +989,9 @@ export function convertMessages(
|
||||
(assistantMsg as any).reasoning_details = reasoningDetails;
|
||||
}
|
||||
}
|
||||
// Skip assistant messages that have no content and no tool calls.
|
||||
// Mistral explicitly requires "either content or tool_calls, but not none".
|
||||
// Other providers also don't accept empty assistant messages.
|
||||
// This handles aborted assistant responses that got no content.
|
||||
// Skip assistant messages that have no content, no tool calls, and no reasoning payload.
|
||||
// Some OpenAI-compatible backends require replaying reasoning-only assistant turns
|
||||
// so follow-up requests preserve the provider-specific reasoning field name.
|
||||
const content = assistantMsg.content;
|
||||
const hasContent =
|
||||
content !== null &&
|
||||
@@ -974,7 +1000,7 @@ export function convertMessages(
|
||||
if (!hasContent && assistantMsg.tool_calls && compat.requiresAssistantContentForToolCalls) {
|
||||
assistantMsg.content = ".";
|
||||
}
|
||||
if (!hasContent && !assistantMsg.tool_calls) {
|
||||
if (!hasContent && !assistantMsg.tool_calls && !hasReasoningField) {
|
||||
continue;
|
||||
}
|
||||
params.push(assistantMsg);
|
||||
@@ -1118,7 +1144,9 @@ export function detectCompat(model: Model<"openai-completions">): ResolvedOpenAI
|
||||
/**
|
||||
* Get resolved compatibility settings for a model.
|
||||
* Uses explicit model.compat if provided, otherwise auto-detects from provider/URL.
|
||||
* @param model - The model configuration
|
||||
* @param resolvedBaseUrl - Optional resolved base URL (e.g., after GitHub Copilot proxy-ep resolution).
|
||||
*/
|
||||
function getCompat(model: Model<"openai-completions">): ResolvedOpenAICompat {
|
||||
return resolveOpenAICompat(model);
|
||||
function getCompat(model: Model<"openai-completions">, resolvedBaseUrl?: string): ResolvedOpenAICompat {
|
||||
return resolveOpenAICompat(model, resolvedBaseUrl);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,13 @@ import {
|
||||
} from "../utils";
|
||||
import { AssistantMessageEventStream } from "../utils/event-stream";
|
||||
import { finalizeErrorMessage, type RawHttpRequestDump } from "../utils/http-inspector";
|
||||
import { getOpenAIStreamIdleTimeoutMs, iterateWithIdleTimeout } from "../utils/idle-iterator";
|
||||
import {
|
||||
createFirstEventWatchdog,
|
||||
getOpenAIStreamIdleTimeoutMs,
|
||||
getStreamFirstEventTimeoutMs,
|
||||
iterateWithIdleTimeout,
|
||||
markFirstStreamEvent,
|
||||
} from "../utils/idle-iterator";
|
||||
import { adaptSchemaForStrict, NO_STRICT } from "../utils/schema";
|
||||
import { mapToOpenAIResponsesToolChoice } from "../utils/tool-choice";
|
||||
import {
|
||||
@@ -167,11 +173,15 @@ export const streamOpenAIResponses: StreamFunction<"openai-responses"> = (
|
||||
options?.initiatorOverride,
|
||||
);
|
||||
const providerSessionState = getOpenAIResponsesProviderSessionState(model, options?.providerSessionState);
|
||||
const { params } = buildParams(model, context, options, providerSessionState);
|
||||
const { params } = buildParams(model, context, options, providerSessionState, baseUrl);
|
||||
const requestAbortController = new AbortController();
|
||||
const requestSignal = options?.signal
|
||||
? AbortSignal.any([options.signal, requestAbortController.signal])
|
||||
: requestAbortController.signal;
|
||||
const idleTimeoutMs = getOpenAIStreamIdleTimeoutMs();
|
||||
const firstEventWatchdog = createFirstEventWatchdog(getStreamFirstEventTimeoutMs(idleTimeoutMs), () =>
|
||||
requestAbortController.abort(),
|
||||
);
|
||||
options?.onPayload?.(params);
|
||||
rawRequestDump = {
|
||||
provider: model.provider,
|
||||
@@ -187,8 +197,8 @@ export const streamOpenAIResponses: StreamFunction<"openai-responses"> = (
|
||||
|
||||
const nativeOutputItems: Array<Record<string, unknown>> = [];
|
||||
await processResponsesStream(
|
||||
iterateWithIdleTimeout(openaiStream, {
|
||||
idleTimeoutMs: getOpenAIStreamIdleTimeoutMs(),
|
||||
iterateWithIdleTimeout(markFirstStreamEvent(openaiStream, firstEventWatchdog), {
|
||||
idleTimeoutMs,
|
||||
errorMessage: "OpenAI responses stream stalled while waiting for the next event",
|
||||
onIdle: () => requestAbortController.abort(),
|
||||
}),
|
||||
@@ -290,6 +300,7 @@ function buildParams(
|
||||
context: Context,
|
||||
options: OpenAIResponsesOptions | undefined,
|
||||
providerSessionState: OpenAIResponsesProviderSessionState | undefined,
|
||||
resolvedBaseUrl?: string,
|
||||
): { conversationMessages: ResponseInput; params: OpenAIResponsesSamplingParams } {
|
||||
const strictResponsesPairing =
|
||||
options?.strictResponsesPairing ??
|
||||
@@ -303,7 +314,7 @@ function buildParams(
|
||||
const messages: ResponseInput = [...conversationMessages];
|
||||
|
||||
if (context.systemPrompt) {
|
||||
const role = model.reasoning ? "developer" : "system";
|
||||
const role = model.reasoning && supportsDeveloperRole(resolvedBaseUrl ?? model) ? "developer" : "system";
|
||||
messages.unshift({
|
||||
role,
|
||||
content: context.systemPrompt.toWellFormed(),
|
||||
@@ -398,6 +409,19 @@ function supportsStrictMode(model: Model<"openai-responses">): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
export function supportsDeveloperRole(modelOrBaseUrl: Pick<Model, "provider" | "baseUrl"> | string): boolean {
|
||||
const baseUrl =
|
||||
typeof modelOrBaseUrl === "string" ? modelOrBaseUrl.toLowerCase() : (modelOrBaseUrl.baseUrl ?? "").toLowerCase();
|
||||
return (
|
||||
baseUrl.includes("api.openai.com") ||
|
||||
baseUrl.includes(".openai.azure.com") ||
|
||||
baseUrl.includes("azure.com/openai") ||
|
||||
baseUrl.includes("models.inference.ai.azure.com") ||
|
||||
baseUrl.includes("githubcopilot.com") ||
|
||||
baseUrl.includes("copilot-api.")
|
||||
);
|
||||
}
|
||||
|
||||
function convertConversationMessages(
|
||||
model: Model<"openai-responses">,
|
||||
context: Context,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { $env } from "@oh-my-pi/pi-utils";
|
||||
|
||||
const DEFAULT_OPENAI_STREAM_IDLE_TIMEOUT_MS = 45_000;
|
||||
const DEFAULT_STREAM_FIRST_EVENT_TIMEOUT_MS = 15_000;
|
||||
|
||||
function normalizeIdleTimeoutMs(value: string | undefined, fallback: number): number | undefined {
|
||||
if (value === undefined) return fallback;
|
||||
@@ -19,20 +20,95 @@ export function getOpenAIStreamIdleTimeoutMs(): number | undefined {
|
||||
return normalizeIdleTimeoutMs($env.PI_OPENAI_STREAM_IDLE_TIMEOUT_MS, DEFAULT_OPENAI_STREAM_IDLE_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timeout used while waiting for the first stream event.
|
||||
*
|
||||
* Set `PI_STREAM_FIRST_EVENT_TIMEOUT_MS=0` to disable the watchdog.
|
||||
*/
|
||||
export function getStreamFirstEventTimeoutMs(idleTimeoutMs?: number): number | undefined {
|
||||
const fallback = Math.min(
|
||||
DEFAULT_STREAM_FIRST_EVENT_TIMEOUT_MS,
|
||||
idleTimeoutMs ?? DEFAULT_STREAM_FIRST_EVENT_TIMEOUT_MS,
|
||||
);
|
||||
return normalizeIdleTimeoutMs($env.PI_STREAM_FIRST_EVENT_TIMEOUT_MS, fallback);
|
||||
}
|
||||
|
||||
export interface FirstEventWatchdog {
|
||||
markFirstEventReceived(): void;
|
||||
cleanup(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a watchdog that aborts a request if no first stream event arrives in time.
|
||||
* Call `markFirstEventReceived()` as soon as the first event is observed.
|
||||
*/
|
||||
export function createFirstEventWatchdog(timeoutMs: number | undefined, onTimeout: () => void): FirstEventWatchdog {
|
||||
let timer: NodeJS.Timeout | undefined;
|
||||
if (timeoutMs !== undefined && timeoutMs > 0) {
|
||||
timer = setTimeout(() => {
|
||||
timer = undefined;
|
||||
onTimeout();
|
||||
}, timeoutMs);
|
||||
}
|
||||
return {
|
||||
markFirstEventReceived() {
|
||||
if (!timer) return;
|
||||
clearTimeout(timer);
|
||||
timer = undefined;
|
||||
},
|
||||
cleanup() {
|
||||
if (!timer) return;
|
||||
clearTimeout(timer);
|
||||
timer = undefined;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps an async iterable and clears the watchdog once the first event arrives.
|
||||
*/
|
||||
export async function* markFirstStreamEvent<T>(
|
||||
iterable: AsyncIterable<T>,
|
||||
watchdog: FirstEventWatchdog,
|
||||
): AsyncGenerator<T> {
|
||||
let sawFirstEvent = false;
|
||||
try {
|
||||
for await (const item of iterable) {
|
||||
if (!sawFirstEvent) {
|
||||
sawFirstEvent = true;
|
||||
watchdog.markFirstEventReceived();
|
||||
}
|
||||
yield item;
|
||||
}
|
||||
} finally {
|
||||
watchdog.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
export interface IdleTimeoutIteratorOptions {
|
||||
idleTimeoutMs?: number;
|
||||
firstItemTimeoutMs?: number;
|
||||
errorMessage: string;
|
||||
firstItemErrorMessage?: string;
|
||||
onIdle?: () => void;
|
||||
onFirstItemTimeout?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Yields items from an async iterable while enforcing a maximum idle gap between items.
|
||||
*
|
||||
* The first item may use a shorter timeout so stuck requests can be aborted and retried
|
||||
* before any user-visible content has streamed.
|
||||
*/
|
||||
export async function* iterateWithIdleTimeout<T>(
|
||||
iterable: AsyncIterable<T>,
|
||||
options: IdleTimeoutIteratorOptions,
|
||||
): AsyncGenerator<T> {
|
||||
if (options.idleTimeoutMs === undefined || options.idleTimeoutMs <= 0) {
|
||||
const firstItemTimeoutMs = options.firstItemTimeoutMs ?? options.idleTimeoutMs;
|
||||
if (
|
||||
(firstItemTimeoutMs === undefined || firstItemTimeoutMs <= 0) &&
|
||||
(options.idleTimeoutMs === undefined || options.idleTimeoutMs <= 0)
|
||||
) {
|
||||
for await (const item of iterable) {
|
||||
yield item;
|
||||
}
|
||||
@@ -40,26 +116,48 @@ export async function* iterateWithIdleTimeout<T>(
|
||||
}
|
||||
|
||||
const iterator = iterable[Symbol.asyncIterator]();
|
||||
let sawFirstItem = false;
|
||||
|
||||
while (true) {
|
||||
const nextResultPromise = iterator.next().then(
|
||||
result => ({ kind: "next" as const, result }),
|
||||
error => ({ kind: "error" as const, error }),
|
||||
);
|
||||
const activeTimeoutMs = sawFirstItem ? options.idleTimeoutMs : firstItemTimeoutMs;
|
||||
|
||||
if (activeTimeoutMs === undefined || activeTimeoutMs <= 0) {
|
||||
const outcome = await nextResultPromise;
|
||||
if (outcome.kind === "error") {
|
||||
throw outcome.error;
|
||||
}
|
||||
if (outcome.result.done) {
|
||||
return;
|
||||
}
|
||||
sawFirstItem = true;
|
||||
yield outcome.result.value;
|
||||
continue;
|
||||
}
|
||||
|
||||
const { promise: timeoutPromise, resolve: resolveTimeout } = Promise.withResolvers<{
|
||||
kind: "timeout";
|
||||
}>();
|
||||
const timer = setTimeout(() => resolveTimeout({ kind: "timeout" }), options.idleTimeoutMs);
|
||||
const timer = setTimeout(() => resolveTimeout({ kind: "timeout" }), activeTimeoutMs);
|
||||
|
||||
try {
|
||||
const outcome = await Promise.race([nextResultPromise, timeoutPromise]);
|
||||
if (outcome.kind === "timeout") {
|
||||
options.onIdle?.();
|
||||
if (sawFirstItem) {
|
||||
options.onIdle?.();
|
||||
} else {
|
||||
options.onFirstItemTimeout?.();
|
||||
}
|
||||
const returnPromise = iterator.return?.();
|
||||
if (returnPromise) {
|
||||
void returnPromise.catch(() => {});
|
||||
}
|
||||
throw new Error(options.errorMessage);
|
||||
throw new Error(
|
||||
sawFirstItem ? options.errorMessage : (options.firstItemErrorMessage ?? options.errorMessage),
|
||||
);
|
||||
}
|
||||
if (outcome.kind === "error") {
|
||||
throw outcome.error;
|
||||
@@ -67,6 +165,7 @@ export async function* iterateWithIdleTimeout<T>(
|
||||
if (outcome.result.done) {
|
||||
return;
|
||||
}
|
||||
sawFirstItem = true;
|
||||
yield outcome.result.value;
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
|
||||
@@ -113,6 +113,8 @@ export { loginTogether } from "./together";
|
||||
export * from "./types";
|
||||
// Venice (API key)
|
||||
export { loginVenice } from "./venice";
|
||||
// Vercel AI Gateway (API key)
|
||||
export { loginVercelAiGateway } from "./vercel-ai-gateway";
|
||||
// vLLM (API key)
|
||||
export { loginVllm } from "./vllm";
|
||||
// Xiaomi MiMo (API key)
|
||||
@@ -303,6 +305,11 @@ const builtInOAuthProviders: OAuthProviderInfo[] = [
|
||||
name: "Cloudflare AI Gateway",
|
||||
available: true,
|
||||
},
|
||||
{
|
||||
id: "vercel-ai-gateway",
|
||||
name: "Vercel AI Gateway",
|
||||
available: true,
|
||||
},
|
||||
];
|
||||
|
||||
const customOAuthProviders = new Map<string, OAuthProviderInterface>();
|
||||
@@ -400,6 +407,7 @@ export async function refreshOAuthToken(
|
||||
case "moonshot":
|
||||
case "kagi":
|
||||
case "cloudflare-ai-gateway":
|
||||
case "vercel-ai-gateway":
|
||||
case "qwen-portal":
|
||||
case "zenmux":
|
||||
case "vllm":
|
||||
|
||||
@@ -41,6 +41,7 @@ export type OAuthProvider =
|
||||
| "tavily"
|
||||
| "together"
|
||||
| "venice"
|
||||
| "vercel-ai-gateway"
|
||||
| "vllm"
|
||||
| "xiaomi"
|
||||
| "zenmux"
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Vercel AI Gateway login flow.
|
||||
*
|
||||
* Vercel AI Gateway proxies upstream model providers through a unified endpoint.
|
||||
*
|
||||
* This is not OAuth - it's a simple API key flow:
|
||||
* 1. Open Vercel AI Gateway docs
|
||||
* 2. User copies their API key
|
||||
* 3. User pastes the API key into the CLI
|
||||
*/
|
||||
|
||||
import type { OAuthController } from "./types";
|
||||
|
||||
const AUTH_URL = "https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai-gateway%2Fapi-keys&title=AI+Gateway+API+Keys";
|
||||
|
||||
/**
|
||||
* Login to Vercel AI Gateway.
|
||||
*
|
||||
* Opens browser to Vercel AI Gateway docs and prompts for an API key.
|
||||
* Returns the API key directly (not OAuthCredentials - this isn't OAuth).
|
||||
*/
|
||||
export async function loginVercelAiGateway(options: OAuthController): Promise<string> {
|
||||
if (!options.onPrompt) {
|
||||
throw new Error("Vercel AI Gateway login requires onPrompt callback");
|
||||
}
|
||||
|
||||
options.onAuth?.({
|
||||
url: AUTH_URL,
|
||||
instructions: "Copy your Vercel AI Gateway API key from the Vercel dashboard",
|
||||
});
|
||||
|
||||
const apiKey = await options.onPrompt({
|
||||
message: "Paste your Vercel AI Gateway API key",
|
||||
placeholder: "vck_...",
|
||||
});
|
||||
|
||||
if (options.signal?.aborted) {
|
||||
throw new Error("Login cancelled");
|
||||
}
|
||||
|
||||
const trimmed = apiKey.trim();
|
||||
if (!trimmed) {
|
||||
throw new Error("API key is required");
|
||||
}
|
||||
|
||||
return trimmed;
|
||||
}
|
||||
@@ -18,6 +18,12 @@ describe("isProviderRetryableError", () => {
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("retries first-event timeout errors", () => {
|
||||
expect(isProviderRetryableError(new Error("Anthropic stream timed out while waiting for the first event"))).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it("does not retry non-transient validation errors", () => {
|
||||
expect(isProviderRetryableError(new Error("Invalid tool schema"))).toBe(false);
|
||||
expect(isProviderRetryableError(new Error("Bad request"))).toBe(false);
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
import { afterEach, describe, expect, it, vi } from "bun:test";
|
||||
import { Messages } from "@anthropic-ai/sdk/resources/messages/messages";
|
||||
import { streamAnthropic } from "../src/providers/anthropic";
|
||||
import type { Context, Model } from "../src/types";
|
||||
|
||||
const originalFirstEventTimeout = Bun.env.PI_STREAM_FIRST_EVENT_TIMEOUT_MS;
|
||||
|
||||
const model: Model<"anthropic-messages"> = {
|
||||
id: "claude-sonnet-4-5",
|
||||
name: "Claude Sonnet 4.5",
|
||||
api: "anthropic-messages",
|
||||
provider: "anthropic",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
contextWindow: 200_000,
|
||||
maxTokens: 8_192,
|
||||
};
|
||||
|
||||
const context: Context = {
|
||||
messages: [{ role: "user", content: "Say hi", timestamp: Date.now() }],
|
||||
};
|
||||
|
||||
function createHangingAnthropicStream(signal: AbortSignal | undefined): AsyncIterable<Record<string, unknown>> {
|
||||
return {
|
||||
async *[Symbol.asyncIterator]() {
|
||||
const { promise, reject } = Promise.withResolvers<void>();
|
||||
const onAbort = () => reject(new Error("request aborted"));
|
||||
if (signal?.aborted) {
|
||||
onAbort();
|
||||
} else {
|
||||
signal?.addEventListener("abort", onAbort, { once: true });
|
||||
}
|
||||
try {
|
||||
await promise;
|
||||
} catch {
|
||||
return;
|
||||
} finally {
|
||||
signal?.removeEventListener("abort", onAbort);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createSuccessfulAnthropicStream(text: string): AsyncIterable<Record<string, unknown>> {
|
||||
return {
|
||||
async *[Symbol.asyncIterator]() {
|
||||
yield {
|
||||
type: "message_start",
|
||||
message: {
|
||||
id: "msg_retry_success",
|
||||
usage: {
|
||||
input_tokens: 12,
|
||||
output_tokens: 0,
|
||||
cache_read_input_tokens: 0,
|
||||
cache_creation_input_tokens: 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
yield {
|
||||
type: "content_block_start",
|
||||
index: 0,
|
||||
content_block: { type: "text", text: "" },
|
||||
};
|
||||
yield {
|
||||
type: "content_block_delta",
|
||||
index: 0,
|
||||
delta: { type: "text_delta", text },
|
||||
};
|
||||
yield { type: "content_block_stop", index: 0 };
|
||||
yield {
|
||||
type: "message_delta",
|
||||
delta: { stop_reason: "end_turn" },
|
||||
usage: {
|
||||
input_tokens: 12,
|
||||
output_tokens: 4,
|
||||
cache_read_input_tokens: 0,
|
||||
cache_creation_input_tokens: 0,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
if (originalFirstEventTimeout === undefined) {
|
||||
delete Bun.env.PI_STREAM_FIRST_EVENT_TIMEOUT_MS;
|
||||
} else {
|
||||
Bun.env.PI_STREAM_FIRST_EVENT_TIMEOUT_MS = originalFirstEventTimeout;
|
||||
}
|
||||
});
|
||||
|
||||
describe("anthropic first-event timeout retries", () => {
|
||||
it("retries when the provider never sends the first stream event", async () => {
|
||||
Bun.env.PI_STREAM_FIRST_EVENT_TIMEOUT_MS = "20";
|
||||
let attempt = 0;
|
||||
|
||||
vi.spyOn(Messages.prototype, "stream").mockImplementation((_body, requestOptions) => {
|
||||
attempt += 1;
|
||||
const signal = (requestOptions as { signal?: AbortSignal } | undefined)?.signal;
|
||||
return (
|
||||
attempt === 1 ? createHangingAnthropicStream(signal) : createSuccessfulAnthropicStream("retry recovered")
|
||||
) as never;
|
||||
});
|
||||
|
||||
const result = await streamAnthropic(model, context, { apiKey: "sk-ant-test" }).result();
|
||||
|
||||
expect(attempt).toBe(2);
|
||||
expect(result.stopReason).toBe("stop");
|
||||
expect(result.content).toEqual([{ type: "text", text: "retry recovered" }]);
|
||||
expect(result.responseId).toBe("msg_retry_success");
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import { afterEach, describe, expect, it } from "bun:test";
|
||||
import { getBundledModel } from "../src/models";
|
||||
import { convertMessages, streamOpenAICompletions } from "../src/providers/openai-completions";
|
||||
import { convertMessages, detectCompat, streamOpenAICompletions } from "../src/providers/openai-completions";
|
||||
import type { AssistantMessage, Context, Model, OpenAICompat } from "../src/types";
|
||||
|
||||
const originalFetch = global.fetch;
|
||||
@@ -235,4 +235,48 @@ describe("openai-completions compatibility", () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("preserves the streamed reasoning field name for follow-up requests", async () => {
|
||||
const model: Model<"openai-completions"> = {
|
||||
...getBundledModel("openai", "gpt-4o-mini"),
|
||||
api: "openai-completions",
|
||||
};
|
||||
global.fetch = createMockFetch([
|
||||
{
|
||||
id: "chatcmpl-reasoning-text",
|
||||
object: "chat.completion.chunk",
|
||||
created: 0,
|
||||
model: model.id,
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: { reasoning_text: "inspect tool output" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "chatcmpl-reasoning-text",
|
||||
object: "chat.completion.chunk",
|
||||
created: 0,
|
||||
model: model.id,
|
||||
choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
|
||||
},
|
||||
"[DONE]",
|
||||
]);
|
||||
|
||||
const result = await streamOpenAICompletions(model, baseContext(), { apiKey: "test-key" }).result();
|
||||
expect(result.content).toContainEqual({
|
||||
type: "thinking",
|
||||
thinking: "inspect tool output",
|
||||
thinkingSignature: "reasoning_text",
|
||||
});
|
||||
|
||||
const messages = convertMessages(model, { messages: [result] }, detectCompat(model));
|
||||
const assistant = messages.find(message => message.role === "assistant");
|
||||
expect(assistant).toBeDefined();
|
||||
const assistantObject = toObject(assistant);
|
||||
expect(assistantObject).toBeDefined();
|
||||
expect(assistantObject ? Reflect.get(assistantObject, "reasoning_text") : undefined).toBe("inspect tool output");
|
||||
expect(assistantObject ? Reflect.get(assistantObject, "reasoning_content") : undefined).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import { supportsDeveloperRole } from "../src/providers/openai-responses";
|
||||
import type { Model } from "../src/types";
|
||||
|
||||
describe("supportsDeveloperRole", () => {
|
||||
it("returns true for openai provider with official API base URL", () => {
|
||||
const model = { provider: "openai", baseUrl: "https://api.openai.com/v1" } as Model;
|
||||
expect(supportsDeveloperRole(model)).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false for openai provider with custom proxy base URL", () => {
|
||||
const model = { provider: "openai", baseUrl: "https://my-proxy.example.com/v1" } as Model;
|
||||
expect(supportsDeveloperRole(model)).toBe(false);
|
||||
});
|
||||
|
||||
it("returns true for github-copilot provider", () => {
|
||||
const model = { provider: "github-copilot", baseUrl: "https://api.githubcopilot.com" } as Model;
|
||||
expect(supportsDeveloperRole(model)).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false for github-copilot provider with custom proxy base URL", () => {
|
||||
const model = { provider: "github-copilot", baseUrl: "https://proxy.example.com/v1" } as Model;
|
||||
expect(supportsDeveloperRole(model)).toBe(false);
|
||||
});
|
||||
|
||||
it("returns true for Azure OpenAI base URL", () => {
|
||||
const model = { provider: "azure-openai", baseUrl: "https://my-resource.openai.azure.com/openai" } as Model;
|
||||
expect(supportsDeveloperRole(model)).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for Azure AI Inference base URL", () => {
|
||||
const model = {
|
||||
provider: "azure-openai",
|
||||
baseUrl: "https://models.inference.ai.azure.com/v1/chat/completions",
|
||||
} as Model;
|
||||
expect(supportsDeveloperRole(model)).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for api.openai.com base URL", () => {
|
||||
const model = { provider: "custom", baseUrl: "https://api.openai.com/v1/chat/completions" } as Model;
|
||||
expect(supportsDeveloperRole(model)).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false for generic third-party provider", () => {
|
||||
const model = { provider: "custom", baseUrl: "https://api.example.com/v1" } as Model;
|
||||
expect(supportsDeveloperRole(model)).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false for local/localhost endpoints", () => {
|
||||
const model = { provider: "custom", baseUrl: "http://localhost:8080/v1" } as Model;
|
||||
expect(supportsDeveloperRole(model)).toBe(false);
|
||||
});
|
||||
|
||||
it("is case-insensitive for base URL matching", () => {
|
||||
const model = { provider: "custom", baseUrl: "https://API.OPENAI.COM/v1" } as Model;
|
||||
expect(supportsDeveloperRole(model)).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for azure.com/openai base URL", () => {
|
||||
const model = { provider: "custom", baseUrl: "https://azure.com/openai/deployments/my-model" } as Model;
|
||||
expect(supportsDeveloperRole(model)).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for github-copilot provider with api.individual.githubcopilot.com", () => {
|
||||
const model = { provider: "github-copilot", baseUrl: "https://api.individual.githubcopilot.com" } as Model;
|
||||
expect(supportsDeveloperRole(model)).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for github-copilot provider with api.enterprise.githubcopilot.com", () => {
|
||||
const model = { provider: "github-copilot", baseUrl: "https://api.enterprise.githubcopilot.com" } as Model;
|
||||
expect(supportsDeveloperRole(model)).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for github-copilot provider with copilot-api enterprise domain", () => {
|
||||
const model = { provider: "github-copilot", baseUrl: "https://copilot-api.mycompany.com" } as Model;
|
||||
expect(supportsDeveloperRole(model)).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -8,6 +8,16 @@
|
||||
- `--scope user|project` flag added to `/marketplace uninstall`, `/marketplace upgrade`, `/plugins enable`, and `/plugins disable` to disambiguate when a plugin is installed in both scopes
|
||||
- `omp plugin upgrade --scope project` with no plugin ID warns that `--scope` is ignored for bulk upgrades
|
||||
|
||||
## [13.17.1] - 2026-04-01
|
||||
### Removed
|
||||
|
||||
- Removed `code_search` tool for code snippet and documentation search
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed edit tool diff rendering to wrap long diff lines with continuation gutters instead of truncating them at terminal width ([#578](https://github.com/can1357/oh-my-pi/issues/578))
|
||||
- Fixed `--list-models` and `/model` provider filtering to hide models from disabled providers ([#588](https://github.com/can1357/oh-my-pi/issues/588))
|
||||
- Fixed edit tool diffstats to use diff-specific add/remove theme colors instead of success/error status colors ([#589](https://github.com/can1357/oh-my-pi/issues/589))
|
||||
## [13.17.0] - 2026-03-30
|
||||
|
||||
### Added
|
||||
@@ -6457,4 +6467,4 @@ Initial public release.
|
||||
- Git branch display in footer
|
||||
- Message queueing during streaming responses
|
||||
- OAuth integration for Gmail and Google Calendar access
|
||||
- HTML export with syntax highlighting and collapsible sections
|
||||
- HTML export with syntax highlighting and collapsible sections
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"type": "module",
|
||||
"name": "@oh-my-pi/pi-coding-agent",
|
||||
"version": "13.17.0",
|
||||
"version": "13.17.1",
|
||||
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
||||
"homepage": "https://github.com/can1357/oh-my-pi",
|
||||
"author": "Can Boluk",
|
||||
|
||||
@@ -189,12 +189,11 @@ function extractFileHeader(diff: string): string {
|
||||
return headerLines.join("\n");
|
||||
}
|
||||
|
||||
function joinPatch(parts: string[]): string {
|
||||
return parts
|
||||
export function joinPatch(parts: string[]): string {
|
||||
return `${parts
|
||||
.map(part => (part.endsWith("\n") ? part : `${part}\n`))
|
||||
.join("\n")
|
||||
.trimEnd()
|
||||
.concat("\n");
|
||||
.replace(/\n+$/, "")}\n`;
|
||||
}
|
||||
|
||||
function selectHunks(file: FileHunks, selector: HunkSelection["hunks"]): FileHunks["hunks"] {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
||||
import type { Api, Model } from "@oh-my-pi/pi-ai";
|
||||
import { MODEL_ROLE_IDS } from "../config/model-registry";
|
||||
import { parseModelPattern, resolveModelRoleValue } from "../config/model-resolver";
|
||||
import { parseModelPattern, resolveModelRoleValue, resolveRoleSelection } from "../config/model-resolver";
|
||||
import type { Settings } from "../config/settings";
|
||||
import MODEL_PRIO from "../priority.json" with { type: "json" };
|
||||
|
||||
@@ -11,24 +11,6 @@ export interface ResolvedCommitModel {
|
||||
thinkingLevel?: ThinkingLevel;
|
||||
}
|
||||
|
||||
function resolveRoleSelection(
|
||||
roles: readonly string[],
|
||||
settings: Settings,
|
||||
availableModels: Model<Api>[],
|
||||
): { model: Model<Api>; thinkingLevel?: ThinkingLevel } | undefined {
|
||||
const matchPreferences = { usageOrder: settings.getStorage()?.getModelUsageOrder() };
|
||||
for (const role of roles) {
|
||||
const resolved = resolveModelRoleValue(settings.getModelRole(role), availableModels, {
|
||||
settings,
|
||||
matchPreferences,
|
||||
});
|
||||
if (resolved.model) {
|
||||
return { model: resolved.model, thinkingLevel: resolved.thinkingLevel };
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export async function resolvePrimaryModel(
|
||||
override: string | undefined,
|
||||
settings: Settings,
|
||||
|
||||
@@ -31,7 +31,7 @@ import { type ConfigError, ConfigFile } from "../config";
|
||||
import { parseModelString } from "../config/model-resolver";
|
||||
import { isValidThemeColor, type ThemeColor } from "../modes/theme/theme";
|
||||
import type { AuthStorage, OAuthCredential } from "../session/auth-storage";
|
||||
import type { Settings } from "./settings";
|
||||
import { type Settings, settings } from "./settings";
|
||||
|
||||
export const kNoAuth = "N/A";
|
||||
|
||||
@@ -730,6 +730,14 @@ function normalizeSuppressedSelector(selector: string): string {
|
||||
return `${parsed.provider}/${parsed.id}`;
|
||||
}
|
||||
|
||||
function getDisabledProviderIdsFromSettings(): Set<string> {
|
||||
try {
|
||||
return new Set(settings.get("disabledProviders"));
|
||||
} catch {
|
||||
return new Set();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Model registry - loads and manages models, resolves API keys via AuthStorage.
|
||||
*/
|
||||
@@ -1670,11 +1678,19 @@ export class ModelRegistry {
|
||||
* This is a fast check that doesn't refresh OAuth tokens.
|
||||
*/
|
||||
getAvailable(): Model<Api>[] {
|
||||
return this.#models.filter(m => this.#keylessProviders.has(m.provider) || this.authStorage.hasAuth(m.provider));
|
||||
const disabledProviders = getDisabledProviderIdsFromSettings();
|
||||
return this.#models.filter(
|
||||
m =>
|
||||
!disabledProviders.has(m.provider) &&
|
||||
(this.#keylessProviders.has(m.provider) || this.authStorage.hasAuth(m.provider)),
|
||||
);
|
||||
}
|
||||
|
||||
getDiscoverableProviders(): string[] {
|
||||
return this.#discoverableProviders.map(provider => provider.provider);
|
||||
const disabledProviders = getDisabledProviderIdsFromSettings();
|
||||
return this.#discoverableProviders
|
||||
.filter(provider => !disabledProviders.has(provider.provider))
|
||||
.map(provider => provider.provider);
|
||||
}
|
||||
|
||||
getProviderDiscoveryState(provider: string): ProviderDiscoveryState | undefined {
|
||||
|
||||
@@ -587,6 +587,27 @@ export function resolveModelOverride(
|
||||
return { explicitThinkingLevel: false };
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a list of role patterns to the first matching model.
|
||||
*/
|
||||
export function resolveRoleSelection(
|
||||
roles: readonly string[],
|
||||
settings: Settings,
|
||||
availableModels: Model<Api>[],
|
||||
): { model: Model<Api>; thinkingLevel?: ThinkingLevel } | undefined {
|
||||
const matchPreferences = { usageOrder: settings.getStorage()?.getModelUsageOrder() };
|
||||
for (const role of roles) {
|
||||
const resolved = resolveModelRoleValue(settings.getModelRole(role), availableModels, {
|
||||
settings,
|
||||
matchPreferences,
|
||||
});
|
||||
if (resolved.model) {
|
||||
return { model: resolved.model, thinkingLevel: resolved.thinkingLevel };
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve model patterns to actual Model objects with optional thinking levels
|
||||
* Format: "pattern:level" where :level is optional
|
||||
|
||||
@@ -541,6 +541,7 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
|
||||
}
|
||||
|
||||
if (parsedArgs.listModels !== undefined) {
|
||||
await logger.timeAsync("settings:init:list-models", () => Settings.init({ cwd: getProjectDir() }));
|
||||
await modelRegistry.refresh("online");
|
||||
const searchPattern = typeof parsedArgs.listModels === "string" ? parsedArgs.listModels : undefined;
|
||||
await listModels(modelRegistry, searchPattern);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
import type { ToolCallContext } from "@oh-my-pi/pi-agent-core";
|
||||
import type { Component } from "@oh-my-pi/pi-tui";
|
||||
import { Text } from "@oh-my-pi/pi-tui";
|
||||
import { Text, visibleWidth, wrapTextWithAnsi } from "@oh-my-pi/pi-tui";
|
||||
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
||||
import type { FileDiagnosticsResult } from "../lsp";
|
||||
import { renderDiff as renderDiffColored } from "../modes/components/diff";
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
shortenPath,
|
||||
truncateDiffByHunk,
|
||||
} from "../tools/render-utils";
|
||||
import { Ellipsis, Hasher, type RenderCache, renderStatusLine, truncateToWidth } from "../tui";
|
||||
import { Hasher, type RenderCache, renderStatusLine, truncateToWidth } from "../tui";
|
||||
import type { HashlineToolEdit } from "./index";
|
||||
import type { DiffError, DiffResult, Operation } from "./types";
|
||||
|
||||
@@ -222,6 +222,31 @@ function renderDiffSection(
|
||||
return text;
|
||||
}
|
||||
|
||||
function wrapEditRendererLine(line: string, width: number): string[] {
|
||||
if (width <= 0) return [line];
|
||||
if (line.length === 0) return [""];
|
||||
|
||||
const startAnsi = line.match(/^((?:\x1b\[[0-9;]*m)*)/)?.[1] ?? "";
|
||||
const bodyWithReset = line.slice(startAnsi.length);
|
||||
const body = bodyWithReset.endsWith("\x1b[39m") ? bodyWithReset.slice(0, -"\x1b[39m".length) : bodyWithReset;
|
||||
const diffMatch = /^([+\-\s])(\s*\d+)\|(.*)$/s.exec(body);
|
||||
|
||||
if (!diffMatch) {
|
||||
return wrapTextWithAnsi(line, width);
|
||||
}
|
||||
|
||||
const [, marker, lineNum, content] = diffMatch;
|
||||
const prefix = `${marker}${lineNum}|`;
|
||||
const prefixWidth = visibleWidth(prefix);
|
||||
const contentWidth = Math.max(1, width - prefixWidth);
|
||||
const continuationPrefix = `${" ".repeat(Math.max(0, prefixWidth - 1))}|`;
|
||||
const wrappedContent = wrapTextWithAnsi(content, contentWidth);
|
||||
|
||||
return wrappedContent.map(
|
||||
(segment, index) => `${startAnsi}${index === 0 ? prefix : continuationPrefix}${segment}\x1b[39m`,
|
||||
);
|
||||
}
|
||||
|
||||
export const editToolRenderer = {
|
||||
mergeCallAndResult: true,
|
||||
|
||||
@@ -357,7 +382,7 @@ export const editToolRenderer = {
|
||||
}
|
||||
|
||||
const lines =
|
||||
width > 0 ? text.split("\n").map(line => truncateToWidth(line, width, Ellipsis.Omit)) : text.split("\n");
|
||||
width > 0 ? text.split("\n").flatMap(line => wrapEditRendererLine(line, width)) : text.split("\n");
|
||||
cached = { key, lines };
|
||||
return lines;
|
||||
},
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
Search code snippets, and technical content.
|
||||
This tool behaves more like grep than natural-language web search.
|
||||
|
||||
<instruction>
|
||||
- Query with exact symbols, identifiers, error strings, CLI flags, filenames, import paths, and short code fragments
|
||||
- Start with the smallest distinctive token; widen or add one nearby token only if the first query is too broad
|
||||
- Prefer exact syntax when punctuation carries meaning, such as `Promise.withResolvers`, `useEffect(`, `--watch`, or `"direnv loading"`
|
||||
- Keep `query` terse; remove filler words, prose, and request framing
|
||||
- Use `code_context` only for a few disambiguating tokens such as language, library, framework, repo, runtime, or API name
|
||||
- If a multi-word literal matters exactly, quote the shortest stable phrase first, then refine
|
||||
- When looking for usage examples of a specific API, search the symbol first; add surrounding call syntax only when needed
|
||||
</instruction>
|
||||
|
||||
<parameters>
|
||||
- query: Grep-style code search query; use exact tokens, short fragments, or short quoted phrases
|
||||
- code_context: Optional disambiguation tokens only, not a sentence
|
||||
</parameters>
|
||||
|
||||
<examples>
|
||||
Good queries:
|
||||
- `Promise.withResolvers`
|
||||
- `DIRENV_LOG_FORMAT`
|
||||
- `"direnv loading"`
|
||||
- `useState` with `code_context: react hooks`
|
||||
- `app.get(` with `code_context: express`
|
||||
- `ERR_REQUIRE_ESM` with `code_context: node`
|
||||
|
||||
Bad queries:
|
||||
- `Need the official or source-backed way to silence direnv loading output`
|
||||
- `How do I use Promise.withResolvers in Bun?`
|
||||
- `find examples of React state hooks in TypeScript projects`
|
||||
- `search GitHub for express routing docs`
|
||||
</examples>
|
||||
|
||||
<avoid>
|
||||
- Do not use this tool for broad conceptual research, comparisons, or authoritative sourcing; use `web_search`, `web_search_deep`, or `fetch` instead
|
||||
- Do not put full-sentence instructions into `query` or `code_context`
|
||||
- Do not pack many weak terms into one query; one strong token plus minimal context usually works better
|
||||
</avoid>
|
||||
|
||||
<critical>
|
||||
- `query` should be grep-style code search, not a natural-language request
|
||||
- `code_context` is optional and should stay short
|
||||
- If you need explanations, best practices, or comprehensive answers, use broader web search tools instead of this one
|
||||
</critical>
|
||||
@@ -1009,7 +1009,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
||||
}
|
||||
|
||||
// Add web search tools
|
||||
customTools.push(...getSearchTools());
|
||||
if (options.toolNames?.includes("web_search")) {
|
||||
customTools.push(...getSearchTools());
|
||||
}
|
||||
|
||||
// Discover and load custom tools from .omp/tools/, .claude/tools/, etc.
|
||||
const builtInToolNames = builtinTools.map(t => t.name);
|
||||
|
||||
@@ -16,7 +16,7 @@ const CHECK_BYTE_COUNT = 1024;
|
||||
const HEADER_LINE_LIMIT = 40;
|
||||
|
||||
const KNOWN_GENERATOR_PATTERN =
|
||||
"(?:protoc(?:-gen-[\\w-]+)?|sqlc|buf|swagger(?:-codegen)?|openapi(?:-generator)?|grpc-gateway|mockery|stringer|easyjson|deepcopy-gen|defaulter-gen|conversion-gen|client-gen|lister-gen|informer-gen|kysely-codegen)";
|
||||
"(?:protoc(?:-gen-[\\w-]+)?|sqlc|buf|swagger(?:-codegen)?|openapi(?:-generator)?|grpc-gateway|mockery|stringer|easyjson|deepcopy-gen|defaulter-gen|conversion-gen|client-gen|lister-gen|informer-gen|kysely-codegen|napi-rs)";
|
||||
|
||||
/**
|
||||
* Strong marker patterns for generated-file headers.
|
||||
|
||||
@@ -382,8 +382,8 @@ export function getDiffStats(diffText: string): DiffStats {
|
||||
|
||||
export function formatDiffStats(added: number, removed: number, hunks: number, theme: Theme): string {
|
||||
const parts: string[] = [];
|
||||
if (added > 0) parts.push(theme.fg("success", `+${added}`));
|
||||
if (removed > 0) parts.push(theme.fg("error", `-${removed}`));
|
||||
if (added > 0) parts.push(theme.fg("toolDiffAdded", `+${added}`));
|
||||
if (removed > 0) parts.push(theme.fg("toolDiffRemoved", `-${removed}`));
|
||||
if (hunks > 0) parts.push(theme.fg("dim", `${hunks} hunk${hunks !== 1 ? "s" : ""}`));
|
||||
return parts.join(theme.fg("dim", " / "));
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import type { Api, Model } from "@oh-my-pi/pi-ai";
|
||||
import { completeSimple } from "@oh-my-pi/pi-ai";
|
||||
import { logger } from "@oh-my-pi/pi-utils";
|
||||
import type { ModelRegistry } from "../config/model-registry";
|
||||
import { resolveModelRoleValue } from "../config/model-resolver";
|
||||
import { resolveRoleSelection } from "../config/model-resolver";
|
||||
import { renderPromptTemplate } from "../config/prompt-templates";
|
||||
import type { Settings } from "../config/settings";
|
||||
import titleSystemPrompt from "../prompts/system/title-system.md" with { type: "text" };
|
||||
@@ -28,13 +28,9 @@ function getTitleModel(
|
||||
const availableModels = registry.getAvailable();
|
||||
if (availableModels.length === 0) return undefined;
|
||||
|
||||
const matchPreferences = { usageOrder: settings.getStorage()?.getModelUsageOrder() };
|
||||
const configuredSmol = resolveModelRoleValue(settings.getModelRole("smol"), availableModels, {
|
||||
settings,
|
||||
matchPreferences,
|
||||
});
|
||||
if (configuredSmol.model) {
|
||||
return { model: configuredSmol.model, thinkingLevel: configuredSmol.thinkingLevel };
|
||||
const titleModel = resolveRoleSelection(["commit", "smol"], settings, availableModels);
|
||||
if (titleModel) {
|
||||
return { model: titleModel.model, thinkingLevel: titleModel.thinkingLevel };
|
||||
}
|
||||
|
||||
if (currentModel) {
|
||||
|
||||
@@ -1,208 +0,0 @@
|
||||
import type { Component } from "@oh-my-pi/pi-tui";
|
||||
import { Text } from "@oh-my-pi/pi-tui";
|
||||
import { callExaTool, findApiKey as findExaKey, formatSearchResults, isSearchResponse } from "../../exa/mcp-client";
|
||||
import type { CustomToolResult, RenderResultOptions } from "../../extensibility/custom-tools/types";
|
||||
import type { Theme } from "../../modes/theme/theme";
|
||||
import {
|
||||
formatCount,
|
||||
formatExpandHint,
|
||||
formatMoreItems,
|
||||
formatStatusIcon,
|
||||
replaceTabs,
|
||||
truncateToWidth,
|
||||
} from "../../tools/render-utils";
|
||||
import type { CodeSearchProviderId } from "./types";
|
||||
|
||||
export interface CodeSearchToolParams {
|
||||
query: string;
|
||||
code_context?: string;
|
||||
}
|
||||
|
||||
export interface CodeSearchSource {
|
||||
title: string;
|
||||
url: string;
|
||||
repository: string;
|
||||
path: string;
|
||||
branch: string;
|
||||
snippet?: string;
|
||||
totalMatches?: string;
|
||||
}
|
||||
|
||||
export interface CodeSearchResponse {
|
||||
provider: CodeSearchProviderId;
|
||||
query: string;
|
||||
totalResults?: number;
|
||||
sources: CodeSearchSource[];
|
||||
}
|
||||
|
||||
export interface CodeSearchRenderDetails {
|
||||
response?: CodeSearchResponse;
|
||||
error?: string;
|
||||
provider: CodeSearchProviderId;
|
||||
}
|
||||
|
||||
function stringifyExaCodeResponse(payload: unknown): string {
|
||||
if (typeof payload === "string") return payload;
|
||||
if (typeof payload === "number" || typeof payload === "boolean") return String(payload);
|
||||
if (payload === null || payload === undefined) return "";
|
||||
const serialized = JSON.stringify(payload, null, 2);
|
||||
return typeof serialized === "string" ? serialized : "";
|
||||
}
|
||||
|
||||
function normalizeExaCodeSearchResponse(
|
||||
params: CodeSearchToolParams,
|
||||
payload: unknown,
|
||||
formattedSearchResponse?: string,
|
||||
): CodeSearchResponse {
|
||||
const snippet = formattedSearchResponse ?? stringifyExaCodeResponse(payload);
|
||||
return {
|
||||
provider: "exa",
|
||||
query: params.query,
|
||||
sources: [
|
||||
{
|
||||
title: params.query,
|
||||
url: "https://exa.ai/",
|
||||
repository: "exa",
|
||||
path: "code-search",
|
||||
branch: "public-mcp",
|
||||
snippet: snippet.length > 0 ? snippet : undefined,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
async function searchCodeWithExa(params: CodeSearchToolParams): Promise<CodeSearchResponse> {
|
||||
const exaParams = params.code_context
|
||||
? { query: params.query, code_context: params.code_context }
|
||||
: { query: params.query };
|
||||
const response = await callExaTool("get_code_context_exa", exaParams, findExaKey());
|
||||
if (isSearchResponse(response)) {
|
||||
return normalizeExaCodeSearchResponse(params, response, formatSearchResults(response));
|
||||
}
|
||||
|
||||
return normalizeExaCodeSearchResponse(params, response);
|
||||
}
|
||||
|
||||
export function formatCodeSearchForLlm(response: CodeSearchResponse): string {
|
||||
const parts: string[] = [];
|
||||
const summaryParts: string[] = [response.provider];
|
||||
if (response.totalResults !== undefined) {
|
||||
summaryParts.push(`${response.totalResults.toLocaleString()} total matches`);
|
||||
}
|
||||
parts.push(`Code search via ${summaryParts.join(" · ")}`);
|
||||
|
||||
if (response.sources.length === 0) {
|
||||
parts.push("No results found.");
|
||||
return parts.join("\n");
|
||||
}
|
||||
|
||||
for (const [index, source] of response.sources.entries()) {
|
||||
const metadata: string[] = [source.repository, source.path];
|
||||
if (source.totalMatches) metadata.push(`${source.totalMatches} matches`);
|
||||
parts.push(`[${index + 1}] ${metadata.join(" · ")}`);
|
||||
parts.push(` ${source.url}`);
|
||||
if (source.snippet) {
|
||||
for (const line of source.snippet.split("\n").slice(0, 8)) {
|
||||
parts.push(` ${line}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parts.join("\n");
|
||||
}
|
||||
|
||||
export async function executeCodeSearch(
|
||||
params: CodeSearchToolParams,
|
||||
): Promise<CustomToolResult<CodeSearchRenderDetails>> {
|
||||
try {
|
||||
const response = await searchCodeWithExa(params);
|
||||
|
||||
return {
|
||||
content: [{ type: "text", text: formatCodeSearchForLlm(response) }],
|
||||
details: { provider: response.provider, response },
|
||||
};
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
return {
|
||||
content: [{ type: "text", text: `Error: ${message}` }],
|
||||
details: { provider: "exa", error: message },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function renderCodeSearchCall(
|
||||
args: CodeSearchToolParams,
|
||||
_options: RenderResultOptions,
|
||||
theme: Theme,
|
||||
): Component {
|
||||
let text = `${theme.fg("toolTitle", "Code Search")} ${theme.fg("accent", truncateToWidth(args.query, 80))}`;
|
||||
if (args.code_context) {
|
||||
text += ` ${theme.fg("dim", truncateToWidth(args.code_context, 40))}`;
|
||||
}
|
||||
return new Text(text, 0, 0);
|
||||
}
|
||||
|
||||
export function renderCodeSearchResult(
|
||||
result: { content: Array<{ type: string; text?: string }>; details?: CodeSearchRenderDetails },
|
||||
options: RenderResultOptions,
|
||||
uiTheme: Theme,
|
||||
): Component {
|
||||
const details = result.details;
|
||||
if (details?.error) {
|
||||
return new Text(
|
||||
`${formatStatusIcon("error", uiTheme)} ${uiTheme.fg("error", `Error: ${replaceTabs(details.error)}`)}`,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
}
|
||||
|
||||
const response = details?.response;
|
||||
if (!response) {
|
||||
return new Text(`${formatStatusIcon("warning", uiTheme)} ${uiTheme.fg("muted", "No code search results")}`, 0, 0);
|
||||
}
|
||||
|
||||
const resultCount = response.sources.length;
|
||||
const meta: string[] = [formatCount("result", resultCount), `provider:${response.provider}`];
|
||||
if (response.totalResults !== undefined) {
|
||||
meta.push(`${response.totalResults.toLocaleString()} total`);
|
||||
}
|
||||
const expandHint = formatExpandHint(uiTheme, options.expanded, resultCount > 1);
|
||||
let text = `${formatStatusIcon(resultCount > 0 ? "success" : "warning", uiTheme)} ${uiTheme.fg("dim", meta.join(uiTheme.sep.dot))}${expandHint}`;
|
||||
|
||||
if (resultCount === 0) {
|
||||
text += `\n ${uiTheme.fg("dim", uiTheme.tree.last)} ${uiTheme.fg("muted", "No results")}`;
|
||||
return new Text(text, 0, 0);
|
||||
}
|
||||
|
||||
const visibleSources = options.expanded ? response.sources : response.sources.slice(0, 1);
|
||||
for (const [index, source] of visibleSources.entries()) {
|
||||
const isLast = index === visibleSources.length - 1;
|
||||
const branch = isLast ? uiTheme.tree.last : uiTheme.tree.branch;
|
||||
const cont = isLast ? " " : uiTheme.tree.vertical;
|
||||
text += `\n ${uiTheme.fg("dim", branch)} ${uiTheme.fg("accent", truncateToWidth(replaceTabs(source.title), 100))}`;
|
||||
text += `\n ${uiTheme.fg("dim", cont)} ${uiTheme.fg("dim", uiTheme.tree.hook)} ${uiTheme.fg("mdLinkUrl", source.url)}`;
|
||||
|
||||
if (source.totalMatches) {
|
||||
text += `\n ${uiTheme.fg("dim", cont)} ${uiTheme.fg("dim", uiTheme.tree.hook)} ${uiTheme.fg("muted", `Matches: ${source.totalMatches}`)}`;
|
||||
}
|
||||
|
||||
if (source.snippet) {
|
||||
const snippetLines = source.snippet.split("\n").slice(0, options.expanded ? 6 : 3);
|
||||
for (const line of snippetLines) {
|
||||
text += `\n ${uiTheme.fg("dim", cont)} ${uiTheme.fg("dim", uiTheme.tree.hook)} ${uiTheme.fg(
|
||||
"toolOutput",
|
||||
truncateToWidth(replaceTabs(line), 100),
|
||||
)}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.expanded && response.sources.length > visibleSources.length) {
|
||||
text += `\n ${uiTheme.fg("dim", uiTheme.tree.last)} ${uiTheme.fg(
|
||||
"muted",
|
||||
formatMoreItems(response.sources.length - visibleSources.length, "result"),
|
||||
)}`;
|
||||
}
|
||||
|
||||
return new Text(text, 0, 0);
|
||||
}
|
||||
@@ -4,7 +4,6 @@
|
||||
* Single tool supporting Anthropic, Perplexity, Exa, Brave, Jina, Kimi, Gemini, Codex, Tavily, Kagi, Z.AI, and Synthetic
|
||||
* providers with provider-specific parameters exposed conditionally.
|
||||
*
|
||||
* Code search is also supported via the code_search tool.
|
||||
*/
|
||||
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
||||
import { StringEnum } from "@oh-my-pi/pi-ai";
|
||||
@@ -13,17 +12,9 @@ import { renderPromptTemplate } from "../../config/prompt-templates";
|
||||
import type { CustomTool, CustomToolContext, RenderResultOptions } from "../../extensibility/custom-tools/types";
|
||||
import type { Theme } from "../../modes/theme/theme";
|
||||
import webSearchSystemPrompt from "../../prompts/system/web-search.md" with { type: "text" };
|
||||
import codeSearchDescription from "../../prompts/tools/code-search.md" with { type: "text" };
|
||||
import webSearchDescription from "../../prompts/tools/web-search.md" with { type: "text" };
|
||||
import type { ToolSession } from "../../tools";
|
||||
import { formatAge } from "../../tools/render-utils";
|
||||
import {
|
||||
type CodeSearchRenderDetails,
|
||||
type CodeSearchToolParams,
|
||||
executeCodeSearch,
|
||||
renderCodeSearchCall,
|
||||
renderCodeSearchResult,
|
||||
} from "./code-search";
|
||||
import { getSearchProvider, resolveProviderChain, type SearchProvider } from "./provider";
|
||||
import { renderSearchCall, renderSearchResult, type SearchRenderDetails } from "./render";
|
||||
import type { SearchProviderId, SearchResponse } from "./types";
|
||||
@@ -262,34 +253,8 @@ export const webSearchCustomTool: CustomTool<typeof webSearchSchema, SearchRende
|
||||
},
|
||||
};
|
||||
|
||||
/** Schema for code context search */
|
||||
const codeSearchParameters = Type.Object({
|
||||
query: Type.String({ description: "Grep-style code search query; use exact tokens or short quoted phrases" }),
|
||||
code_context: Type.Optional(Type.String({ description: "Optional disambiguation tokens only, not a sentence" })),
|
||||
});
|
||||
|
||||
/** Code context search - optimized for code snippets and documentation */
|
||||
export const codeSearchTool: CustomTool<typeof codeSearchParameters, CodeSearchRenderDetails> = {
|
||||
name: "code_search",
|
||||
label: "Code Search",
|
||||
description: renderPromptTemplate(codeSearchDescription),
|
||||
parameters: codeSearchParameters,
|
||||
|
||||
async execute(_toolCallId, params, _onUpdate, _ctx, _signal) {
|
||||
return executeCodeSearch(params);
|
||||
},
|
||||
|
||||
renderCall(args: CodeSearchToolParams, options: RenderResultOptions, theme: Theme) {
|
||||
return renderCodeSearchCall(args, options, theme);
|
||||
},
|
||||
|
||||
renderResult(result, options, theme) {
|
||||
return renderCodeSearchResult(result, options, theme);
|
||||
},
|
||||
};
|
||||
|
||||
export function getSearchTools(): CustomTool<any, any>[] {
|
||||
return [webSearchCustomTool, codeSearchTool];
|
||||
return [webSearchCustomTool];
|
||||
}
|
||||
|
||||
export { getSearchProvider, setPreferredSearchProvider } from "./provider";
|
||||
|
||||
@@ -20,8 +20,6 @@ export type SearchProviderId =
|
||||
| "kagi"
|
||||
| "synthetic";
|
||||
|
||||
export type CodeSearchProviderId = "exa";
|
||||
|
||||
export function isSearchProviderId(value: string): value is SearchProviderId {
|
||||
return [
|
||||
"exa",
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { joinPatch } from "@oh-my-pi/pi-coding-agent/commit/git";
|
||||
|
||||
describe("joinPatch", () => {
|
||||
test("preserves space character in empty context line at end of patch", () => {
|
||||
// This simulates a hunk ending with an empty context line represented as " \n"
|
||||
const parts = [
|
||||
"@@ -1,2 +1,2 @@\n",
|
||||
"foo\n",
|
||||
"@@ -10,4 +10,4 @@\n",
|
||||
"line1\n",
|
||||
"-old\n",
|
||||
"+new\n",
|
||||
" \n", // Empty context line = space + newline
|
||||
];
|
||||
|
||||
const result = joinPatch(parts);
|
||||
|
||||
// The result should end with a space character (the empty context line)
|
||||
// but NOT start/end with multiple newlines
|
||||
expect(result.endsWith(" \n")).toBe(true);
|
||||
expect(result.replace(/[ \t]+$/, "")).toEqual(result); // No trailing spaces should be removed
|
||||
});
|
||||
|
||||
test("normalizes multiple trailing newlines in parts", () => {
|
||||
const parts = ["line1\n", "line2\n", "line3"];
|
||||
const result = joinPatch(parts);
|
||||
|
||||
// Should join with single newlines and end with one newline
|
||||
expect(result.endsWith("\n")).toBe(true);
|
||||
});
|
||||
|
||||
test("adds newline to parts that are missing them", () => {
|
||||
const parts = ["line1", "line2"];
|
||||
const result = joinPatch(parts);
|
||||
|
||||
// Should add newlines to both parts
|
||||
expect(result.includes("line1\n")).toBe(true);
|
||||
expect(result.endsWith("line2\n")).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -4,6 +4,7 @@ import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { Effort, type OpenAICompat, type ThinkingConfig } from "@oh-my-pi/pi-ai";
|
||||
import { kNoAuth, MODEL_ROLES, ModelRegistry } from "@oh-my-pi/pi-coding-agent/config/model-registry";
|
||||
import { _resetSettingsForTest, Settings } from "@oh-my-pi/pi-coding-agent/config/settings";
|
||||
import { AuthStorage } from "@oh-my-pi/pi-coding-agent/session/auth-storage";
|
||||
import { hookFetch, Snowflake } from "@oh-my-pi/pi-utils";
|
||||
|
||||
@@ -18,6 +19,7 @@ describe("ModelRegistry", () => {
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
_resetSettingsForTest();
|
||||
tempDir = path.join(os.tmpdir(), `pi-test-model-registry-${Snowflake.next()}`);
|
||||
fs.mkdirSync(tempDir, { recursive: true });
|
||||
modelsJsonPath = path.join(tempDir, "models.json");
|
||||
@@ -25,6 +27,7 @@ describe("ModelRegistry", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
_resetSettingsForTest();
|
||||
authStorage.close();
|
||||
if (tempDir && fs.existsSync(tempDir)) {
|
||||
fs.rmSync(tempDir, { recursive: true });
|
||||
@@ -1034,6 +1037,38 @@ describe("ModelRegistry", () => {
|
||||
expect(model.baseUrl).toBe(initialBaseUrl);
|
||||
});
|
||||
});
|
||||
|
||||
describe("disabled provider filtering", () => {
|
||||
test("getAvailable and getDiscoverableProviders exclude disabled providers from settings", async () => {
|
||||
writeRawModelsJson({
|
||||
ollama: {
|
||||
baseUrl: "http://127.0.0.1:11434/v1",
|
||||
api: "openai-completions",
|
||||
auth: "none",
|
||||
discovery: { type: "ollama" },
|
||||
},
|
||||
});
|
||||
await authStorage.set("github-copilot", [
|
||||
{
|
||||
type: "oauth",
|
||||
access: "tid=1;proxy-ep=proxy.individual.githubcopilot.com;exp=9999999999",
|
||||
refresh: "refresh-individual",
|
||||
expires: Date.now() + 60_000,
|
||||
},
|
||||
]);
|
||||
await Settings.init({
|
||||
inMemory: true,
|
||||
overrides: {
|
||||
disabledProviders: ["github-copilot", "ollama"],
|
||||
},
|
||||
});
|
||||
|
||||
const registry = new ModelRegistry(authStorage, modelsJsonPath);
|
||||
|
||||
expect(registry.getAvailable().some(model => model.provider === "github-copilot")).toBe(false);
|
||||
expect(registry.getDiscoverableProviders()).not.toContain("ollama");
|
||||
});
|
||||
});
|
||||
describe("runtime discovery", () => {
|
||||
test("auto-discovers ollama models without provider config", async () => {
|
||||
using _hook = hookFetch(input => {
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import { hookFetch } from "@oh-my-pi/pi-utils";
|
||||
import { executeCodeSearch } from "../../src/web/search/code-search";
|
||||
|
||||
function getFirstTextContent(result: { content: Array<{ type: string; text?: string }> }): string {
|
||||
const firstContent = result.content[0];
|
||||
if (firstContent?.type === "text") return firstContent.text ?? "";
|
||||
return "";
|
||||
}
|
||||
|
||||
describe("code_search", () => {
|
||||
it("normalizes Exa response and surfaces it as code search result", async () => {
|
||||
using _hook = hookFetch(
|
||||
() =>
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
result: {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "Need the official or source-backed way to silence direnv loading output.",
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
{ status: 200, headers: { "Content-Type": "application/json" } },
|
||||
),
|
||||
);
|
||||
|
||||
const result = await executeCodeSearch({ query: "DIRENV_LOG_FORMAT direnv loading silence" });
|
||||
const output = getFirstTextContent(result);
|
||||
expect(output).toContain("Code search via exa");
|
||||
expect(output).toContain("Need the official or source-backed way to silence direnv loading output.");
|
||||
expect(result.details?.provider).toBe("exa");
|
||||
expect(result.details?.response?.sources[0]?.snippet).toContain(
|
||||
"Need the official or source-backed way to silence direnv loading output.",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"type": "module",
|
||||
"name": "@oh-my-pi/pi-natives",
|
||||
"version": "13.17.0",
|
||||
"version": "13.17.1",
|
||||
"description": "Native Rust bindings for grep, clipboard, image processing, syntax highlighting, PTY, and shell operations via N-API",
|
||||
"homepage": "https://github.com/can1357/oh-my-pi",
|
||||
"author": "Can Boluk",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"type": "module",
|
||||
"name": "@oh-my-pi/omp-stats",
|
||||
"version": "13.17.0",
|
||||
"version": "13.17.1",
|
||||
"description": "Local observability dashboard for pi AI usage statistics",
|
||||
"homepage": "https://github.com/can1357/oh-my-pi",
|
||||
"author": "Can Boluk",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"type": "module",
|
||||
"name": "@oh-my-pi/swarm-extension",
|
||||
"version": "13.17.0",
|
||||
"version": "13.17.1",
|
||||
"description": "Swarm orchestration extension for omp",
|
||||
"homepage": "https://github.com/can1357/oh-my-pi",
|
||||
"author": "Derek Rynd",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"type": "module",
|
||||
"name": "@oh-my-pi/pi-tui",
|
||||
"version": "13.17.0",
|
||||
"version": "13.17.1",
|
||||
"description": "Terminal User Interface library with differential rendering for efficient text-based applications",
|
||||
"homepage": "https://github.com/can1357/oh-my-pi",
|
||||
"author": "Can Boluk",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"type": "module",
|
||||
"name": "@oh-my-pi/pi-utils",
|
||||
"version": "13.17.0",
|
||||
"version": "13.17.1",
|
||||
"description": "Shared utilities for pi packages",
|
||||
"homepage": "https://github.com/can1357/oh-my-pi",
|
||||
"author": "Can Boluk",
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
[toolchain]
|
||||
channel = "nightly"
|
||||
channel = "nightly-2026-03-27"
|
||||
components = ["rustfmt", "clippy", "rust-analyzer"]
|
||||
targets = ["x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"]
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
/**
|
||||
* Checks spoofed external tool versions against their latest GitHub releases.
|
||||
*
|
||||
* We impersonate several external tools (Gemini CLI, Antigravity) via User-Agent
|
||||
* strings. When these tools release new versions, the upstream service may start
|
||||
* rejecting or deprioritizing older versions. This script detects drift so we
|
||||
* can bump before users hit 403s/429s.
|
||||
*
|
||||
* Usage:
|
||||
* bun scripts/check-spoofed-versions.ts # check and report
|
||||
* bun scripts/check-spoofed-versions.ts --update # update source in-place
|
||||
*/
|
||||
|
||||
import * as path from "node:path";
|
||||
|
||||
const PROVIDER_FILE = path.join(
|
||||
import.meta.dir,
|
||||
"../packages/ai/src/providers/google-gemini-cli.ts",
|
||||
);
|
||||
|
||||
interface VersionCheck {
|
||||
/** Human label for the report. */
|
||||
name: string;
|
||||
/** Regex to extract the current hardcoded version from PROVIDER_FILE. */
|
||||
sourcePattern: RegExp;
|
||||
/** GitHub owner/repo to fetch latest release from. */
|
||||
repo: string;
|
||||
/** Extract semver from the release tag name (e.g. "v0.35.3" -> "0.35.3"). */
|
||||
parseTag: (tag: string) => string | null;
|
||||
}
|
||||
|
||||
/** Fetch latest non-prerelease tag from a GitHub repo. */
|
||||
async function fetchLatestGitHubRelease(repo: string, parseTag: (tag: string) => string | null): Promise<string | null> {
|
||||
try {
|
||||
// /releases/latest only returns non-prerelease, non-draft releases
|
||||
const res = await fetch(`https://api.github.com/repos/${repo}/releases/latest`, {
|
||||
headers: { Accept: "application/vnd.github+json", "User-Agent": "oh-my-pi/version-check" },
|
||||
});
|
||||
if (!res.ok) return null;
|
||||
const data = (await res.json()) as { tag_name?: string };
|
||||
return data.tag_name ? parseTag(data.tag_name) : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const SEMVER_RE = /(\d+\.\d+\.\d+)/;
|
||||
|
||||
const checks: VersionCheck[] = [
|
||||
{
|
||||
name: "Gemini CLI",
|
||||
sourcePattern: /PI_AI_GEMINI_CLI_VERSION\s*\|\|\s*"(\d+\.\d+\.\d+)"/,
|
||||
repo: "google-gemini/gemini-cli",
|
||||
parseTag: (tag) => SEMVER_RE.exec(tag)?.[1] ?? null,
|
||||
},
|
||||
];
|
||||
|
||||
async function run() {
|
||||
const doUpdate = process.argv.includes("--update");
|
||||
let source = await Bun.file(PROVIDER_FILE).text();
|
||||
let anyDrift = false;
|
||||
let anyUpdate = false;
|
||||
let anyChecked = false;
|
||||
|
||||
for (const check of checks) {
|
||||
const match = check.sourcePattern.exec(source);
|
||||
if (!match?.[1]) {
|
||||
console.error(`[WARN] Could not extract current ${check.name} version from source`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const current = match[1];
|
||||
const latest = await fetchLatestGitHubRelease(check.repo, check.parseTag);
|
||||
|
||||
if (!latest) {
|
||||
console.error(`[FAIL] Could not fetch latest ${check.name} version from ${check.repo}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
anyChecked = true;
|
||||
|
||||
if (current === latest) {
|
||||
console.log(`[OK] ${check.name}: ${current} (up to date)`);
|
||||
} else {
|
||||
console.log(`[DRIFT] ${check.name}: ${current} -> ${latest}`);
|
||||
anyDrift = true;
|
||||
|
||||
if (doUpdate) {
|
||||
source = source.replace(match[0], match[0].replace(current, latest));
|
||||
anyUpdate = true;
|
||||
console.log(` Updated in source.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (anyUpdate) {
|
||||
await Bun.write(PROVIDER_FILE, source);
|
||||
console.log(`\nWrote updates to ${path.relative(process.cwd(), PROVIDER_FILE)}`);
|
||||
}
|
||||
|
||||
if (!anyChecked) {
|
||||
console.error("\nNo version checks succeeded. Cannot verify freshness.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (anyDrift && !doUpdate) {
|
||||
console.log("\nRun with --update to apply version bumps.");
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
run();
|
||||
Reference in New Issue
Block a user