Compare commits

..

33 Commits

Author SHA1 Message Date
Kit Langton 873cb69319 chore: drop unused exports across opencode 2026-05-21 17:15:30 -04:00
Kit Langton 2935d1819e refactor(server): rename Fence.waitEffect to Fence.wait (#28717) 2026-05-21 20:48:15 +00:00
Kit Langton 86907e2e4a refactor(server): drop dead Fence.wait + redundant casts (#28710) 2026-05-21 16:35:44 -04:00
Frank 31d2d38d7a sync 2026-05-21 16:20:33 -04:00
Frank d21477d55a sync 2026-05-21 16:11:41 -04:00
Frank 80fa6e6c49 sync 2026-05-21 16:02:56 -04:00
Kit Langton b99787e95b refactor(opencode): fetch remote config with http client (#28661) 2026-05-21 15:53:44 -04:00
Frank 562d299a41 sync 2026-05-21 15:46:54 -04:00
opencode-agent[bot] 9ecb04e35b chore: generate 2026-05-21 19:19:25 +00:00
Kit Langton 231689c767 test(config): port env-var config tests to it.instance (#28706) 2026-05-21 19:17:29 +00:00
Frank d70942079a sync 2026-05-21 15:06:10 -04:00
Frank 0cc55c11ac sync 2026-05-21 14:53:56 -04:00
Kit Langton 40da77e77c test(opencode): cover native reasoning continuation (#28683) 2026-05-21 14:50:37 -04:00
Frank 0d2de7db65 sync 2026-05-21 14:25:43 -04:00
Frank a568f616c9 sync 2026-05-21 13:55:31 -04:00
opencode-agent[bot] e4957a78ea chore: update nix node_modules hashes 2026-05-21 17:27:59 +00:00
opencode-agent[bot] 7e35cff4f5 chore: generate 2026-05-21 17:25:26 +00:00
Shoubhit Dash 82b796ce31 fix(httpapi): return session busy error bodies (#28684) 2026-05-21 22:53:49 +05:30
Vladimir Glafirov 6c24062d2f chore(deps): bump gitlab-ai-provider to 6.7.0 (#28218) 2026-05-21 12:12:39 -05:00
opencode-agent[bot] 3ca09ee3ef chore: generate 2026-05-21 16:50:49 +00:00
Kit Langton 003f1ce0f4 test(control-plane): finish porting workspace.test.ts to it.instance (#28665) 2026-05-21 12:48:59 -04:00
Kit Langton 61390dbb49 fix(llm): preserve native continuation metadata (#28678) 2026-05-21 11:57:45 -04:00
opencode a58c3c53a9 sync release versions for v1.15.7 2026-05-21 15:51:26 +00:00
Victor Navarro 75ce8feff5 chore: upgrade sst version (#26224) 2026-05-21 17:19:15 +02:00
opencode-agent[bot] 6bee6ee755 chore: generate 2026-05-21 11:01:36 +00:00
Shoubhit Dash 9739d75892 fix(httpapi): handle corrupt v2 session messages (#28633) 2026-05-21 16:30:04 +05:30
opencode-agent[bot] 4a976482b1 chore: generate 2026-05-21 10:46:02 +00:00
Brendan Allan 2caac055ef feat(desktop): add pinch zoom setting (#28632) 2026-05-21 10:44:30 +00:00
Shoubhit Dash 2697cb8001 fix(httpapi): remove config error middleware special case (#28631) 2026-05-21 15:57:25 +05:30
opencode-agent[bot] dbd709376b chore: generate 2026-05-21 10:15:54 +00:00
Shoubhit Dash fc08292136 fix(httpapi): add unknown error references (#28629) 2026-05-21 15:44:35 +05:30
Jack ee594ba55b docs: sync TUI config translations and Zen pricing (#28620) 2026-05-21 18:03:22 +08:00
opencode-agent[bot] 516af86d53 chore: generate 2026-05-21 09:43:40 +00:00
148 changed files with 3599 additions and 3398 deletions
-9
View File
@@ -21,15 +21,6 @@ jobs:
with:
node-version: "24"
# Workaround for Pulumi version conflict:
# GitHub runners have Pulumi 3.212.0+ pre-installed, which removed the -root flag
# from pulumi-language-nodejs (see https://github.com/pulumi/pulumi/pull/21065).
# SST 3.17.x uses Pulumi SDK 3.210.0 which still passes -root, causing a conflict.
# Removing the system language plugin forces SST to use its bundled compatible version.
# TODO: Remove when sst supports Pulumi >3.210.0
- name: Fix Pulumi version conflict
run: sudo rm -f /usr/local/bin/pulumi-language-nodejs
- run: bun sst deploy --stage=${{ github.ref_name }}
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
+118 -130
View File
@@ -23,13 +23,13 @@
"oxlint-tsgolint": "0.21.0",
"prettier": "3.6.2",
"semver": "^7.6.0",
"sst": "3.18.10",
"sst": "4.13.1",
"turbo": "2.8.13",
},
},
"packages/app": {
"name": "@opencode-ai/app",
"version": "1.15.6",
"version": "1.15.7",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/core": "workspace:*",
@@ -84,7 +84,7 @@
},
"packages/console/app": {
"name": "@opencode-ai/console-app",
"version": "1.15.6",
"version": "1.15.7",
"dependencies": {
"@cloudflare/vite-plugin": "1.15.2",
"@ibm/plex": "6.4.1",
@@ -119,7 +119,7 @@
},
"packages/console/core": {
"name": "@opencode-ai/console-core",
"version": "1.15.6",
"version": "1.15.7",
"dependencies": {
"@aws-sdk/client-sts": "3.782.0",
"@jsx-email/render": "1.1.1",
@@ -146,7 +146,7 @@
},
"packages/console/function": {
"name": "@opencode-ai/console-function",
"version": "1.15.6",
"version": "1.15.7",
"dependencies": {
"@ai-sdk/anthropic": "3.0.64",
"@ai-sdk/openai": "3.0.48",
@@ -168,7 +168,7 @@
},
"packages/console/mail": {
"name": "@opencode-ai/console-mail",
"version": "1.15.6",
"version": "1.15.7",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",
@@ -192,7 +192,7 @@
},
"packages/core": {
"name": "@opencode-ai/core",
"version": "1.15.6",
"version": "1.15.7",
"bin": {
"opencode": "./bin/opencode",
},
@@ -230,7 +230,7 @@
"ai-gateway-provider": "3.1.2",
"cross-spawn": "catalog:",
"effect": "catalog:",
"gitlab-ai-provider": "6.6.0",
"gitlab-ai-provider": "6.7.0",
"glob": "13.0.5",
"google-auth-library": "10.5.0",
"immer": "11.1.4",
@@ -253,7 +253,7 @@
},
"packages/desktop": {
"name": "@opencode-ai/desktop",
"version": "1.15.6",
"version": "1.15.7",
"dependencies": {
"@zip.js/zip.js": "2.7.62",
"drizzle-orm": "catalog:",
@@ -308,7 +308,7 @@
},
"packages/effect-drizzle-sqlite": {
"name": "@opencode-ai/effect-drizzle-sqlite",
"version": "1.15.5",
"version": "1.15.7",
"dependencies": {
"drizzle-orm": "catalog:",
"effect": "catalog:",
@@ -322,7 +322,7 @@
},
"packages/enterprise": {
"name": "@opencode-ai/enterprise",
"version": "1.15.6",
"version": "1.15.7",
"dependencies": {
"@opencode-ai/core": "workspace:*",
"@opencode-ai/ui": "workspace:*",
@@ -352,7 +352,7 @@
},
"packages/function": {
"name": "@opencode-ai/function",
"version": "1.15.6",
"version": "1.15.7",
"dependencies": {
"@octokit/auth-app": "8.0.1",
"@octokit/rest": "catalog:",
@@ -368,7 +368,7 @@
},
"packages/http-recorder": {
"name": "@opencode-ai/http-recorder",
"version": "1.15.6",
"version": "1.15.7",
"dependencies": {
"@effect/platform-node": "catalog:",
"effect": "catalog:",
@@ -381,7 +381,7 @@
},
"packages/llm": {
"name": "@opencode-ai/llm",
"version": "1.15.6",
"version": "1.15.7",
"dependencies": {
"@smithy/eventstream-codec": "4.2.14",
"@smithy/util-utf8": "4.2.2",
@@ -399,7 +399,7 @@
},
"packages/opencode": {
"name": "opencode",
"version": "1.15.6",
"version": "1.15.7",
"bin": {
"opencode": "./bin/opencode",
},
@@ -469,7 +469,7 @@
"drizzle-orm": "catalog:",
"effect": "catalog:",
"fuzzysort": "3.1.0",
"gitlab-ai-provider": "6.6.0",
"gitlab-ai-provider": "6.7.0",
"glob": "13.0.5",
"google-auth-library": "10.5.0",
"gray-matter": "4.0.3",
@@ -537,7 +537,7 @@
},
"packages/plugin": {
"name": "@opencode-ai/plugin",
"version": "1.15.6",
"version": "1.15.7",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"effect": "catalog:",
@@ -575,7 +575,7 @@
},
"packages/sdk/js": {
"name": "@opencode-ai/sdk",
"version": "1.15.6",
"version": "1.15.7",
"dependencies": {
"cross-spawn": "catalog:",
},
@@ -590,7 +590,7 @@
},
"packages/slack": {
"name": "@opencode-ai/slack",
"version": "1.15.6",
"version": "1.15.7",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"@slack/bolt": "^3.17.1",
@@ -625,7 +625,7 @@
},
"packages/ui": {
"name": "@opencode-ai/ui",
"version": "1.15.6",
"version": "1.15.7",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/core": "workspace:*",
@@ -674,7 +674,7 @@
},
"packages/web": {
"name": "@opencode-ai/web",
"version": "1.15.6",
"version": "1.15.7",
"dependencies": {
"@astrojs/cloudflare": "12.6.3",
"@astrojs/markdown-remark": "6.3.1",
@@ -907,6 +907,8 @@
"@aws-sdk/client-cognito-identity": ["@aws-sdk/client-cognito-identity@3.993.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.11", "@aws-sdk/credential-provider-node": "^3.972.10", "@aws-sdk/middleware-host-header": "^3.972.3", "@aws-sdk/middleware-logger": "^3.972.3", "@aws-sdk/middleware-recursion-detection": "^3.972.3", "@aws-sdk/middleware-user-agent": "^3.972.11", "@aws-sdk/region-config-resolver": "^3.972.3", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.993.0", "@aws-sdk/util-user-agent-browser": "^3.972.3", "@aws-sdk/util-user-agent-node": "^3.972.9", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.23.2", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.16", "@smithy/middleware-retry": "^4.4.33", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.10", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.32", "@smithy/util-defaults-mode-node": "^4.2.35", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7Ne3Yk/bgQPVebAkv7W+RfhiwTRSbfER9BtbhOa2w/+dIr902LrJf6vrZlxiqaJbGj2ALx8M+ZK1YIHVxSwu9A=="],
"@aws-sdk/client-lambda": ["@aws-sdk/client-lambda@3.1048.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.11", "@aws-sdk/credential-provider-node": "^3.972.42", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/fetch-http-handler": "^5.4.2", "@smithy/node-http-handler": "^4.7.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-ryEYNVdilyWkKsOs/7Xy/l7+qjtSz4sll8NpcWD6AtONxjG/5OMaAhxxDkQb4iBoNMKnISxsARzQAp/Wa8pXIg=="],
"@aws-sdk/client-s3": ["@aws-sdk/client-s3@3.933.0", "", { "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.932.0", "@aws-sdk/credential-provider-node": "3.933.0", "@aws-sdk/middleware-bucket-endpoint": "3.930.0", "@aws-sdk/middleware-expect-continue": "3.930.0", "@aws-sdk/middleware-flexible-checksums": "3.932.0", "@aws-sdk/middleware-host-header": "3.930.0", "@aws-sdk/middleware-location-constraint": "3.930.0", "@aws-sdk/middleware-logger": "3.930.0", "@aws-sdk/middleware-recursion-detection": "3.933.0", "@aws-sdk/middleware-sdk-s3": "3.932.0", "@aws-sdk/middleware-ssec": "3.930.0", "@aws-sdk/middleware-user-agent": "3.932.0", "@aws-sdk/region-config-resolver": "3.930.0", "@aws-sdk/signature-v4-multi-region": "3.932.0", "@aws-sdk/types": "3.930.0", "@aws-sdk/util-endpoints": "3.930.0", "@aws-sdk/util-user-agent-browser": "3.930.0", "@aws-sdk/util-user-agent-node": "3.932.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.2", "@smithy/eventstream-serde-browser": "^4.2.5", "@smithy/eventstream-serde-config-resolver": "^4.3.5", "@smithy/eventstream-serde-node": "^4.2.5", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-blob-browser": "^4.2.6", "@smithy/hash-node": "^4.2.5", "@smithy/hash-stream-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/md5-js": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.9", "@smithy/middleware-retry": "^4.4.9", "@smithy/middleware-serde": "^4.2.5", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.5", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.8", "@smithy/util-defaults-mode-node": "^4.2.11", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-stream": "^4.5.6", "@smithy/util-utf8": "^4.2.0", "@smithy/util-waiter": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-KxwZvdxdCeWK6o8mpnb+kk7Kgb8V+8AjTwSXUWH1UAD85B0tjdo1cSfE5zoR5fWGol4Ml5RLez12a6LPhsoTqA=="],
"@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.933.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.932.0", "@aws-sdk/middleware-host-header": "3.930.0", "@aws-sdk/middleware-logger": "3.930.0", "@aws-sdk/middleware-recursion-detection": "3.933.0", "@aws-sdk/middleware-user-agent": "3.932.0", "@aws-sdk/region-config-resolver": "3.930.0", "@aws-sdk/types": "3.930.0", "@aws-sdk/util-endpoints": "3.930.0", "@aws-sdk/util-user-agent-browser": "3.930.0", "@aws-sdk/util-user-agent-node": "3.932.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.2", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.9", "@smithy/middleware-retry": "^4.4.9", "@smithy/middleware-serde": "^4.2.5", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.5", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.8", "@smithy/util-defaults-mode-node": "^4.2.11", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zwGLSiK48z3PzKpQiDMKP85+fpIrPMF1qQOQW9OW7BGj5AuBZIisT2O4VzIgYJeh+t47MLU7VgBQL7muc+MJDg=="],
@@ -977,6 +979,8 @@
"@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="],
"@aws/durable-execution-sdk-js": ["@aws/durable-execution-sdk-js@1.0.2", "", { "dependencies": { "@aws-sdk/client-lambda": "^3.943.0" } }, "sha512-KIYBVqV9gArkWdPEDYOjJqLlO+NecmCXOXadNBlOspJTmqztwuiyb6aZc468bYWSGuL4Me/gyTIK97ubkwFNCw=="],
"@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.4", "", {}, "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ=="],
"@azure-rest/core-client": ["@azure-rest/core-client@2.6.0", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.10.0", "@azure/core-rest-pipeline": "^1.22.0", "@azure/core-tracing": "^1.3.0", "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-iuFKDm8XPzNxPfRjhyU5/xKZmcRDzSuEghXDHHk4MjBV/wFL34GmYVBZnn9wmuoLBeS1qAw9ceMdaeJBPcB1QQ=="],
@@ -1463,6 +1467,8 @@
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.3", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ=="],
"@nodable/entities": ["@nodable/entities@2.1.0", "", {}, "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA=="],
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
@@ -2613,8 +2619,6 @@
"avvio": ["avvio@9.2.0", "", { "dependencies": { "@fastify/error": "^4.0.0", "fastq": "^1.17.1" } }, "sha512-2t/sy01ArdHHE0vRH5Hsay+RtCZt3dLPji7W7/MMOCEgze5b7SNDC4j5H6FnVgPkI1MTNFGzHdHrVXDDl7QSSQ=="],
"aws-sdk": ["aws-sdk@2.1692.0", "", { "dependencies": { "buffer": "4.9.2", "events": "1.1.1", "ieee754": "1.1.13", "jmespath": "0.16.0", "querystring": "0.2.0", "sax": "1.2.1", "url": "0.10.3", "util": "^0.12.4", "uuid": "8.0.0", "xml2js": "0.6.2" } }, "sha512-x511uiJ/57FIsbgUe5csJ13k3uzu25uWQE+XqfBis/sB0SFoiElJWXRkgEAUh0U6n40eT3ay5Ue4oPkRMu1LYw=="],
"aws-ssl-profiles": ["aws-ssl-profiles@1.1.2", "", {}, "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g=="],
"aws4fetch": ["aws4fetch@1.0.20", "", {}, "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g=="],
@@ -2699,7 +2703,7 @@
"browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="],
"buffer": ["buffer@4.9.2", "", { "dependencies": { "base64-js": "^1.0.2", "ieee754": "^1.1.4", "isarray": "^1.0.0" } }, "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg=="],
"buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
"buffer-crc32": ["buffer-crc32@1.0.0", "", {}, "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w=="],
@@ -3295,7 +3299,7 @@
"github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="],
"gitlab-ai-provider": ["gitlab-ai-provider@6.6.0", "", { "dependencies": { "@anthropic-ai/sdk": "^0.71.0", "@anycable/core": "^0.9.2", "graphql-request": "^6.1.0", "isomorphic-ws": "^5.0.0", "openai": "^6.16.0", "socket.io-client": "^4.8.1", "vscode-jsonrpc": "^8.2.1", "zod": "^3.25.76" }, "peerDependencies": { "@ai-sdk/provider": ">=3.0.0", "@ai-sdk/provider-utils": ">=4.0.0" } }, "sha512-jUxYnKA4XQaPc3wxACDZ8bPDXO0Mzx7cZaBDxbT2uGgLqtGZmSi+9tVNIg7louSS+s/ioVra3SoUz3iOFVhKPA=="],
"gitlab-ai-provider": ["gitlab-ai-provider@6.7.0", "", { "dependencies": { "@anthropic-ai/sdk": "^0.71.0", "@anycable/core": "^0.9.2", "graphql-request": "^6.1.0", "isomorphic-ws": "^5.0.0", "openai": "^6.16.0", "socket.io-client": "^4.8.1", "vscode-jsonrpc": "^8.2.1", "zod": "^3.25.76" }, "peerDependencies": { "@ai-sdk/provider": ">=3.0.0", "@ai-sdk/provider-utils": ">=4.0.0" } }, "sha512-J7apROAmDwDJcLPcbQ9YQiPhyFN1Vvt/f3ugsePaUnzh3sedfHCHGETgG0Mm0dJCB2rC7DYwmV/sgxLQcQ/sjw=="],
"glob": ["glob@13.0.5", "", { "dependencies": { "minimatch": "^10.2.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-BzXxZg24Ibra1pbQ/zE7Kys4Ua1ks7Bn6pKLkVPZ9FZe4JQS6/Q7ef3LG1H+k7lUf5l4T3PLSyYyYJVYUvfgTw=="],
@@ -3437,7 +3441,7 @@
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
"ieee754": ["ieee754@1.1.13", "", {}, "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="],
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
"ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
@@ -3569,7 +3573,7 @@
"is64bit": ["is64bit@2.0.0", "", { "dependencies": { "system-architecture": "^0.1.0" } }, "sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw=="],
"isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
"isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
"isbinaryfile": ["isbinaryfile@5.0.7", "", {}, "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ=="],
@@ -3587,8 +3591,6 @@
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
"jmespath": ["jmespath@0.16.0", "", {}, "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw=="],
"jose": ["jose@6.0.11", "", {}, "sha512-QxG7EaliDARm1O1S8BGakqncGT9s25bKL1WSf6/oa17Tkqwi8D2ZNglqCF+DsYF88/rV66Q/Q2mFAy697E1DUg=="],
"js-base64": ["js-base64@3.7.7", "", {}, "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw=="],
@@ -4075,8 +4077,6 @@
"opencode-poe-auth": ["opencode-poe-auth@0.0.1", "", { "dependencies": { "open": "^10.0.0", "poe-oauth": "*" }, "peerDependencies": { "@opencode-ai/plugin": "*" } }, "sha512-cXqTlS6AXHzo1oBdosnxbT47ZJEZ9WXn050X8Re6wZ1vaNnTpB/l2fMQt90evT7RBK0fB8UjXQUDMKyd7bbiqg=="],
"opencontrol": ["opencontrol@0.0.6", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.6.1", "@tsconfig/bun": "1.0.7", "hono": "4.7.4", "zod": "3.24.2", "zod-to-json-schema": "3.24.3" }, "bin": { "opencontrol": "bin/index.mjs" } }, "sha512-QeCrpOK5D15QV8kjnGVeD/BHFLwcVr+sn4T6KKmP0WAMs2pww56e4h+eOGHb5iPOufUQXbdbBKi6WV2kk7tefQ=="],
"openid-client": ["openid-client@5.6.4", "", { "dependencies": { "jose": "^4.15.4", "lru-cache": "^6.0.0", "object-hash": "^2.2.0", "oidc-token-hash": "^5.0.3" } }, "sha512-T1h3B10BRPKfcObdBklX639tVz+xh34O7GjofqrqiAQdm7eHsQ00ih18x6wuJ/E6FxdtS2u3FmUGPDeEcMwzNA=="],
"opentui-spinner": ["opentui-spinner@0.0.6", "", { "dependencies": { "cli-spinners": "^3.3.0" }, "peerDependencies": { "@opentui/core": "^0.1.49", "@opentui/react": "^0.1.49", "@opentui/solid": "^0.1.49", "typescript": "^5" }, "optionalPeers": ["@opentui/react", "@opentui/solid"] }, "sha512-xupLOeVQEAXEvVJCvHkfX6fChDWmJIPHe5jyUrVb8+n4XVTX8mBNhitFfB9v2ZbkC1H2UwPab/ElePHoW37NcA=="],
@@ -4273,7 +4273,7 @@
"pump": ["pump@3.0.4", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA=="],
"punycode": ["punycode@1.3.2", "", {}, "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw=="],
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
"pupa": ["pupa@3.3.0", "", { "dependencies": { "escape-goat": "^4.0.0" } }, "sha512-LjgDO2zPtoXP2wJpDjZrGdojii1uqO0cnwKoIoUzkfS98HDmbeiGmYiXo3lXeFlq2xvne1QFQhwYXSUCLKtEuA=="],
@@ -4283,8 +4283,6 @@
"quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="],
"querystring": ["querystring@0.2.0", "", {}, "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g=="],
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
"quick-format-unescaped": ["quick-format-unescaped@4.0.4", "", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="],
@@ -4469,7 +4467,7 @@
"sanitize-filename": ["sanitize-filename@1.6.4", "", { "dependencies": { "truncate-utf8-bytes": "^1.0.0" } }, "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg=="],
"sax": ["sax@1.2.1", "", {}, "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA=="],
"sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
"scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="],
@@ -4597,23 +4595,23 @@
"ssri": ["ssri@13.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ=="],
"sst": ["sst@3.18.10", "", { "dependencies": { "aws-sdk": "2.1692.0", "aws4fetch": "1.0.18", "jose": "5.2.3", "opencontrol": "0.0.6", "openid-client": "5.6.4" }, "optionalDependencies": { "sst-darwin-arm64": "3.18.10", "sst-darwin-x64": "3.18.10", "sst-linux-arm64": "3.18.10", "sst-linux-x64": "3.18.10", "sst-linux-x86": "3.18.10", "sst-win32-arm64": "3.18.10", "sst-win32-x64": "3.18.10", "sst-win32-x86": "3.18.10" }, "bin": { "sst": "bin/sst.mjs" } }, "sha512-SY+ldeJ9K5E9q+DhjXA3e2W3BEOzBwkE3IyLSD71uA3/5nRhUAST31iOWEpW36LbIvSQ9uOVDFcebztoLJ8s7w=="],
"sst": ["sst@4.13.1", "", { "dependencies": { "@aws/durable-execution-sdk-js": "1.0.2", "aws4fetch": "1.0.18", "jose": "5.2.3", "openid-client": "5.6.4" }, "optionalDependencies": { "sst-darwin-arm64": "4.13.1", "sst-darwin-x64": "4.13.1", "sst-linux-arm64": "4.13.1", "sst-linux-x64": "4.13.1", "sst-linux-x86": "4.13.1", "sst-win32-arm64": "4.13.1", "sst-win32-x64": "4.13.1", "sst-win32-x86": "4.13.1" }, "bin": { "sst": "bin/sst.mjs" } }, "sha512-FKzhWuqV5NPhFWH7b+Ktf5+Mvox6DCMc8iOrjaS3mkjnnmyEcOZq7HNx2/ZlIcBwgOZE9sCPR3ot1zXtXkXzZg=="],
"sst-darwin-arm64": ["sst-darwin-arm64@3.18.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3MwIpMZhhdZKDqLp9ZQNlwkWix5+q+N0PWstuTomYwgZOxCCe6u9IIsoIszSk+GAJJN/jvGZyLiXKeV4iiQvw=="],
"sst-darwin-arm64": ["sst-darwin-arm64@4.13.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gGwfgZGOsulU6iGeZJtxP5BpSZFsRUy1dG/yTqJa++n1P1APWXZC64u2qRHm6rdwLAjEeWaCQ9k7ouKgn0zh5Q=="],
"sst-darwin-x64": ["sst-darwin-x64@3.18.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-nQ0jMKkPOa+kj6Ygz8+kYhBua/vgNTLkd+4r8NSmk7v+Zs78lKnx3T//kEzS0yik6Q6QwGfokwrTcA1Jii2xSw=="],
"sst-darwin-x64": ["sst-darwin-x64@4.13.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-RcKLI2oAY+Wk5fWUF0gJFKvwMSpid3qw9L3OcfgFL2ymZH652ybLpQPBxtoJ95FdyknWIMnYf6ZK9C2jnH5eaw=="],
"sst-linux-arm64": ["sst-linux-arm64@3.18.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-mj9VNj3SvLS+HaXx2PhCX0aTA7CwJNoM6JhRc0s/zCilqchcvqDjbhpYBJO4brEPv6aOaaa7T3WvIQqtYauK4Q=="],
"sst-linux-arm64": ["sst-linux-arm64@4.13.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-Lp/8BWhH8KZnb50xPd8A6bkZ7QqCH3Dar9l16TPwuAmPxxRUlkmg42h+ACZ9n02vSJl8y/WLnV4JB6SgJpRFVg=="],
"sst-linux-x64": ["sst-linux-x64@3.18.10", "", { "os": "linux", "cpu": "x64" }, "sha512-7iy1Eq2eqnT9Ag/8OVgC04vRjV7AAQyf/BvzLc+6Sz+GvRiKA8VEuPnbXNYQF+NIvEqsawfcd7MknSTtImpsvQ=="],
"sst-linux-x64": ["sst-linux-x64@4.13.1", "", { "os": "linux", "cpu": "x64" }, "sha512-f4eHOsApoP+CpUzLUGzfoTBYsqqr6nl/M0gjYsVNi1T+6v92lc84AhQ4DoDZx033QggyxkqRezj8jOkjkKxa7w=="],
"sst-linux-x86": ["sst-linux-x86@3.18.10", "", { "os": "linux", "cpu": "none" }, "sha512-77qZSuPZeQ5bdRCiq1pQEdY8EcGNHboKrx4P2yFid2FBDKJsXxOXtIxJdloyx+ljBn0+nxl/g040QBmXxdc9tA=="],
"sst-linux-x86": ["sst-linux-x86@4.13.1", "", { "os": "linux", "cpu": "none" }, "sha512-5OHdtPa0GmozDpBPdsD9Tv+SGnpjJSoTXptgK59ivQg3UYNWBBotidvEyUJs1MU9B/3+I+Fmw32zsF2O+ZtPgg=="],
"sst-win32-arm64": ["sst-win32-arm64@3.18.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-aY+FhMxvYs8crlrKALpLn/kKmud8YQj6LkMHsrOAAIJhfNyxhCja2vrYQaY+bcqdsS5W2LMVcS2hyaMqKXZKcg=="],
"sst-win32-arm64": ["sst-win32-arm64@4.13.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-qAUuCQj6wDdZ84a9W4FhnRTHYlEnYRHGYFqO3cmMImbnAQEoKSQR1XuWqt0Ev4RT3kU4Lz/fgvJjGkR7WUstLg=="],
"sst-win32-x64": ["sst-win32-x64@3.18.10", "", { "os": "win32", "cpu": "x64" }, "sha512-rY+yJXOpG+P5xXnaQRpCvBK2zwwLhjzpYidGkp6F+cGgiVdh2Wre/CIQNRaVHr20ncj8lLe/RsHWa9QCNM48jg=="],
"sst-win32-x64": ["sst-win32-x64@4.13.1", "", { "os": "win32", "cpu": "x64" }, "sha512-r+83NMwpe4MLAvkVoOxi1KNs6nIqilFucsTr+VN1PgknsiVBY1emITjSWS2jDJJMuH8xJXL5xJzF/l5rcKWErg=="],
"sst-win32-x86": ["sst-win32-x86@3.18.10", "", { "os": "win32", "cpu": "none" }, "sha512-pq8SmV0pIjBFMY6DraUZ4akyTxHnfjIKCRbBLdMxFUZK8TzA1NK2YdjRt1AwrgXRYGRyctrz/mt4WyO0SMOVQQ=="],
"sst-win32-x86": ["sst-win32-x86@4.13.1", "", { "os": "win32", "cpu": "none" }, "sha512-YPxBVdac/MsrzwlC6pF0NrrvMcmfdBLYjv7MbzHc5jNh1FQ1WPh6bdWQqgv0KD9EQTNLLEkej0beydgUvcCWJg=="],
"stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="],
@@ -4893,8 +4891,6 @@
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
"url": ["url@0.10.3", "", { "dependencies": { "punycode": "1.3.2", "querystring": "0.2.0" } }, "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ=="],
"use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="],
"use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="],
@@ -4903,8 +4899,6 @@
"utf8-byte-length": ["utf8-byte-length@1.0.5", "", {}, "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA=="],
"util": ["util@0.12.5", "", { "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", "which-typed-array": "^1.1.2" } }, "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA=="],
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
"utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="],
@@ -5031,6 +5025,8 @@
"xdg-basedir": ["xdg-basedir@5.1.0", "", {}, "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ=="],
"xml-naming": ["xml-naming@0.1.0", "", {}, "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw=="],
"xml2js": ["xml2js@0.6.2", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA=="],
"xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="],
@@ -5193,6 +5189,20 @@
"@aws-sdk/client-cognito-identity/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
"@aws-sdk/client-lambda/@aws-sdk/core": ["@aws-sdk/core@3.974.11", "", { "dependencies": { "@aws-sdk/types": "^3.973.8", "@aws-sdk/xml-builder": "^3.972.24", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/core": "^3.24.2", "@smithy/signature-v4": "^5.4.2", "@smithy/types": "^4.14.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-QpnINq5FZH6EOaDEkmHdT7eUunbvD27pDNQypaWjFyYz7Zl1q3UCMQErBZxpmfGfI7MvI2TlK8KTkgNpv8b1ug=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.42", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.37", "@aws-sdk/credential-provider-http": "^3.972.39", "@aws-sdk/credential-provider-ini": "^3.972.41", "@aws-sdk/credential-provider-process": "^3.972.37", "@aws-sdk/credential-provider-sso": "^3.972.41", "@aws-sdk/credential-provider-web-identity": "^3.972.41", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/credential-provider-imds": "^4.3.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-D4oon2zbqqsWOJUM99Gm3/ZyJ0IJvTXVN3PyloGb3kQEyI36fjCZheZj422lAgTWWd6TSHgiImLt3RIaLdv3dQ=="],
"@aws-sdk/client-lambda/@aws-sdk/types": ["@aws-sdk/types@3.973.8", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw=="],
"@aws-sdk/client-lambda/@smithy/core": ["@smithy/core@3.24.3", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-Ep/7tPamGY8mgESE3LyLKtxJyy6U52WWAqr/3wial47Sj4u3PiIF73AOGI27UyLy9duTkhZbgzodOfLV4TduZg=="],
"@aws-sdk/client-lambda/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.4.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-F+DRf8IJazRJgYog2A/yJK7eYVc0rqTlRzO+5ZxjJd4WkZoKz0IJRncf7G6t1pdVT3kryJcwuTFhN1c5m6N47A=="],
"@aws-sdk/client-lambda/@smithy/node-http-handler": ["@smithy/node-http-handler@4.7.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-/jPhevcTFPMVl6KNjbaI47iOg1zxC7IsnX4PQDGVZKMFceOXtB8IEYaB7a9VvkP/3oC60WzTeKocvSI7vLT0vA=="],
"@aws-sdk/client-lambda/@smithy/types": ["@smithy/types@4.14.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-59b5HtSVrVR/eYNei3BUj3DCPKD/G7EtDDe7OEJE7i7FtQFugYo6MxbotS8mVJkLNVf8gYaAlEBwwtJ9HzhWSg=="],
"@aws-sdk/client-sso/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
"@aws-sdk/client-sts/@aws-sdk/core": ["@aws-sdk/core@3.775.0", "", { "dependencies": { "@aws-sdk/types": "3.775.0", "@smithy/core": "^3.2.0", "@smithy/node-config-provider": "^4.0.2", "@smithy/property-provider": "^4.0.2", "@smithy/protocol-http": "^5.1.0", "@smithy/signature-v4": "^5.0.2", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/util-middleware": "^4.0.2", "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" } }, "sha512-8vpW4WihVfz0DX+7WnnLGm3GuQER++b0IwQG35JlQMlgqnc44M//KbJPsIHA0aJUJVwJAEShgfr5dUbY8WUzaA=="],
@@ -5657,28 +5667,18 @@
"astro/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
"aws-sdk/events": ["events@1.1.1", "", {}, "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw=="],
"aws-sdk/uuid": ["uuid@8.0.0", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw=="],
"babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="],
"babel-plugin-module-resolver/glob": ["glob@9.3.5", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="],
"bl/buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
"body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
"body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
"body-parser/qs": ["qs@6.14.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q=="],
"buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
"builder-util/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"builder-util-runtime/sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
"c12/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
"c12/dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="],
@@ -5735,8 +5735,6 @@
"engine.io-client/ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
"es-get-iterator/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
"esbuild-plugin-copy/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"esbuild-plugin-copy/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
@@ -5845,16 +5843,6 @@
"opencode-poe-auth/open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="],
"opencontrol/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.6.1", "", { "dependencies": { "content-type": "^1.0.5", "cors": "^2.8.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^4.1.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-oxzMzYCkZHMntzuyerehK3fV6A2Kwh5BD6CGEJSVDU2QNEhfLOptf2X7esQgaHZXHZY0oHmMsOtIDLP71UJXgA=="],
"opencontrol/@tsconfig/bun": ["@tsconfig/bun@1.0.7", "", {}, "sha512-udGrGJBNQdXGVulehc1aWT73wkR9wdaGBtB6yL70RJsqwW/yJhIg6ZbRlPOfIUiFNrnBuYLBi9CSmMKfDC7dvA=="],
"opencontrol/hono": ["hono@4.7.4", "", {}, "sha512-Pst8FuGqz3L7tFF+u9Pu70eI0xa5S3LPUmrNd5Jm8nTHze9FxLTK9Kaj5g/k4UcwuJSXTP65SyHOPLrffpcAJg=="],
"opencontrol/zod": ["zod@3.24.2", "", {}, "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="],
"opencontrol/zod-to-json-schema": ["zod-to-json-schema@3.24.3", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A=="],
"openid-client/jose": ["jose@4.15.9", "", {}, "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA=="],
"openid-client/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
@@ -5901,8 +5889,6 @@
"raw-body/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
"readable-stream/buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
"readdir-glob/minimatch": ["minimatch@5.1.9", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw=="],
"restore-cursor/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
@@ -5913,10 +5899,6 @@
"router/path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="],
"safe-array-concat/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
"safe-push-apply/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
"send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
"send/mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="],
@@ -5929,8 +5911,6 @@
"shiki/@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="],
"sitemap/sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
"slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
"slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="],
@@ -5973,8 +5953,6 @@
"unused-filename/path-exists": ["path-exists@5.0.0", "", {}, "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ=="],
"uri-js/punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
"venice-ai-sdk-provider/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="],
"vite-plugin-icons-spritesheet/glob": ["glob@11.1.0", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw=="],
@@ -5993,8 +5971,6 @@
"vscode-languageserver-protocol/vscode-jsonrpc": ["vscode-jsonrpc@8.2.0", "", {}, "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="],
"which-builtin-type/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
"wrangler/esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="],
"wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
@@ -6003,8 +5979,6 @@
"wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"xml2js/sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
"yaml-language-server/request-light": ["request-light@0.5.8", "", {}, "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg=="],
"yaml-language-server/yaml": ["yaml@2.7.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ=="],
@@ -6069,6 +6043,36 @@
"@aws-sdk/client-cognito-identity/@aws-sdk/middleware-user-agent/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.996.6", "", { "dependencies": { "@aws-sdk/types": "^3.973.7", "@smithy/types": "^4.14.0", "@smithy/url-parser": "^4.2.13", "@smithy/util-endpoints": "^3.3.4", "tslib": "^2.6.2" } }, "sha512-2nUQ+2ih7CShuKHpGSIYvvAIOHy52dOZguYG36zptBukhw6iFwcvGfG0tes0oZFWQqEWvgZe9HLWaNlvXGdOrg=="],
"@aws-sdk/client-lambda/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.24", "", { "dependencies": { "@nodable/entities": "2.1.0", "@smithy/types": "^4.14.1", "fast-xml-parser": "5.7.3", "tslib": "^2.6.2" } }, "sha512-V8z5YcDPfsvzrBlj0xR1vhRtocblhYbqdreCJB/voGd4Sr5zjNAeWxexbnqVtskTJe0vFb5KMqbSL++ePl+zRw=="],
"@aws-sdk/client-lambda/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.4.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g=="],
"@aws-sdk/client-lambda/@aws-sdk/core/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.37", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-/jpPvEh6f7ntmIzf7dNxoNX6Q8vt8UpesCjbW6mFfk4V1NW6bIy9qxcQ6WbA8As5yQhsZOe+xeNd4xHX8kdY2Q=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.39", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/fetch-http-handler": "^5.4.2", "@smithy/node-http-handler": "^4.7.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-pIgTpisWyWg7X1bUbzSjuUYosYTD0Ghz2M0hkSTmb3a6i3qV3uU+NYJPI/E2XSC0HcsZh5rsLPzeXrkb2DS0Cg=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.972.41", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/credential-provider-env": "^3.972.37", "@aws-sdk/credential-provider-http": "^3.972.39", "@aws-sdk/credential-provider-login": "^3.972.41", "@aws-sdk/credential-provider-process": "^3.972.37", "@aws-sdk/credential-provider-sso": "^3.972.41", "@aws-sdk/credential-provider-web-identity": "^3.972.41", "@aws-sdk/nested-clients": "^3.997.9", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/credential-provider-imds": "^4.3.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-u2tyjaxJJzW8UtW4SM1ZcPMDwO6y+kV+llvou+Adts0FAKyzes5jG4izQN+KX3yE8ZROpS5y1LJ//xL2iSf76w=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.37", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-7nVaHBUaWIddASYfVaA9O4D5ZVjewU3sCol9WqZPGfW0nR+0WqE0xHZnD/U2L33PlOB8KNXGKZ6wOES/QijKzg=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.972.41", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/nested-clients": "^3.997.9", "@aws-sdk/token-providers": "3.1048.0", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-IOWAWEHe5LkjSKkkUUX9ciV6Y1scHTsnfEkdt5yyC4Slrc7AGbkLPrpntjqh18ksJAMOaVhoBsO8p2WyTcY2wQ=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.41", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/nested-clients": "^3.997.9", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-mbACk9Yypa8nm4iGZLs0PofOXEcTDOUw6wDnsPXNDNSd2WNXs1tSo+6nc/fh0jLYdfVZThhBL98PHW4aXFsG5A=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.3.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-I2Bti0DKFo2IJyN28ijCsx51BAumEYR4/1yZ1FXyBygy9MqbnMqCev4JPth/MbpRfBSRAX35hITSnAdJRo1u5w=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/client-lambda/@aws-sdk/types/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/client-lambda/@smithy/core/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/client-lambda/@smithy/fetch-http-handler/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/client-lambda/@smithy/node-http-handler/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.775.0", "", { "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-6ESVxwCbGm7WZ17kY1fjmxQud43vzJFoLd4bmlR+idQSWdqlzGDYdcfzpjDKTcivdtNrVYmFvcH1JBUwCRAZhw=="],
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.775.0", "", { "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/types": "3.775.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/property-provider": "^4.0.2", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/util-stream": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-PjDQeDH/J1S0yWV32wCj2k5liRo0ssXMseCBEkCsD3SqsU8o5cU82b0hMX4sAib/RkglCSZqGO0xMiN0/7ndww=="],
@@ -6239,8 +6243,6 @@
"@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="],
"@azure/core-http/xml2js/sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
"@azure/core-xml/fast-xml-parser/strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="],
"@azure/identity/open/wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="],
@@ -6553,14 +6555,10 @@
"babel-plugin-module-resolver/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
"bl/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
"body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
"c12/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
"crc/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
"cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
"dir-compare/minimatch/brace-expansion": ["brace-expansion@1.1.14", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g=="],
@@ -6603,6 +6601,8 @@
"lazystream/readable-stream/core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
"lazystream/readable-stream/isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
"lazystream/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
"lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
@@ -6617,18 +6617,6 @@
"opencode-poe-auth/open/wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="],
"opencontrol/@modelcontextprotocol/sdk/express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="],
"opencontrol/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@7.5.1", "", { "peerDependencies": { "express": ">= 4.11" } }, "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw=="],
"opencontrol/@modelcontextprotocol/sdk/pkce-challenge": ["pkce-challenge@4.1.0", "", {}, "sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ=="],
"opencontrol/@modelcontextprotocol/sdk/raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
"opencontrol/@modelcontextprotocol/sdk/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
"opencontrol/@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="],
"ora/bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
"ora/bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
@@ -6641,8 +6629,6 @@
"pkg-up/find-up/locate-path": ["locate-path@3.0.0", "", { "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="],
"readable-stream/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
"readdir-glob/minimatch/brace-expansion": ["brace-expansion@2.1.0", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w=="],
"restore-cursor/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
@@ -6761,6 +6747,18 @@
"@aws-sdk/client-cognito-identity/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.5.8", "", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.2.0", "strnum": "^2.2.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ=="],
"@aws-sdk/client-lambda/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.7.3", "", { "dependencies": { "@nodable/entities": "^2.1.0", "fast-xml-builder": "^1.1.7", "path-expression-matcher": "^1.5.0", "strnum": "^2.2.3" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.41", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/nested-clients": "^3.997.9", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-0LBitxXiAiaE5nlFPfpNIww/8FRY/I7WIndWsc9GmNFOM7cE1wNpVNQEGEk9Outg5l8xl+3vybxFyUy4l9q/LQ=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.9", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.11", "@aws-sdk/signature-v4-multi-region": "^3.996.27", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/fetch-http-handler": "^5.4.2", "@smithy/node-http-handler": "^4.7.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-jPR3rnmRI4hWYyzfmTGBr7NblMp8QYYeflHXba1H6+7CGrWVqWKQzaXFQ4qbExqPRsXN3T3L3JxFhr6aouXUGQ=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.9", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.11", "@aws-sdk/signature-v4-multi-region": "^3.996.27", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/fetch-http-handler": "^5.4.2", "@smithy/node-http-handler": "^4.7.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-jPR3rnmRI4hWYyzfmTGBr7NblMp8QYYeflHXba1H6+7CGrWVqWKQzaXFQ4qbExqPRsXN3T3L3JxFhr6aouXUGQ=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1048.0", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/nested-clients": "^3.997.9", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-k0y/GcuesuSfWyUM0WamrGyeZmltRYaPbHO82UDA6mZ/doB+FOHKutikPAtSXMn/hDz970cF+iRuuiYO9VEbAA=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.9", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.11", "@aws-sdk/signature-v4-multi-region": "^3.996.27", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/fetch-http-handler": "^5.4.2", "@smithy/node-http-handler": "^4.7.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-jPR3rnmRI4hWYyzfmTGBr7NblMp8QYYeflHXba1H6+7CGrWVqWKQzaXFQ4qbExqPRsXN3T3L3JxFhr6aouXUGQ=="],
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.782.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.775.0", "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", "@aws-sdk/middleware-user-agent": "3.782.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.782.0", "@aws-sdk/util-user-agent-browser": "3.775.0", "@aws-sdk/util-user-agent-node": "3.782.0", "@smithy/config-resolver": "^4.1.0", "@smithy/core": "^3.2.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", "@smithy/middleware-endpoint": "^4.1.0", "@smithy/middleware-retry": "^4.1.0", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.8", "@smithy/util-defaults-mode-node": "^4.0.8", "@smithy/util-endpoints": "^3.0.2", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-QOYC8q7luzHFXrP0xYAqBctoPkynjfV0r9dqntFu4/IWMTyC1vlo1UTxFAjIPyclYw92XJyEkVCVg9v/nQnsUA=="],
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.782.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.775.0", "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", "@aws-sdk/middleware-user-agent": "3.782.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.782.0", "@aws-sdk/util-user-agent-browser": "3.775.0", "@aws-sdk/util-user-agent-node": "3.782.0", "@smithy/config-resolver": "^4.1.0", "@smithy/core": "^3.2.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", "@smithy/middleware-endpoint": "^4.1.0", "@smithy/middleware-retry": "^4.1.0", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.8", "@smithy/util-defaults-mode-node": "^4.0.8", "@smithy/util-endpoints": "^3.0.2", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-5GlJBejo8wqMpSSEKb45WE82YxI2k73YuebjLH/eWDNQeE6VI5Bh9lA1YQ7xNkLLH8hIsb0pSfKVuwh0VEzVrg=="],
@@ -6957,30 +6955,6 @@
"js-beautify/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
"opencontrol/@modelcontextprotocol/sdk/express/accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
"opencontrol/@modelcontextprotocol/sdk/express/body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
"opencontrol/@modelcontextprotocol/sdk/express/content-disposition": ["content-disposition@1.1.0", "", {}, "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g=="],
"opencontrol/@modelcontextprotocol/sdk/express/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
"opencontrol/@modelcontextprotocol/sdk/express/cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
"opencontrol/@modelcontextprotocol/sdk/express/finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="],
"opencontrol/@modelcontextprotocol/sdk/express/fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="],
"opencontrol/@modelcontextprotocol/sdk/express/merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
"opencontrol/@modelcontextprotocol/sdk/express/send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="],
"opencontrol/@modelcontextprotocol/sdk/express/serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="],
"opencontrol/@modelcontextprotocol/sdk/express/type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
"ora/bl/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
"pkg-dir/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
"pkg-up/find-up/locate-path/p-locate": ["p-locate@3.0.0", "", { "dependencies": { "p-limit": "^2.0.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="],
@@ -7003,6 +6977,16 @@
"@aws-sdk/client-cognito-identity/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="],
"@aws-sdk/client-lambda/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/fast-xml-builder": ["fast-xml-builder@1.2.0", "", { "dependencies": { "path-expression-matcher": "^1.5.0", "xml-naming": "^0.1.0" } }, "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q=="],
"@aws-sdk/client-lambda/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.996.27", "", { "dependencies": { "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/signature-v4": "^5.4.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-0Phbz4t6HI3D3skxvG2uI+VWU034/nSIw1T8d+FPzzQG9EQTrw94o9mOKO2Gv3n3Oc8P7JD7RAUxkoneLWv5Eg=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/nested-clients/@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.996.27", "", { "dependencies": { "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/signature-v4": "^5.4.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-0Phbz4t6HI3D3skxvG2uI+VWU034/nSIw1T8d+FPzzQG9EQTrw94o9mOKO2Gv3n3Oc8P7JD7RAUxkoneLWv5Eg=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.996.27", "", { "dependencies": { "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/signature-v4": "^5.4.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-0Phbz4t6HI3D3skxvG2uI+VWU034/nSIw1T8d+FPzzQG9EQTrw94o9mOKO2Gv3n3Oc8P7JD7RAUxkoneLWv5Eg=="],
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
@@ -7077,8 +7061,6 @@
"js-beautify/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"opencontrol/@modelcontextprotocol/sdk/express/type-is/media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
"pkg-dir/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
"pkg-up/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
@@ -7087,6 +7069,12 @@
"tw-to-css/tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4": ["@smithy/signature-v4@5.4.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/nested-clients/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4": ["@smithy/signature-v4@5.4.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g=="],
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4": ["@smithy/signature-v4@5.4.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g=="],
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
"@aws-sdk/credential-provider-cognito-identity/@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="],
+1
View File
@@ -278,6 +278,7 @@ new sst.cloudflare.x.SolidStart("Console", {
//VITE_API_URL: gateway.url.apply((url) => url!),
VITE_AUTH_URL: auth.url.apply((url) => url!),
VITE_STRIPE_PUBLISHABLE_KEY: STRIPE_PUBLISHABLE_KEY.value,
PLACEHOLDER: "keepalive",
},
transform: {
server: {
+11 -11
View File
@@ -4,7 +4,7 @@ import { domain } from "./stage"
const description = "Managed by SST (Don't edit in Honeycomb UI)"
const alertsDisabled = $app.stage !== "production"
const webhookRecipient = new honeycomb.WebhookRecipient("DiscordAlerts", {
const webhookRecipient = new honeycombio.WebhookRecipient("DiscordAlerts", {
name: $app.stage === "production" ? "Discord Alerts" : `Discord Alerts (${$app.stage})`,
url: `https://${domain}/honeycomb/webhook`,
secret: SECRET.HoneycombWebhookSecret.result,
@@ -67,7 +67,7 @@ IF(
)`,
})
return honeycomb.getQuerySpecificationOutput({
return honeycombio.getQuerySpecificationOutput({
breakdowns: ["model"],
calculatedFields: [failedHttpStatus],
calculations: [
@@ -99,7 +99,7 @@ const providerHttpErrorsQuery = () => {
expression: `IF(GT($llm.error.code, "400"), 1, 0)`,
})
return honeycomb.getQuerySpecificationOutput({
return honeycombio.getQuerySpecificationOutput({
breakdowns: ["provider"],
calculatedFields: [successHttpStatus, failedProviderHttpStatus],
calculations: [
@@ -140,7 +140,7 @@ const modelLowTpsQuery = (product: "go" | "zen") => {
{ column: "tps.output", op: "exists" },
]
return honeycomb.getQuerySpecificationOutput({
return honeycombio.getQuerySpecificationOutput({
breakdowns: ["model"],
calculations: [
{ op: "COUNT", name: "TOTAL", filterCombination: "AND", filters },
@@ -157,7 +157,7 @@ const modelLowTpsQuery = (product: "go" | "zen") => {
}).json
}
new honeycomb.Trigger("IncreasedModelHttpErrorsGo", {
new honeycombio.Trigger("IncreasedModelHttpErrorsGo", {
name: "Increased Model HTTP Errors [Go]",
description,
disabled: alertsDisabled,
@@ -177,7 +177,7 @@ new honeycomb.Trigger("IncreasedModelHttpErrorsGo", {
],
})
new honeycomb.Trigger("IncreasedModelHttpErrorsZen", {
new honeycombio.Trigger("IncreasedModelHttpErrorsZen", {
name: "Increased Model HTTP Errors [Zen]",
description,
disabled: alertsDisabled,
@@ -197,7 +197,7 @@ new honeycomb.Trigger("IncreasedModelHttpErrorsZen", {
],
})
new honeycomb.Trigger("LowModelTpsGo", {
new honeycombio.Trigger("LowModelTpsGo", {
name: "Low Model TPS [Go]",
description,
disabled: alertsDisabled,
@@ -217,7 +217,7 @@ new honeycomb.Trigger("LowModelTpsGo", {
],
})
new honeycomb.Trigger("LowModelTpsZen", {
new honeycombio.Trigger("LowModelTpsZen", {
name: "Low Model TPS [Zen]",
description,
disabled: alertsDisabled,
@@ -237,7 +237,7 @@ new honeycomb.Trigger("LowModelTpsZen", {
],
})
new honeycomb.Trigger("IncreasedProviderHttpErrors", {
new honeycombio.Trigger("IncreasedProviderHttpErrors", {
name: "Increased Provider HTTP Errors",
description,
disabled: alertsDisabled,
@@ -257,11 +257,11 @@ new honeycomb.Trigger("IncreasedProviderHttpErrors", {
],
})
new honeycomb.Trigger("IncreasedFreeTierRequests", {
new honeycombio.Trigger("IncreasedFreeTierRequests", {
name: "Increased Free Tier Requests",
description,
disabled: alertsDisabled,
queryJson: honeycomb.getQuerySpecificationOutput({
queryJson: honeycombio.getQuerySpecificationOutput({
calculations: [{ op: "COUNT" }],
filters: [
{ column: "event_type", op: "=", value: "completions" },
+4 -4
View File
@@ -1,8 +1,8 @@
{
"nodeModules": {
"x86_64-linux": "sha256-hIarzU3QNIvkwpfnearfsGaBMCSdovkOWAuvX+EBQI8=",
"aarch64-linux": "sha256-qu+86RNsBZhqzE3CEyg4XHD/3Czc/BqHhY3O1vyogtY=",
"aarch64-darwin": "sha256-7sXZdPh/R4fQBBMq36YdpQE6G2ELMZd08BqMhHbhwv4=",
"x86_64-darwin": "sha256-g5QWpX33R143sNR1G9m7UsSA3jygFyJjT7Kjyjo6sCc="
"x86_64-linux": "sha256-1RiaZQHzIhdtcOJUMsLagpP+nBBL/Qu6zQgrAXMHDCI=",
"aarch64-linux": "sha256-5QZhtkWuNpY/qUxlKRHcGbILOAVnuyzu+h8VDIuMQcU=",
"aarch64-darwin": "sha256-9w3QA22XNc1itGLyhikYU90xGH/iLUUM+SSGXla7lhw=",
"x86_64-darwin": "sha256-cSYiyhhSqIYiTeK1uWHDkHbYstQYD5jEB7JaYjWjgi4="
}
}
+1 -1
View File
@@ -98,7 +98,7 @@
"oxlint-tsgolint": "0.21.0",
"prettier": "3.6.2",
"semver": "^7.6.0",
"sst": "3.18.10",
"sst": "4.13.1",
"turbo": "2.8.13"
},
"dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/app",
"version": "1.15.6",
"version": "1.15.7",
"description": "",
"type": "module",
"exports": {
@@ -193,6 +193,12 @@ export const SettingsGeneral: Component = () => {
{ initialValue: null as DisplayBackend | null },
)
const [pinchZoom, { mutate: setPinchZoom }] = createResource(
() => (desktop() && platform.getPinchZoomEnabled ? true : false),
() => Promise.resolve(platform.getPinchZoomEnabled?.() ?? false).catch(() => false),
{ initialValue: false },
)
onMount(() => {
void theme.loadThemes()
})
@@ -239,6 +245,13 @@ export const SettingsGeneral: Component = () => {
})
}
const onPinchZoomChange = (checked: boolean) => {
setPinchZoom(checked)
const update = platform.setPinchZoomEnabled?.(checked)
if (!update) return
void update.catch(() => setPinchZoom(!checked))
}
const colorSchemeOptions = createMemo((): { value: ColorScheme; label: string }[] => [
{ value: "system", label: language.t("theme.scheme.system") },
{ value: "light", label: language.t("theme.scheme.light") },
@@ -729,6 +742,45 @@ export const SettingsGeneral: Component = () => {
</div>
)
const DisplaySection = () => (
<Show when={desktop()}>
<div class="flex flex-col gap-1">
<h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.display")}</h3>
<SettingsList>
<SettingsRow
title={language.t("settings.general.row.pinchZoom.title")}
description={language.t("settings.general.row.pinchZoom.description")}
>
<div data-action="settings-pinch-zoom">
<Switch checked={pinchZoom.latest} onChange={onPinchZoomChange} />
</div>
</SettingsRow>
<Show when={linux()}>
<SettingsRow
title={
<div class="flex items-center gap-2">
<span>{language.t("settings.general.row.wayland.title")}</span>
<Tooltip value={language.t("settings.general.row.wayland.tooltip")} placement="top">
<span class="text-text-weak">
<Icon name="help" size="small" />
</span>
</Tooltip>
</div>
}
description={language.t("settings.general.row.wayland.description")}
>
<div data-action="settings-wayland">
<Switch checked={displayBackend.latest === "wayland"} onChange={onDisplayBackendChange} />
</div>
</SettingsRow>
</Show>
</SettingsList>
</div>
</Show>
)
console.log(import.meta.env)
return (
<div class="flex flex-col h-full overflow-y-auto no-scrollbar px-4 pb-10 sm:px-10 sm:pb-10">
@@ -749,31 +801,7 @@ export const SettingsGeneral: Component = () => {
<UpdatesSection />
<Show when={linux()}>
<div class="flex flex-col gap-1">
<h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.display")}</h3>
<SettingsList>
<SettingsRow
title={
<div class="flex items-center gap-2">
<span>{language.t("settings.general.row.wayland.title")}</span>
<Tooltip value={language.t("settings.general.row.wayland.tooltip")} placement="top">
<span class="text-text-weak">
<Icon name="help" size="small" />
</span>
</Tooltip>
</div>
}
description={language.t("settings.general.row.wayland.description")}
>
<div data-action="settings-wayland">
<Switch checked={displayBackend.latest === "wayland"} onChange={onDisplayBackendChange} />
</div>
</SettingsRow>
</SettingsList>
</div>
</Show>
<DisplaySection />
<Show when={desktop() && import.meta.env.VITE_OPENCODE_CHANNEL === "beta"}>
<AdvancedSection />
+33 -4
View File
@@ -22,6 +22,7 @@ import { iife } from "@opencode-ai/core/util/iife"
import { base64Encode } from "@opencode-ai/core/util/encode"
import { Avatar as AvatarV2 } from "@opencode-ai/ui/v2/components/avatar-v2.jsx"
import { displayName, getProjectAvatarSource, projectForSession } from "@/pages/layout/helpers"
import { makeEventListener } from "@solid-primitives/event-listener"
type TauriDesktopWindow = {
startDragging?: () => Promise<void>
@@ -298,6 +299,34 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
() => new Map(projects().flatMap((project) => (project.id ? [[project.id, project] as const] : []))),
)
const currentSessionTab = () => {
if (!params.dir || !params.id) return
const href = makeSessionHref(params.dir, params.id)
if (!tabsStore.some((tab) => tab.href === href)) return
return href
}
const closeCurrentSessionTab = () => {
const href = currentSessionTab()
if (!href) return false
tabsStoreActions.removeTab(href)
return true
}
makeEventListener(
document,
"keydown",
(event) => {
if (!event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return
if (event.key.toLowerCase() !== "w") return
if (!closeCurrentSessionTab()) return
event.preventDefault()
event.stopPropagation()
},
{ capture: true },
)
const tabsEnriched = iife(() => {
const base = mapArray(
() => tabsStore,
@@ -578,15 +607,15 @@ function TabNavItem(props: {
const isActive = () => !!match()
return (
<div
class="group relative flex h-7 min-w-24 max-w-60 flex-row items-center gap-1.5 overflow-hidden whitespace-nowrap rounded-[6px] bg-[var(--tab-bg)] pl-1.5 pr-8 [--tab-bg:var(--v2-background-bg-deep)] hover:[--tab-bg:var(--v2-background-bg-layer-02)] data-[active='true']:[--tab-bg:var(--v2-background-bg-layer-02)]"
class="group relative flex h-7 min-w-24 max-w-60 flex-row items-center gap-1.5 overflow-hidden whitespace-nowrap rounded-[6px] bg-[var(--tab-bg)] pl-1.5 [--tab-bg:var(--v2-background-bg-deep)] hover:[--tab-bg:var(--v2-background-bg-layer-02)] data-[active='true']:[--tab-bg:var(--v2-background-bg-layer-02)]"
data-active={isActive()}
>
<a
href={props.href}
class="flex h-full min-w-0 flex-1 flex-row items-center gap-1.5 overflow-hidden text-[13px] font-medium leading-none tracking-[-0.04px] text-v2-text-text-faint group-data-[active='true']:text-v2-text-text-base"
class="flex h-full min-w-0 flex-1 flex-row items-center gap-1.5 overflow-hidden text-[13px] font-medium text-v2-text-text-faint group-data-[active='true']:text-v2-text-text-base"
>
<ProjectTabAvatar project={props.project} directory={props.directory} />
<span class="truncate">{props.title}</span>
<span class="text-clip">{props.title}</span>
</a>
<div class="absolute right-0 inset-y-0 flex flex-row items-center pr-1 py-1 w-8 pl-2">
@@ -624,7 +653,7 @@ function ProjectTabAvatar(props: { project?: LocalProject; directory: string })
function NewSessionTabItem(props: { href: string; title: string; onClose: () => void }) {
return (
<div class="group relative flex h-7 w-[135px] min-w-24 max-w-60 flex-row items-center gap-1.5 overflow-hidden rounded-[6px] bg-[var(--v2-overlay-simple-overlay-pressed)] pl-1.5 pr-8 whitespace-nowrap focus-within:outline focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-[var(--v2-border-border-focus)]">
<div class="group relative flex h-7 max-w-60 flex-row items-center gap-1.5 overflow-hidden rounded-[6px] bg-[var(--v2-overlay-simple-overlay-pressed)] pl-1.5 pr-8 whitespace-nowrap focus-within:outline focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-[var(--v2-border-border-focus)]">
<a
href={props.href}
aria-current="page"
+6
View File
@@ -93,6 +93,12 @@ export type Platform = {
/** Webview zoom level (desktop only) */
webviewZoom?: Accessor<number>
/** Get whether native pinch/Ctrl-scroll zoom gestures are enabled (desktop only) */
getPinchZoomEnabled?(): Promise<boolean> | boolean
/** Allow native pinch/Ctrl-scroll zoom gestures (desktop only) */
setPinchZoomEnabled?(enabled: boolean): Promise<void> | void
/** Run a desktop-only menu action from the app chrome */
runDesktopMenuAction?(action: DesktopMenuAction): Promise<void> | void
+2
View File
@@ -784,6 +784,8 @@ export const dict = {
"settings.general.row.showSessionProgressBar.title": "Show session progress bar",
"settings.general.row.showSessionProgressBar.description":
"Display the animated progress bar at the top of the session when the agent is working",
"settings.general.row.pinchZoom.title": "Pinch to zoom",
"settings.general.row.pinchZoom.description": "Allow trackpad pinch and Ctrl-scroll gestures to zoom",
"settings.general.row.wayland.title": "Use native Wayland",
"settings.general.row.wayland.description": "Disable X11 fallback on Wayland. Requires restart.",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-app",
"version": "1.15.6",
"version": "1.15.7",
"type": "module",
"license": "MIT",
"scripts": {
+2 -2
View File
@@ -348,8 +348,8 @@ export const dict = {
"Free models include Big Pickle plus promotional models available at the time, with a quota of 200 requests/day. Go includes GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro, and DeepSeek V4 Flash with higher request quotas enforced across rolling windows (5-hour, weekly, and monthly), roughly equivalent to $12 per 5 hours, $30 per week, and $60 per month (actual request counts vary by model and usage).",
"zen.api.error.rateLimitExceeded": "Rate limit exceeded. Please try again later.",
"zen.api.error.modelNotSupported": "Model {{model}} not supported",
"zen.api.error.modelFormatNotSupported": "Model {{model}} not supported for format {{format}}",
"zen.api.error.modelNotSupported": "Model {{model}} is not supported",
"zen.api.error.modelFormatNotSupported": "Model {{model}} is not supported for format {{format}}",
"zen.api.error.noProviderAvailable": "No provider available",
"zen.api.error.providerNotSupported": "Provider {{provider}} not supported",
"zen.api.error.missingApiKey": "Missing API key.",
@@ -0,0 +1,19 @@
import { createHash } from "crypto"
import { ZenData } from "@opencode-ai/console-core/model.js"
export async function GET() {
const zenData = ZenData.list("full")
return new Response(
JSON.stringify({
hash: createHash("sha1").update(JSON.stringify(zenData)).digest("hex"),
timestamp: Date.now(),
check1: "alpha-di-k2.6" in zenData.models,
check2: "qwen3.6-plus-free" in zenData.models,
}),
{
headers: {
"Content-Type": "application/json",
},
},
)
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/console-core",
"version": "1.15.6",
"version": "1.15.7",
"private": true,
"type": "module",
"license": "MIT",
+1 -298
View File
@@ -4,304 +4,7 @@
/* deno-fmt-ignore-file */
/* biome-ignore-all lint: auto-generated */
import "sst"
declare module "sst" {
export interface Resource {
"ADMIN_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"AUTH_API_URL": {
"type": "sst.sst.Linkable"
"value": string
}
"AWS_SES_ACCESS_KEY_ID": {
"type": "sst.sst.Secret"
"value": string
}
"AWS_SES_SECRET_ACCESS_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"CLOUDFLARE_API_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
"CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"Console": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
"DISCORD_INCIDENT_WEBHOOK_URL": {
"type": "sst.sst.Secret"
"value": string
}
"DISCORD_SUPPORT_BOT_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
"DISCORD_SUPPORT_CHANNEL_ID": {
"type": "sst.sst.Secret"
"value": string
}
"Database": {
"database": string
"host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
}
"EMAILOCTOPUS_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"FEISHU_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"FEISHU_APP_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_APP_PRIVATE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_CLIENT_ID_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_CLIENT_SECRET_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
"GOOGLE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"HONEYCOMB_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"HoneycombWebhookSecret": {
"type": "random.index/randomPassword.RandomPassword"
"value": string
}
"R2AccessKey": {
"type": "sst.sst.Secret"
"value": string
}
"R2SecretKey": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_CLIENT_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_INSTANCE_URL": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_PUBLISHABLE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
}
"Teams": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
}
"WebApp": {
"type": "sst.cloudflare.StaticSite"
"url": string
}
"ZEN_BLACK_PRICE": {
"plan100": string
"plan20": string
"plan200": string
"product": string
"type": "sst.sst.Linkable"
}
"ZEN_LIMITS": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_LITE_PRICE": {
"firstMonth100Coupon": string
"firstMonth50Coupon": string
"price": string
"priceInr": number
"product": string
"sixMonths100Coupon": string
"threeMonths100Coupon": string
"twelveMonths100Coupon": string
"type": "sst.sst.Linkable"
}
"ZEN_MODELS1": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS10": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS11": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS12": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS13": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS14": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS15": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS16": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS17": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS18": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS19": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS2": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS20": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS21": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS22": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS23": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS24": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS25": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS26": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS27": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS28": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS29": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS3": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS30": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS4": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS5": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS6": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS7": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS8": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS9": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_SESSION_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
}
}
// cloudflare
import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" {
export interface Resource {
"Api": cloudflare.Service
"AuthApi": cloudflare.Service
"AuthStorage": cloudflare.KVNamespace
"Bucket": cloudflare.R2Bucket
"EnterpriseStorage": cloudflare.R2Bucket
"LogProcessor": cloudflare.Service
"Stat": cloudflare.Service
"ZenData": cloudflare.R2Bucket
"ZenDataNew": cloudflare.R2Bucket
}
}
/// <reference path="../../../sst-env.d.ts" />
import "sst"
export {}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-function",
"version": "1.15.6",
"version": "1.15.7",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",
+1 -298
View File
@@ -4,304 +4,7 @@
/* deno-fmt-ignore-file */
/* biome-ignore-all lint: auto-generated */
import "sst"
declare module "sst" {
export interface Resource {
"ADMIN_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"AUTH_API_URL": {
"type": "sst.sst.Linkable"
"value": string
}
"AWS_SES_ACCESS_KEY_ID": {
"type": "sst.sst.Secret"
"value": string
}
"AWS_SES_SECRET_ACCESS_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"CLOUDFLARE_API_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
"CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"Console": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
"DISCORD_INCIDENT_WEBHOOK_URL": {
"type": "sst.sst.Secret"
"value": string
}
"DISCORD_SUPPORT_BOT_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
"DISCORD_SUPPORT_CHANNEL_ID": {
"type": "sst.sst.Secret"
"value": string
}
"Database": {
"database": string
"host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
}
"EMAILOCTOPUS_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"FEISHU_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"FEISHU_APP_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_APP_PRIVATE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_CLIENT_ID_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_CLIENT_SECRET_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
"GOOGLE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"HONEYCOMB_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"HoneycombWebhookSecret": {
"type": "random.index/randomPassword.RandomPassword"
"value": string
}
"R2AccessKey": {
"type": "sst.sst.Secret"
"value": string
}
"R2SecretKey": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_CLIENT_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_INSTANCE_URL": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_PUBLISHABLE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
}
"Teams": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
}
"WebApp": {
"type": "sst.cloudflare.StaticSite"
"url": string
}
"ZEN_BLACK_PRICE": {
"plan100": string
"plan20": string
"plan200": string
"product": string
"type": "sst.sst.Linkable"
}
"ZEN_LIMITS": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_LITE_PRICE": {
"firstMonth100Coupon": string
"firstMonth50Coupon": string
"price": string
"priceInr": number
"product": string
"sixMonths100Coupon": string
"threeMonths100Coupon": string
"twelveMonths100Coupon": string
"type": "sst.sst.Linkable"
}
"ZEN_MODELS1": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS10": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS11": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS12": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS13": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS14": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS15": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS16": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS17": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS18": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS19": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS2": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS20": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS21": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS22": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS23": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS24": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS25": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS26": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS27": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS28": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS29": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS3": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS30": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS4": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS5": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS6": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS7": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS8": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS9": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_SESSION_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
}
}
// cloudflare
import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" {
export interface Resource {
"Api": cloudflare.Service
"AuthApi": cloudflare.Service
"AuthStorage": cloudflare.KVNamespace
"Bucket": cloudflare.R2Bucket
"EnterpriseStorage": cloudflare.R2Bucket
"LogProcessor": cloudflare.Service
"Stat": cloudflare.Service
"ZenData": cloudflare.R2Bucket
"ZenDataNew": cloudflare.R2Bucket
}
}
/// <reference path="../../../sst-env.d.ts" />
import "sst"
export {}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-mail",
"version": "1.15.6",
"version": "1.15.7",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",
@@ -5,9 +5,12 @@ export const Resource = new Proxy(
{},
{
get(_target, prop: string) {
if (prop in env) {
console.log(`111 ${prop}`)
if (`SST_RESOURCE_${prop}` in env) {
console.log(`222 ${prop}`)
// @ts-expect-error
const value = env[prop]
const value = env[`SST_RESOURCE_${prop}`]
console.log(`333 ${value}`)
return typeof value === "string" ? JSON.parse(value) : value
} else if (prop === "App") {
// @ts-expect-error
+1 -298
View File
@@ -4,304 +4,7 @@
/* deno-fmt-ignore-file */
/* biome-ignore-all lint: auto-generated */
import "sst"
declare module "sst" {
export interface Resource {
"ADMIN_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"AUTH_API_URL": {
"type": "sst.sst.Linkable"
"value": string
}
"AWS_SES_ACCESS_KEY_ID": {
"type": "sst.sst.Secret"
"value": string
}
"AWS_SES_SECRET_ACCESS_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"CLOUDFLARE_API_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
"CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"Console": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
"DISCORD_INCIDENT_WEBHOOK_URL": {
"type": "sst.sst.Secret"
"value": string
}
"DISCORD_SUPPORT_BOT_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
"DISCORD_SUPPORT_CHANNEL_ID": {
"type": "sst.sst.Secret"
"value": string
}
"Database": {
"database": string
"host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
}
"EMAILOCTOPUS_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"FEISHU_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"FEISHU_APP_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_APP_PRIVATE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_CLIENT_ID_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_CLIENT_SECRET_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
"GOOGLE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"HONEYCOMB_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"HoneycombWebhookSecret": {
"type": "random.index/randomPassword.RandomPassword"
"value": string
}
"R2AccessKey": {
"type": "sst.sst.Secret"
"value": string
}
"R2SecretKey": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_CLIENT_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_INSTANCE_URL": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_PUBLISHABLE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
}
"Teams": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
}
"WebApp": {
"type": "sst.cloudflare.StaticSite"
"url": string
}
"ZEN_BLACK_PRICE": {
"plan100": string
"plan20": string
"plan200": string
"product": string
"type": "sst.sst.Linkable"
}
"ZEN_LIMITS": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_LITE_PRICE": {
"firstMonth100Coupon": string
"firstMonth50Coupon": string
"price": string
"priceInr": number
"product": string
"sixMonths100Coupon": string
"threeMonths100Coupon": string
"twelveMonths100Coupon": string
"type": "sst.sst.Linkable"
}
"ZEN_MODELS1": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS10": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS11": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS12": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS13": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS14": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS15": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS16": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS17": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS18": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS19": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS2": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS20": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS21": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS22": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS23": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS24": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS25": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS26": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS27": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS28": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS29": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS3": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS30": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS4": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS5": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS6": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS7": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS8": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS9": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_SESSION_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
}
}
// cloudflare
import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" {
export interface Resource {
"Api": cloudflare.Service
"AuthApi": cloudflare.Service
"AuthStorage": cloudflare.KVNamespace
"Bucket": cloudflare.R2Bucket
"EnterpriseStorage": cloudflare.R2Bucket
"LogProcessor": cloudflare.Service
"Stat": cloudflare.Service
"ZenData": cloudflare.R2Bucket
"ZenDataNew": cloudflare.R2Bucket
}
}
/// <reference path="../../../sst-env.d.ts" />
import "sst"
export {}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.15.6",
"version": "1.15.7",
"name": "@opencode-ai/core",
"type": "module",
"license": "MIT",
@@ -59,7 +59,7 @@
"ai-gateway-provider": "3.1.2",
"cross-spawn": "catalog:",
"effect": "catalog:",
"gitlab-ai-provider": "6.6.0",
"gitlab-ai-provider": "6.7.0",
"glob": "13.0.5",
"google-auth-library": "10.5.0",
"immer": "11.1.4",
+1
View File
@@ -65,5 +65,6 @@ export abstract class NamedError extends Error {
public static readonly Unknown = NamedError.create("UnknownError", {
message: Schema.String,
ref: Schema.optional(Schema.String),
})
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@opencode-ai/desktop",
"private": true,
"version": "1.15.6",
"version": "1.15.7",
"type": "module",
"license": "MIT",
"homepage": "https://opencode.ai",
+1
View File
@@ -7,4 +7,5 @@ export const CHANNEL: Channel = raw === "dev" || raw === "beta" || raw === "prod
export const SETTINGS_STORE = "opencode.settings"
export const DEFAULT_SERVER_URL_KEY = "defaultServerUrl"
export const WSL_ENABLED_KEY = "wslEnabled"
export const PINCH_ZOOM_ENABLED_KEY = "pinchZoomEnabled"
export const UPDATER_ENABLED = app.isPackaged && CHANNEL !== "dev"
+5 -1
View File
@@ -14,7 +14,7 @@ import type {
} from "../preload/types"
import { runDesktopMenuAction } from "./desktop-menu-actions"
import { getStore } from "./store"
import { setTitlebar, updateTitlebar } from "./windows"
import { getPinchZoomEnabled, setPinchZoomEnabled, setTitlebar, updateTitlebar } from "./windows"
const pickerFilters = (ext?: string[]) => {
if (!ext || ext.length === 0) return undefined
@@ -202,6 +202,10 @@ export function registerIpcHandlers(deps: Deps) {
if (!win) return
updateTitlebar(win)
})
ipcMain.handle("get-pinch-zoom-enabled", () => getPinchZoomEnabled())
ipcMain.handle("set-pinch-zoom-enabled", (_event: IpcMainInvokeEvent, enabled: boolean) => {
setPinchZoomEnabled(enabled)
})
ipcMain.handle("set-titlebar", (event: IpcMainInvokeEvent, theme: TitlebarTheme) => {
const win = BrowserWindow.fromWebContents(event.sender)
if (!win) return
+38 -3
View File
@@ -3,7 +3,9 @@ import { app, BrowserWindow, dialog, net, nativeImage, nativeTheme, protocol } f
import { dirname, isAbsolute, join, relative, resolve } from "node:path"
import { fileURLToPath, pathToFileURL } from "node:url"
import type { TitlebarTheme } from "../preload/types"
import { PINCH_ZOOM_ENABLED_KEY } from "./constants"
import { exportDebugLogs, write as writeLog } from "./logging"
import { getStore } from "./store"
import { createUnresponsiveSampler } from "./unresponsive"
const root = dirname(fileURLToPath(import.meta.url))
@@ -33,7 +35,10 @@ let relaunchHandler = () => {
app.exit(0)
}
const titlebarThemes = new WeakMap<BrowserWindow, Partial<TitlebarTheme>>()
const pinchZoomEnabled = new WeakMap<BrowserWindow, boolean>()
const titlebarHeight = 40
const maxZoomLevel = 10
const minZoomLevel = 0.2
export function setRelaunchHandler(handler: () => void) {
relaunchHandler = handler
@@ -79,6 +84,20 @@ export function updateTitlebar(win: BrowserWindow) {
win.setTitleBarOverlay(overlay(titlebarThemes.get(win), win.webContents.getZoomFactor()))
}
export function setPinchZoomEnabled(enabled: boolean) {
getStore().set(PINCH_ZOOM_ENABLED_KEY, enabled)
for (const win of BrowserWindow.getAllWindows()) {
pinchZoomEnabled.set(win, enabled)
win.webContents.send("pinch-zoom-enabled-changed", enabled)
if (!enabled && win.webContents.getZoomFactor() !== 1) win.webContents.setZoomFactor(1)
updateZoom(win)
}
}
export function getPinchZoomEnabled() {
return getStore().get(PINCH_ZOOM_ENABLED_KEY) === true
}
export function setDockIcon() {
if (process.platform !== "darwin") return
const icon = nativeImage.createFromPath(join(iconsDir(), "dock.png"))
@@ -392,13 +411,29 @@ function isRendererUrl(value?: string, html = false) {
}
function wireZoom(win: BrowserWindow) {
pinchZoomEnabled.set(win, getPinchZoomEnabled())
win.webContents.setZoomFactor(1)
win.webContents.on("zoom-changed", () => {
win.webContents.setZoomFactor(1)
updateTitlebar(win)
win.webContents.on("zoom-changed", (event, zoomDirection) => {
event.preventDefault()
if (pinchZoomEnabled.get(win)) {
win.webContents.setZoomFactor(clampZoom(win.webContents.getZoomFactor() + (zoomDirection === "in" ? 0.2 : -0.2)))
updateZoom(win)
return
}
if (win.webContents.getZoomFactor() !== 1) win.webContents.setZoomFactor(1)
updateZoom(win)
})
}
function clampZoom(value: number) {
return Math.min(Math.max(value, minZoomLevel), maxZoomLevel)
}
function updateZoom(win: BrowserWindow) {
updateTitlebar(win)
win.webContents.send("zoom-factor-changed", win.webContents.getZoomFactor())
}
function upsertKeyValue(obj: Record<string, any>, keyToChange: string, value: any) {
const keyToChangeLower = keyToChange.toLowerCase()
for (const key of Object.keys(obj)) {
+12
View File
@@ -60,6 +60,18 @@ const api: ElectronAPI = {
relaunch: () => ipcRenderer.send("relaunch"),
getZoomFactor: () => ipcRenderer.invoke("get-zoom-factor"),
setZoomFactor: (factor) => ipcRenderer.invoke("set-zoom-factor", factor),
getPinchZoomEnabled: () => ipcRenderer.invoke("get-pinch-zoom-enabled"),
setPinchZoomEnabled: (enabled) => ipcRenderer.invoke("set-pinch-zoom-enabled", enabled),
onPinchZoomEnabledChanged: (cb) => {
const handler = (_: unknown, enabled: boolean) => cb(enabled)
ipcRenderer.on("pinch-zoom-enabled-changed", handler)
return () => ipcRenderer.removeListener("pinch-zoom-enabled-changed", handler)
},
onZoomFactorChanged: (cb) => {
const handler = (_: unknown, factor: number) => cb(factor)
ipcRenderer.on("zoom-factor-changed", handler)
return () => ipcRenderer.removeListener("zoom-factor-changed", handler)
},
setTitlebar: (theme) => ipcRenderer.invoke("set-titlebar", theme),
runDesktopMenuAction: (action) => ipcRenderer.invoke("run-desktop-menu-action", action),
loadingWindowComplete: () => ipcRenderer.send("loading-window-complete"),
+4
View File
@@ -79,6 +79,10 @@ export type ElectronAPI = {
relaunch: () => void
getZoomFactor: () => Promise<number>
setZoomFactor: (factor: number) => Promise<void>
getPinchZoomEnabled: () => Promise<boolean>
setPinchZoomEnabled: (enabled: boolean) => Promise<void>
onPinchZoomEnabledChanged: (cb: (enabled: boolean) => void) => () => void
onZoomFactorChanged: (cb: (factor: number) => void) => () => void
setTitlebar: (theme: TitlebarTheme) => Promise<void>
runDesktopMenuAction: (action: DesktopMenuAction) => Promise<void>
loadingWindowComplete: () => void
+5 -1
View File
@@ -21,7 +21,7 @@ import { createEffect, createResource, onCleanup, onMount, Show } from "solid-js
import { render } from "solid-js/web"
import pkg from "../../package.json"
import { initI18n, t } from "./i18n"
import { resetZoom, webviewZoom, zoomIn, zoomOut } from "./webview-zoom"
import { resetZoom, setPinchZoomEnabled, webviewZoom, zoomIn, zoomOut } from "./webview-zoom"
import "./styles.css"
import { useTheme } from "@opencode-ai/ui/theme"
@@ -274,6 +274,10 @@ const createPlatform = (): Platform => {
webviewZoom,
getPinchZoomEnabled: () => window.api.getPinchZoomEnabled(),
setPinchZoomEnabled,
runDesktopMenuAction,
checkAppExists: async (appName: string) => {
+80 -1
View File
@@ -13,9 +13,21 @@ const OS_NAME = (() => {
const [webviewZoom, setWebviewZoom] = createSignal(1)
let requestedZoom = 1
let pinchZoomEnabled = false
let wheelPinch = undefined as
| {
active: boolean
startZoom: number
totalDelta: number
timeout: ReturnType<typeof setTimeout> | undefined
}
| undefined
const MAX_ZOOM_LEVEL = 10
const MIN_ZOOM_LEVEL = 0.2
const WHEEL_PINCH_THRESHOLD = 20
const WHEEL_PINCH_STEP = 0.2
const WHEEL_PINCH_END_DELAY = 160
const clamp = (value: number) => Math.min(Math.max(value, MIN_ZOOM_LEVEL), MAX_ZOOM_LEVEL)
@@ -33,10 +45,77 @@ const applyZoom = (next: number) => {
})
}
window.api.onZoomFactorChanged((factor) => {
requestedZoom = clamp(factor)
setWebviewZoom(requestedZoom)
})
void window.api.getPinchZoomEnabled().then((enabled) => {
pinchZoomEnabled = enabled
})
window.api.onPinchZoomEnabledChanged((enabled) => {
pinchZoomEnabled = enabled
resetWheelPinch()
})
const setPinchZoomEnabled = (enabled: boolean) => {
pinchZoomEnabled = enabled
resetWheelPinch()
return window.api.setPinchZoomEnabled(enabled)
}
const resetZoom = () => applyZoom(1)
const zoomIn = () => applyZoom(clamp(requestedZoom + 0.2))
const zoomOut = () => applyZoom(clamp(requestedZoom - 0.2))
const resetWheelPinch = () => {
clearTimeout(wheelPinch?.timeout)
wheelPinch = undefined
}
const normalizeWheelDelta = (event: WheelEvent) => {
if (event.deltaMode === WheelEvent.DOM_DELTA_LINE) return event.deltaY * 16
if (event.deltaMode === WheelEvent.DOM_DELTA_PAGE) return event.deltaY * window.innerHeight
return event.deltaY
}
const updateWheelPinch = (event: WheelEvent) => {
wheelPinch ??= {
active: false,
startZoom: requestedZoom,
totalDelta: 0,
timeout: undefined,
}
clearTimeout(wheelPinch.timeout)
wheelPinch.timeout = setTimeout(resetWheelPinch, WHEEL_PINCH_END_DELAY)
wheelPinch.totalDelta += normalizeWheelDelta(event)
if (!wheelPinch.active && Math.abs(wheelPinch.totalDelta) < WHEEL_PINCH_THRESHOLD) return
if (!wheelPinch.active) {
wheelPinch.active = true
wheelPinch.startZoom = requestedZoom
wheelPinch.totalDelta = 0
return
}
wheelPinch.active = true
applyZoom(clamp(wheelPinch.startZoom - (wheelPinch.totalDelta / WHEEL_PINCH_THRESHOLD) * WHEEL_PINCH_STEP))
}
window.addEventListener(
"wheel",
(event) => {
if (!pinchZoomEnabled) return
if (!event.ctrlKey) return
event.preventDefault()
updateWheelPinch(event)
},
{ passive: false },
)
window.addEventListener("keydown", (event) => {
if (!(OS_NAME === "macos" ? event.metaKey : event.ctrlKey)) return
@@ -56,4 +135,4 @@ window.addEventListener("keydown", (event) => {
}
})
export { webviewZoom, resetZoom, zoomIn, zoomOut }
export { webviewZoom, resetZoom, setPinchZoomEnabled, zoomIn, zoomOut }
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.15.5",
"version": "1.15.7",
"name": "@opencode-ai/effect-drizzle-sqlite",
"type": "module",
"license": "MIT",
+10
View File
@@ -0,0 +1,10 @@
/* This file is auto-generated by SST. Do not edit. */
/* tslint:disable */
/* eslint-disable */
/* deno-fmt-ignore-file */
/* biome-ignore-all lint: auto-generated */
/// <reference path="../../sst-env.d.ts" />
import "sst"
export {}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/enterprise",
"version": "1.15.6",
"version": "1.15.7",
"private": true,
"type": "module",
"license": "MIT",
+1 -298
View File
@@ -4,304 +4,7 @@
/* deno-fmt-ignore-file */
/* biome-ignore-all lint: auto-generated */
import "sst"
declare module "sst" {
export interface Resource {
"ADMIN_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"AUTH_API_URL": {
"type": "sst.sst.Linkable"
"value": string
}
"AWS_SES_ACCESS_KEY_ID": {
"type": "sst.sst.Secret"
"value": string
}
"AWS_SES_SECRET_ACCESS_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"CLOUDFLARE_API_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
"CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"Console": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
"DISCORD_INCIDENT_WEBHOOK_URL": {
"type": "sst.sst.Secret"
"value": string
}
"DISCORD_SUPPORT_BOT_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
"DISCORD_SUPPORT_CHANNEL_ID": {
"type": "sst.sst.Secret"
"value": string
}
"Database": {
"database": string
"host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
}
"EMAILOCTOPUS_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"FEISHU_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"FEISHU_APP_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_APP_PRIVATE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_CLIENT_ID_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_CLIENT_SECRET_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
"GOOGLE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"HONEYCOMB_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"HoneycombWebhookSecret": {
"type": "random.index/randomPassword.RandomPassword"
"value": string
}
"R2AccessKey": {
"type": "sst.sst.Secret"
"value": string
}
"R2SecretKey": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_CLIENT_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_INSTANCE_URL": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_PUBLISHABLE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
}
"Teams": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
}
"WebApp": {
"type": "sst.cloudflare.StaticSite"
"url": string
}
"ZEN_BLACK_PRICE": {
"plan100": string
"plan20": string
"plan200": string
"product": string
"type": "sst.sst.Linkable"
}
"ZEN_LIMITS": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_LITE_PRICE": {
"firstMonth100Coupon": string
"firstMonth50Coupon": string
"price": string
"priceInr": number
"product": string
"sixMonths100Coupon": string
"threeMonths100Coupon": string
"twelveMonths100Coupon": string
"type": "sst.sst.Linkable"
}
"ZEN_MODELS1": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS10": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS11": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS12": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS13": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS14": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS15": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS16": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS17": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS18": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS19": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS2": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS20": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS21": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS22": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS23": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS24": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS25": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS26": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS27": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS28": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS29": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS3": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS30": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS4": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS5": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS6": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS7": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS8": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS9": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_SESSION_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
}
}
// cloudflare
import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" {
export interface Resource {
"Api": cloudflare.Service
"AuthApi": cloudflare.Service
"AuthStorage": cloudflare.KVNamespace
"Bucket": cloudflare.R2Bucket
"EnterpriseStorage": cloudflare.R2Bucket
"LogProcessor": cloudflare.Service
"Stat": cloudflare.Service
"ZenData": cloudflare.R2Bucket
"ZenDataNew": cloudflare.R2Bucket
}
}
/// <reference path="../../sst-env.d.ts" />
import "sst"
export {}
+6 -6
View File
@@ -1,7 +1,7 @@
id = "opencode"
name = "OpenCode"
description = "The open source coding agent."
version = "1.15.6"
version = "1.15.7"
schema_version = 1
authors = ["Anomaly"]
repository = "https://github.com/anomalyco/opencode"
@@ -11,26 +11,26 @@ name = "OpenCode"
icon = "./icons/opencode.svg"
[agent_servers.opencode.targets.darwin-aarch64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.15.6/opencode-darwin-arm64.zip"
archive = "https://github.com/anomalyco/opencode/releases/download/v1.15.7/opencode-darwin-arm64.zip"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.darwin-x86_64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.15.6/opencode-darwin-x64.zip"
archive = "https://github.com/anomalyco/opencode/releases/download/v1.15.7/opencode-darwin-x64.zip"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.linux-aarch64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.15.6/opencode-linux-arm64.tar.gz"
archive = "https://github.com/anomalyco/opencode/releases/download/v1.15.7/opencode-linux-arm64.tar.gz"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.linux-x86_64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.15.6/opencode-linux-x64.tar.gz"
archive = "https://github.com/anomalyco/opencode/releases/download/v1.15.7/opencode-linux-x64.tar.gz"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.windows-x86_64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.15.6/opencode-windows-x64.zip"
archive = "https://github.com/anomalyco/opencode/releases/download/v1.15.7/opencode-windows-x64.zip"
cmd = "./opencode.exe"
args = ["acp"]
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/function",
"version": "1.15.6",
"version": "1.15.7",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",
+1 -298
View File
@@ -4,304 +4,7 @@
/* deno-fmt-ignore-file */
/* biome-ignore-all lint: auto-generated */
import "sst"
declare module "sst" {
export interface Resource {
"ADMIN_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"AUTH_API_URL": {
"type": "sst.sst.Linkable"
"value": string
}
"AWS_SES_ACCESS_KEY_ID": {
"type": "sst.sst.Secret"
"value": string
}
"AWS_SES_SECRET_ACCESS_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"CLOUDFLARE_API_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
"CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"Console": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
"DISCORD_INCIDENT_WEBHOOK_URL": {
"type": "sst.sst.Secret"
"value": string
}
"DISCORD_SUPPORT_BOT_TOKEN": {
"type": "sst.sst.Secret"
"value": string
}
"DISCORD_SUPPORT_CHANNEL_ID": {
"type": "sst.sst.Secret"
"value": string
}
"Database": {
"database": string
"host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
}
"EMAILOCTOPUS_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"FEISHU_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"FEISHU_APP_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_APP_PRIVATE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_CLIENT_ID_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_CLIENT_SECRET_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
"GOOGLE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"HONEYCOMB_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"HoneycombWebhookSecret": {
"type": "random.index/randomPassword.RandomPassword"
"value": string
}
"R2AccessKey": {
"type": "sst.sst.Secret"
"value": string
}
"R2SecretKey": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_CLIENT_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
"SALESFORCE_INSTANCE_URL": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_PUBLISHABLE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
}
"Teams": {
"type": "sst.cloudflare.SolidStart"
"url": string
}
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
}
"WebApp": {
"type": "sst.cloudflare.StaticSite"
"url": string
}
"ZEN_BLACK_PRICE": {
"plan100": string
"plan20": string
"plan200": string
"product": string
"type": "sst.sst.Linkable"
}
"ZEN_LIMITS": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_LITE_PRICE": {
"firstMonth100Coupon": string
"firstMonth50Coupon": string
"price": string
"priceInr": number
"product": string
"sixMonths100Coupon": string
"threeMonths100Coupon": string
"twelveMonths100Coupon": string
"type": "sst.sst.Linkable"
}
"ZEN_MODELS1": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS10": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS11": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS12": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS13": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS14": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS15": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS16": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS17": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS18": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS19": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS2": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS20": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS21": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS22": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS23": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS24": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS25": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS26": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS27": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS28": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS29": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS3": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS30": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS4": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS5": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS6": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS7": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS8": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_MODELS9": {
"type": "sst.sst.Secret"
"value": string
}
"ZEN_SESSION_SECRET": {
"type": "sst.sst.Secret"
"value": string
}
}
}
// cloudflare
import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" {
export interface Resource {
"Api": cloudflare.Service
"AuthApi": cloudflare.Service
"AuthStorage": cloudflare.KVNamespace
"Bucket": cloudflare.R2Bucket
"EnterpriseStorage": cloudflare.R2Bucket
"LogProcessor": cloudflare.Service
"Stat": cloudflare.Service
"ZenData": cloudflare.R2Bucket
"ZenDataNew": cloudflare.R2Bucket
}
}
/// <reference path="../../sst-env.d.ts" />
import "sst"
export {}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.15.6",
"version": "1.15.7",
"name": "@opencode-ai/http-recorder",
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.15.6",
"version": "1.15.7",
"name": "@opencode-ai/llm",
"type": "module",
"license": "MIT",
@@ -10,6 +10,7 @@ import {
type CacheHint,
type FinishReason,
type LLMRequest,
type MediaPart,
type ProviderMetadata,
type ToolCallPart,
type ToolDefinition,
@@ -39,6 +40,17 @@ const AnthropicTextBlock = Schema.Struct({
})
type AnthropicTextBlock = Schema.Schema.Type<typeof AnthropicTextBlock>
const AnthropicImageBlock = Schema.Struct({
type: Schema.tag("image"),
source: Schema.Struct({
type: Schema.tag("base64"),
media_type: Schema.String,
data: Schema.String,
}),
cache_control: Schema.optional(AnthropicCacheControl),
})
type AnthropicImageBlock = Schema.Schema.Type<typeof AnthropicImageBlock>
const AnthropicThinkingBlock = Schema.Struct({
type: Schema.tag("thinking"),
thinking: Schema.String,
@@ -92,7 +104,8 @@ const AnthropicToolResultBlock = Schema.Struct({
cache_control: Schema.optional(AnthropicCacheControl),
})
const AnthropicUserBlock = Schema.Union([AnthropicTextBlock, AnthropicToolResultBlock])
const AnthropicUserBlock = Schema.Union([AnthropicTextBlock, AnthropicImageBlock, AnthropicToolResultBlock])
type AnthropicUserBlock = Schema.Schema.Type<typeof AnthropicUserBlock>
const AnthropicAssistantBlock = Schema.Union([
AnthropicTextBlock,
AnthropicThinkingBlock,
@@ -272,6 +285,19 @@ const lowerServerToolResult = Effect.fn("AnthropicMessages.lowerServerToolResult
return { type: wireType, tool_use_id: part.id, content: part.result.value } satisfies AnthropicServerToolResultBlock
})
const lowerImage = Effect.fn("AnthropicMessages.lowerImage")(function* (part: MediaPart) {
if (!part.mediaType.startsWith("image/"))
return yield* invalid(`Anthropic Messages user media content only supports images`)
return {
type: "image" as const,
source: {
type: "base64" as const,
media_type: part.mediaType,
data: ProviderShared.mediaBase64(part),
},
} satisfies AnthropicImageBlock
})
const lowerMessages = Effect.fn("AnthropicMessages.lowerMessages")(function* (
request: LLMRequest,
breakpoints: Cache.Breakpoints,
@@ -280,11 +306,17 @@ const lowerMessages = Effect.fn("AnthropicMessages.lowerMessages")(function* (
for (const message of request.messages) {
if (message.role === "user") {
const content: AnthropicTextBlock[] = []
const content: AnthropicUserBlock[] = []
for (const part of message.content) {
if (!ProviderShared.supportsContent(part, ["text"]))
return yield* ProviderShared.unsupportedContent("Anthropic Messages", "user", ["text"])
content.push({ type: "text", text: part.text, cache_control: cacheControl(breakpoints, part.cache) })
if (part.type === "text") {
content.push({ type: "text", text: part.text, cache_control: cacheControl(breakpoints, part.cache) })
continue
}
if (part.type === "media") {
content.push(yield* lowerImage(part))
continue
}
return yield* ProviderShared.unsupportedContent("Anthropic Messages", "user", ["text", "media"])
}
messages.push({ role: "user", content })
continue
+79 -20
View File
@@ -6,11 +6,11 @@ import { HttpTransport, WebSocketTransport } from "../route/transport"
import { Protocol } from "../route/protocol"
import {
LLMEvent,
type MediaPart,
Usage,
type FinishReason,
type LLMRequest,
type ProviderMetadata,
type ReasoningPart,
type TextPart,
type ToolCallPart,
type ToolDefinition,
@@ -43,10 +43,23 @@ const OpenAIResponsesOutputText = Schema.Struct({
text: Schema.String,
})
const OpenAIResponsesReasoningSummaryText = Schema.Struct({
type: Schema.tag("summary_text"),
text: Schema.String,
})
const OpenAIResponsesReasoningItem = Schema.Struct({
type: Schema.tag("reasoning"),
id: Schema.String,
summary: Schema.Array(OpenAIResponsesReasoningSummaryText),
encrypted_content: optionalNull(Schema.String),
})
const OpenAIResponsesInputItem = Schema.Union([
Schema.Struct({ role: Schema.tag("system"), content: Schema.String }),
Schema.Struct({ role: Schema.tag("user"), content: Schema.Array(OpenAIResponsesInputContent) }),
Schema.Struct({ role: Schema.tag("assistant"), content: Schema.Array(OpenAIResponsesOutputText) }),
OpenAIResponsesReasoningItem,
Schema.Struct({
type: Schema.tag("function_call"),
call_id: Schema.String,
@@ -149,6 +162,7 @@ const OpenAIResponsesStreamItem = Schema.Struct({
server_label: Schema.optional(Schema.String),
output: Schema.optional(Schema.Unknown),
error: Schema.optional(Schema.Unknown),
encrypted_content: optionalNull(Schema.String),
})
type OpenAIResponsesStreamItem = Schema.Schema.Type<typeof OpenAIResponsesStreamItem>
@@ -206,17 +220,31 @@ const lowerToolCall = (part: ToolCallPart): OpenAIResponsesInputItem => ({
arguments: ProviderShared.encodeJson(part.input),
})
const imageUrl = (part: MediaPart) =>
typeof part.data === "string" && part.data.startsWith("data:")
? part.data
: `data:${part.mediaType};base64,${ProviderShared.mediaBytes(part)}`
const lowerReasoning = (part: ReasoningPart, store: boolean | undefined): OpenAIResponsesInputItem | undefined => {
const openai = part.providerMetadata?.openai
if (!ProviderShared.isRecord(openai) || typeof openai.itemId !== "string") return undefined
// With store:false, OpenAI only accepts previous reasoning items when the
// encrypted state is present. Bare rs_* ids point to non-persisted items.
if (store === false && typeof openai.reasoningEncryptedContent !== "string") return undefined
return {
type: "reasoning",
id: openai.itemId,
summary: part.text.length > 0 ? [{ type: "summary_text", text: part.text }] : [],
encrypted_content:
typeof openai.reasoningEncryptedContent === "string"
? openai.reasoningEncryptedContent
: openai.reasoningEncryptedContent === null
? null
: undefined,
}
}
const lowerUserContent = Effect.fn("OpenAIResponses.lowerUserContent")(function* (
part: LLMRequest["messages"][number]["content"][number],
) {
if (part.type === "text") return { type: "input_text" as const, text: part.text }
if (part.type === "media" && part.mediaType.startsWith("image/")) {
return { type: "input_image" as const, image_url: imageUrl(part) }
return { type: "input_image" as const, image_url: ProviderShared.mediaDataUrl(part) }
}
if (part.type === "media") return yield* invalid("OpenAI Responses user media content only supports images")
return yield* ProviderShared.unsupportedContent("OpenAI Responses", "user", ["text", "media"])
@@ -226,6 +254,7 @@ const lowerMessages = Effect.fn("OpenAIResponses.lowerMessages")(function* (requ
const system: OpenAIResponsesInputItem[] =
request.system.length === 0 ? [] : [{ role: "system", content: ProviderShared.joinText(request.system) }]
const input: OpenAIResponsesInputItem[] = [...system]
const store = OpenAIOptions.store(request)
for (const message of request.messages) {
if (message.role === "user") {
@@ -235,20 +264,34 @@ const lowerMessages = Effect.fn("OpenAIResponses.lowerMessages")(function* (requ
if (message.role === "assistant") {
const content: TextPart[] = []
const flushText = () => {
if (content.length === 0) return
input.push({ role: "assistant", content: content.map((part) => ({ type: "output_text", text: part.text })) })
content.splice(0, content.length)
}
for (const part of message.content) {
if (!ProviderShared.supportsContent(part, ["text", "tool-call"]))
return yield* ProviderShared.unsupportedContent("OpenAI Responses", "assistant", ["text", "tool-call"])
if (part.type === "text") {
content.push(part)
continue
}
if (part.type === "reasoning") {
flushText()
const reasoning = lowerReasoning(part, store)
if (reasoning) input.push(reasoning)
continue
}
if (part.type === "tool-call") {
flushText()
input.push(lowerToolCall(part))
continue
}
return yield* ProviderShared.unsupportedContent("OpenAI Responses", "assistant", [
"text",
"reasoning",
"tool-call",
])
}
if (content.length > 0)
input.push({ role: "assistant", content: content.map((part) => ({ type: "output_text", text: part.text })) })
flushText()
continue
}
@@ -367,6 +410,11 @@ const isHostedToolItem = (
): item is OpenAIResponsesStreamItem & { type: HostedToolType; id: string } =>
item.type in HOSTED_TOOLS && typeof item.id === "string" && item.id.length > 0
const isReasoningItem = (
item: OpenAIResponsesStreamItem,
): item is OpenAIResponsesStreamItem & { type: "reasoning"; id: string } =>
item.type === "reasoning" && typeof item.id === "string" && item.id.length > 0
// Round-trip the full item as the structured result so consumers can extract
// outputs / sources / status without re-decoding.
const hostedToolResult = (item: OpenAIResponsesStreamItem) => {
@@ -428,16 +476,12 @@ const onReasoningDelta = (state: ParserState, event: OpenAIResponsesEvent): Step
]
}
const onReasoningDone = (state: ParserState, event: OpenAIResponsesEvent): StepResult => {
const events: LLMEvent[] = []
return [
{
...state,
lifecycle: Lifecycle.reasoningEnd(state.lifecycle, events, event.item_id ?? "reasoning-0"),
},
events,
]
}
// The summary done event does not carry encrypted continuation state. Finish the
// common reasoning block when the full reasoning item arrives in output_item.done.
const onReasoningDone = (state: ParserState, _event: OpenAIResponsesEvent): StepResult => [state, NO_EVENTS]
const reasoningMetadata = (item: OpenAIResponsesStreamItem & { id: string }) =>
openaiMetadata({ itemId: item.id, reasoningEncryptedContent: item.encrypted_content ?? null })
const onOutputItemAdded = (state: ParserState, event: OpenAIResponsesEvent): StepResult => {
const item = event.item
@@ -518,6 +562,21 @@ const onOutputItemDone = Effect.fn("OpenAIResponses.onOutputItemDone")(function*
return [{ ...state, lifecycle }, events] satisfies StepResult
}
if (isReasoningItem(item)) {
const events: LLMEvent[] = []
const providerMetadata = reasoningMetadata(item)
if (!state.lifecycle.reasoning.has(item.id)) {
const lifecycle = Lifecycle.stepStart(state.lifecycle, events)
events.push(LLMEvent.reasoningStart({ id: item.id, providerMetadata }))
events.push(LLMEvent.reasoningEnd({ id: item.id, providerMetadata }))
return [{ ...state, lifecycle }, events] satisfies StepResult
}
return [
{ ...state, lifecycle: Lifecycle.reasoningEnd(state.lifecycle, events, item.id, providerMetadata) },
events,
] satisfies StepResult
}
return [state, NO_EVENTS] satisfies StepResult
})
+11 -1
View File
@@ -80,7 +80,7 @@ export const subtractTokens = (total: number | undefined, subtrahend: number | u
*/
export const sumTokens = (...values: ReadonlyArray<number | undefined>): number | undefined => {
if (values.every((value) => value === undefined)) return undefined
return values.reduce<number>((acc, value) => acc + (value ?? 0), 0)
return values.reduce((acc: number, value) => acc + (value ?? 0), 0)
}
export const eventError = (route: string, message: string, raw?: string) =>
@@ -122,6 +122,16 @@ export const parseToolInput = (route: string, name: string, raw: string) =>
export const mediaBytes = (part: MediaPart) =>
typeof part.data === "string" ? part.data : Buffer.from(part.data).toString("base64")
export const mediaBase64 = (part: MediaPart) => {
if (typeof part.data !== "string" || !part.data.startsWith("data:")) return mediaBytes(part)
return part.data.slice(part.data.indexOf(",") + 1)
}
export const mediaDataUrl = (part: MediaPart) =>
typeof part.data === "string" && part.data.startsWith("data:")
? part.data
: `data:${part.mediaType};base64,${mediaBytes(part)}`
export const trimBaseUrl = (value: string) => value.replace(/\/+$/, "")
export const toolResultText = (part: ToolResultPart) => {
+104
View File
@@ -0,0 +1,104 @@
import { LLM, Message, ToolCallPart, ToolDefinition, ToolResultPart, type ContentPart, type Model } from "../src"
export const basicContinuation = ["system", "user-text", "assistant-text", "user-follow-up"] as const
export const toolContinuation = ["tool-call", "tool-result"] as const
export const reasoningContinuation = ["assistant-reasoning", "encrypted-reasoning"] as const
export const mediaContinuation = ["user-image"] as const
export const maximalContinuation = [
...basicContinuation,
...toolContinuation,
...reasoningContinuation,
...mediaContinuation,
] as const
export type ContinuationFeature = (typeof maximalContinuation)[number]
export const nativeOpenAIResponsesContinuation = [
...basicContinuation,
...toolContinuation,
"encrypted-reasoning",
...mediaContinuation,
] as const satisfies ReadonlyArray<ContinuationFeature>
export const nativeAnthropicMessagesContinuation = [
...basicContinuation,
...toolContinuation,
"assistant-reasoning",
...mediaContinuation,
] as const satisfies ReadonlyArray<ContinuationFeature>
export const continuationTool = ToolDefinition.make({
name: "get_weather",
description: "Get current weather for a city.",
inputSchema: {
type: "object",
properties: { city: { type: "string" } },
required: ["city"],
additionalProperties: false,
},
})
export function continuationRequest(input: {
readonly id: string
readonly model: Model
readonly features: ReadonlyArray<ContinuationFeature>
readonly image?: string
}) {
const features = new Set(input.features)
const messages = []
const firstUser: ContentPart[] = []
const firstAssistant: ContentPart[] = []
if (features.has("user-text")) firstUser.push({ type: "text", text: "What is shown here?" })
if (features.has("user-image"))
firstUser.push({ type: "media", mediaType: "image/png", data: input.image ?? "AAECAw==" })
if (firstUser.length > 0) messages.push(Message.user(firstUser))
if (features.has("assistant-reasoning"))
firstAssistant.push({
type: "reasoning",
text: "I inspected the previous turn.",
providerMetadata: { anthropic: { signature: "sig_continuation_1" } },
})
if (features.has("encrypted-reasoning"))
firstAssistant.push({
type: "reasoning",
text: "I inspected the previous turn.",
providerMetadata: {
openai: {
itemId: "rs_continuation_1",
reasoningEncryptedContent: "encrypted-continuation-state",
},
},
})
if (features.has("assistant-text")) firstAssistant.push({ type: "text", text: "It shows a small test image." })
if (firstAssistant.length > 0) messages.push(Message.assistant(firstAssistant))
if (features.has("tool-call")) {
messages.push(Message.user("Check the weather in Paris before continuing."))
messages.push(
Message.assistant([ToolCallPart.make({ id: "call_weather_1", name: "get_weather", input: { city: "Paris" } })]),
)
}
if (features.has("tool-result")) {
messages.push(
Message.tool(ToolResultPart.make({ id: "call_weather_1", name: "get_weather", result: { temperature: 22 } })),
)
if (features.has("assistant-text")) messages.push(Message.assistant("Paris is 22 degrees."))
}
if (features.has("user-follow-up"))
messages.push(Message.user("Continue from this conversation in one short sentence."))
return LLM.request({
id: input.id,
model: input.model,
system: features.has("system") ? "You are concise. Continue from the provided history." : undefined,
messages,
tools: features.has("tool-call") ? [continuationTool] : [],
cache: "none",
providerOptions: features.has("encrypted-reasoning")
? { openai: { store: false, includeEncryptedReasoning: true, reasoningSummary: "auto" } }
: undefined,
generation: { maxTokens: 80, temperature: 0 },
})
}
File diff suppressed because one or more lines are too long
@@ -1,10 +1,12 @@
import { describe, expect } from "bun:test"
import { Effect } from "effect"
import { HttpClientRequest } from "effect/unstable/http"
import { CacheHint, LLM, LLMError, Message, ToolCallPart, Usage } from "../../src"
import { Auth, LLMClient } from "../../src/route"
import * as AnthropicMessages from "../../src/protocols/anthropic-messages"
import { continuationRequest, nativeAnthropicMessagesContinuation } from "../continuation-scenarios"
import { it } from "../lib/effect"
import { fixedResponse } from "../lib/http"
import { dynamicResponse, fixedResponse } from "../lib/http"
import { sseEvents } from "../lib/sse"
const model = AnthropicMessages.route
@@ -40,7 +42,7 @@ describe("Anthropic Messages route", () => {
it.effect("prepares tool call and tool result messages", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare(
const prepared = yield* LLMClient.prepare<AnthropicMessages.AnthropicMessagesBody>(
LLM.request({
id: "req_tool_result",
model,
@@ -69,6 +71,50 @@ describe("Anthropic Messages route", () => {
}),
)
it.effect("prepares the composed native continuation request", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare<AnthropicMessages.AnthropicMessagesBody>(
continuationRequest({
id: "req_native_continuation_anthropic",
model,
features: nativeAnthropicMessagesContinuation,
}),
)
expect(prepared.body).toMatchObject({
system: [{ type: "text", text: "You are concise. Continue from the provided history." }],
messages: [
{
role: "user",
content: [
{ type: "text", text: "What is shown here?" },
{ type: "image", source: { type: "base64", media_type: "image/png", data: "AAECAw==" } },
],
},
{
role: "assistant",
content: [
{ type: "thinking", thinking: "I inspected the previous turn.", signature: "sig_continuation_1" },
{ type: "text", text: "It shows a small test image." },
],
},
{ role: "user", content: [{ type: "text", text: "Check the weather in Paris before continuing." }] },
{
role: "assistant",
content: [{ type: "tool_use", id: "call_weather_1", name: "get_weather", input: { city: "Paris" } }],
},
{
role: "user",
content: [{ type: "tool_result", tool_use_id: "call_weather_1", content: '{"temperature":22}' }],
},
{ role: "assistant", content: [{ type: "text", text: "Paris is 22 degrees." }] },
{ role: "user", content: [{ type: "text", text: "Continue from this conversation in one short sentence." }] },
],
})
expect(prepared.body.tools).toEqual([expect.objectContaining({ name: "get_weather" })])
}),
)
it.effect("lowers preserved Anthropic reasoning signature metadata", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare(
@@ -392,17 +438,51 @@ describe("Anthropic Messages route", () => {
}),
)
it.effect("rejects unsupported user media content", () =>
it.effect("continues a conversation with user image content", () =>
Effect.gen(function* () {
const error = yield* LLMClient.prepare(
const response = yield* LLMClient.generate(
LLM.request({
id: "req_media",
model,
messages: [Message.user({ type: "media", mediaType: "image/png", data: "AAECAw==" })],
messages: [
Message.user([
{ type: "text", text: "What is in this image?" },
{ type: "media", mediaType: "image/png", data: "AAECAw==" },
]),
],
}),
).pipe(Effect.flip)
).pipe(
Effect.provide(
dynamicResponse((input) =>
Effect.gen(function* () {
const web = yield* HttpClientRequest.toWeb(input.request).pipe(Effect.orDie)
expect(yield* Effect.promise(() => web.json())).toMatchObject({
messages: [
{
role: "user",
content: [
{ type: "text", text: "What is in this image?" },
{ type: "image", source: { type: "base64", media_type: "image/png", data: "AAECAw==" } },
],
},
],
})
return input.respond(
sseEvents(
{ type: "content_block_start", index: 0, content_block: { type: "text", text: "" } },
{ type: "content_block_delta", index: 0, delta: { type: "text_delta", text: "An image." } },
{ type: "content_block_stop", index: 0 },
{ type: "message_delta", delta: { stop_reason: "end_turn" }, usage: { output_tokens: 3 } },
{ type: "message_stop" },
),
{ headers: { "content-type": "text/event-stream" } },
)
}),
),
),
)
expect(error.message).toContain("Anthropic Messages user messages only support text content for now")
expect(response.text).toBe("An image.")
}),
)
@@ -84,6 +84,7 @@ describeRecordedGoldenScenarios([
scenarios: [
{ id: "text", temperature: false },
{ id: "reasoning", temperature: false },
{ id: "reasoning-continuation", temperature: false },
{ id: "tool-call", temperature: false },
{ id: "tool-loop", temperature: false },
],
@@ -7,6 +7,7 @@ import * as Azure from "../../src/providers/azure"
import * as OpenAI from "../../src/providers/openai"
import * as OpenAIResponses from "../../src/protocols/openai-responses"
import * as ProviderShared from "../../src/protocols/shared"
import { continuationRequest, nativeOpenAIResponsesContinuation } from "../continuation-scenarios"
import { it } from "../lib/effect"
import { dynamicResponse, fixedResponse } from "../lib/http"
import { sseEvents } from "../lib/sse"
@@ -247,6 +248,49 @@ describe("OpenAI Responses route", () => {
}),
)
it.effect("prepares the composed native continuation request", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare<OpenAIResponses.OpenAIResponsesBody>(
continuationRequest({
id: "req_native_continuation_openai",
model,
features: nativeOpenAIResponsesContinuation,
}),
)
expect(prepared.body).toMatchObject({
input: [
{ role: "system", content: "You are concise. Continue from the provided history." },
{
role: "user",
content: [
{ type: "input_text", text: "What is shown here?" },
{ type: "input_image", image_url: "data:image/png;base64,AAECAw==" },
],
},
{
type: "reasoning",
id: "rs_continuation_1",
encrypted_content: "encrypted-continuation-state",
summary: [{ type: "summary_text", text: "I inspected the previous turn." }],
},
{ role: "assistant", content: [{ type: "output_text", text: "It shows a small test image." }] },
{ role: "user", content: [{ type: "input_text", text: "Check the weather in Paris before continuing." }] },
{ type: "function_call", call_id: "call_weather_1", name: "get_weather", arguments: '{"city":"Paris"}' },
{ type: "function_call_output", call_id: "call_weather_1", output: '{"temperature":22}' },
{ role: "assistant", content: [{ type: "output_text", text: "Paris is 22 degrees." }] },
{
role: "user",
content: [{ type: "input_text", text: "Continue from this conversation in one short sentence." }],
},
],
include: ["reasoning.encrypted_content"],
store: false,
})
expect(prepared.body.tools).toEqual([expect.objectContaining({ type: "function", name: "get_weather" })])
}),
)
it.effect("maps OpenAI provider options to Responses options", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare<OpenAIResponses.OpenAIResponsesBody>(
@@ -380,6 +424,172 @@ describe("OpenAI Responses route", () => {
}),
)
it.effect("preserves encrypted reasoning metadata for continuation", () =>
Effect.gen(function* () {
const response = yield* LLMClient.generate(request).pipe(
Effect.provide(
fixedResponse(
sseEvents(
{ type: "response.reasoning_summary_text.delta", item_id: "rs_1", delta: "thinking" },
{
type: "response.output_item.done",
item: {
type: "reasoning",
id: "rs_1",
encrypted_content: "encrypted-state",
summary: [{ type: "summary_text", text: "thinking" }],
},
},
{ type: "response.completed", response: { id: "resp_1" } },
),
),
),
)
expect(response.events).toContainEqual(
expect.objectContaining({
type: "reasoning-end",
id: "rs_1",
providerMetadata: { openai: { itemId: "rs_1", reasoningEncryptedContent: "encrypted-state" } },
}),
)
}),
)
it.effect("continues a stateless reasoning conversation", () =>
Effect.gen(function* () {
const response = yield* LLMClient.generate(
LLM.request({
id: "req_reasoning_continue",
model,
messages: [
Message.user("What changed?"),
Message.assistant([
{
type: "reasoning",
text: "Checked the previous diff.",
providerMetadata: {
openai: {
itemId: "rs_1",
reasoningEncryptedContent: "encrypted-state",
},
},
},
{ type: "text", text: "The parser changed." },
]),
Message.user("Summarize it."),
],
}),
).pipe(
Effect.provide(
dynamicResponse((input) =>
Effect.gen(function* () {
const web = yield* HttpClientRequest.toWeb(input.request).pipe(Effect.orDie)
expect(yield* Effect.promise(() => web.json())).toMatchObject({
input: [
{ role: "user", content: [{ type: "input_text", text: "What changed?" }] },
{
type: "reasoning",
id: "rs_1",
encrypted_content: "encrypted-state",
summary: [{ type: "summary_text", text: "Checked the previous diff." }],
},
{ role: "assistant", content: [{ type: "output_text", text: "The parser changed." }] },
{ role: "user", content: [{ type: "input_text", text: "Summarize it." }] },
],
})
return input.respond(
sseEvents(
{ type: "response.output_text.delta", item_id: "msg_1", delta: "Parser now round-trips reasoning." },
{ type: "response.completed", response: { id: "resp_1" } },
),
{ headers: { "content-type": "text/event-stream" } },
)
}),
),
),
)
expect(response.text).toBe("Parser now round-trips reasoning.")
}),
)
it.effect("preserves assistant content order around reasoning items", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare<OpenAIResponses.OpenAIResponsesBody>(
LLM.request({
id: "req_reasoning_order",
model,
messages: [
Message.assistant([
{ type: "text", text: "Before." },
{
type: "reasoning",
text: "Checked order.",
providerMetadata: {
openai: {
itemId: "rs_1",
reasoningEncryptedContent: "encrypted-state",
},
},
},
{ type: "text", text: "After." },
]),
],
}),
)
expect(prepared.body.input).toEqual([
{ role: "assistant", content: [{ type: "output_text", text: "Before." }] },
{
type: "reasoning",
id: "rs_1",
encrypted_content: "encrypted-state",
summary: [{ type: "summary_text", text: "Checked order." }],
},
{ role: "assistant", content: [{ type: "output_text", text: "After." }] },
])
}),
)
it.effect("skips non-persisted reasoning ids without encrypted state", () =>
Effect.gen(function* () {
const prepared = yield* LLMClient.prepare(
LLM.request({
id: "req_reasoning_without_encrypted_state",
model,
messages: [
Message.user("What changed?"),
Message.assistant([
{
type: "reasoning",
text: "Checked the previous diff.",
providerMetadata: {
openai: {
itemId: "rs_1",
reasoningEncryptedContent: null,
},
},
},
{ type: "text", text: "The parser changed." },
]),
Message.user("Summarize it."),
],
providerOptions: { openai: { store: false } },
}),
)
expect(prepared.body).toMatchObject({
input: [
{ role: "user", content: [{ type: "input_text", text: "What changed?" }] },
{ role: "assistant", content: [{ type: "output_text", text: "The parser changed." }] },
{ role: "user", content: [{ type: "input_text", text: "Summarize it." }] },
],
store: false,
})
}),
)
it.effect("assembles streamed function call input", () =>
Effect.gen(function* () {
const body = sseEvents(
+2 -10
View File
@@ -2,7 +2,7 @@ import type { HttpRecorder } from "@opencode-ai/http-recorder"
import { describe } from "bun:test"
import { Effect } from "effect"
import type { Model } from "../src"
import { goldenScenarioTags, runGoldenScenario, type GoldenScenarioID } from "./recorded-scenarios"
import { goldenScenarioTags, goldenScenarioTitle, runGoldenScenario, type GoldenScenarioID } from "./recorded-scenarios"
import { recordedTests } from "./recorded-test"
import { kebab } from "./recorded-utils"
@@ -35,14 +35,6 @@ type TargetInput = {
const scenarioInput = (input: ScenarioInput) => (typeof input === "string" ? { id: input } : input)
const scenarioTitle = (id: GoldenScenarioID) => {
if (id === "text") return "streams text"
if (id === "tool-call") return "streams tool call"
if (id === "reasoning") return "uses reasoning"
if (id === "image") return "reads image text"
return "drives a tool loop"
}
const defaultPrefix = (target: TargetInput) => {
if (target.prefix) return target.prefix
const transport = target.transport === "websocket" ? "-websocket" : ""
@@ -77,7 +69,7 @@ const runTarget = (target: TargetInput) => {
describe(`${target.name} recorded`, () => {
target.scenarios.forEach((raw) => {
const input = scenarioInput(raw)
const name = input.name ?? scenarioTitle(input.id)
const name = input.name ?? goldenScenarioTitle(input.id)
recorded.effect.with(
name,
{
+209 -147
View File
@@ -1,6 +1,17 @@
import { expect } from "bun:test"
import { Effect, Schema, Stream } from "effect"
import { LLM, LLMEvent, LLMResponse, Message, ToolChoice, ToolDefinition, type LLMRequest, type Model } from "../src"
import {
LLM,
LLMEvent,
LLMResponse,
Message,
ToolChoice,
ToolDefinition,
type ContentPart,
type FinishReason,
type LLMRequest,
type Model,
} from "../src"
import { LLMClient } from "../src/route"
import { tool } from "../src/tool"
@@ -39,47 +50,6 @@ export const weatherRuntimeTool = tool({
),
})
export const textRequest = (input: {
readonly id: string
readonly model: Model
readonly prompt?: string
readonly maxTokens?: number
readonly temperature?: number | false
}) =>
LLM.request({
id: input.id,
model: input.model,
system: "You are concise.",
prompt: input.prompt ?? "Reply with exactly: Hello!",
cache: "none",
providerOptions:
input.model.route.id === "gemini" ? { gemini: { thinkingConfig: { thinkingBudget: 0 } } } : undefined,
generation:
input.temperature === false
? { maxTokens: input.maxTokens ?? 80 }
: { maxTokens: input.maxTokens ?? 80, temperature: input.temperature ?? 0 },
})
export const weatherToolRequest = (input: {
readonly id: string
readonly model: Model
readonly maxTokens?: number
readonly temperature?: number | false
}) =>
LLM.request({
id: input.id,
model: input.model,
system: "Call tools exactly as requested.",
prompt: "Call get_weather with city exactly Paris.",
tools: [weatherTool],
toolChoice: ToolChoice.make(weatherTool),
cache: "none",
generation:
input.temperature === false
? { maxTokens: input.maxTokens ?? 80 }
: { maxTokens: input.maxTokens ?? 80, temperature: input.temperature ?? 0 },
})
export const weatherToolLoopRequest = (input: {
readonly id: string
readonly model: Model
@@ -116,52 +86,6 @@ const restroomImage = () =>
Effect.map((bytes) => Buffer.from(bytes).toString("base64")),
)
export const imageRequest = (input: {
readonly id: string
readonly model: Model
readonly image: string
readonly maxTokens?: number
readonly temperature?: number | false
}) =>
LLM.request({
id: input.id,
model: input.model,
system: "Read images carefully. Reply only with the visible text.",
messages: [
Message.user([
{
type: "text",
text: "The image contains exactly three lowercase English words. Read them left to right and reply with only those words.",
},
{ type: "media", mediaType: "image/png", data: input.image },
]),
],
cache: "none",
generation:
input.temperature === false
? { maxTokens: input.maxTokens ?? 20 }
: { maxTokens: input.maxTokens ?? 20, temperature: input.temperature ?? 0 },
})
export const reasoningRequest = (input: {
readonly id: string
readonly model: Model
readonly maxTokens?: number
readonly temperature?: number | false
}) =>
LLM.request({
id: input.id,
model: input.model,
system: "Show concise reasoning when the provider supports visible reasoning summaries.",
prompt: "Think briefly, then reply exactly with: Hello!",
cache: "none",
providerOptions: { openai: { reasoningEffort: "low", reasoningSummary: "auto" } },
generation:
input.temperature === false
? { maxTokens: input.maxTokens ?? 120 }
: { maxTokens: input.maxTokens ?? 120, temperature: input.temperature ?? 0 },
})
export const runWeatherToolLoop = (request: LLMRequest) =>
LLMClient.stream({
request,
@@ -212,8 +136,6 @@ export const expectGoldenWeatherToolLoop = (events: ReadonlyArray<LLMEvent>) =>
expect(LLMResponse.text({ events }).trim()).toMatch(/^Paris is sunny\.?$/)
}
export type GoldenScenarioID = "text" | "tool-call" | "tool-loop" | "image" | "reasoning"
export interface GoldenScenarioContext {
readonly id: string
readonly model: Model
@@ -223,6 +145,9 @@ export interface GoldenScenarioContext {
const generate = (request: LLMRequest) => LLMClient.generate(request)
const generation = (context: GoldenScenarioContext, maxTokens: number) =>
context.temperature === false ? { maxTokens } : { maxTokens, temperature: context.temperature ?? 0 }
const normalizeImageText = (value: string) =>
value
.toLowerCase()
@@ -230,75 +155,193 @@ const normalizeImageText = (value: string) =>
.replace(/\s+/g, " ")
.trim()
export const goldenScenarioTags = (id: GoldenScenarioID) => {
if (id === "text") return ["text", "golden"]
if (id === "tool-call") return ["tool", "tool-call", "golden"]
if (id === "image") return ["media", "image", "vision", "golden"]
if (id === "reasoning") return ["reasoning", "golden"]
return ["tool", "tool-loop", "golden"]
const encryptedReasoningOptions = {
openai: {
store: false,
includeEncryptedReasoning: true,
reasoningEffort: "low",
reasoningSummary: "auto",
},
} as const
type AssistantTextExpectation = string | RegExp
type UserStep = { readonly type: "user"; readonly content: Message.ContentInput }
type AssistantStep = {
readonly type: "assistant"
readonly text?: AssistantTextExpectation
readonly toolCall?: { readonly name: string; readonly input: unknown }
readonly reasoning?: "openai-encrypted"
readonly id?: string
readonly system?: string
readonly maxTokens?: number
readonly finish?: FinishReason
readonly tools?: LLM.RequestInput["tools"]
readonly toolChoice?: LLM.RequestInput["toolChoice"]
readonly providerOptions?: LLMRequest["providerOptions"]
readonly assert?: (response: LLMResponse) => void
}
type ConversationStep = UserStep | AssistantStep
const user = (content: Message.ContentInput): ConversationStep => ({ type: "user", content })
const assistant = {
expectText: (
text: AssistantTextExpectation,
options?: Omit<AssistantStep, "type" | "text" | "reasoning" | "toolCall">,
): ConversationStep => ({ type: "assistant", text, ...options }),
expectToolCall: (
name: string,
input: unknown,
options?: Omit<AssistantStep, "type" | "text" | "reasoning" | "toolCall" | "finish">,
): ConversationStep => ({ type: "assistant", toolCall: { name, input }, finish: "tool-calls", ...options }),
expectEncryptedReasoningText: (
text: AssistantTextExpectation,
options?: Omit<AssistantStep, "type" | "text" | "reasoning" | "toolCall" | "providerOptions">,
): ConversationStep => ({
type: "assistant",
text,
reasoning: "openai-encrypted",
providerOptions: encryptedReasoningOptions,
...options,
}),
}
export const runGoldenScenario = (id: GoldenScenarioID, context: GoldenScenarioContext) =>
const assertAssistantText = (actual: string, expected: AssistantTextExpectation) => {
if (typeof expected === "string") {
expect(actual.trim()).toBe(expected)
return
}
expect(actual.trim()).toMatch(expected)
}
const assertAssistantToolCall = (response: LLMResponse, expected: NonNullable<AssistantStep["toolCall"]>) => {
expect(response.toolCalls).toMatchObject([
{ type: "tool-call", id: expect.any(String), name: expected.name, input: expected.input },
])
}
// The generated golden scenarios only model one assistant shape at a time:
// encrypted reasoning + text, text, or tool call. Keep mixed interleavings in
// focused protocol tests where event order can be asserted directly.
const assistantMessageFromResponse = (response: LLMResponse, step: AssistantStep) => {
const content: ContentPart[] = []
if (step.reasoning === "openai-encrypted") {
const reasoning = response.events.find(
(event): event is Extract<LLMEvent, { readonly type: "reasoning-end" }> =>
LLMEvent.is.reasoningEnd(event) && typeof event.providerMetadata?.openai?.itemId === "string",
)
if (!reasoning) throw new Error("OpenAI Responses did not return reasoning metadata")
expect(reasoning.providerMetadata?.openai?.reasoningEncryptedContent).toEqual(expect.any(String))
content.push({ type: "reasoning", text: response.reasoning, providerMetadata: reasoning.providerMetadata })
}
if (response.text.length > 0) content.push({ type: "text", text: response.text })
content.push(...response.toolCalls)
return Message.assistant(content)
}
const runGeneratedConversation = (context: GoldenScenarioContext, steps: ReadonlyArray<ConversationStep>) =>
Effect.gen(function* () {
if (id === "text") {
const messages: Message[] = []
let generated = 0
for (const step of steps) {
if (step.type === "user") {
messages.push(Message.user(step.content))
continue
}
generated += 1
const response = yield* generate(
textRequest({
id: context.id,
LLM.request({
id: step.id ? `${context.id}_${step.id}` : `${context.id}_${generated}`,
model: context.model,
prompt: "Reply exactly with: Hello!",
maxTokens: context.maxTokens ?? 40,
temperature: context.temperature,
system: step.system,
cache: "none",
messages,
tools: step.tools,
toolChoice: step.toolChoice,
providerOptions: step.providerOptions,
generation: generation(context, step.maxTokens ?? context.maxTokens ?? 80),
}),
)
expect(response.text.trim()).toMatch(/^Hello!?$/)
expectFinish(response.events, "stop")
return
if (step.text !== undefined) assertAssistantText(response.text, step.text)
if (step.toolCall) assertAssistantToolCall(response, step.toolCall)
step.assert?.(response)
expectFinish(response.events, step.finish ?? "stop")
messages.push(assistantMessageFromResponse(response, step))
}
})
if (id === "tool-call") {
const response = yield* generate(
weatherToolRequest({
id: context.id,
model: context.model,
maxTokens: context.maxTokens ?? 80,
temperature: context.temperature,
}),
)
expectWeatherToolCall(response)
expectFinish(response.events, "tool-calls")
return
}
const runTextScenario = (context: GoldenScenarioContext) =>
runGeneratedConversation(context, [
user("Reply exactly with: Hello!"),
assistant.expectText(/^Hello!?$/, {
system: "You are concise.",
maxTokens: context.maxTokens ?? 40,
providerOptions:
context.model.route.id === "gemini" ? { gemini: { thinkingConfig: { thinkingBudget: 0 } } } : undefined,
}),
])
if (id === "image") {
const response = yield* generate(
imageRequest({
id: context.id,
model: context.model,
image: yield* restroomImage(),
maxTokens: context.maxTokens ?? 20,
temperature: context.temperature,
}),
)
expect(normalizeImageText(response.text)).toBe(RESTROOM_IMAGE_TEXT)
expectFinish(response.events, "stop")
return
}
const runToolCallScenario = (context: GoldenScenarioContext) =>
runGeneratedConversation(context, [
user("Call get_weather with city exactly Paris."),
assistant.expectToolCall(
weatherToolName,
{ city: "Paris" },
{
system: "Call tools exactly as requested.",
tools: [weatherTool],
toolChoice: ToolChoice.make(weatherTool),
maxTokens: context.maxTokens ?? 80,
},
),
])
if (id === "reasoning") {
const response = yield* generate(
reasoningRequest({
id: context.id,
model: context.model,
maxTokens: context.maxTokens ?? 120,
temperature: context.temperature,
}),
)
expect(response.text.trim()).toMatch(/^Hello!?$/)
expect(response.usage?.reasoningTokens ?? 0).toBeGreaterThan(0)
expectFinish(response.events, "stop")
return
}
const runImageScenario = (context: GoldenScenarioContext) =>
Effect.gen(function* () {
yield* runGeneratedConversation(context, [
user([
{
type: "text",
text: "The image contains exactly three lowercase English words. Read them left to right and reply with only those words.",
},
{ type: "media", mediaType: "image/png", data: yield* restroomImage() },
]),
assistant.expectText(/.+/, {
system: "Read images carefully. Reply only with the visible text.",
maxTokens: context.maxTokens ?? 20,
assert: (response) => expect(normalizeImageText(response.text)).toBe(RESTROOM_IMAGE_TEXT),
}),
])
})
const runReasoningScenario = (context: GoldenScenarioContext) =>
runGeneratedConversation(context, [
user("Think briefly, then reply exactly with: Hello!"),
assistant.expectText(/^Hello!?$/, {
system: "Show concise reasoning when the provider supports visible reasoning summaries.",
providerOptions: { openai: { reasoningEffort: "low", reasoningSummary: "auto" } },
maxTokens: context.maxTokens ?? 120,
assert: (response) => expect(response.usage?.reasoningTokens ?? 0).toBeGreaterThan(0),
}),
])
const runReasoningContinuationScenario = (context: GoldenScenarioContext) =>
runGeneratedConversation(context, [
user("Think briefly, then reply exactly with: Hello!"),
assistant.expectEncryptedReasoningText(/^Hello!?$/, {
id: "first",
system: "Show concise reasoning when the provider supports visible reasoning summaries.",
maxTokens: context.maxTokens ?? 120,
}),
user("Now reply exactly with: Done."),
assistant.expectText(/^Done\.?$/, { id: "second", maxTokens: 40, providerOptions: encryptedReasoningOptions }),
])
const runToolLoopScenario = (context: GoldenScenarioContext) =>
Effect.gen(function* () {
expectGoldenWeatherToolLoop(
yield* runWeatherToolLoop(
goldenWeatherToolLoopRequest({
@@ -311,6 +354,25 @@ export const runGoldenScenario = (id: GoldenScenarioID, context: GoldenScenarioC
)
})
const goldenScenarios = {
text: { title: "streams text", tags: ["text", "golden"], run: runTextScenario },
"tool-call": { title: "streams tool call", tags: ["tool", "tool-call", "golden"], run: runToolCallScenario },
"tool-loop": { title: "drives a tool loop", tags: ["tool", "tool-loop", "golden"], run: runToolLoopScenario },
image: { title: "reads image text", tags: ["media", "image", "vision", "golden"], run: runImageScenario },
reasoning: { title: "uses reasoning", tags: ["reasoning", "golden"], run: runReasoningScenario },
"reasoning-continuation": {
title: "continues encrypted reasoning",
tags: ["reasoning", "continuation", "encrypted-reasoning", "golden"],
run: runReasoningContinuationScenario,
},
} as const
export type GoldenScenarioID = keyof typeof goldenScenarios
export const goldenScenarioTitle = (id: GoldenScenarioID) => goldenScenarios[id].title
export const goldenScenarioTags = (id: GoldenScenarioID) => [...goldenScenarios[id].tags]
export const runGoldenScenario = (id: GoldenScenarioID, context: GoldenScenarioContext) =>
goldenScenarios[id].run(context)
const usageSummary = (usage: LLMResponse["usage"] | undefined) => {
if (!usage) return undefined
return Object.fromEntries(
+2 -2
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.15.6",
"version": "1.15.7",
"name": "opencode",
"type": "module",
"license": "MIT",
@@ -135,7 +135,7 @@
"drizzle-orm": "catalog:",
"effect": "catalog:",
"fuzzysort": "3.1.0",
"gitlab-ai-provider": "6.6.0",
"gitlab-ai-provider": "6.7.0",
"glob": "13.0.5",
"google-auth-library": "10.5.0",
"gray-matter": "4.0.3",
@@ -34,10 +34,10 @@ const AUTOCOMPLETE_ROWS = FOOTER_MENU_ROWS
const AUTOCOMPLETE_BOTTOM_ROWS = 1
export const TEXTAREA_MIN_ROWS = 1
export const TEXTAREA_MAX_ROWS = 6
const TEXTAREA_MAX_ROWS = 6
export const PROMPT_MAX_ROWS = TEXTAREA_MAX_ROWS + AUTOCOMPLETE_ROWS - 1 + AUTOCOMPLETE_BOTTOM_ROWS
export const HINT_BREAKPOINTS = {
const HINT_BREAKPOINTS = {
send: 50,
newline: 66,
history: 80,
@@ -96,8 +96,6 @@ type RunFooterViewProps = {
onSubagentSelect?: (sessionID: string | undefined) => void
}
export { TEXTAREA_MIN_ROWS, TEXTAREA_MAX_ROWS } from "./footer.prompt"
export function RunFooterView(props: RunFooterViewProps) {
const term = useTerminalDimensions()
const active = createMemo<FooterView>(() => props.view?.() ?? { type: "prompt" })
@@ -27,7 +27,7 @@ function todoColor(theme: RunTheme, status: string) {
return status === "in_progress" ? theme.footer.warning : theme.block.muted
}
export function entryGroupKey(commit: StreamCommit): string | undefined {
function entryGroupKey(commit: StreamCommit): string | undefined {
if (!commit.partID) {
return undefined
}
@@ -49,7 +49,7 @@ export function sameEntryGroup(left: StreamCommit | undefined, right: StreamComm
return Boolean(current && next && current === next)
}
export function entryLayout(commit: StreamCommit, body: RunEntryBody = entryBody(commit)): EntryLayout {
function entryLayout(commit: StreamCommit, body: RunEntryBody = entryBody(commit)): EntryLayout {
if (commit.kind === "tool") {
if (body.type === "structured" || body.type === "markdown") {
return "block"
@@ -5,8 +5,6 @@ import { useDialog } from "@tui/ui/dialog"
import { useSync } from "@tui/context/sync"
import { For, Match, Switch, Show, createMemo } from "solid-js"
export type DialogStatusProps = {}
export function DialogStatus() {
const sync = useSync()
const { theme } = useTheme()
@@ -49,7 +49,7 @@ export function recentConnectedWorkspaces<WorkspaceInfo extends { id: string; ti
return { recent, hasMore: recent.length < workspaces.length }
}
export function warpReminderText(dir: string) {
function warpReminderText(dir: string) {
return `<system-reminder>The user has changed the current working directory to "${dir}". This is still the same project but at a possibly new location; take this into account when working with any files from now on.</system-reminder>`
}
@@ -169,7 +169,7 @@ export async function confirmWorkspaceFileChanges(input: {
return fileChangeChoice === "yes"
}
export function DialogWorkspaceSelect(props: {
function DialogWorkspaceSelect(props: {
adapters?: Adapter[]
onSelect: (selection: WorkspaceSelection) => Promise<void> | void
}) {
@@ -878,8 +878,3 @@ export function Logo(props: { shape?: LogoShape; ink?: RGBA; idle?: boolean } =
)
}
export function GoLogo() {
const { theme } = useTheme()
const base = tint(theme.background, theme.text, 0.62)
return <Logo shape={go} ink={base} idle />
}
@@ -543,22 +543,8 @@ export function Autocomplete(props: {
),
)
function insertSlashCommand(name: string) {
const newText = `/${name} `
const cursor = props.input().logicalCursor
props.input().deleteRange(0, 0, cursor.row, cursor.col)
props.input().insertText(newText)
props.input().cursorOffset = Bun.stringWidth(newText)
}
const commands = createMemo((): AutocompleteOption[] => {
const results: AutocompleteOption[] = slashes().map((command) => {
if (!command.input) return command
return {
...command,
onSelect: () => insertSlashCommand(command.name),
}
})
const results: AutocompleteOption[] = [...slashes()]
for (const serverCommand of sync.data.command) {
if (serverCommand.source === "skill") continue
@@ -566,7 +552,13 @@ export function Autocomplete(props: {
results.push({
display: "/" + serverCommand.name + label,
description: serverCommand.description,
onSelect: () => insertSlashCommand(serverCommand.name),
onSelect: () => {
const newText = "/" + serverCommand.name + " "
const cursor = props.input().logicalCursor
props.input().deleteRange(0, 0, cursor.row, cursor.col)
props.input().insertText(newText)
props.input().cursorOffset = Bun.stringWidth(newText)
},
})
}
@@ -59,14 +59,7 @@ import { DialogWorkspaceUnavailable } from "../dialog-workspace-unavailable"
import { useArgs } from "@tui/context/args"
import { Flag } from "@opencode-ai/core/flag/flag"
import { type WorkspaceStatus } from "../workspace-label"
import {
OPENCODE_BASE_MODE,
useBindings,
useCommandShortcut,
useCommandSlashes,
useLeaderActive,
useOpencodeKeymap,
} from "../../keymap"
import { OPENCODE_BASE_MODE, useBindings, useCommandShortcut, useLeaderActive, useOpencodeKeymap } from "../../keymap"
import { useTuiConfig } from "../../context/tui-config"
export type PromptProps = {
@@ -158,7 +151,6 @@ export function Prompt(props: PromptProps) {
const history = usePromptHistory()
const stash = usePromptStash()
const keymap = useOpencodeKeymap()
const slashCommands = useCommandSlashes()
const agentShortcut = useCommandShortcut("agent.cycle")
const paletteShortcut = useCommandShortcut("command.palette.show")
const renderer = useRenderer()
@@ -1140,8 +1132,6 @@ export function Prompt(props: PromptProps) {
]
: []
const slash = parseSlashCommand(inputText)
if (store.mode === "shell") {
void sdk.client.session.shell({
sessionID,
@@ -1153,13 +1143,25 @@ export function Prompt(props: PromptProps) {
command: inputText,
})
setStore("mode", "normal")
} else if (slash && slashCommand(slash.name)?.input) {
slashCommand(slash.name)?.onSelect()
} else if (slash && sync.data.command.some((x) => x.name === slash.name)) {
} else if (
inputText.startsWith("/") &&
iife(() => {
const firstLine = inputText.split("\n")[0]
const command = firstLine.split(" ")[0].slice(1)
return sync.data.command.some((x) => x.name === command)
})
) {
// Parse command from first line, preserve multi-line content in arguments
const firstLineEnd = inputText.indexOf("\n")
const firstLine = firstLineEnd === -1 ? inputText : inputText.slice(0, firstLineEnd)
const [command, ...firstLineArgs] = firstLine.split(" ")
const restOfInput = firstLineEnd === -1 ? "" : inputText.slice(firstLineEnd + 1)
const args = firstLineArgs.join(" ") + (restOfInput ? "\n" + restOfInput : "")
void sdk.client.session.command({
sessionID,
command: slash.name,
arguments: slash.arguments,
command: command.slice(1),
arguments: args,
agent: agent.name,
model: `${selectedModel.providerID}/${selectedModel.modelID}`,
messageID,
@@ -1218,22 +1220,6 @@ export function Prompt(props: PromptProps) {
input.clear()
return true
}
function parseSlashCommand(text: string) {
if (!text.startsWith("/")) return
const firstLineEnd = text.indexOf("\n")
const firstLine = firstLineEnd === -1 ? text : text.slice(0, firstLineEnd)
const [command, ...firstLineArgs] = firstLine.split(" ")
const restOfInput = firstLineEnd === -1 ? "" : text.slice(firstLineEnd + 1)
return {
name: command.slice(1),
arguments: firstLineArgs.join(" ") + (restOfInput ? "\n" + restOfInput : ""),
}
}
function slashCommand(name: string) {
return slashCommands().find((item) => item.name === name || item.aliases?.includes(`/${name}`))
}
const exit = useExit()
function pasteText(text: string, virtualText: string) {
@@ -15,7 +15,7 @@ import { useSDK } from "./sdk"
import { RGBA } from "@opentui/core"
import { Filesystem } from "@/util/filesystem"
export function parseModel(model: string) {
function parseModel(model: string) {
const [providerID, ...rest] = model.split("/")
return {
providerID: providerID,
@@ -2,18 +2,18 @@ import { createStore, reconcile } from "solid-js/store"
import { createSimpleContext } from "./helper"
import type { PromptInfo } from "../component/prompt/history"
export type HomeRoute = {
type HomeRoute = {
type: "home"
prompt?: PromptInfo
}
export type SessionRoute = {
type SessionRoute = {
type: "session"
sessionID: string
prompt?: PromptInfo
}
export type PluginRoute = {
type PluginRoute = {
type: "plugin"
id: string
data?: Record<string, unknown>
@@ -44,8 +44,6 @@ export const { use: useRoute, provider: RouteProvider } = createSimpleContext({
},
})
export type RouteContext = ReturnType<typeof useRoute>
export function useRouteData<T extends Route["type"]>(type: T) {
const route = useRoute()
return route.data as Extract<Route, { type: typeof type }>
@@ -518,7 +518,7 @@ export function tint(base: RGBA, overlay: RGBA, alpha: number): RGBA {
return RGBA.fromInts(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255))
}
export function generateSystem(colors: TerminalColors, mode: "dark" | "light"): ThemeJson {
function generateSystem(colors: TerminalColors, mode: "dark" | "light"): ThemeJson {
const bg = RGBA.fromHex(colors.defaultBackground ?? colors.palette[0]!)
const fg = RGBA.fromHex(colors.defaultForeground ?? colors.palette[7]!)
const transparent = RGBA.fromValues(bg.r, bg.g, bg.b, 0)
@@ -7,7 +7,7 @@ import { buildFileTree, flattenFileTree, type FileTreeItem } from "./diff-viewer
const FILE_TREE_WIDTH = 32
const FILE_TREE_HORIZONTAL_PADDING = 2
export type DiffViewerFileTreeTheme = {
type DiffViewerFileTreeTheme = {
readonly background: ColorInput
readonly backgroundPanel: ColorInput
readonly backgroundElement: ColorInput
+2 -6
View File
@@ -11,7 +11,7 @@ import type { TuiConfig } from "./config/tui"
import { useTuiConfig } from "./context/tui-config"
import { TuiKeybind } from "./config/keybind"
export const LEADER_TOKEN = "leader"
const LEADER_TOKEN = "leader"
export const OPENCODE_BASE_MODE = "base"
export const COMMAND_PALETTE_COMMAND = "command.palette.show"
@@ -25,11 +25,9 @@ export { useBindings, useKeymapSelector }
export type OpenTuiKeymap = ReturnType<typeof useKeymap>
type OpencodeModeStack = ReturnType<typeof createOpencodeModeStack>
type CommandSlashEntry = {
name: string
display: string
description?: string
aliases?: string[]
input?: boolean
onSelect: () => void
}
type Command = ReturnType<OpenTuiKeymap["getCommands"]>[number]
@@ -40,7 +38,7 @@ function isVisiblePaletteCommand(command: Command) {
return command.hidden !== true && command.name !== COMMAND_PALETTE_COMMAND
}
export function createOpencodeModeStack(keymap: OpenTuiKeymap) {
function createOpencodeModeStack(keymap: OpenTuiKeymap) {
keymap.setData(OPENCODE_MODE_KEY, OPENCODE_BASE_MODE)
const offFields = keymap.registerLayerFields({
@@ -258,7 +256,6 @@ export function useCommandSlashes(): Accessor<readonly CommandSlashEntry[]> {
if (typeof slashName !== "string" || !slashName) return []
const slashAliases = entry.command.slashAliases
return {
name: slashName,
display: `/${slashName}`,
description:
typeof entry.command.desc === "string"
@@ -269,7 +266,6 @@ export function useCommandSlashes(): Accessor<readonly CommandSlashEntry[]> {
aliases: Array.isArray(slashAliases)
? slashAliases.filter((alias): alias is string => typeof alias === "string").map((alias) => `/${alias}`)
: undefined,
input: entry.command.slashInput === true,
onSelect: () => keymap.dispatchCommand(entry.command.name),
}
}),
@@ -5,7 +5,7 @@ import { isRecord } from "@/util/record"
type RuntimeSlotMap = TuiSlotMap<Record<string, object>>
type Slot = <Name extends string>(props: TuiSlotProps<Name>) => JSX.Element | null
export type HostSlotPlugin<Slots extends Record<string, object> = {}> = SolidPlugin<TuiSlotMap<Slots>, TuiSlotContext>
type HostSlotPlugin<Slots extends Record<string, object> = {}> = SolidPlugin<TuiSlotMap<Slots>, TuiSlotContext>
export type HostPluginApi = TuiPluginApi
export type HostSlots = {
@@ -314,17 +314,6 @@ export function Session() {
const dialog = useDialog()
const renderer = useRenderer()
function slashArguments(name: string) {
const input = prompt?.current.input
if (!input?.startsWith("/")) return
const firstLineEnd = input.indexOf("\n")
const firstLine = firstLineEnd === -1 ? input : input.slice(0, firstLineEnd)
const [command, ...firstLineArgs] = firstLine.split(" ")
if (command !== `/${name}`) return
const restOfInput = firstLineEnd === -1 ? "" : input.slice(firstLineEnd + 1)
return firstLineArgs.join(" ") + (restOfInput ? "\n" + restOfInput : "")
}
event.on("session.status", (evt) => {
if (evt.properties.sessionID !== route.sessionID) return
if (evt.properties.status.type !== "retry") return
@@ -557,7 +546,6 @@ export function Session() {
slash: {
name: "compact",
aliases: ["summarize"],
input: true,
},
run: () => {
const selectedModel = local.model.current()
@@ -569,14 +557,11 @@ export function Session() {
})
return
}
const instructions = slashArguments("compact") ?? slashArguments("summarize")
const payload = {
void sdk.client.session.summarize({
sessionID: route.sessionID,
modelID: selectedModel.modelID,
providerID: selectedModel.providerID,
...(instructions?.trim() ? { $body_instructions: instructions.trim() } : {}),
} satisfies Parameters<typeof sdk.client.session.summarize>[0] & { $body_instructions?: string }
void sdk.client.session.summarize(payload)
})
dialog.clear()
},
},
@@ -1061,7 +1046,6 @@ export function Session() {
desc: "description" in command ? command.description : undefined,
slashName: "slash" in command ? command.slash?.name : undefined,
slashAliases: "slash" in command ? command.slash?.aliases : undefined,
slashInput: "slash" in command ? command.slash?.input : undefined,
...command,
})),
)
@@ -14,7 +14,7 @@ export type DialogConfirmProps = {
label?: string
}
export type DialogConfirmResult = boolean | undefined
type DialogConfirmResult = boolean | undefined
export function DialogConfirm(props: DialogConfirmProps) {
const dialog = useDialog()
@@ -8,7 +8,7 @@ import { Schema } from "effect"
import { TuiEvent } from "../event"
type ToastInput = Schema.Codec.Encoded<typeof TuiEvent.ToastShow.properties>
export type ToastOptions = Schema.Schema.Type<typeof TuiEvent.ToastShow.properties>
type ToastOptions = Schema.Schema.Type<typeof TuiEvent.ToastShow.properties>
const decodeToastOptions = Schema.decodeUnknownSync(TuiEvent.ToastShow.properties)
@@ -84,7 +84,7 @@ function init() {
return toast
}
export type ToastContext = ReturnType<typeof init>
type ToastContext = ReturnType<typeof init>
const ctx = createContext<ToastContext>()
+76 -39
View File
@@ -19,6 +19,7 @@ import type { ConsoleState } from "./console-state"
import { AppFileSystem } from "@opencode-ai/core/filesystem"
import { InstanceState } from "@/effect/instance-state"
import { Context, Duration, Effect, Exit, Fiber, Layer, Option, Schema } from "effect"
import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
import { EffectFlock } from "@opencode-ai/core/util/effect-flock"
import { containsPath, type InstanceContext } from "../project/instance-context"
import { NonNegativeInt, PositiveInt, type DeepMutable } from "@opencode-ai/core/schema"
@@ -41,6 +42,7 @@ import { ConfigServer } from "./server"
import { ConfigSkills } from "./skills"
import { ConfigVariable } from "./variable"
import { Npm } from "@opencode-ai/core/npm"
import { withTransientReadRetry } from "@/util/effect-http-client"
const log = Log.create({ service: "config" })
@@ -70,14 +72,20 @@ function normalizeLoadedConfig(data: unknown, source: string) {
return copy
}
async function substituteWellKnownRemoteConfig(input: { value: unknown; dir: string; source: string }) {
if (!isRecord(input.value) || typeof input.value.url !== "string") return
async function substituteWellKnownRemoteConfig(input: {
value: unknown
dir: string
source: string
env: Record<string, string>
}) {
if (!isRecord(input.value) || typeof input.value.url !== "string") return undefined
const url = await ConfigVariable.substitute({
text: input.value.url,
type: "virtual",
dir: input.dir,
source: input.source,
env: input.env,
})
const headers = isRecord(input.value.headers)
? Object.fromEntries(
@@ -91,6 +99,7 @@ async function substituteWellKnownRemoteConfig(input: { value: unknown; dir: str
type: "virtual",
dir: input.dir,
source: input.source,
env: input.env,
}),
]),
),
@@ -100,6 +109,11 @@ async function substituteWellKnownRemoteConfig(input: { value: unknown; dir: str
return { url, headers }
}
const WellKnownConfig = Schema.Struct({
config: Schema.optional(Schema.Json),
remote_config: Schema.optional(Schema.Json),
})
async function resolveLoadedPlugins<T extends { plugin?: ConfigPlugin.Spec[] }>(config: T, filepath: string) {
if (!config.plugin) return config
for (let i = 0; i < config.plugin.length; i++) {
@@ -303,7 +317,7 @@ export type Info = DeepMutable<Schema.Schema.Type<typeof Info>> & {
type State = {
config: Info
directories: string[]
deps: Fiber.Fiber<void, never>[]
deps: Fiber.Fiber<void>[]
consoleState: ConsoleState
}
@@ -372,17 +386,38 @@ export const layer = Layer.effect(
const accountSvc = yield* Account.Service
const env = yield* Env.Service
const npmSvc = yield* Npm.Service
const http = yield* HttpClient.HttpClient
const readConfigFile = (filepath: string) => fs.readFileStringSafe(filepath).pipe(Effect.orDie)
const fetchRemoteJson = Effect.fnUntraced(function* <S extends Schema.Top>(
url: string,
headers: Record<string, string> | undefined,
schema: S,
) {
const response = yield* HttpClient.filterStatusOk(withTransientReadRetry(http))
.execute(
HttpClientRequest.get(url).pipe(HttpClientRequest.acceptJson, HttpClientRequest.setHeaders(headers ?? {})),
)
.pipe(
Effect.catch((error) => Effect.die(new Error(`failed to fetch remote config from ${url}: ${String(error)}`))),
)
return yield* HttpClientResponse.schemaBodyJson(schema)(response).pipe(
Effect.catch((error) => Effect.die(new Error(`failed to decode remote config from ${url}: ${String(error)}`))),
)
})
const loadConfig = Effect.fnUntraced(function* (
text: string,
options: { path: string } | { dir: string; source: string },
env?: Record<string, string>,
) {
const source = "path" in options ? options.path : options.source
const expanded = yield* Effect.promise(() =>
ConfigVariable.substitute(
"path" in options ? { text, type: "path", path: options.path } : { text, type: "virtual", ...options },
"path" in options
? { text, type: "path", path: options.path, env }
: { text, type: "virtual", ...options, env },
),
)
const parsed = ConfigParse.jsonc(expanded, source)
@@ -398,14 +433,14 @@ export const layer = Layer.effect(
return data
})
const loadFile = Effect.fnUntraced(function* (filepath: string) {
const loadFile = Effect.fnUntraced(function* (filepath: string, env?: Record<string, string>) {
log.info("loading", { path: filepath })
const text = yield* readConfigFile(filepath)
if (!text) return {} as Info
return yield* loadConfig(text, { path: filepath })
return yield* loadConfig(text, { path: filepath }, env)
})
const loadGlobal = Effect.fnUntraced(function* () {
const loadGlobal = Effect.fnUntraced(function* (env?: Record<string, string>) {
let result: Info = {}
// Seed the default global config with the schema for editor completion, but avoid writing when the user
// explicitly routes config through env-provided paths or content.
@@ -417,9 +452,9 @@ export const layer = Layer.effect(
.pipe(Effect.catch(() => Effect.void))
}
}
result = mergeConfig(result, yield* loadFile(path.join(Global.Path.config, "config.json")))
result = mergeConfig(result, yield* loadFile(path.join(Global.Path.config, "opencode.json")))
result = mergeConfig(result, yield* loadFile(path.join(Global.Path.config, "opencode.jsonc")))
result = mergeConfig(result, yield* loadFile(path.join(Global.Path.config, "config.json"), env))
result = mergeConfig(result, yield* loadFile(path.join(Global.Path.config, "opencode.json"), env))
result = mergeConfig(result, yield* loadFile(path.join(Global.Path.config, "opencode.jsonc"), env))
const legacy = path.join(Global.Path.config, "config")
if (existsSync(legacy)) {
@@ -477,6 +512,7 @@ export const layer = Layer.effect(
const auth = yield* authSvc.all().pipe(Effect.orDie)
let result: Info = {}
const authEnv: Record<string, string> = {}
const consoleManagedProviders = new Set<string>()
let activeOrgName: string | undefined
@@ -516,56 +552,56 @@ export const layer = Layer.effect(
for (const [key, value] of Object.entries(auth)) {
if (value.type === "wellknown") {
const url = key.replace(/\/+$/, "")
process.env[value.key] = value.token
log.debug("fetching remote config", { url: `${url}/.well-known/opencode` })
const response = yield* Effect.promise(() => fetch(`${url}/.well-known/opencode`))
if (!response.ok) {
throw new Error(`failed to fetch remote config from ${url}: ${response.status}`)
}
const wellknown = (yield* Effect.promise(() => response.json())) as {
config?: Record<string, unknown>
remote_config?: unknown
}
authEnv[value.key] = value.token
const wellknownURL = `${url}/.well-known/opencode`
log.debug("fetching remote config", { url: wellknownURL })
const wellknown = yield* fetchRemoteJson(wellknownURL, undefined, WellKnownConfig)
const remote = yield* Effect.promise(() =>
substituteWellKnownRemoteConfig({
value: wellknown.remote_config,
dir: url,
source: `${url}/.well-known/opencode`,
source: wellknownURL,
env: authEnv,
}),
)
const fetchedConfig = remote
? ((yield* Effect.promise(async () => {
? yield* Effect.gen(function* () {
log.debug("fetching remote config", { url: remote.url })
const response = await fetch(remote.url, { headers: remote.headers })
if (!response.ok)
throw new Error(`failed to fetch remote config from ${remote.url}: ${response.status}`)
const data = await response.json()
return isRecord(data) && isRecord(data.config) ? data.config : data
})) as Record<string, unknown>)
const data = yield* fetchRemoteJson(remote.url, remote.headers, Schema.Json)
if (isRecord(data) && isRecord(data.config)) return data.config
if (isRecord(data)) return data
return yield* Effect.die(
new Error(`failed to decode remote config from ${remote.url}: expected object`),
)
})
: {}
const remoteConfig = mergeConfig(wellknown.config ?? {}, fetchedConfig as Info)
const remoteConfig = mergeConfig(isRecord(wellknown.config) ? wellknown.config : {}, fetchedConfig)
if (!remoteConfig.$schema) remoteConfig.$schema = "https://opencode.ai/config.json"
const source = `${url}/.well-known/opencode`
const next = yield* loadConfig(JSON.stringify(remoteConfig), {
dir: path.dirname(source),
source,
})
const source = wellknownURL
const next = yield* loadConfig(
JSON.stringify(remoteConfig),
{
dir: path.dirname(source),
source,
},
authEnv,
)
yield* merge(source, next, "global")
log.debug("loaded remote config from well-known", { url })
}
}
const global = yield* getGlobal()
const global = Object.keys(authEnv).length ? yield* loadGlobal(authEnv) : yield* getGlobal()
yield* merge(Global.Path.config, global, "global")
if (Flag.OPENCODE_CONFIG) {
yield* merge(Flag.OPENCODE_CONFIG, yield* loadFile(Flag.OPENCODE_CONFIG))
yield* merge(Flag.OPENCODE_CONFIG, yield* loadFile(Flag.OPENCODE_CONFIG, authEnv))
log.debug("loaded custom config", { path: Flag.OPENCODE_CONFIG })
}
if (!Flag.OPENCODE_DISABLE_PROJECT_CONFIG) {
for (const file of yield* ConfigPaths.files("opencode", ctx.directory, ctx.worktree).pipe(Effect.orDie)) {
yield* merge(file, yield* loadFile(file), "local")
yield* merge(file, yield* loadFile(file, authEnv), "local")
}
}
@@ -579,14 +615,14 @@ export const layer = Layer.effect(
log.debug("loading config from OPENCODE_CONFIG_DIR", { path: Flag.OPENCODE_CONFIG_DIR })
}
const deps: Fiber.Fiber<void, never>[] = []
const deps: Fiber.Fiber<void>[] = []
for (const dir of directories) {
if (dir.endsWith(".opencode") || dir === Flag.OPENCODE_CONFIG_DIR) {
for (const file of ["opencode.json", "opencode.jsonc"]) {
const source = path.join(dir, file)
log.debug(`loading config from ${source}`)
yield* merge(source, yield* loadFile(source))
yield* merge(source, yield* loadFile(source, authEnv))
result.agent ??= {}
result.mode ??= {}
result.plugin ??= []
@@ -835,6 +871,7 @@ export const defaultLayer = layer.pipe(
Layer.provide(Auth.defaultLayer),
Layer.provide(Account.defaultLayer),
Layer.provide(Npm.defaultLayer),
Layer.provide(FetchHttpClient.layer),
)
export * as Config from "./config"
+3 -2
View File
@@ -19,6 +19,7 @@ type ParseSource =
type SubstituteInput = ParseSource & {
text: string
missing?: "error" | "empty"
env?: Record<string, string>
}
function source(input: ParseSource) {
@@ -33,7 +34,7 @@ function dir(input: ParseSource) {
export async function substitute(input: SubstituteInput) {
const missing = input.missing ?? "error"
let text = input.text.replace(/\{env:([^}]+)\}/g, (_, varName) => {
return process.env[varName] || ""
return (input.env?.[varName] ?? process.env[varName]) || ""
})
const fileMatches = Array.from(text.matchAll(/\{file:[^}]+\}/g))
@@ -46,7 +47,7 @@ export async function substitute(input: SubstituteInput) {
for (const match of fileMatches) {
const token = match[0]
const index = match.index!
const index = match.index
out += text.slice(cursor, index)
const lineStart = text.lastIndexOf("\n", index - 1) + 1
@@ -19,7 +19,7 @@ import {
WorkspaceRoutingQuery,
WorkspaceRoutingQueryFields,
} from "../middleware/workspace-routing"
import { ApiNotFoundError } from "../errors"
import { ApiNotFoundError, SessionBusyError } from "../errors"
import { described } from "./metadata"
import { QueryBoolean } from "./query"
@@ -62,7 +62,6 @@ export const SummarizePayload = Schema.Struct({
providerID: ProviderID,
modelID: ModelID,
auto: Schema.optional(Schema.Boolean),
instructions: Schema.optional(Schema.String),
})
export const PromptPayload = Schema.Struct(Struct.omit(SessionPrompt.PromptInput.fields, ["sessionID"]))
export const CommandPayload = Schema.Struct(Struct.omit(SessionPrompt.CommandInput.fields, ["sessionID"]))
@@ -355,7 +354,7 @@ export const SessionApi = HttpApi.make("session")
query: WorkspaceRoutingQuery,
payload: ShellPayload,
success: described(MessageV2.WithParts, "Created message"),
error: [HttpApiError.BadRequest, ApiNotFoundError],
error: [HttpApiError.BadRequest, ApiNotFoundError, SessionBusyError],
}).annotateMerge(
OpenApi.annotations({
identifier: "session.shell",
@@ -368,7 +367,7 @@ export const SessionApi = HttpApi.make("session")
query: WorkspaceRoutingQuery,
payload: RevertPayload,
success: described(Session.Info, "Updated session"),
error: [HttpApiError.BadRequest, ApiNotFoundError],
error: [HttpApiError.BadRequest, ApiNotFoundError, SessionBusyError],
}).annotateMerge(
OpenApi.annotations({
identifier: "session.revert",
@@ -381,7 +380,7 @@ export const SessionApi = HttpApi.make("session")
params: { sessionID: SessionID },
query: WorkspaceRoutingQuery,
success: described(Session.Info, "Updated session"),
error: [HttpApiError.BadRequest, ApiNotFoundError],
error: [HttpApiError.BadRequest, ApiNotFoundError, SessionBusyError],
}).annotateMerge(
OpenApi.annotations({
identifier: "session.unrevert",
@@ -407,7 +406,7 @@ export const SessionApi = HttpApi.make("session")
params: { sessionID: SessionID, messageID: MessageID },
query: WorkspaceRoutingQuery,
success: described(Schema.Boolean, "Successfully deleted message"),
error: [HttpApiError.BadRequest, ApiNotFoundError],
error: [HttpApiError.BadRequest, ApiNotFoundError, SessionBusyError],
}).annotateMerge(
OpenApi.annotations({
identifier: "session.deleteMessage",
@@ -2,7 +2,7 @@ import { SessionID } from "@/session/schema"
import { SessionMessage } from "@opencode-ai/core/session-message"
import { Schema } from "effect"
import { HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
import { InvalidCursorError, SessionNotFoundError } from "../../errors"
import { InvalidCursorError, SessionNotFoundError, UnknownError } from "../../errors"
import { V2Authorization } from "../../middleware/authorization"
import { WorkspaceRoutingQueryFields } from "../../middleware/workspace-routing"
@@ -36,7 +36,7 @@ export const MessageGroup = HttpApiGroup.make("v2.message")
next: Schema.String.pipe(Schema.optional),
}),
}).annotate({ identifier: "V2SessionMessagesResponse" }),
error: [InvalidCursorError, SessionNotFoundError],
error: [InvalidCursorError, SessionNotFoundError, UnknownError],
}).annotateMerge(
OpenApi.annotations({
identifier: "v2.session.messages",
@@ -4,7 +4,13 @@ import { Prompt } from "@opencode-ai/core/session-prompt"
import { SessionV2 } from "@/v2/session"
import { Schema } from "effect"
import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema, OpenApi } from "effect/unstable/httpapi"
import { InvalidCursorError, InvalidRequestError, ServiceUnavailableError, SessionNotFoundError } from "../../errors"
import {
InvalidCursorError,
InvalidRequestError,
ServiceUnavailableError,
SessionNotFoundError,
UnknownError,
} from "../../errors"
import { V2Authorization } from "../../middleware/authorization"
import { WorkspaceRoutingQuery, WorkspaceRoutingQueryFields } from "../../middleware/workspace-routing"
import { QueryBoolean } from "../query"
@@ -103,7 +109,7 @@ export const SessionGroup = HttpApiGroup.make("v2.session")
params: { sessionID: SessionID },
query: WorkspaceRoutingQuery,
success: Schema.Array(SessionMessage.Message),
error: SessionNotFoundError,
error: [SessionNotFoundError, UnknownError],
}).annotateMerge(
OpenApi.annotations({
identifier: "v2.session.context",
@@ -1,7 +1,6 @@
import type { NotFoundError as StorageNotFoundError } from "@/storage/storage"
import type { Session } from "@/session/session"
import { Effect } from "effect"
import { HttpApiError } from "effect/unstable/httpapi"
import * as ApiError from "../errors"
export function mapStorageNotFound<A, R>(self: Effect.Effect<A, StorageNotFoundError, R>) {
@@ -9,5 +8,14 @@ export function mapStorageNotFound<A, R>(self: Effect.Effect<A, StorageNotFoundE
}
export function mapBusy<A, R>(self: Effect.Effect<A, Session.BusyError, R>) {
return self.pipe(Effect.catchTag("SessionBusyError", () => Effect.fail(new HttpApiError.BadRequest({}))))
return self.pipe(
Effect.catchTag("SessionBusyError", (error) =>
Effect.fail(
new ApiError.SessionBusyError({
sessionID: error.sessionID,
message: `Session is busy: ${error.sessionID}`,
}),
),
),
)
}
@@ -277,7 +277,6 @@ export const sessionHandlers = HttpApiBuilder.group(InstanceHttpApi, "session",
modelID: ctx.payload.modelID,
},
auto: ctx.payload.auto ?? false,
instructions: ctx.payload.instructions,
})
yield* promptSvc.loop({ sessionID: ctx.params.sessionID })
return true
@@ -4,7 +4,7 @@ import { Effect, Schema } from "effect"
import * as DateTime from "effect/DateTime"
import { HttpApiBuilder } from "effect/unstable/httpapi"
import { InstanceHttpApi } from "../../api"
import { InvalidCursorError, SessionNotFoundError } from "../../errors"
import { InvalidCursorError, SessionNotFoundError, UnknownError } from "../../errors"
const DefaultMessagesLimit = 50
@@ -58,6 +58,20 @@ export const messageHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.message
}),
),
),
Effect.catchTag("Session.MessageDecodeError", (error) => {
const ref = `err_${crypto.randomUUID().slice(0, 8)}`
return Effect.logError("failed to decode v2 session message").pipe(
Effect.annotateLogs({ ref, sessionID: error.sessionID, messageID: error.messageID }),
Effect.andThen(
Effect.fail(
new UnknownError({
message: "Unexpected server error. Check server logs for details.",
ref,
}),
),
),
)
}),
)
const first = messages[0]
const last = messages.at(-1)
@@ -3,7 +3,13 @@ import { SessionV2 } from "@/v2/session"
import { DateTime, Effect, Option, Schema } from "effect"
import { HttpApiBuilder, HttpApiSchema } from "effect/unstable/httpapi"
import { InstanceHttpApi } from "../../api"
import { InvalidCursorError, InvalidRequestError, ServiceUnavailableError, SessionNotFoundError } from "../../errors"
import {
InvalidCursorError,
InvalidRequestError,
ServiceUnavailableError,
SessionNotFoundError,
UnknownError,
} from "../../errors"
const DefaultSessionsLimit = 50
@@ -219,6 +225,20 @@ export const sessionHandlers = HttpApiBuilder.group(InstanceHttpApi, "v2.session
}),
),
),
Effect.catchTag("Session.MessageDecodeError", (error) => {
const ref = `err_${crypto.randomUUID().slice(0, 8)}`
return Effect.logError("failed to decode v2 session message").pipe(
Effect.annotateLogs({ ref, sessionID: error.sessionID, messageID: error.messageID }),
Effect.andThen(
Effect.fail(
new UnknownError({
message: "Unexpected server error. Check server logs for details.",
ref,
}),
),
),
)
}),
)
}),
)
@@ -1,6 +1,5 @@
import { NamedError } from "@opencode-ai/core/util/error"
import * as Log from "@opencode-ai/core/util/log"
import { ConfigError } from "@/config/error"
import { Cause, Effect } from "effect"
import { HttpRouter, HttpServerError, HttpServerRespondable, HttpServerResponse } from "effect/unstable/http"
@@ -19,19 +18,15 @@ export const errorLayer = HttpRouter.middleware<{ handles: unknown }>()((effect)
if (!defect) return Effect.failCause(cause)
const error = defect.defect
if (
error instanceof NamedError &&
(ConfigError.InvalidError.isInstance(error) || ConfigError.JsonError.isInstance(error))
) {
return Effect.succeed(HttpServerResponse.jsonUnsafe(error.toObject(), { status: 400 }))
}
const ref = `err_${crypto.randomUUID().slice(0, 8)}`
log.error("failed", { error, cause: Cause.pretty(cause) })
log.error("failed", { ref, error, cause: Cause.pretty(cause) })
return Effect.succeed(
HttpServerResponse.jsonUnsafe(
new NamedError.Unknown({
message: "Unexpected server error. Check server logs for details.",
ref,
}).toObject(),
{ status: 500 },
),
@@ -131,7 +131,7 @@ function proxyRemote(
const response = yield* HttpApiProxy.http(client, proxyURL, target.headers, request)
const sync = Fence.parse(new Headers(response.headers))
if (sync) {
const syncFailure = yield* Fence.waitEffect(
const syncFailure = yield* Fence.wait(
workspace.id,
sync,
request.source instanceof Request ? request.source.signal : undefined,
+7 -13
View File
@@ -4,7 +4,6 @@ import { EventSequenceTable } from "@/sync/event.sql"
import { Workspace } from "@/control-plane/workspace"
import type { WorkspaceID } from "@/control-plane/schema"
import * as Log from "@opencode-ai/core/util/log"
import { AppRuntime } from "@/effect/app-runtime"
import { Effect } from "effect"
export const HEADER = "x-opencode-sync"
@@ -20,7 +19,7 @@ export function load(ids?: string[]) {
return db.select().from(EventSequenceTable).where(inArray(EventSequenceTable.aggregate_id, ids)).all()
})
return Object.fromEntries(rows.map((row) => [row.aggregate_id, row.seq])) as State
return Object.fromEntries(rows.map((row) => [row.aggregate_id, row.seq]))
}
export function diff(prev: State, next: State) {
@@ -31,15 +30,14 @@ export function diff(prev: State, next: State) {
.filter(([id, seq]) => {
return (prev[id] ?? -1) !== seq
}),
) as State
)
}
export function parse(headers: Headers) {
export function parse(headers: Headers): State | undefined {
const raw = headers.get(HEADER)
if (!raw) return
let data
try {
data = JSON.parse(raw)
} catch {
@@ -49,13 +47,13 @@ export function parse(headers: Headers) {
if (!data || typeof data !== "object") return
return Object.fromEntries(
Object.entries(data).filter(([id, seq]) => {
return typeof id === "string" && Number.isInteger(seq)
Object.entries(data).filter((entry): entry is [string, number] => {
return typeof entry[0] === "string" && Number.isInteger(entry[1])
}),
) as State
)
}
export function waitEffect(workspaceID: WorkspaceID, state: State, signal?: AbortSignal) {
export function wait(workspaceID: WorkspaceID, state: State, signal?: AbortSignal) {
return Effect.gen(function* () {
log.info("waiting for state", {
workspaceID,
@@ -68,7 +66,3 @@ export function waitEffect(workspaceID: WorkspaceID, state: State, signal?: Abor
})
})
}
export async function wait(workspaceID: WorkspaceID, state: State, signal?: AbortSignal) {
await AppRuntime.runPromise(waitEffect(workspaceID, state, signal))
}
+1 -13
View File
@@ -133,12 +133,6 @@ function buildPrompt(input: { previousSummary?: string; context: string[] }) {
return [anchor, SUMMARY_TEMPLATE, ...input.context].join("\n\n")
}
function appendInstructions(prompt: string, instructions: string | undefined) {
const trimmed = instructions?.trim()
if (!trimmed) return prompt
return [prompt, "Additional user instructions for this compaction:", trimmed].join("\n\n")
}
function preserveRecentBudget(input: { cfg: Config.Info; model: Provider.Model }) {
return (
input.cfg.compaction?.preserve_recent_tokens ??
@@ -208,7 +202,6 @@ export interface Interface {
model: { providerID: ProviderID; modelID: ModelID }
auto: boolean
overflow?: boolean
instructions?: string
}) => Effect.Effect<void>
}
@@ -407,10 +400,7 @@ export const layer = Layer.effect(
{ sessionID: input.sessionID },
{ context: [], prompt: undefined },
)
const nextPrompt = appendInstructions(
compacting.prompt ?? buildPrompt({ previousSummary, context: compacting.context }),
compactionPart?.instructions,
)
const nextPrompt = compacting.prompt ?? buildPrompt({ previousSummary, context: compacting.context })
const msgs = structuredClone(selected.head)
yield* plugin.trigger("experimental.chat.messages.transform", {}, { messages: msgs })
const modelMessages = yield* MessageV2.toModelMessagesEffect(msgs, model, {
@@ -597,7 +587,6 @@ export const layer = Layer.effect(
model: { providerID: ProviderID; modelID: ModelID }
auto: boolean
overflow?: boolean
instructions?: string
}) {
const msg = yield* session.updateMessage({
id: MessageID.ascending(),
@@ -614,7 +603,6 @@ export const layer = Layer.effect(
type: "compaction",
auto: input.auto,
overflow: input.overflow,
instructions: input.instructions,
})
if (flags.experimentalEventSystem) {
yield* events.publish(SessionEvent.Compaction.Started, {
@@ -42,10 +42,15 @@ const providerMetadata = (value: unknown): ProviderMetadata | undefined => {
return Object.keys(result).length === 0 ? undefined : result
}
// Stored AI SDK parts historically kept provider-owned continuation metadata in
// `providerOptions`; native parts now use `providerMetadata` directly.
const partProviderMetadata = (part: Record<string, unknown>) =>
providerMetadata(part.providerMetadata) ?? providerMetadata(part.providerOptions)
const textPart = (part: Record<string, unknown>) => ({
type: "text" as const,
text: typeof part.text === "string" ? part.text : "",
providerMetadata: providerMetadata(part.providerOptions),
providerMetadata: partProviderMetadata(part),
})
const mediaPart = (part: Record<string, unknown>) => {
@@ -68,7 +73,7 @@ const toolResult = (part: Record<string, unknown>) => {
result: "value" in output ? output.value : output,
resultType: type,
providerExecuted: typeof part.providerExecuted === "boolean" ? part.providerExecuted : undefined,
providerMetadata: providerMetadata(part.providerOptions),
providerMetadata: partProviderMetadata(part),
})
}
@@ -80,7 +85,7 @@ const contentPart = (part: unknown) => {
return {
type: "reasoning" as const,
text: typeof part.text === "string" ? part.text : "",
providerMetadata: providerMetadata(part.providerOptions),
providerMetadata: partProviderMetadata(part),
}
if (part.type === "tool-call")
return ToolCallPart.make({
@@ -88,7 +93,7 @@ const contentPart = (part: unknown) => {
name: typeof part.toolName === "string" ? part.toolName : "",
input: part.input,
providerExecuted: typeof part.providerExecuted === "boolean" ? part.providerExecuted : undefined,
providerMetadata: providerMetadata(part.providerOptions),
providerMetadata: partProviderMetadata(part),
})
if (part.type === "tool-result") return toolResult(part)
throw new Error(`Native LLM request adapter does not support ${String(part.type)} content parts`)
@@ -186,7 +186,6 @@ export const CompactionPart = Schema.Struct({
type: Schema.Literal("compaction"),
auto: Schema.Boolean,
overflow: Schema.optional(Schema.Boolean),
instructions: Schema.optional(Schema.String),
tail_start_id: Schema.optional(MessageID),
}).annotate({ identifier: "CompactionPart" })
export type CompactionPart = Types.DeepMutable<Schema.Schema.Type<typeof CompactionPart>>
+22 -7
View File
@@ -72,6 +72,11 @@ export class OperationUnavailableError extends Schema.TaggedErrorClass<Operation
},
) {}
export class MessageDecodeError extends Schema.TaggedErrorClass<MessageDecodeError>()("Session.MessageDecodeError", {
sessionID: SessionID,
messageID: SessionMessage.ID,
}) {}
export interface Interface {
readonly create: (input?: {
agent?: string
@@ -104,8 +109,10 @@ export interface Interface {
time: number
direction: "previous" | "next"
}
}) => Effect.Effect<SessionMessage.Message[], NotFoundError>
readonly context: (sessionID: SessionID) => Effect.Effect<SessionMessage.Message[], NotFoundError>
}) => Effect.Effect<SessionMessage.Message[], NotFoundError | MessageDecodeError>
readonly context: (
sessionID: SessionID,
) => Effect.Effect<SessionMessage.Message[], NotFoundError | MessageDecodeError>
readonly prompt: (input: {
id?: EventV2.ID
sessionID: SessionID
@@ -120,7 +127,7 @@ export interface Interface {
prompt: Prompt
agent: string
model?: ModelV2.Ref
}) => Effect.Effect<void, NotFoundError | OperationUnavailableError>
}) => Effect.Effect<void, NotFoundError | OperationUnavailableError | MessageDecodeError>
readonly switchAgent: (input: { sessionID: SessionID; agent: string }) => Effect.Effect<void, never>
readonly switchModel: (input: { sessionID: SessionID; model: ModelV2.Ref }) => Effect.Effect<void, never>
readonly compact: (sessionID: SessionID) => Effect.Effect<void, NotFoundError | OperationUnavailableError>
@@ -133,10 +140,18 @@ export const layer = Layer.effect(
Service,
Effect.gen(function* () {
const events = yield* EventV2Bridge.Service
const decodeMessage = Schema.decodeUnknownSync(SessionMessage.Message)
const decodeMessage = Schema.decodeUnknownEffect(SessionMessage.Message)
const decode = (row: typeof SessionMessageTable.$inferSelect) =>
decodeMessage({ ...row.data, id: row.id, type: row.type })
decodeMessage({ ...row.data, id: row.id, type: row.type }).pipe(
Effect.mapError(
() =>
new MessageDecodeError({
sessionID: SessionID.make(row.session_id),
messageID: SessionMessage.ID.make(row.id),
}),
),
)
function fromRow(row: typeof SessionTable.$inferSelect): Info {
return new Info({
@@ -262,7 +277,7 @@ export const layer = Layer.effect(
const rows = input.limit === undefined ? query.all() : query.limit(input.limit).all()
return direction === "previous" ? rows.toReversed() : rows
})
return rows.map((row) => decode(row))
return yield* Effect.forEach(rows, (row) => decode(row))
}),
context: Effect.fn("V2Session.context")(function* (sessionID) {
yield* result.get(sessionID)
@@ -295,7 +310,7 @@ export const layer = Layer.effect(
.orderBy(asc(SessionMessageTable.time_created), asc(SessionMessageTable.id))
.all()
})
return rows.map((row) => decode(row))
return yield* Effect.forEach(rows, (row) => decode(row))
}),
prompt: Effect.fn("V2Session.prompt")(function* (input) {
yield* result.get(input.sessionID)
@@ -1,6 +1,7 @@
import { expect } from "bun:test"
import { AppFileSystem } from "@opencode-ai/core/filesystem"
import { Effect, Layer } from "effect"
import { FetchHttpClient } from "effect/unstable/http"
import path from "path"
import { pathToFileURL } from "url"
import { Agent } from "../../src/agent/agent"
@@ -29,6 +30,7 @@ const configLayer = Config.layer.pipe(
Layer.provide(AuthTest.empty),
Layer.provide(AccountTest.empty),
Layer.provide(NpmTest.noop),
Layer.provide(FetchHttpClient.layer),
)
const pluginLayer = Plugin.layer.pipe(
Layer.provide(Bus.layer),
@@ -9,7 +9,7 @@ import { SDKProvider, type EventSource } from "../../../../src/cli/cmd/tui/conte
import { SyncProvider, useSync } from "../../../../src/cli/cmd/tui/context/sync"
import type { GlobalEvent } from "@opencode-ai/sdk/v2"
export const worktree = "/tmp/opencode"
const worktree = "/tmp/opencode"
export const directory = `${worktree}/packages/opencode`
export async function wait(fn: () => boolean, timeout = 2000) {
@@ -27,11 +27,7 @@ export function json(data: unknown, init?: ResponseInit) {
})
}
export function eventSource(): EventSource {
return { subscribe: async () => () => {} }
}
export function createEventSource() {
function createEventSource() {
let fn: ((event: GlobalEvent) => void) | undefined
return {
@@ -52,7 +48,7 @@ export function createEventSource() {
type FetchHandler = (url: URL) => Response | Promise<Response> | undefined
export function createFetch(override?: FetchHandler) {
function createFetch(override?: FetchHandler) {
const session = [] as URL[]
const fetch = (async (input: RequestInfo | URL) => {
const url = new URL(input instanceof Request ? input.url : String(input))
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -207,6 +207,12 @@ export function provideTmpdirInstance<A, E, R>(
export class TestInstance extends Context.Service<TestInstance, { readonly directory: string }>()("@test/Instance") {}
export const requireInstance = Effect.gen(function* () {
const instance = yield* InstanceRef
if (!instance) return yield* Effect.die(new Error("missing test instance"))
return instance
})
export const withTmpdirInstance =
(options?: { git?: boolean; config?: Partial<Config.Info> | (() => Partial<Config.Info>) }) =>
<A, E, R>(self: Effect.Effect<A, E, R>) =>
@@ -11,7 +11,7 @@ type ResolvedInput = Omit<TuiConfig.Resolved, "attention" | "keybinds" | "leader
leader_timeout?: number
}
export function createTuiResolvedKeybinds(input: Partial<TuiKeybind.Keybinds> = {}): TuiConfig.Resolved["keybinds"] {
function createTuiResolvedKeybinds(input: Partial<TuiKeybind.Keybinds> = {}): TuiConfig.Resolved["keybinds"] {
const keybinds = TuiKeybind.Keybinds.parse(input)
return createBindingLookup(TuiKeybind.toBindingConfig(keybinds), {
commandMap: TuiKeybind.CommandMap,
+2 -2
View File
@@ -31,7 +31,7 @@ import { it } from "./effect"
const opencodeRoot = path.resolve(import.meta.dir, "../../")
const cliEntry = path.join(opencodeRoot, "src/index.ts")
export const testModelID = "test/test-model"
const testModelID = "test/test-model"
// Wrap a Bun subprocess pipe (or any ReadableStream<Uint8Array>) as a Stream.
// Centralizes the `evaluate` + `onError` boilerplate and tags errors with the
@@ -177,7 +177,7 @@ export type CliFixture = {
// up the tmpdir on scope exit. TestLLMServer.layer is provided internally so
// the caller doesn't need to wire it up — the fixture's lifetime is tied to
// the surrounding Scope.
export function withCliFixture<A, E>(
function withCliFixture<A, E>(
fn: (input: CliFixture) => Effect.Effect<A, E, Scope.Scope | HttpClient.HttpClient>,
): Effect.Effect<A, E | unknown, Scope.Scope> {
return Effect.gen(function* () {
+2 -2
View File
@@ -449,7 +449,7 @@ function fail(item: HttpError) {
})
}
export class Reply {
class Reply {
#head: unknown[] = [role()]
#tail: unknown[] = []
#usage: Usage | undefined
@@ -557,7 +557,7 @@ export function reply() {
return new Reply()
}
export function httpError(status: number, body: unknown): Item {
function httpError(status: number, body: unknown): Item {
return {
type: "http-error",
status,
+2 -11
View File
@@ -21,25 +21,16 @@ const REAL_TMP = fs.realpathSync(TMP)
* snapshots captured on macOS/Linux contain LF, so a Windows run without
* this step always diffs.
*/
export function stripCrlf(text: string): string {
function stripCrlf(text: string): string {
return text.replaceAll("\r\n", "\n")
}
/**
* Converts Windows-style `\` separators to POSIX `/` so paths render
* identically across OSes. Use for path strings you want stable in a
* snapshot, not for filesystem operations.
*/
export function toPosixPath(p: string): string {
return p.replaceAll("\\", "/")
}
/**
* Strips both the OS-level `os.tmpdir()` and its realpath form (macOS
* `/var/folders` `/private/var/folders`) from text, replacing each
* occurrence with `marker` (default `<TMPDIR>`).
*/
export function withTmpdirStripped(text: string, marker = "<TMPDIR>"): string {
function withTmpdirStripped(text: string, marker = "<TMPDIR>"): string {
return text.replaceAll(REAL_TMP, marker).replaceAll(TMP, marker)
}
+7 -12
View File
@@ -1,12 +1,11 @@
import { describe, expect } from "bun:test"
import { Effect, Layer, Option } from "effect"
import { Effect, Layer } from "effect"
import { FetchHttpClient } from "effect/unstable/http"
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
import { AppFileSystem } from "@opencode-ai/core/filesystem"
import { EffectFlock } from "@opencode-ai/core/util/effect-flock"
import path from "path"
import { pathToFileURL } from "url"
import { Account } from "../../src/account/account"
import { Auth } from "../../src/auth"
import { Bus } from "../../src/bus"
import { Config } from "../../src/config/config"
import { Env } from "../../src/env"
@@ -15,22 +14,18 @@ import { Plugin } from "../../src/plugin/index"
import { ModelID, ProviderID } from "../../src/provider/schema"
import { provideTmpdirInstance } from "../fixture/fixture"
import { testEffect } from "../lib/effect"
import { AccountTest } from "../fake/account"
import { AuthTest } from "../fake/auth"
import { NpmTest } from "../fake/npm"
const emptyAccount = Layer.mock(Account.Service)({
active: () => Effect.succeed(Option.none()),
activeOrg: () => Effect.succeed(Option.none()),
})
const emptyAuth = Layer.mock(Auth.Service)({
all: () => Effect.succeed({}),
})
const configLayer = Config.layer.pipe(
Layer.provide(EffectFlock.defaultLayer),
Layer.provide(AppFileSystem.defaultLayer),
Layer.provide(Env.defaultLayer),
Layer.provide(emptyAuth),
Layer.provide(emptyAccount),
Layer.provide(AuthTest.empty),
Layer.provide(AccountTest.empty),
Layer.provide(NpmTest.noop),
Layer.provide(FetchHttpClient.layer),
)
const it = testEffect(
Layer.mergeAll(
@@ -1,12 +1,11 @@
import { afterEach, describe, expect } from "bun:test"
import { Effect, Layer, Option } from "effect"
import { Effect, Layer } from "effect"
import { FetchHttpClient } from "effect/unstable/http"
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
import { AppFileSystem } from "@opencode-ai/core/filesystem"
import { EffectFlock } from "@opencode-ai/core/util/effect-flock"
import path from "path"
import { pathToFileURL } from "url"
import { Account } from "../../src/account/account"
import { Auth } from "../../src/auth"
import { Bus } from "../../src/bus"
import { Config } from "../../src/config/config"
@@ -24,22 +23,18 @@ import { SessionPrompt } from "../../src/session/prompt"
import { SyncEvent } from "../../src/sync"
import { disposeAllInstances, provideTmpdirInstance } from "../fixture/fixture"
import { testEffect } from "../lib/effect"
import { AccountTest } from "../fake/account"
import { AuthTest } from "../fake/auth"
import { NpmTest } from "../fake/npm"
const emptyAccount = Layer.mock(Account.Service)({
active: () => Effect.succeed(Option.none()),
activeOrg: () => Effect.succeed(Option.none()),
})
const emptyAuth = Layer.mock(Auth.Service)({
all: () => Effect.succeed({}),
})
const configLayer = Config.layer.pipe(
Layer.provide(EffectFlock.defaultLayer),
Layer.provide(AppFileSystem.defaultLayer),
Layer.provide(Env.defaultLayer),
Layer.provide(emptyAuth),
Layer.provide(emptyAccount),
Layer.provide(AuthTest.empty),
Layer.provide(AccountTest.empty),
Layer.provide(NpmTest.noop),
Layer.provide(FetchHttpClient.layer),
)
const pluginLayer = Plugin.layer.pipe(
Layer.provide(Bus.layer),
@@ -10,6 +10,14 @@ import { testEffect } from "../lib/effect"
const it = testEffect(Layer.mergeAll(NodeHttpServer.layerTest, NodeServices.layer))
function expectUnknownErrorBody(body: unknown) {
expect(body).toMatchObject({
name: "UnknownError",
data: { message: "Unexpected server error. Check server logs for details." },
})
expect((body as { data?: { ref?: unknown } }).data?.ref).toMatch(/^err_[0-9a-f-]{8}$/)
}
describe("HttpApi error middleware", () => {
it.live("returns a safe body for unknown 500 defects", () =>
Effect.gen(function* () {
@@ -23,10 +31,7 @@ describe("HttpApi error middleware", () => {
const body = yield* response.json
expect(response.status).toBe(500)
expect(body).toEqual({
name: "UnknownError",
data: { message: "Unexpected server error. Check server logs for details." },
})
expectUnknownErrorBody(body)
expect(JSON.stringify(body)).not.toContain("secret stack marker")
}),
)
@@ -43,15 +48,12 @@ describe("HttpApi error middleware", () => {
const body = yield* response.json
expect(response.status).toBe(500)
expect(body).toEqual({
name: "UnknownError",
data: { message: "Unexpected server error. Check server logs for details." },
})
expectUnknownErrorBody(body)
expect(JSON.stringify(body)).not.toContain("secret named marker")
}),
)
it.live("preserves config defects as client-visible bad requests", () =>
it.live("does not expose config defects from generic middleware", () =>
Effect.gen(function* () {
const configError = new ConfigError.InvalidError({
path: "/tmp/opencode.json",
@@ -66,9 +68,13 @@ describe("HttpApi error middleware", () => {
const response = yield* HttpClientRequest.get("/config-error").pipe(HttpClient.execute)
const body = yield* response.json
const serialized = JSON.stringify(body)
expect(response.status).toBe(400)
expect(JSON.stringify(body)).toBe(JSON.stringify(configError.toObject()))
expect(response.status).toBe(500)
expectUnknownErrorBody(body)
expect(serialized).not.toContain("/tmp/opencode.json")
expect(serialized).not.toContain("provider")
expect(serialized).not.toContain("anthropic")
}),
)
@@ -84,10 +90,7 @@ describe("HttpApi error middleware", () => {
const body = yield* response.json
expect(response.status).toBe(500)
expect(body).toEqual({
name: "UnknownError",
data: { message: "Unexpected server error. Check server logs for details." },
})
expectUnknownErrorBody(body)
}),
)
})
@@ -11,7 +11,6 @@ import type {
RequestSpec,
ScenarioContext,
SeededContext,
TodoScenario,
} from "./types"
class ScenarioBuilder<S = undefined> {
@@ -186,14 +185,6 @@ export const http = {
ticketBypass: routes("ticket-bypass"),
}
export const pending = (method: Method, path: string, name: string, reason: string): TodoScenario => ({
kind: "todo",
method,
path,
name,
reason,
})
export function route(template: string, params: Record<string, string>) {
return Object.entries(params).reduce(
(next, [key, value]) => next.replaceAll(`{${key}}`, value).replaceAll(`:${key}`, value),
@@ -55,7 +55,7 @@ export function parseOptions(args: string[]): Options {
}
}
export function matches(options: Options, scenario: Scenario) {
function matches(options: Options, scenario: Scenario) {
if (!options.include) return true
return (
scenario.name.includes(options.include) ||
@@ -6,7 +6,7 @@ import type { MessageV2 } from "../../../src/session/message-v2"
import type { SessionID } from "../../../src/session/schema"
export const OpenApiMethods = ["get", "post", "put", "delete", "patch"] as const
export const Methods = ["GET", "POST", "PUT", "DELETE", "PATCH"] as const
const Methods = ["GET", "POST", "PUT", "DELETE", "PATCH"] as const
export type Method = (typeof Methods)[number]
export type OpenApiMethod = (typeof OpenApiMethods)[number]
@@ -129,4 +129,32 @@ describe("PublicApi OpenAPI v2 errors", () => {
)
}
})
test("documents v2 session read data errors", () => {
const spec = OpenApi.fromApi(PublicApi) as OpenApiSpec
for (const route of [
["get", "/api/session/{sessionID}/context"],
["get", "/api/session/{sessionID}/message"],
] as const) {
expect(componentName(responseRef(spec.paths[route[1]]?.[route[0]]?.responses?.["500"]) ?? "")).toMatch(
/^UnknownError\d*$/,
)
}
})
test("documents session busy errors", () => {
const spec = OpenApi.fromApi(PublicApi) as OpenApiSpec
for (const route of [
["post", "/session/{sessionID}/shell"],
["post", "/session/{sessionID}/revert"],
["post", "/session/{sessionID}/unrevert"],
["delete", "/session/{sessionID}/message/{messageID}"],
] as const) {
expect(componentName(responseRef(spec.paths[route[1]]?.[route[0]]?.responses?.["409"]) ?? "")).toBe(
"SessionBusyError",
)
}
})
})

Some files were not shown because too many files have changed in this diff Show More