mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-03 16:56:33 -04:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| caf3263f3f |
@@ -178,7 +178,6 @@
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.17.11",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
"@opencode-ai/console-mail": "workspace:*",
|
||||
"@opencode-ai/console-resource": "workspace:*",
|
||||
@@ -317,7 +316,6 @@
|
||||
"ai-gateway-provider": "3.1.2",
|
||||
"bun-pty": "0.4.8",
|
||||
"cross-spawn": "catalog:",
|
||||
"diff": "catalog:",
|
||||
"drizzle-orm": "catalog:",
|
||||
"effect": "catalog:",
|
||||
"fuzzysort": "3.1.0",
|
||||
@@ -1051,7 +1049,6 @@
|
||||
"@modelcontextprotocol/sdk@1.29.0": "patches/@modelcontextprotocol%2Fsdk@1.29.0.patch",
|
||||
"gcp-metadata@8.1.2": "patches/gcp-metadata@8.1.2.patch",
|
||||
"@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch",
|
||||
"effect@4.0.0-beta.83": "patches/effect@4.0.0-beta.83.patch",
|
||||
"@npmcli/agent@4.0.2": "patches/@npmcli%2Fagent@4.0.2.patch",
|
||||
"@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch",
|
||||
"@tanstack/solid-virtual@3.13.28": "patches/@tanstack%2Fsolid-virtual@3.13.28.patch",
|
||||
@@ -1259,8 +1256,6 @@
|
||||
|
||||
"@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=="],
|
||||
|
||||
"@aws-sdk/client-sts": ["@aws-sdk/client-sts@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/credential-provider-node": "3.782.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-Q1QLY3xE2z1trgriusP/6w40mI/yJjM524bN4gs+g6YX4sZGufpa7+Dj+JjL4fz8f9BCJ3ZlI+p4WxFxH7qvdQ=="],
|
||||
|
||||
"@aws-sdk/core": ["@aws-sdk/core@3.932.0", "", { "dependencies": { "@aws-sdk/types": "3.930.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.2", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.5", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-AS8gypYQCbNojwgjvZGkJocC2CoEICDx9ZJ15ILsv+MlcCVLtUJSRSx3VzJOUY2EEIaGLRrPNlIqyn/9/fySvA=="],
|
||||
|
||||
"@aws-sdk/credential-provider-cognito-identity": ["@aws-sdk/credential-provider-cognito-identity@3.972.38", "", { "dependencies": { "@aws-sdk/nested-clients": "^3.997.13", "@aws-sdk/types": "^3.973.9", "@smithy/core": "^3.24.5", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-OHkK6xOx/IHkSbQdDWxnVCLU+j28EFl8wyWgBILQDFAPY8n240C/O4gjmFx+zFU12lL8njgJQ5GWAIWq88CnSQ=="],
|
||||
@@ -3703,7 +3698,7 @@
|
||||
|
||||
"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=="],
|
||||
|
||||
"fast-xml-parser": ["fast-xml-parser@4.4.1", "", { "dependencies": { "strnum": "^1.0.5" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw=="],
|
||||
"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=="],
|
||||
|
||||
"fastify": ["fastify@5.8.5", "", { "dependencies": { "@fastify/ajv-compiler": "^4.0.5", "@fastify/error": "^4.0.0", "@fastify/fast-json-stringify-compiler": "^5.0.0", "@fastify/proxy-addr": "^5.0.0", "abstract-logging": "^2.0.1", "avvio": "^9.0.0", "fast-json-stringify": "^6.0.0", "find-my-way": "^9.0.0", "light-my-request": "^6.0.0", "pino": "^9.14.0 || ^10.1.0", "process-warning": "^5.0.0", "rfdc": "^1.3.1", "secure-json-parse": "^4.0.0", "semver": "^7.6.0", "toad-cache": "^3.7.0" } }, "sha512-Yqptv59pQzPgQUSIm87hMqHJmdkb1+GPxdE6vW6FRyVE9G86mt7rOghitiU4JHRaTyDUk9pfeKmDeu70lAwM4Q=="],
|
||||
|
||||
@@ -5181,7 +5176,7 @@
|
||||
|
||||
"stripe": ["stripe@18.0.0", "", { "dependencies": { "@types/node": ">=8.1.0", "qs": "^6.11.0" } }, "sha512-3Fs33IzKUby//9kCkCa1uRpinAoTvj6rJgQ2jrBEysoxEvfsclvXdna1amyEYbA2EKkjynuB4+L/kleCCaWTpA=="],
|
||||
|
||||
"strnum": ["strnum@1.1.2", "", {}, "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA=="],
|
||||
"strnum": ["strnum@2.3.0", "", {}, "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q=="],
|
||||
|
||||
"stubborn-fs": ["stubborn-fs@2.0.0", "", { "dependencies": { "stubborn-utils": "^1.0.1" } }, "sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA=="],
|
||||
|
||||
@@ -5737,30 +5732,6 @@
|
||||
|
||||
"@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=="],
|
||||
|
||||
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.782.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.775.0", "@aws-sdk/credential-provider-http": "3.775.0", "@aws-sdk/credential-provider-ini": "3.782.0", "@aws-sdk/credential-provider-process": "3.775.0", "@aws-sdk/credential-provider-sso": "3.782.0", "@aws-sdk/credential-provider-web-identity": "3.782.0", "@aws-sdk/types": "3.775.0", "@smithy/credential-provider-imds": "^4.0.2", "@smithy/property-provider": "^4.0.2", "@smithy/shared-ini-file-loader": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-HZiAF+TCEyKjju9dgysjiPIWgt/+VerGaeEp18mvKLNfgKz1d+/82A2USEpNKTze7v3cMFASx3CvL8yYyF7mJw=="],
|
||||
|
||||
"@aws-sdk/client-sts/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.775.0", "", { "dependencies": { "@aws-sdk/types": "3.775.0", "@smithy/protocol-http": "^5.1.0", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-tkSegM0Z6WMXpLB8oPys/d+umYIocvO298mGvcMCncpRl77L9XkvSLJIFzaHes+o7djAgIduYw8wKIMStFss2w=="],
|
||||
|
||||
"@aws-sdk/client-sts/@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.775.0", "", { "dependencies": { "@aws-sdk/types": "3.775.0", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-FaxO1xom4MAoUJsldmR92nT1G6uZxTdNYOFYtdHfd6N2wcNaTuxgjIvqzg5y7QIH9kn58XX/dzf1iTjgqUStZw=="],
|
||||
|
||||
"@aws-sdk/client-sts/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.775.0", "", { "dependencies": { "@aws-sdk/types": "3.775.0", "@smithy/protocol-http": "^5.1.0", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GLCzC8D0A0YDG5u3F5U03Vb9j5tcOEFhr8oc6PDk0k0vm5VwtZOE6LvK7hcCSoAB4HXyOUM0sQuXrbaAh9OwXA=="],
|
||||
|
||||
"@aws-sdk/client-sts/@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.782.0", "", { "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.782.0", "@smithy/core": "^3.2.0", "@smithy/protocol-http": "^5.1.0", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-i32H2R6IItX+bQ2p4+v2gGO2jA80jQoJO2m1xjU9rYWQW3+ErWy4I5YIuQHTBfb6hSdAHbaRfqPDgbv9J2rjEg=="],
|
||||
|
||||
"@aws-sdk/client-sts/@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.775.0", "", { "dependencies": { "@aws-sdk/types": "3.775.0", "@smithy/node-config-provider": "^4.0.2", "@smithy/types": "^4.2.0", "@smithy/util-config-provider": "^4.0.0", "@smithy/util-middleware": "^4.0.2", "tslib": "^2.6.2" } }, "sha512-40iH3LJjrQS3LKUJAl7Wj0bln7RFPEvUYKFxtP8a+oKFDO0F65F52xZxIJbPn6sHkxWDAnZlGgdjZXM3p2g5wQ=="],
|
||||
|
||||
"@aws-sdk/client-sts/@aws-sdk/types": ["@aws-sdk/types@3.775.0", "", { "dependencies": { "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ZoGKwa4C9fC9Av6bdfqcW6Ix5ot05F/S4VxWR2nHuMv7hzfmAjTOcUiWT7UR4hM/U0whf84VhDtXN/DWAk52KA=="],
|
||||
|
||||
"@aws-sdk/client-sts/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.782.0", "", { "dependencies": { "@aws-sdk/types": "3.775.0", "@smithy/types": "^4.2.0", "@smithy/util-endpoints": "^3.0.2", "tslib": "^2.6.2" } }, "sha512-/RJOAO7o7HI6lEa4ASbFFLHGU9iPK876BhsVfnl54MvApPVYWQ9sHO0anOUim2S5lQTwd/6ghuH3rFYSq/+rdw=="],
|
||||
|
||||
"@aws-sdk/client-sts/@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.775.0", "", { "dependencies": { "@aws-sdk/types": "3.775.0", "@smithy/types": "^4.2.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-txw2wkiJmZKVdDbscK7VBK+u+TJnRtlUjRTLei+elZg2ADhpQxfVAQl436FUeIv6AhB/oRHW6/K/EAGXUSWi0A=="],
|
||||
|
||||
"@aws-sdk/client-sts/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.782.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.782.0", "@aws-sdk/types": "3.775.0", "@smithy/node-config-provider": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-dMFkUBgh2Bxuw8fYZQoH/u3H4afQ12VSkzEi//qFiDTwbKYq+u+RYjc8GLDM6JSK1BShMu5AVR7HD4ap1TYUnA=="],
|
||||
|
||||
"@aws-sdk/client-sts/@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/core/@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/types": ["@aws-sdk/types@3.973.9", "", { "dependencies": { "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-kuBfgQVdcz5Bmapc4A13YbpVw/pXkesfhetcFYwbntqas8sF41OHyd4o28+/TG2ZQdHBsv90Lsu5y6oitvYCdg=="],
|
||||
@@ -5833,8 +5804,6 @@
|
||||
|
||||
"@azure/core-http/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="],
|
||||
|
||||
"@azure/core-xml/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=="],
|
||||
|
||||
"@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||
@@ -6575,18 +6544,6 @@
|
||||
|
||||
"@aws-sdk/client-lambda/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.26", "", { "dependencies": { "@smithy/types": "^4.14.2", "fast-xml-parser": "5.7.3", "tslib": "^2.6.2" } }, "sha512-cDbrqvDS73whl6YAPSPq0U6whzG6UWI9PuWh0wrUuGoZexhWEqhdunbukV7iBoaWnFV1AODutM5hOD6rtn439g=="],
|
||||
|
||||
"@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=="],
|
||||
|
||||
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.782.0", "", { "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/credential-provider-env": "3.775.0", "@aws-sdk/credential-provider-http": "3.775.0", "@aws-sdk/credential-provider-process": "3.775.0", "@aws-sdk/credential-provider-sso": "3.782.0", "@aws-sdk/credential-provider-web-identity": "3.782.0", "@aws-sdk/nested-clients": "3.782.0", "@aws-sdk/types": "3.775.0", "@smithy/credential-provider-imds": "^4.0.2", "@smithy/property-provider": "^4.0.2", "@smithy/shared-ini-file-loader": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-wd4KdRy2YjLsE4Y7pz00470Iip06GlRHkG4dyLW7/hFMzEO2o7ixswCWp6J2VGZVAX64acknlv2Q0z02ebjmhw=="],
|
||||
|
||||
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.775.0", "", { "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/shared-ini-file-loader": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-A6k68H9rQp+2+7P7SGO90Csw6nrUEm0Qfjpn9Etc4EboZhhCLs9b66umUsTsSBHus4FDIe5JQxfCUyt1wgNogg=="],
|
||||
|
||||
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.782.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.782.0", "@aws-sdk/core": "3.775.0", "@aws-sdk/token-providers": "3.782.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/shared-ini-file-loader": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-1y1ucxTtTIGDSNSNxriQY8msinilhe9gGvQpUDYW9gboyC7WQJPDw66imy258V6osdtdi+xoHzVCbCz3WhosMQ=="],
|
||||
|
||||
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.782.0", "", { "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/nested-clients": "3.782.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-xCna0opVPaueEbJoclj5C6OpDNi0Gynj+4d7tnuXGgQhTHPyAz8ZyClkVqpi5qvHTgxROdUEDxWqEO5jqRHZHQ=="],
|
||||
|
||||
"@aws-sdk/credential-provider-env/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.26", "", { "dependencies": { "@smithy/types": "^4.14.2", "fast-xml-parser": "5.7.3", "tslib": "^2.6.2" } }, "sha512-cDbrqvDS73whl6YAPSPq0U6whzG6UWI9PuWh0wrUuGoZexhWEqhdunbukV7iBoaWnFV1AODutM5hOD6rtn439g=="],
|
||||
|
||||
"@aws-sdk/credential-provider-http/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.26", "", { "dependencies": { "@smithy/types": "^4.14.2", "fast-xml-parser": "5.7.3", "tslib": "^2.6.2" } }, "sha512-cDbrqvDS73whl6YAPSPq0U6whzG6UWI9PuWh0wrUuGoZexhWEqhdunbukV7iBoaWnFV1AODutM5hOD6rtn439g=="],
|
||||
@@ -6613,10 +6570,6 @@
|
||||
|
||||
"@aws-sdk/token-providers/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.26", "", { "dependencies": { "@smithy/types": "^4.14.2", "fast-xml-parser": "5.7.3", "tslib": "^2.6.2" } }, "sha512-cDbrqvDS73whl6YAPSPq0U6whzG6UWI9PuWh0wrUuGoZexhWEqhdunbukV7iBoaWnFV1AODutM5hOD6rtn439g=="],
|
||||
|
||||
"@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.3.0", "", {}, "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q=="],
|
||||
|
||||
"@azure/core-xml/fast-xml-parser/strnum": ["strnum@2.3.0", "", {}, "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q=="],
|
||||
|
||||
"@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
||||
|
||||
"@develar/schema-utils/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
|
||||
@@ -7167,44 +7120,12 @@
|
||||
|
||||
"@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
|
||||
|
||||
"@aws-sdk/client-cognito-identity/@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/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-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=="],
|
||||
|
||||
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.782.0", "", { "dependencies": { "@aws-sdk/nested-clients": "3.782.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/shared-ini-file-loader": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-4tPuk/3+THPrzKaXW4jE2R67UyGwHLFizZ47pcjJWbhb78IIJAy94vbeqEQ+veS84KF5TXcU7g5jGTXC0D70Wg=="],
|
||||
|
||||
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@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/credential-provider-env/@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/credential-provider-http/@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/credential-provider-ini/@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/credential-provider-login/@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/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/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@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-o1GX0+IPlFi/D8ei9y/jj3yucJWNfPnbB5appVBWevAyUdZA5KzQ2nK/hDxiu9olTZlFEFpf1m1Rn3FaGxHqsw=="],
|
||||
|
||||
"@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@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-process/@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/credential-provider-sso/@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/credential-provider-web-identity/@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/credential-providers/@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/nested-clients/@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/token-providers/@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=="],
|
||||
|
||||
"@electron/asar/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||
|
||||
"@electron/universal/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||
@@ -7389,34 +7310,8 @@
|
||||
|
||||
"@astrojs/check/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"@aws-sdk/client-cognito-identity/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.3.0", "", {}, "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q=="],
|
||||
|
||||
"@aws-sdk/client-lambda/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.3.0", "", {}, "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q=="],
|
||||
|
||||
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@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/credential-provider-env/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.3.0", "", {}, "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q=="],
|
||||
|
||||
"@aws-sdk/credential-provider-http/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.3.0", "", {}, "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q=="],
|
||||
|
||||
"@aws-sdk/credential-provider-ini/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.3.0", "", {}, "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q=="],
|
||||
|
||||
"@aws-sdk/credential-provider-login/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.3.0", "", {}, "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q=="],
|
||||
|
||||
"@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-process/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.3.0", "", {}, "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q=="],
|
||||
|
||||
"@aws-sdk/credential-provider-sso/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.3.0", "", {}, "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q=="],
|
||||
|
||||
"@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.3.0", "", {}, "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q=="],
|
||||
|
||||
"@aws-sdk/credential-providers/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.3.0", "", {}, "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q=="],
|
||||
|
||||
"@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.3.0", "", {}, "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q=="],
|
||||
|
||||
"@aws-sdk/token-providers/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.3.0", "", {}, "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q=="],
|
||||
|
||||
"@jsx-email/cli/tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
|
||||
|
||||
"@sentry/bundler-plugin-core/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||
|
||||
+4
-4
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"nodeModules": {
|
||||
"x86_64-linux": "sha256-JXQ9PAqqRlJtHa8T3ZxqdRRyxC+0ip+2wSnehvKXUbI=",
|
||||
"aarch64-linux": "sha256-nI+RaxDXmAcjhSjCtIvyi292xBEg0E5NXaoyGrJE69s=",
|
||||
"aarch64-darwin": "sha256-UleKpm7Khf3kibm4BqZJ9bmu0N2kOjNd77g1Bd2tmfw=",
|
||||
"x86_64-darwin": "sha256-V4AQH868dOfVhEj1mKTnZlhpZwFqBYsHS28Tx9VXHkE="
|
||||
"x86_64-linux": "sha256-rB/CcrBUQVZ08nBFSYA8u2w88rQmTpKxKPkIreDEKgI=",
|
||||
"aarch64-linux": "sha256-ZRTphtic8Ip96MnILteFgZAUxjK9O4YfJu2O6u/0H8k=",
|
||||
"aarch64-darwin": "sha256-VK5XIzraP0HtqnPwPCejiDKer4ewtNtX1vxP5uuyjSk=",
|
||||
"x86_64-darwin": "sha256-ZLPHqcCZB1EmxQk95cmUpiODTTKOyi7PSF0yr/rDk6Y="
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -153,7 +153,6 @@
|
||||
"@tanstack/solid-virtual@3.13.28": "patches/@tanstack%2Fsolid-virtual@3.13.28.patch",
|
||||
"@pierre/trees@1.0.0-beta.4": "patches/@pierre%2Ftrees@1.0.0-beta.4.patch",
|
||||
"@modelcontextprotocol/sdk@1.29.0": "patches/@modelcontextprotocol%2Fsdk@1.29.0.patch",
|
||||
"@tanstack/virtual-core@3.17.0": "patches/@tanstack%2Fvirtual-core@3.17.0.patch",
|
||||
"effect@4.0.0-beta.83": "patches/effect@4.0.0-beta.83.patch"
|
||||
"@tanstack/virtual-core@3.17.0": "patches/@tanstack%2Fvirtual-core@3.17.0.patch"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { batch, createEffect, onCleanup, onMount } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { makeEventListener } from "@solid-primitives/event-listener"
|
||||
import { Tooltip } from "@opencode-ai/ui/tooltip"
|
||||
import { TooltipV2 } from "@opencode-ai/ui/v2/tooltip-v2"
|
||||
import { useLanguage } from "@/context/language"
|
||||
|
||||
type Mem = Performance & {
|
||||
@@ -52,62 +51,31 @@ const bad = (n: number | undefined, limit: number, low = false) => {
|
||||
|
||||
const session = (path: string) => path.includes("/session")
|
||||
|
||||
function Cell(props: {
|
||||
bad?: boolean
|
||||
dim?: boolean
|
||||
inline?: boolean
|
||||
label: string
|
||||
tip: string
|
||||
value: string
|
||||
wide?: boolean
|
||||
}) {
|
||||
const content = () => (
|
||||
<div
|
||||
classList={{
|
||||
"flex min-w-0 items-center": true,
|
||||
"min-h-[20px] w-fit flex-row justify-start gap-1.5 px-1.5 py-0.5 text-left": !!props.inline,
|
||||
"justify-center text-center": !props.inline,
|
||||
"min-h-[42px] w-full flex-col rounded-[8px] px-0.5 py-1": !props.inline,
|
||||
"col-span-2": !!props.wide && !props.inline,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
classList={{
|
||||
"text-[10px] leading-none font-black uppercase tracking-[0.04em] opacity-70": true,
|
||||
}}
|
||||
>
|
||||
{props.label}
|
||||
</div>
|
||||
<div
|
||||
classList={{
|
||||
"uppercase leading-none font-bold tabular-nums": true,
|
||||
"text-[11px]": !!props.inline,
|
||||
"text-[13px] sm:text-[14px]": !props.inline,
|
||||
"text-text-on-critical-base": !!props.bad,
|
||||
"opacity-70": !!props.dim,
|
||||
}}
|
||||
>
|
||||
{props.value}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
if (props.inline) {
|
||||
return (
|
||||
<TooltipV2 value={props.tip} placement="top">
|
||||
{content()}
|
||||
</TooltipV2>
|
||||
)
|
||||
}
|
||||
|
||||
function Cell(props: { bad?: boolean; dim?: boolean; label: string; tip: string; value: string; wide?: boolean }) {
|
||||
return (
|
||||
<Tooltip value={props.tip} placement="top">
|
||||
{content()}
|
||||
<div
|
||||
classList={{
|
||||
"flex min-h-[42px] w-full min-w-0 flex-col items-center justify-center rounded-[8px] px-0.5 py-1 text-center": true,
|
||||
"col-span-2": !!props.wide,
|
||||
}}
|
||||
>
|
||||
<div class="text-[10px] leading-none font-black uppercase tracking-[0.04em] opacity-70">{props.label}</div>
|
||||
<div
|
||||
classList={{
|
||||
"text-[13px] leading-none font-bold tabular-nums sm:text-[14px]": true,
|
||||
"text-text-on-critical-base": !!props.bad,
|
||||
"opacity-70": !!props.dim,
|
||||
}}
|
||||
>
|
||||
{props.value}
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
export function DebugBar(props: { inline?: boolean } = {}) {
|
||||
export function DebugBar() {
|
||||
const language = useLanguage()
|
||||
const location = useLocation()
|
||||
const routing = useIsRouting()
|
||||
@@ -133,7 +101,7 @@ export function DebugBar(props: { inline?: boolean } = {}) {
|
||||
},
|
||||
})
|
||||
|
||||
const na = () => language.t("debugBar.na").toUpperCase()
|
||||
const na = () => language.t("debugBar.na")
|
||||
const heap = () => (state.heap.limit ? (state.heap.used ?? 0) / state.heap.limit : undefined)
|
||||
const heapv = () => {
|
||||
const value = heap()
|
||||
@@ -395,30 +363,15 @@ export function DebugBar(props: { inline?: boolean } = {}) {
|
||||
return (
|
||||
<aside
|
||||
aria-label={language.t("debugBar.ariaLabel")}
|
||||
classList={{
|
||||
"pointer-events-auto hidden overflow-hidden text-text-strong md:block": true,
|
||||
"mt-[-6px] w-full shrink-0 px-3 py-1": !!props.inline,
|
||||
"fixed bottom-3 right-3 z-50 w-[308px] max-w-[calc(100vw-1.5rem)] rounded-xl border border-border-base bg-surface-raised-stronger-non-alpha p-0.5 shadow-[var(--shadow-lg-border-base)] sm:bottom-4 sm:right-4 sm:w-[324px]":
|
||||
!props.inline,
|
||||
}}
|
||||
class="pointer-events-auto fixed bottom-3 right-3 z-50 hidden w-[308px] max-w-[calc(100vw-1.5rem)] overflow-hidden rounded-xl border border-border-base bg-surface-raised-stronger-non-alpha p-0.5 text-text-strong shadow-[var(--shadow-lg-border-base)] md:block sm:bottom-4 sm:right-4 sm:w-[324px]"
|
||||
>
|
||||
<div
|
||||
classList={{
|
||||
"font-mono": true,
|
||||
"gap-[9px]": !!props.inline,
|
||||
"gap-px": !props.inline,
|
||||
"flex w-full flex-nowrap items-center justify-start": !!props.inline,
|
||||
"grid-cols-5": !props.inline,
|
||||
grid: !props.inline,
|
||||
}}
|
||||
>
|
||||
<div class="grid grid-cols-5 gap-px font-mono">
|
||||
<Cell
|
||||
label={language.t("debugBar.nav.label")}
|
||||
tip={language.t("debugBar.nav.tip")}
|
||||
value={navv()}
|
||||
bad={bad(state.nav.dur, 400)}
|
||||
dim={state.nav.dur === undefined && !state.nav.pending}
|
||||
inline={props.inline}
|
||||
/>
|
||||
<Cell
|
||||
label={language.t("debugBar.fps.label")}
|
||||
@@ -426,7 +379,6 @@ export function DebugBar(props: { inline?: boolean } = {}) {
|
||||
value={state.fps === undefined ? na() : `${Math.round(state.fps)}`}
|
||||
bad={bad(state.fps, 50, true)}
|
||||
dim={state.fps === undefined}
|
||||
inline={props.inline}
|
||||
/>
|
||||
<Cell
|
||||
label={language.t("debugBar.frame.label")}
|
||||
@@ -434,7 +386,6 @@ export function DebugBar(props: { inline?: boolean } = {}) {
|
||||
value={time(state.gap) ?? na()}
|
||||
bad={bad(state.gap, 50)}
|
||||
dim={state.gap === undefined}
|
||||
inline={props.inline}
|
||||
/>
|
||||
<Cell
|
||||
label={language.t("debugBar.jank.label")}
|
||||
@@ -442,7 +393,6 @@ export function DebugBar(props: { inline?: boolean } = {}) {
|
||||
value={state.jank === undefined ? na() : `${state.jank}`}
|
||||
bad={bad(state.jank, 8)}
|
||||
dim={state.jank === undefined}
|
||||
inline={props.inline}
|
||||
/>
|
||||
<Cell
|
||||
label={language.t("debugBar.long.label")}
|
||||
@@ -450,7 +400,6 @@ export function DebugBar(props: { inline?: boolean } = {}) {
|
||||
value={longv()}
|
||||
bad={bad(state.long.block, 200)}
|
||||
dim={state.long.count === undefined}
|
||||
inline={props.inline}
|
||||
/>
|
||||
<Cell
|
||||
label={language.t("debugBar.delay.label")}
|
||||
@@ -458,7 +407,6 @@ export function DebugBar(props: { inline?: boolean } = {}) {
|
||||
value={time(state.delay) ?? na()}
|
||||
bad={bad(state.delay, 100)}
|
||||
dim={state.delay === undefined}
|
||||
inline={props.inline}
|
||||
/>
|
||||
<Cell
|
||||
label={language.t("debugBar.inp.label")}
|
||||
@@ -466,7 +414,6 @@ export function DebugBar(props: { inline?: boolean } = {}) {
|
||||
value={time(state.inp) ?? na()}
|
||||
bad={bad(state.inp, 200)}
|
||||
dim={state.inp === undefined}
|
||||
inline={props.inline}
|
||||
/>
|
||||
<Cell
|
||||
label={language.t("debugBar.cls.label")}
|
||||
@@ -474,7 +421,6 @@ export function DebugBar(props: { inline?: boolean } = {}) {
|
||||
value={state.cls === undefined ? na() : state.cls.toFixed(2)}
|
||||
bad={bad(state.cls, 0.1)}
|
||||
dim={state.cls === undefined}
|
||||
inline={props.inline}
|
||||
/>
|
||||
<Cell
|
||||
label={language.t("debugBar.mem.label")}
|
||||
@@ -489,7 +435,6 @@ export function DebugBar(props: { inline?: boolean } = {}) {
|
||||
value={heapv()}
|
||||
bad={bad(heap(), 0.8)}
|
||||
dim={state.heap.used === undefined}
|
||||
inline={props.inline}
|
||||
wide
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -65,7 +65,6 @@ export function DialogConnectProvider(props: { provider: string; directory?: Acc
|
||||
const [store, setStore] = createStore({
|
||||
methodIndex: undefined as undefined | number,
|
||||
authorization: undefined as undefined | ProviderAuthAuthorization,
|
||||
promptInputs: undefined as undefined | Record<string, string>,
|
||||
state: "pending" as undefined | "pending" | "complete" | "error" | "prompt",
|
||||
error: undefined as string | undefined,
|
||||
})
|
||||
@@ -74,7 +73,6 @@ export function DialogConnectProvider(props: { provider: string; directory?: Acc
|
||||
| { type: "method.select"; index: number }
|
||||
| { type: "method.reset" }
|
||||
| { type: "auth.prompt" }
|
||||
| { type: "auth.inputs"; inputs: Record<string, string> }
|
||||
| { type: "auth.pending" }
|
||||
| { type: "auth.complete"; authorization: ProviderAuthAuthorization }
|
||||
| { type: "auth.error"; error: string }
|
||||
@@ -85,7 +83,6 @@ export function DialogConnectProvider(props: { provider: string; directory?: Acc
|
||||
if (action.type === "method.select") {
|
||||
draft.methodIndex = action.index
|
||||
draft.authorization = undefined
|
||||
draft.promptInputs = undefined
|
||||
draft.state = undefined
|
||||
draft.error = undefined
|
||||
return
|
||||
@@ -93,7 +90,6 @@ export function DialogConnectProvider(props: { provider: string; directory?: Acc
|
||||
if (action.type === "method.reset") {
|
||||
draft.methodIndex = undefined
|
||||
draft.authorization = undefined
|
||||
draft.promptInputs = undefined
|
||||
draft.state = undefined
|
||||
draft.error = undefined
|
||||
return
|
||||
@@ -103,12 +99,6 @@ export function DialogConnectProvider(props: { provider: string; directory?: Acc
|
||||
draft.error = undefined
|
||||
return
|
||||
}
|
||||
if (action.type === "auth.inputs") {
|
||||
draft.promptInputs = action.inputs
|
||||
draft.state = undefined
|
||||
draft.error = undefined
|
||||
return
|
||||
}
|
||||
if (action.type === "auth.pending") {
|
||||
draft.state = "pending"
|
||||
draft.error = undefined
|
||||
@@ -161,15 +151,6 @@ export function DialogConnectProvider(props: { provider: string; directory?: Acc
|
||||
const method = methods()[index]
|
||||
dispatch({ type: "method.select", index })
|
||||
|
||||
if (method.type === "api" && method.prompts?.length) {
|
||||
if (!inputs) {
|
||||
dispatch({ type: "auth.prompt" })
|
||||
return
|
||||
}
|
||||
dispatch({ type: "auth.inputs", inputs })
|
||||
return
|
||||
}
|
||||
|
||||
if (method.type === "oauth") {
|
||||
if (method.prompts?.length && !inputs) {
|
||||
dispatch({ type: "auth.prompt" })
|
||||
@@ -209,7 +190,7 @@ export function DialogConnectProvider(props: { provider: string; directory?: Acc
|
||||
}
|
||||
}
|
||||
|
||||
function AuthPromptsView() {
|
||||
function OAuthPromptsView() {
|
||||
const [formStore, setFormStore] = createStore({
|
||||
value: {} as Record<string, string>,
|
||||
index: 0,
|
||||
@@ -217,7 +198,8 @@ export function DialogConnectProvider(props: { provider: string; directory?: Acc
|
||||
|
||||
const prompts = createMemo<NonNullable<ProviderAuthMethod["prompts"]>>(() => {
|
||||
const value = method()
|
||||
return value?.prompts ?? []
|
||||
if (value?.type !== "oauth") return []
|
||||
return value.prompts ?? []
|
||||
})
|
||||
const matches = (prompt: NonNullable<ReturnType<typeof prompts>[number]>, value: Record<string, string>) => {
|
||||
if (!prompt.when) return true
|
||||
@@ -248,10 +230,6 @@ export function DialogConnectProvider(props: { provider: string; directory?: Acc
|
||||
setFormStore("index", next)
|
||||
return
|
||||
}
|
||||
if (method()?.type === "api") {
|
||||
dispatch({ type: "auth.inputs", inputs: value })
|
||||
return
|
||||
}
|
||||
await selectMethod(store.methodIndex, value)
|
||||
}
|
||||
|
||||
@@ -436,7 +414,6 @@ export function DialogConnectProvider(props: { provider: string; directory?: Acc
|
||||
auth: {
|
||||
type: "api",
|
||||
key: apiKey,
|
||||
...(store.promptInputs ? { metadata: store.promptInputs } : {}),
|
||||
},
|
||||
})
|
||||
await complete()
|
||||
@@ -645,7 +622,7 @@ export function DialogConnectProvider(props: { provider: string; directory?: Acc
|
||||
</div>
|
||||
</Match>
|
||||
<Match when={store.state === "prompt"}>
|
||||
<AuthPromptsView />
|
||||
<OAuthPromptsView />
|
||||
</Match>
|
||||
<Match when={store.state === "error"}>
|
||||
<div class="text-14-regular text-text-base">
|
||||
|
||||
@@ -35,8 +35,6 @@ import { DockShellForm, DockTray } from "@opencode-ai/ui/dock-surface"
|
||||
import { Icon } from "@opencode-ai/ui/icon"
|
||||
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
|
||||
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
|
||||
import { KeybindV2 } from "@opencode-ai/ui/v2/keybind-v2"
|
||||
import { TooltipV2 } from "@opencode-ai/ui/v2/tooltip-v2"
|
||||
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||
import { Select } from "@opencode-ai/ui/select"
|
||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||
@@ -1364,7 +1362,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
||||
shouldAnimate: providersShouldFadeIn(),
|
||||
paid: props.controls.model.paid,
|
||||
title: language.t("command.model.choose"),
|
||||
keybind: command.keybindParts("model.choose"),
|
||||
keybind: command.keybind("model.choose"),
|
||||
model: props.controls.model.selection,
|
||||
providerID: props.controls.model.selection.current()?.provider?.id,
|
||||
modelName: props.controls.model.selection.current()?.name ?? language.t("dialog.model.select.title"),
|
||||
@@ -1381,7 +1379,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
||||
const showAgentControl = createMemo(() => props.controls.agents.visible && props.controls.agents.options.length > 0)
|
||||
const agentControlState = createMemo<ComposerAgentControlState>(() => ({
|
||||
title: language.t("command.agent.cycle"),
|
||||
keybind: command.keybindParts("agent.cycle"),
|
||||
keybind: command.keybind("agent.cycle"),
|
||||
options: props.controls.agents.options,
|
||||
current: props.controls.agents.current,
|
||||
style: control(),
|
||||
@@ -1499,14 +1497,10 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
||||
<div class="flex h-11 items-center px-2">
|
||||
<div class="flex min-w-0 flex-1 items-center gap-0">
|
||||
{fileAttachmentInput()}
|
||||
<TooltipV2
|
||||
<TooltipKeybind
|
||||
placement="top"
|
||||
value={
|
||||
<>
|
||||
{language.t("prompt.action.attachFile")}
|
||||
<KeybindV2 keys={command.keybindParts("file.attach")} variant="neutral" />
|
||||
</>
|
||||
}
|
||||
title={language.t("prompt.action.attachFile")}
|
||||
keybind={command.keybind("file.attach")}
|
||||
>
|
||||
<IconButton
|
||||
data-action="prompt-attach"
|
||||
@@ -1520,7 +1514,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
||||
tabIndex={store.mode === "normal" ? undefined : -1}
|
||||
aria-label={language.t("prompt.action.attachFile")}
|
||||
/>
|
||||
</TooltipV2>
|
||||
</TooltipKeybind>
|
||||
<Show when={showAgentControl()}>
|
||||
<ComposerAgentControl state={agentControlState()} />
|
||||
</Show>
|
||||
@@ -1535,15 +1529,11 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
||||
!props.controls.model.selection.variant.current() && !store.variantOpen,
|
||||
}}
|
||||
>
|
||||
<TooltipV2
|
||||
<TooltipKeybind
|
||||
placement="top"
|
||||
gutter={4}
|
||||
value={
|
||||
<>
|
||||
{language.t("command.model.variant.cycle")}
|
||||
<KeybindV2 keys={command.keybindParts("model.variant.cycle")} variant="neutral" />
|
||||
</>
|
||||
}
|
||||
title={language.t("command.model.variant.cycle")}
|
||||
keybind={command.keybind("model.variant.cycle")}
|
||||
>
|
||||
<Select
|
||||
size="normal"
|
||||
@@ -1561,11 +1551,11 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
||||
triggerProps={{ "data-action": "prompt-model-variant" }}
|
||||
variant="ghost"
|
||||
/>
|
||||
</TooltipV2>
|
||||
</TooltipKeybind>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
<TooltipV2 placement="top" inactive={!working() && blank()} value={tip()}>
|
||||
<Tooltip placement="top" inactive={!working() && blank()} value={tip()}>
|
||||
<IconButton
|
||||
data-action="prompt-submit"
|
||||
type="submit"
|
||||
@@ -1580,7 +1570,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
||||
}}
|
||||
aria-label={stopping() ? language.t("prompt.action.stop") : language.t("prompt.action.send")}
|
||||
/>
|
||||
</TooltipV2>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</DockShellForm>
|
||||
</div>
|
||||
@@ -1926,7 +1916,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
||||
|
||||
type ComposerAgentControlState = {
|
||||
title: string
|
||||
keybind: string[]
|
||||
keybind: string
|
||||
options: string[]
|
||||
current: string
|
||||
style: JSX.CSSProperties | undefined
|
||||
@@ -1938,7 +1928,7 @@ type ComposerModelControlState = {
|
||||
shouldAnimate: boolean
|
||||
paid: boolean
|
||||
title: string
|
||||
keybind: string[]
|
||||
keybind: string
|
||||
model: ReturnType<typeof useLocal>["model"]
|
||||
providerID?: string
|
||||
modelName: string
|
||||
@@ -1953,16 +1943,7 @@ function ComposerAgentControl(props: { state: ComposerAgentControlState }) {
|
||||
<div class="pointer-events-none absolute left-2 top-1/2 z-10 flex size-4 -translate-y-1/2 items-center justify-center text-v2-icon-icon-muted">
|
||||
<Icon name="sliders" size="small" />
|
||||
</div>
|
||||
<TooltipV2
|
||||
placement="top"
|
||||
gutter={4}
|
||||
value={
|
||||
<>
|
||||
{props.state.title}
|
||||
<KeybindV2 keys={props.state.keybind} variant="neutral" />
|
||||
</>
|
||||
}
|
||||
>
|
||||
<TooltipKeybind placement="top" gutter={4} title={props.state.title} keybind={props.state.keybind}>
|
||||
<Select
|
||||
size="normal"
|
||||
options={props.state.options}
|
||||
@@ -1974,7 +1955,7 @@ function ComposerAgentControl(props: { state: ComposerAgentControlState }) {
|
||||
triggerProps={{ "data-action": "prompt-agent" }}
|
||||
variant="ghost"
|
||||
/>
|
||||
</TooltipV2>
|
||||
</TooltipKeybind>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1985,16 +1966,7 @@ function ComposerModelControl(props: { state: ComposerModelControlState }) {
|
||||
<Show
|
||||
when={props.state.paid}
|
||||
fallback={
|
||||
<TooltipV2
|
||||
placement="top"
|
||||
gutter={4}
|
||||
value={
|
||||
<>
|
||||
{props.state.title}
|
||||
<KeybindV2 keys={props.state.keybind} variant="neutral" />
|
||||
</>
|
||||
}
|
||||
>
|
||||
<TooltipKeybind placement="top" gutter={4} title={props.state.title} keybind={props.state.keybind}>
|
||||
<Button
|
||||
data-action="prompt-model"
|
||||
as="div"
|
||||
@@ -2019,19 +1991,10 @@ function ComposerModelControl(props: { state: ComposerModelControlState }) {
|
||||
<Icon name="chevron-down" size="small" class="text-v2-icon-icon-muted" />
|
||||
</span>
|
||||
</Button>
|
||||
</TooltipV2>
|
||||
</TooltipKeybind>
|
||||
}
|
||||
>
|
||||
<TooltipV2
|
||||
placement="top"
|
||||
gutter={4}
|
||||
value={
|
||||
<>
|
||||
{props.state.title}
|
||||
<KeybindV2 keys={props.state.keybind} variant="neutral" />
|
||||
</>
|
||||
}
|
||||
>
|
||||
<TooltipKeybind placement="top" gutter={4} title={props.state.title} keybind={props.state.keybind}>
|
||||
<ModelSelectorPopover
|
||||
model={props.state.model}
|
||||
triggerAs={Button}
|
||||
@@ -2060,7 +2023,7 @@ function ComposerModelControl(props: { state: ComposerModelControlState }) {
|
||||
<Icon name="chevron-down" size="small" class="text-v2-icon-icon-muted" />
|
||||
</span>
|
||||
</ModelSelectorPopover>
|
||||
</TooltipV2>
|
||||
</TooltipKeybind>
|
||||
</Show>
|
||||
</Show>
|
||||
)
|
||||
|
||||
@@ -99,8 +99,6 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
|
||||
|
||||
const path = () => `${location.pathname}${location.search}${location.hash}`
|
||||
const creating = createMemo(() => {
|
||||
const route = layout.route()
|
||||
if (route.type === "draft" || route.type === "dir-new-sesssion") return true
|
||||
if (!params.dir) return false
|
||||
if (params.id) return false
|
||||
const parts = location.pathname.replace(/\/+$/, "").split("/")
|
||||
@@ -468,7 +466,7 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
|
||||
}}
|
||||
onReorder={(keys) => tabsStoreActions.reorder(keys)}
|
||||
/>
|
||||
<Show when={!creating()}>
|
||||
<Show when={!(creating() && params.dir)}>
|
||||
<TooltipV2
|
||||
placement="bottom"
|
||||
value={
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { coalesceServerEvents, enqueueServerEvent, resumeStreamAfterPageShow } from "./server-sdk"
|
||||
import { coalesceServerEvents, resumeStreamAfterPageShow } from "./server-sdk"
|
||||
import type { Event } from "@opencode-ai/sdk/v2/client"
|
||||
|
||||
describe("resumeStreamAfterPageShow", () => {
|
||||
@@ -15,23 +15,19 @@ describe("resumeStreamAfterPageShow", () => {
|
||||
})
|
||||
|
||||
describe("coalesceServerEvents", () => {
|
||||
const delta = (value: string, field = "text", partID = "part") => ({
|
||||
const delta = (value: string, field = "text") => ({
|
||||
directory: "/repo",
|
||||
payload: {
|
||||
type: "message.part.delta",
|
||||
properties: { messageID: "msg", partID, field, delta: value },
|
||||
properties: { messageID: "msg", partID: "part", field, delta: value },
|
||||
} as Event,
|
||||
})
|
||||
|
||||
test("merges adjacent deltas for the same field", () => {
|
||||
const first = delta("hello ")
|
||||
const second = delta("world")
|
||||
first.payload.id = "first"
|
||||
second.payload.id = "second"
|
||||
const result = coalesceServerEvents([first, second])
|
||||
const result = coalesceServerEvents([delta("hello "), delta("world")])
|
||||
|
||||
expect(result).toHaveLength(1)
|
||||
expect(result[0]?.payload).toMatchObject({ id: "second", properties: { delta: "hello world" } })
|
||||
expect(result[0]?.payload).toMatchObject({ properties: { delta: "hello world" } })
|
||||
})
|
||||
|
||||
test("preserves event boundaries and distinct fields", () => {
|
||||
@@ -49,112 +45,9 @@ describe("coalesceServerEvents", () => {
|
||||
])
|
||||
})
|
||||
|
||||
test("preserves event ID order across interleaved deltas", () => {
|
||||
const first = delta("a")
|
||||
const other = delta("b", "text", "other")
|
||||
const last = delta("c")
|
||||
first.payload.id = "1"
|
||||
other.payload.id = "2"
|
||||
last.payload.id = "3"
|
||||
test("drops stale deltas", () => {
|
||||
const result = coalesceServerEvents([delta("stale")], new Set(["/repo:msg:part"]))
|
||||
|
||||
const result = coalesceServerEvents([first, other, last])
|
||||
|
||||
expect(result.map((event) => event.payload.id)).toEqual(["1", "2", "3"])
|
||||
})
|
||||
})
|
||||
|
||||
describe("enqueueServerEvent", () => {
|
||||
const partUpdated = (text: string) =>
|
||||
({
|
||||
type: "message.part.updated",
|
||||
properties: {
|
||||
sessionID: "session",
|
||||
part: { id: "part", sessionID: "session", messageID: "message", type: "text", text },
|
||||
},
|
||||
}) as Event
|
||||
|
||||
test("preserves part updates across message remove and re-add barriers", () => {
|
||||
const events: Array<{ directory: string; payload: Event }> = []
|
||||
const enqueue = (payload: Event) => enqueueServerEvent(events, { directory: "/repo", payload })
|
||||
|
||||
enqueue(partUpdated("old"))
|
||||
enqueue({ type: "message.removed", properties: { sessionID: "session", messageID: "message" } } as Event)
|
||||
enqueue({
|
||||
type: "message.updated",
|
||||
properties: {
|
||||
sessionID: "session",
|
||||
info: {
|
||||
id: "message",
|
||||
sessionID: "session",
|
||||
role: "user",
|
||||
time: { created: 1 },
|
||||
agent: "build",
|
||||
model: { providerID: "provider", modelID: "model" },
|
||||
},
|
||||
},
|
||||
} as Event)
|
||||
enqueue(partUpdated("new"))
|
||||
|
||||
expect(events.map((event) => event.payload.type)).toEqual([
|
||||
"message.part.updated",
|
||||
"message.removed",
|
||||
"message.updated",
|
||||
"message.part.updated",
|
||||
])
|
||||
})
|
||||
|
||||
test("preserves deltas after a replacement snapshot", () => {
|
||||
const events: Array<{ directory: string; payload: Event }> = []
|
||||
const enqueue = (payload: Event) => enqueueServerEvent(events, { directory: "/repo", payload })
|
||||
|
||||
enqueue(partUpdated("a"))
|
||||
enqueue(partUpdated("ab"))
|
||||
enqueue({
|
||||
type: "message.part.delta",
|
||||
properties: { sessionID: "session", messageID: "message", partID: "part", field: "text", delta: "c" },
|
||||
} as Event)
|
||||
|
||||
const result = coalesceServerEvents(events)
|
||||
expect(result.map((event) => event.payload.type)).toEqual(["message.part.updated", "message.part.delta"])
|
||||
expect(result[0]?.payload).toMatchObject({ properties: { part: { text: "ab" } } })
|
||||
expect(result[1]?.payload).toMatchObject({ properties: { delta: "c" } })
|
||||
})
|
||||
|
||||
test("preserves updates after session deletion", () => {
|
||||
const events: Array<{ directory: string; payload: Event }> = []
|
||||
const enqueue = (payload: Event) => enqueueServerEvent(events, { directory: "/repo", payload })
|
||||
|
||||
enqueue(partUpdated("old"))
|
||||
enqueue({
|
||||
type: "session.deleted",
|
||||
properties: { sessionID: "session", info: { id: "session" } },
|
||||
} as Event)
|
||||
enqueue(partUpdated("new"))
|
||||
|
||||
expect(events.map((event) => event.payload.type)).toEqual([
|
||||
"message.part.updated",
|
||||
"session.deleted",
|
||||
"message.part.updated",
|
||||
])
|
||||
})
|
||||
|
||||
test("does not coalesce edge-triggered session statuses", () => {
|
||||
const events: Array<{ directory: string; payload: Event }> = []
|
||||
const enqueue = (status: "retry" | "busy") =>
|
||||
enqueueServerEvent(events, {
|
||||
directory: "/repo",
|
||||
payload: {
|
||||
type: "session.status",
|
||||
properties: {
|
||||
sessionID: "session",
|
||||
status: status === "retry" ? { type: "retry", attempt: 1, message: "retry", next: 1 } : { type: "busy" },
|
||||
},
|
||||
} as Event,
|
||||
})
|
||||
|
||||
enqueue("retry")
|
||||
enqueue("busy")
|
||||
|
||||
expect(events).toHaveLength(2)
|
||||
expect(result).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -17,56 +17,34 @@ const isAbortError = (error: unknown) =>
|
||||
const isStreamClosed = (error: unknown, signal?: AbortSignal) => isAbortError(error) || signal?.aborted === true
|
||||
type QueuedServerEvent = { directory: string; payload: Event }
|
||||
|
||||
const coalescedKey = (event: QueuedServerEvent) => {
|
||||
if (event.payload.type === "lsp.updated") return `lsp.updated:${event.directory}`
|
||||
if (event.payload.type === "message.part.updated") {
|
||||
const part = event.payload.properties.part
|
||||
return `message.part.updated:${event.directory}:${part.messageID}:${part.id}`
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
const deltaKey = (directory: string, messageID: string, partID: string) => `${directory}:${messageID}:${partID}`
|
||||
|
||||
export function enqueueServerEvent(queue: QueuedServerEvent[], event: QueuedServerEvent) {
|
||||
const key = coalescedKey(event)
|
||||
const previous = queue[queue.length - 1]
|
||||
if (key && previous && coalescedKey(previous) === key) {
|
||||
queue[queue.length - 1] = event
|
||||
return false
|
||||
}
|
||||
queue.push(event)
|
||||
return true
|
||||
}
|
||||
|
||||
export function coalesceServerEvents(events: QueuedServerEvent[]) {
|
||||
export function coalesceServerEvents(events: QueuedServerEvent[], stale?: Set<string>) {
|
||||
const output: QueuedServerEvent[] = []
|
||||
const deltas = new Map<string, number>()
|
||||
events.forEach((event) => {
|
||||
if (stale && event.payload.type === "message.part.delta") {
|
||||
const props = event.payload.properties
|
||||
if (stale.has(deltaKey(event.directory, props.messageID, props.partID))) return
|
||||
}
|
||||
if (event.payload.type !== "message.part.delta") {
|
||||
deltas.clear()
|
||||
output.push(event)
|
||||
return
|
||||
}
|
||||
const props = event.payload.properties
|
||||
const previous = output[output.length - 1]
|
||||
if (
|
||||
!previous ||
|
||||
previous.payload.type !== "message.part.delta" ||
|
||||
previous.directory !== event.directory ||
|
||||
previous.payload.properties.messageID !== props.messageID ||
|
||||
previous.payload.properties.partID !== props.partID ||
|
||||
previous.payload.properties.field !== props.field
|
||||
) {
|
||||
const id = `${deltaKey(event.directory, props.messageID, props.partID)}:${props.field}`
|
||||
const index = deltas.get(id)
|
||||
const existing = index === undefined ? undefined : output[index]
|
||||
if (!existing || existing.payload.type !== "message.part.delta") {
|
||||
deltas.set(id, output.length)
|
||||
output.push({
|
||||
directory: event.directory,
|
||||
payload: { ...event.payload, properties: { ...props } },
|
||||
})
|
||||
return
|
||||
}
|
||||
output[output.length - 1] = {
|
||||
directory: event.directory,
|
||||
payload: {
|
||||
...event.payload,
|
||||
properties: { ...props, delta: previous.payload.properties.delta + props.delta },
|
||||
},
|
||||
}
|
||||
existing.payload.properties.delta += props.delta
|
||||
})
|
||||
return output
|
||||
}
|
||||
@@ -107,9 +85,20 @@ function createServerSdkContextBase(server: ServerConnection.Any, scope: ServerS
|
||||
|
||||
let queue: Queued[] = []
|
||||
let buffer: Queued[] = []
|
||||
const coalesced = new Map<string, number>()
|
||||
const staleDeltas = new Set<string>()
|
||||
let timer: ReturnType<typeof setTimeout> | undefined
|
||||
let last = 0
|
||||
|
||||
const key = (directory: string, payload: Event) => {
|
||||
if (payload.type === "session.status") return `session.status:${directory}:${payload.properties.sessionID}`
|
||||
if (payload.type === "lsp.updated") return `lsp.updated:${directory}`
|
||||
if (payload.type === "message.part.updated") {
|
||||
const part = payload.properties.part
|
||||
return `message.part.updated:${directory}:${part.messageID}:${part.id}`
|
||||
}
|
||||
}
|
||||
|
||||
const flush = () => {
|
||||
if (timer) clearTimeout(timer)
|
||||
timer = undefined
|
||||
@@ -117,12 +106,15 @@ function createServerSdkContextBase(server: ServerConnection.Any, scope: ServerS
|
||||
if (queue.length === 0) return
|
||||
|
||||
const events = queue
|
||||
const skip = staleDeltas.size > 0 ? new Set(staleDeltas) : undefined
|
||||
queue = buffer
|
||||
buffer = events
|
||||
queue.length = 0
|
||||
coalesced.clear()
|
||||
staleDeltas.clear()
|
||||
|
||||
last = Date.now()
|
||||
const output = coalesceServerEvents(events)
|
||||
const output = coalesceServerEvents(events, skip)
|
||||
batch(() => {
|
||||
output.forEach((event) => emitter.emit(event.directory, event.payload))
|
||||
})
|
||||
@@ -192,12 +184,29 @@ function createServerSdkContextBase(server: ServerConnection.Any, scope: ServerS
|
||||
for await (const event of events.stream) {
|
||||
resetHeartbeat()
|
||||
streamErrorLogged = false
|
||||
if (event.payload.type !== "sync") {
|
||||
const directory = event.directory ?? "global"
|
||||
const payload = event.payload as Event
|
||||
if (enqueueServerEvent(queue, { directory, payload })) schedule()
|
||||
const directory = event.directory ?? "global"
|
||||
if (event.payload.type === "sync") {
|
||||
continue
|
||||
}
|
||||
|
||||
const payload = event.payload as Event
|
||||
|
||||
const k = key(directory, payload)
|
||||
if (k) {
|
||||
const i = coalesced.get(k)
|
||||
if (i !== undefined) {
|
||||
queue[i] = { directory, payload }
|
||||
if (payload.type === "message.part.updated") {
|
||||
const part = payload.properties.part
|
||||
staleDeltas.add(deltaKey(directory, part.messageID, part.id))
|
||||
}
|
||||
continue
|
||||
}
|
||||
coalesced.set(k, queue.length)
|
||||
}
|
||||
queue.push({ directory, payload })
|
||||
schedule()
|
||||
|
||||
if (Date.now() - yielded < STREAM_YIELD_MS) continue
|
||||
yielded = Date.now()
|
||||
await wait(0)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,67 +18,44 @@ import { rootSession } from "@/utils/session-route"
|
||||
import { dropSessionCaches, pickSessionCacheEvictions, SESSION_CACHE_LIMIT } from "./global-sync/session-cache"
|
||||
|
||||
const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0)
|
||||
const cmpMessage = (a: Message, b: Message) => a.time.created - b.time.created || cmp(a.id, b.id)
|
||||
const SKIP_PARTS = new Set(["patch", "step-start", "step-finish"])
|
||||
const initialMessagePageSize = 2
|
||||
const historyMessagePageSize = 200
|
||||
const sessionInfoLimit = 2_048
|
||||
const emptyIDs: ReadonlySet<string> = new Set()
|
||||
|
||||
type OptimisticItem = {
|
||||
message: Message
|
||||
parts: Part[]
|
||||
confirmedParts?: Part[]
|
||||
confirmedMessage?: boolean
|
||||
}
|
||||
|
||||
type MessagePage = {
|
||||
session: Message[]
|
||||
part: { id: string; part: Part[] }[]
|
||||
cursor?: string
|
||||
complete: boolean
|
||||
const hasParts = (parts: Part[] | undefined, want: Part[]) => {
|
||||
if (!parts) return want.length === 0
|
||||
return want.every((part) => Binary.search(parts, part.id, (item) => item.id).found)
|
||||
}
|
||||
|
||||
// Most markers describe the current HTTP attempt; deltaParts persists non-durable stream state across retries.
|
||||
type MessageLoadState = {
|
||||
touchedMessages: Set<string>
|
||||
removedMessages: Set<string>
|
||||
retainedMessages: Set<string>
|
||||
touchedParts: Map<string, Set<string>>
|
||||
deltaParts: Map<string, Set<string>>
|
||||
carriedDeltaParts: Map<string, Set<string>>
|
||||
removedParts: Map<string, Set<string>>
|
||||
optimisticParts: Map<string, Set<string>>
|
||||
orphanParents: Set<string>
|
||||
clearedMessageParts: Set<string>
|
||||
}
|
||||
|
||||
function mergeOptimisticPage(page: MessagePage, items: OptimisticItem[]) {
|
||||
if (items.length === 0) return { ...page, observed: [] as { messageID: string; parts: Part[] }[] }
|
||||
function mergeOptimisticPage(
|
||||
page: { session: Message[]; part: { id: string; part: Part[] }[]; cursor?: string; complete: boolean },
|
||||
items: OptimisticItem[],
|
||||
) {
|
||||
if (items.length === 0) return { ...page, confirmed: [] as string[] }
|
||||
const session = [...page.session]
|
||||
const part = new Map(page.part.map((item) => [item.id, item.part]))
|
||||
const observed: { messageID: string; parts: Part[] }[] = []
|
||||
const confirmed: string[] = []
|
||||
for (const item of items) {
|
||||
const result = Binary.search(session, item.message.id, (message) => message.id)
|
||||
if (!result.found) session.splice(result.index, 0, item.message)
|
||||
const current = part.get(item.message.id)
|
||||
const confirmed = result.found
|
||||
? item.parts.filter((part) => Binary.search(current ?? [], part.id, (value) => value.id).found)
|
||||
: []
|
||||
if (result.found) observed.push({ messageID: item.message.id, parts: confirmed })
|
||||
part.set(
|
||||
item.message.id,
|
||||
merge(
|
||||
result.found ? (current ?? []) : merge(item.confirmedParts ?? [], current ?? []),
|
||||
item.parts.filter((part) => !confirmed.includes(part)),
|
||||
),
|
||||
)
|
||||
if (result.found && hasParts(current, item.parts)) {
|
||||
confirmed.push(item.message.id)
|
||||
continue
|
||||
}
|
||||
part.set(item.message.id, merge(current ?? [], item.parts))
|
||||
}
|
||||
return {
|
||||
...page,
|
||||
session,
|
||||
part: [...part.entries()].sort((a, b) => cmp(a[0], b[0])).map(([id, parts]) => ({ id, part: parts })),
|
||||
observed,
|
||||
confirmed,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,38 +75,7 @@ function merge<T extends { id: string }>(a: readonly T[], b: readonly T[]) {
|
||||
return [...items.values()].sort((x, y) => cmp(x.id, y.id))
|
||||
}
|
||||
|
||||
function reconcileFetched<T extends { id: string }>(
|
||||
fetched: T[],
|
||||
current: readonly T[],
|
||||
options: {
|
||||
touched?: ReadonlySet<string>
|
||||
retained?: ReadonlySet<string>
|
||||
preserveUnfetched?: boolean | ((item: T) => boolean)
|
||||
} = {},
|
||||
) {
|
||||
const result = new Map(fetched.map((item) => [item.id, item]))
|
||||
const live = new Map(current.map((item) => [item.id, item]))
|
||||
if (options.preserveUnfetched) {
|
||||
for (const item of current) {
|
||||
if (!result.has(item.id) && (options.preserveUnfetched === true || options.preserveUnfetched(item)))
|
||||
result.set(item.id, item)
|
||||
}
|
||||
}
|
||||
for (const id of options.retained ?? emptyIDs) {
|
||||
if (result.has(id)) continue
|
||||
const item = live.get(id)
|
||||
if (item) result.set(id, item)
|
||||
}
|
||||
// Events observed while the request is pending are the freshest client state for those identities.
|
||||
for (const id of options.touched ?? emptyIDs) {
|
||||
const item = live.get(id)
|
||||
if (item) result.set(id, item)
|
||||
if (!item) result.delete(id)
|
||||
}
|
||||
return [...result.values()].sort((a, b) => cmp(a.id, b.id))
|
||||
}
|
||||
|
||||
export function createServerSession(client: OpencodeClient, options?: { retry?: typeof retry }) {
|
||||
export function createServerSession(client: OpencodeClient) {
|
||||
const [data, setData] = createStore({
|
||||
info: {} as Record<string, Session | undefined>,
|
||||
session_status: {} as Record<string, SessionStatus>,
|
||||
@@ -149,32 +95,10 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
||||
const inflightDiff = new Map<string, Promise<void>>()
|
||||
const inflightTodo = new Map<string, Promise<void>>()
|
||||
const optimistic = new Map<string, Map<string, OptimisticItem>>()
|
||||
const messageLoads = new Map<string, MessageLoadState>()
|
||||
const pendingParts = new Map<string, Map<string, Set<string>>>()
|
||||
const orphanParts = new Map<string, Set<string>>()
|
||||
const removedMessages = new Map<string, Set<string>>()
|
||||
const deltaBases = new Map<string, { base: string; sessionID: string }>()
|
||||
const deleteMessageParts = (
|
||||
cache: { part: Record<string, Part[] | undefined>; part_text_accum_delta: Record<string, string | undefined> },
|
||||
messageID: string,
|
||||
) => {
|
||||
for (const part of cache.part[messageID] ?? []) {
|
||||
delete cache.part_text_accum_delta[part.id]
|
||||
deltaBases.delete(part.id)
|
||||
}
|
||||
delete cache.part[messageID]
|
||||
}
|
||||
const seen = new Set<string>()
|
||||
const infoSeen = new Set<string>()
|
||||
const pinned = new Map<string, number>()
|
||||
const generations = new Map<string, object>()
|
||||
const generation = (sessionID: string) => {
|
||||
const current = generations.get(sessionID)
|
||||
if (current) return current
|
||||
const created = {}
|
||||
generations.set(sessionID, created)
|
||||
return created
|
||||
}
|
||||
const generations = new Map<string, number>()
|
||||
const [meta, setMeta] = createStore({
|
||||
limit: {} as Record<string, number | undefined>,
|
||||
cursor: {} as Record<string, string | undefined>,
|
||||
@@ -191,11 +115,6 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
||||
const preserve = new Set([
|
||||
...pinned.keys(),
|
||||
...requests.keys(),
|
||||
...inflight.keys(),
|
||||
...inflightDiff.keys(),
|
||||
...inflightTodo.keys(),
|
||||
...messageLoads.keys(),
|
||||
...optimistic.keys(),
|
||||
...Object.entries(data.permission)
|
||||
.filter(([, items]) => items.length > 0)
|
||||
.map(([sessionID]) => sessionID),
|
||||
@@ -219,7 +138,6 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
||||
if (!preserve.has(sessionID)) stale.push(sessionID)
|
||||
}
|
||||
stale.forEach((sessionID) => infoSeen.delete(sessionID))
|
||||
stale.forEach((sessionID) => generations.delete(sessionID))
|
||||
setData(
|
||||
"info",
|
||||
produce((draft) => stale.forEach((sessionID) => delete draft[sessionID])),
|
||||
@@ -233,27 +151,21 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
||||
if (cached && !options?.force) return Promise.resolve(cached)
|
||||
const pending = requests.get(sessionID)
|
||||
if (pending) return pending
|
||||
const active = generation(sessionID)
|
||||
const generation = generations.get(sessionID) ?? 0
|
||||
const request = client.session.get({ sessionID }).then((result) => {
|
||||
if (!result.data) throw new Error(`Session not found: ${sessionID}`)
|
||||
if (generations.get(sessionID) !== active) return result.data
|
||||
if ((generations.get(sessionID) ?? 0) !== generation) return result.data
|
||||
return remember(result.data)
|
||||
})
|
||||
requests.set(sessionID, request)
|
||||
const cleanup = () => {
|
||||
if (requests.get(sessionID) === request) requests.delete(sessionID)
|
||||
if (
|
||||
generations.get(sessionID) === active &&
|
||||
!data.info[sessionID] &&
|
||||
!requests.has(sessionID) &&
|
||||
!messageLoads.has(sessionID) &&
|
||||
!inflight.has(sessionID) &&
|
||||
!inflightDiff.has(sessionID) &&
|
||||
!inflightTodo.has(sessionID)
|
||||
)
|
||||
generations.delete(sessionID)
|
||||
}
|
||||
void request.then(cleanup, cleanup)
|
||||
void request.then(
|
||||
() => {
|
||||
if (requests.get(sessionID) === request) requests.delete(sessionID)
|
||||
},
|
||||
() => {
|
||||
if (requests.get(sessionID) === request) requests.delete(sessionID)
|
||||
},
|
||||
)
|
||||
return request
|
||||
}
|
||||
|
||||
@@ -283,121 +195,15 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
||||
if (items.size === 0) optimistic.delete(sessionID)
|
||||
}
|
||||
|
||||
const clearOptimisticPart = (sessionID: string, messageID: string, partID: string) => {
|
||||
const items = optimistic.get(sessionID)
|
||||
const item = items?.get(messageID)
|
||||
if (!items || !item) return
|
||||
const parts = item.parts.filter((part) => part.id !== partID)
|
||||
const confirmedParts = item.confirmedParts?.filter((part) => part.id !== partID)
|
||||
if (parts.length === 0) {
|
||||
clearOptimistic(sessionID, messageID)
|
||||
return
|
||||
}
|
||||
items.set(messageID, { ...item, parts, confirmedParts, confirmedMessage: true })
|
||||
}
|
||||
|
||||
const confirmOptimisticPart = (sessionID: string, messageID: string, part: Part) => {
|
||||
const items = optimistic.get(sessionID)
|
||||
const item = items?.get(messageID)
|
||||
if (!items || !item) return
|
||||
const parts = item.parts.filter((value) => value.id !== part.id)
|
||||
if (parts.length === 0) {
|
||||
clearOptimistic(sessionID, messageID)
|
||||
return
|
||||
}
|
||||
items.set(messageID, {
|
||||
...item,
|
||||
parts,
|
||||
confirmedParts: merge(item.confirmedParts ?? [], [part]),
|
||||
confirmedMessage: true,
|
||||
})
|
||||
}
|
||||
|
||||
const confirmOptimistic = (sessionID: string, messageID: string, confirmedParts: Part[]) => {
|
||||
const items = optimistic.get(sessionID)
|
||||
const item = items?.get(messageID)
|
||||
if (!items || !item) return
|
||||
const confirmed = new Set(confirmedParts.map((part) => part.id))
|
||||
const parts = item.parts.filter((part) => !confirmed.has(part.id))
|
||||
if (parts.length === 0) {
|
||||
clearOptimistic(sessionID, messageID)
|
||||
return
|
||||
}
|
||||
items.set(messageID, {
|
||||
...item,
|
||||
parts,
|
||||
confirmedParts: merge(item.confirmedParts ?? [], confirmedParts),
|
||||
confirmedMessage: true,
|
||||
})
|
||||
}
|
||||
|
||||
const trackPartChange = (sessionID: string, messageID: string, partID: string) => {
|
||||
const load = messageLoads.get(sessionID)
|
||||
if (!load) return
|
||||
// A part event keeps an existing parent when the fetched page omits it without overriding fetched metadata.
|
||||
const messages = data.message[sessionID]
|
||||
if (messages && Binary.search(messages, messageID, (message) => message.id).found)
|
||||
load.retainedMessages.add(messageID)
|
||||
const parts = load.touchedParts.get(messageID)
|
||||
if (parts) {
|
||||
parts.add(partID)
|
||||
return
|
||||
}
|
||||
load.touchedParts.set(messageID, new Set([partID]))
|
||||
}
|
||||
|
||||
const resetMessageLoad = (sessionID: string, load: MessageLoadState) => {
|
||||
load.touchedMessages.clear()
|
||||
load.retainedMessages.clear()
|
||||
load.touchedParts.clear()
|
||||
load.carriedDeltaParts.clear()
|
||||
load.clearedMessageParts.clear()
|
||||
for (const messageID of load.removedMessages) {
|
||||
load.touchedMessages.add(messageID)
|
||||
load.clearedMessageParts.add(messageID)
|
||||
}
|
||||
for (const [messageID, parts] of load.deltaParts) {
|
||||
load.touchedParts.set(messageID, new Set(parts))
|
||||
load.carriedDeltaParts.set(messageID, new Set(parts))
|
||||
const messages = data.message[sessionID]
|
||||
if (messages && Binary.search(messages, messageID, (message) => message.id).found)
|
||||
load.retainedMessages.add(messageID)
|
||||
}
|
||||
for (const [messageID, parts] of load.removedParts) {
|
||||
const touched = load.touchedParts.get(messageID) ?? new Set<string>()
|
||||
parts.forEach((partID) => touched.add(partID))
|
||||
load.touchedParts.set(messageID, touched)
|
||||
const messages = data.message[sessionID]
|
||||
if (messages && Binary.search(messages, messageID, (message) => message.id).found)
|
||||
load.retainedMessages.add(messageID)
|
||||
}
|
||||
for (const [messageID, parts] of load.optimisticParts) {
|
||||
load.removedMessages.delete(messageID)
|
||||
load.clearedMessageParts.add(messageID)
|
||||
load.touchedMessages.add(messageID)
|
||||
const touched = load.touchedParts.get(messageID) ?? new Set<string>()
|
||||
parts.forEach((partID) => touched.add(partID))
|
||||
load.touchedParts.set(messageID, touched)
|
||||
}
|
||||
}
|
||||
|
||||
const evict = (sessionIDs: string[]) => {
|
||||
if (sessionIDs.length === 0) return
|
||||
const evicted = new Set(sessionIDs)
|
||||
for (const [partID, item] of deltaBases) {
|
||||
if (evicted.has(item.sessionID)) deltaBases.delete(partID)
|
||||
}
|
||||
sessionIDs.forEach((sessionID) => {
|
||||
generations.delete(sessionID)
|
||||
generations.set(sessionID, (generations.get(sessionID) ?? 0) + 1)
|
||||
clearOptimistic(sessionID)
|
||||
requests.delete(sessionID)
|
||||
inflight.delete(sessionID)
|
||||
inflightDiff.delete(sessionID)
|
||||
inflightTodo.delete(sessionID)
|
||||
messageLoads.delete(sessionID)
|
||||
pendingParts.delete(sessionID)
|
||||
orphanParts.delete(sessionID)
|
||||
removedMessages.delete(sessionID)
|
||||
})
|
||||
setData(
|
||||
produce((draft) => {
|
||||
@@ -424,7 +230,6 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
||||
...inflight.keys(),
|
||||
...inflightDiff.keys(),
|
||||
...inflightTodo.keys(),
|
||||
...messageLoads.keys(),
|
||||
...optimistic.keys(),
|
||||
...Object.entries(data.permission)
|
||||
.filter(([, items]) => items.length > 0)
|
||||
@@ -442,11 +247,8 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
||||
pickSessionCacheEvictions({ seen, keep: sessionID, limit: SESSION_CACHE_LIMIT, preserve: protectedSessions() }),
|
||||
)
|
||||
|
||||
const fetchMessages = async (sessionID: string, limit: number, before?: string, onAttempt?: () => void) => {
|
||||
const response = await (options?.retry ?? retry)(() => {
|
||||
onAttempt?.()
|
||||
return client.session.messages({ sessionID, limit, before })
|
||||
})
|
||||
const fetchMessages = async (sessionID: string, limit: number, before?: string) => {
|
||||
const response = await retry(() => client.session.messages({ sessionID, limit, before }))
|
||||
const items = (response.data ?? []).filter((item) => !!item?.info?.id)
|
||||
return {
|
||||
session: items.map((item) => cleanMessage(item.info)).sort((a, b) => cmp(a.id, b.id)),
|
||||
@@ -459,153 +261,30 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
||||
}
|
||||
}
|
||||
|
||||
const replaceMessages = (sessionID: string, messages: Message[]) => {
|
||||
const messageIDs = new Set(messages.map((message) => message.id))
|
||||
const dropped = (data.message[sessionID] ?? []).filter((message) => !messageIDs.has(message.id))
|
||||
setData("message", sessionID, reconcile(messages, { key: "id" }))
|
||||
setData(
|
||||
produce((draft) => {
|
||||
for (const message of dropped) deleteMessageParts(draft, message.id)
|
||||
}),
|
||||
)
|
||||
return messageIDs
|
||||
}
|
||||
|
||||
const replaceParts = (
|
||||
sessionID: string,
|
||||
items: MessagePage["part"],
|
||||
messageIDs: Set<string>,
|
||||
load?: MessageLoadState,
|
||||
) => {
|
||||
for (const item of items) {
|
||||
if (!messageIDs.has(item.id)) continue
|
||||
const fetched = load?.clearedMessageParts.has(item.id)
|
||||
? []
|
||||
: item.part.filter((part) => !SKIP_PARTS.has(part.type))
|
||||
const fetchedIDs = new Set(fetched.map((part) => part.id))
|
||||
const pending = pendingParts.get(sessionID)?.get(item.id)
|
||||
const touched = new Set([...(load?.touchedParts.get(item.id) ?? []), ...(pending ?? [])])
|
||||
for (const part of fetched) {
|
||||
const accumulated = data.part_text_accum_delta[part.id]
|
||||
const base = deltaBases.get(part.id)?.base
|
||||
const preserveDelta =
|
||||
base !== undefined &&
|
||||
accumulated !== undefined &&
|
||||
"text" in part &&
|
||||
typeof part.text === "string" &&
|
||||
part.text.startsWith(base) &&
|
||||
accumulated.startsWith(part.text) &&
|
||||
accumulated !== part.text
|
||||
if (preserveDelta) touched.add(part.id)
|
||||
if (load?.carriedDeltaParts.get(item.id)?.has(part.id) && !preserveDelta) touched.delete(part.id)
|
||||
}
|
||||
for (const partID of load?.carriedDeltaParts.get(item.id) ?? []) {
|
||||
if (!fetchedIDs.has(partID)) touched.delete(partID)
|
||||
}
|
||||
const parts = reconcileFetched(fetched, data.part[item.id] ?? [], { touched })
|
||||
if (!parts.length) {
|
||||
orphanParts.get(sessionID)?.delete(item.id)
|
||||
setData(produce((draft) => deleteMessageParts(draft, item.id)))
|
||||
continue
|
||||
}
|
||||
const partIDs = new Set(parts.map((part) => part.id))
|
||||
setData(
|
||||
"part_text_accum_delta",
|
||||
produce((draft) => {
|
||||
for (const part of data.part[item.id] ?? []) {
|
||||
if (!partIDs.has(part.id) || !touched.has(part.id)) {
|
||||
delete draft[part.id]
|
||||
deltaBases.delete(part.id)
|
||||
}
|
||||
}
|
||||
}),
|
||||
)
|
||||
setData("part", item.id, reconcile(parts, { key: "id" }))
|
||||
orphanParts.get(sessionID)?.delete(item.id)
|
||||
}
|
||||
}
|
||||
|
||||
const applyMessagePage = (
|
||||
sessionID: string,
|
||||
page: MessagePage,
|
||||
load: MessageLoadState | undefined,
|
||||
preserveUnfetched: boolean | ((message: Message) => boolean),
|
||||
cleanupOrphans: boolean,
|
||||
) => {
|
||||
const merged = mergeOptimisticPage(page, [...(optimistic.get(sessionID)?.values() ?? [])])
|
||||
merged.observed.forEach((item) => {
|
||||
if (!load?.clearedMessageParts.has(item.messageID)) confirmOptimistic(sessionID, item.messageID, item.parts)
|
||||
})
|
||||
const touchedMessages = new Set([...(load?.touchedMessages ?? []), ...(removedMessages.get(sessionID) ?? [])])
|
||||
const messages = reconcileFetched(merged.session, data.message[sessionID] ?? [], {
|
||||
touched: touchedMessages,
|
||||
retained: load?.retainedMessages,
|
||||
preserveUnfetched,
|
||||
})
|
||||
batch(() => {
|
||||
const messageIDs = replaceMessages(sessionID, messages)
|
||||
replaceParts(sessionID, merged.part, messageIDs, load)
|
||||
const orphans = orphanParts.get(sessionID)
|
||||
if (cleanupOrphans && page.complete && orphans) {
|
||||
for (const messageID of orphans) {
|
||||
if (!messageIDs.has(messageID)) setData(produce((draft) => deleteMessageParts(draft, messageID)))
|
||||
}
|
||||
orphanParts.delete(sessionID)
|
||||
}
|
||||
setMeta("limit", sessionID, messages.length)
|
||||
setMeta("cursor", sessionID, merged.cursor)
|
||||
setMeta("complete", sessionID, merged.complete)
|
||||
setMeta("at", sessionID, Date.now())
|
||||
})
|
||||
}
|
||||
|
||||
const loadMessages = async (sessionID: string, limit: number, before?: string, mode?: "replace" | "prepend") => {
|
||||
if (meta.loading[sessionID]) return
|
||||
const active = generation(sessionID)
|
||||
const load: MessageLoadState = {
|
||||
touchedMessages: new Set(),
|
||||
removedMessages: new Set(),
|
||||
retainedMessages: new Set(),
|
||||
touchedParts: new Map(),
|
||||
deltaParts: new Map(),
|
||||
carriedDeltaParts: new Map(),
|
||||
removedParts: new Map(),
|
||||
optimisticParts: new Map(),
|
||||
orphanParents: new Set(),
|
||||
clearedMessageParts: new Set(),
|
||||
}
|
||||
messageLoads.set(sessionID, load)
|
||||
const generation = generations.get(sessionID) ?? 0
|
||||
setMeta("loading", sessionID, true)
|
||||
let applied = false
|
||||
await fetchMessages(sessionID, limit, before, () => resetMessageLoad(sessionID, load))
|
||||
await fetchMessages(sessionID, limit, before)
|
||||
.then((page) => {
|
||||
if (generations.get(sessionID) !== active) return
|
||||
const first = page.session.reduce<Message | undefined>(
|
||||
(oldest, message) => (!oldest || cmpMessage(message, oldest) < 0 ? message : oldest),
|
||||
undefined,
|
||||
)
|
||||
const preserveUnfetched =
|
||||
mode === "prepend" || (!page.complete && (!first || ((message: Message) => cmpMessage(message, first) < 0)))
|
||||
applyMessagePage(
|
||||
sessionID,
|
||||
page,
|
||||
messageLoads.get(sessionID) === load ? load : undefined,
|
||||
preserveUnfetched,
|
||||
mode !== "prepend",
|
||||
)
|
||||
applied = true
|
||||
if ((generations.get(sessionID) ?? 0) !== generation) return
|
||||
const next = mergeOptimisticPage(page, [...(optimistic.get(sessionID)?.values() ?? [])])
|
||||
next.confirmed.forEach((messageID) => clearOptimistic(sessionID, messageID))
|
||||
const messages = mode === "prepend" ? merge(data.message[sessionID] ?? [], next.session) : next.session
|
||||
batch(() => {
|
||||
setData("message", sessionID, reconcile(messages, { key: "id" }))
|
||||
for (const item of next.part) {
|
||||
const parts = item.part.filter((part) => !SKIP_PARTS.has(part.type))
|
||||
if (parts.length) setData("part", item.id, reconcile(parts, { key: "id" }))
|
||||
}
|
||||
setMeta("limit", sessionID, messages.length)
|
||||
setMeta("cursor", sessionID, next.cursor)
|
||||
setMeta("complete", sessionID, next.complete)
|
||||
setMeta("at", sessionID, Date.now())
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
if (!applied && generations.get(sessionID) === active && messageLoads.get(sessionID) === load) {
|
||||
for (const messageID of load.orphanParents) {
|
||||
if (!orphanParts.get(sessionID)?.has(messageID)) continue
|
||||
setData(produce((draft) => deleteMessageParts(draft, messageID)))
|
||||
orphanParts.get(sessionID)?.delete(messageID)
|
||||
}
|
||||
if (orphanParts.get(sessionID)?.size === 0) orphanParts.delete(sessionID)
|
||||
}
|
||||
if (messageLoads.get(sessionID) === load) messageLoads.delete(sessionID)
|
||||
if (generations.get(sessionID) === active) setMeta("loading", sessionID, false)
|
||||
if ((generations.get(sessionID) ?? 0) === generation) setMeta("loading", sessionID, false)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -660,13 +339,7 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
||||
const eventID = eventSessionID(event)
|
||||
if (eventID) {
|
||||
touch(eventID)
|
||||
if (
|
||||
!data.info[eventID] &&
|
||||
event.type !== "session.created" &&
|
||||
event.type !== "session.updated" &&
|
||||
event.type !== "session.deleted"
|
||||
)
|
||||
void resolve(eventID).catch(() => {})
|
||||
if (!data.info[eventID]) void resolve(eventID).catch(() => {})
|
||||
}
|
||||
switch (event.type) {
|
||||
case "session.created":
|
||||
@@ -705,21 +378,6 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
||||
}
|
||||
case "message.updated": {
|
||||
const info = cleanMessage((event.properties as { info: Message }).info)
|
||||
const load = messageLoads.get(info.sessionID)
|
||||
load?.touchedMessages.add(info.id)
|
||||
load?.removedMessages.delete(info.id)
|
||||
const items = optimistic.get(info.sessionID)
|
||||
const item = items?.get(info.id)
|
||||
if (items && item) {
|
||||
if (item.parts.length === 0) clearOptimistic(info.sessionID, info.id)
|
||||
if (item.parts.length > 0) items.set(info.id, { ...item, confirmedMessage: true })
|
||||
}
|
||||
const orphans = orphanParts.get(info.sessionID)
|
||||
orphans?.delete(info.id)
|
||||
if (orphans?.size === 0) orphanParts.delete(info.sessionID)
|
||||
const removedMessagesForSession = removedMessages.get(info.sessionID)
|
||||
removedMessagesForSession?.delete(info.id)
|
||||
if (removedMessagesForSession?.size === 0) removedMessages.delete(info.sessionID)
|
||||
const messages = data.message[info.sessionID]
|
||||
if (!messages) {
|
||||
setData("message", info.sessionID, [info])
|
||||
@@ -737,20 +395,6 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
||||
}
|
||||
case "message.removed": {
|
||||
const props = event.properties as { sessionID: string; messageID: string }
|
||||
const load = messageLoads.get(props.sessionID)
|
||||
load?.touchedMessages.add(props.messageID)
|
||||
load?.removedMessages.add(props.messageID)
|
||||
load?.clearedMessageParts.add(props.messageID)
|
||||
load?.deltaParts.delete(props.messageID)
|
||||
load?.carriedDeltaParts.delete(props.messageID)
|
||||
load?.removedParts.delete(props.messageID)
|
||||
load?.optimisticParts.delete(props.messageID)
|
||||
pendingParts.get(props.sessionID)?.delete(props.messageID)
|
||||
if (pendingParts.get(props.sessionID)?.size === 0) pendingParts.delete(props.sessionID)
|
||||
const removedMessagesForSession = removedMessages.get(props.sessionID) ?? new Set<string>()
|
||||
removedMessagesForSession.add(props.messageID)
|
||||
removedMessages.set(props.sessionID, removedMessagesForSession)
|
||||
clearOptimistic(props.sessionID, props.messageID)
|
||||
setData(
|
||||
produce((draft) => {
|
||||
const messages = draft.message[props.sessionID]
|
||||
@@ -758,7 +402,8 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
||||
const result = Binary.search(messages, props.messageID, (message) => message.id)
|
||||
if (result.found) messages.splice(result.index, 1)
|
||||
}
|
||||
deleteMessageParts(draft, props.messageID)
|
||||
for (const part of draft.part[props.messageID] ?? []) delete draft.part_text_accum_delta[part.id]
|
||||
delete draft.part[props.messageID]
|
||||
}),
|
||||
)
|
||||
return
|
||||
@@ -766,42 +411,6 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
||||
case "message.part.updated": {
|
||||
const part = (event.properties as { part: Part }).part
|
||||
if (SKIP_PARTS.has(part.type)) return
|
||||
const messages = data.message[part.sessionID]
|
||||
const load = messageLoads.get(part.sessionID)
|
||||
const missing = !messages || !Binary.search(messages, part.messageID, (message) => message.id).found
|
||||
// Outside a page load, accepting a part without its ordered parent event would create an unbounded orphan.
|
||||
if (
|
||||
missing &&
|
||||
(!load ||
|
||||
load.clearedMessageParts.has(part.messageID) ||
|
||||
removedMessages.get(part.sessionID)?.has(part.messageID))
|
||||
)
|
||||
return
|
||||
if (missing) {
|
||||
const orphans = orphanParts.get(part.sessionID) ?? new Set<string>()
|
||||
orphans.add(part.messageID)
|
||||
orphanParts.set(part.sessionID, orphans)
|
||||
load?.orphanParents.add(part.messageID)
|
||||
}
|
||||
const deltas = load?.deltaParts.get(part.messageID)
|
||||
deltas?.delete(part.id)
|
||||
if (deltas?.size === 0) load?.deltaParts.delete(part.messageID)
|
||||
const carried = load?.carriedDeltaParts.get(part.messageID)
|
||||
carried?.delete(part.id)
|
||||
if (carried?.size === 0) load?.carriedDeltaParts.delete(part.messageID)
|
||||
const removed = load?.removedParts.get(part.messageID)
|
||||
removed?.delete(part.id)
|
||||
if (removed?.size === 0) load?.removedParts.delete(part.messageID)
|
||||
const pending = pendingParts.get(part.sessionID)?.get(part.messageID)
|
||||
pending?.delete(part.id)
|
||||
if (pending?.size === 0) pendingParts.get(part.sessionID)?.delete(part.messageID)
|
||||
if (pendingParts.get(part.sessionID)?.size === 0) pendingParts.delete(part.sessionID)
|
||||
const optimistic = load?.optimisticParts.get(part.messageID)
|
||||
optimistic?.delete(part.id)
|
||||
if (optimistic?.size === 0) load?.optimisticParts.delete(part.messageID)
|
||||
deltaBases.delete(part.id)
|
||||
trackPartChange(part.sessionID, part.messageID, part.id)
|
||||
confirmOptimisticPart(part.sessionID, part.messageID, part)
|
||||
setData(
|
||||
"part_text_accum_delta",
|
||||
produce((draft) => void delete draft[part.id]),
|
||||
@@ -822,34 +431,10 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
||||
return
|
||||
}
|
||||
case "message.part.removed": {
|
||||
const props = event.properties as { sessionID: string; messageID: string; partID: string }
|
||||
// Part removal is event-only on the server, so its tombstone lasts until a later update or eviction.
|
||||
const pending = pendingParts.get(props.sessionID) ?? new Map<string, Set<string>>()
|
||||
const parts = pending.get(props.messageID) ?? new Set<string>()
|
||||
parts.add(props.partID)
|
||||
pending.set(props.messageID, parts)
|
||||
pendingParts.set(props.sessionID, pending)
|
||||
const deltas = messageLoads.get(props.sessionID)?.deltaParts.get(props.messageID)
|
||||
deltas?.delete(props.partID)
|
||||
if (deltas?.size === 0) messageLoads.get(props.sessionID)?.deltaParts.delete(props.messageID)
|
||||
const load = messageLoads.get(props.sessionID)
|
||||
const carried = load?.carriedDeltaParts.get(props.messageID)
|
||||
carried?.delete(props.partID)
|
||||
if (carried?.size === 0) load?.carriedDeltaParts.delete(props.messageID)
|
||||
if (load) {
|
||||
const parts = load.removedParts.get(props.messageID) ?? new Set<string>()
|
||||
parts.add(props.partID)
|
||||
load.removedParts.set(props.messageID, parts)
|
||||
const optimistic = load.optimisticParts.get(props.messageID)
|
||||
optimistic?.delete(props.partID)
|
||||
if (optimistic?.size === 0) load.optimisticParts.delete(props.messageID)
|
||||
}
|
||||
trackPartChange(props.sessionID, props.messageID, props.partID)
|
||||
clearOptimisticPart(props.sessionID, props.messageID, props.partID)
|
||||
const props = event.properties as { messageID: string; partID: string }
|
||||
setData(
|
||||
produce((draft) => {
|
||||
delete draft.part_text_accum_delta[props.partID]
|
||||
deltaBases.delete(props.partID)
|
||||
const parts = draft.part[props.messageID]
|
||||
if (!parts) return
|
||||
const result = Binary.search(parts, props.partID, (part) => part.id)
|
||||
@@ -860,31 +445,13 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
||||
return
|
||||
}
|
||||
case "message.part.delta": {
|
||||
const props = event.properties as {
|
||||
sessionID: string
|
||||
messageID: string
|
||||
partID: string
|
||||
field: string
|
||||
delta: string
|
||||
}
|
||||
const props = event.properties as { messageID: string; partID: string; field: string; delta: string }
|
||||
const parts = data.part[props.messageID]
|
||||
if (!parts) return
|
||||
const result = Binary.search(parts, props.partID, (part) => part.id)
|
||||
if (!result.found) return
|
||||
trackPartChange(props.sessionID, props.messageID, props.partID)
|
||||
const load = messageLoads.get(props.sessionID)
|
||||
if (load) {
|
||||
const parts = load.deltaParts.get(props.messageID) ?? new Set<string>()
|
||||
parts.add(props.partID)
|
||||
load.deltaParts.set(props.messageID, parts)
|
||||
const carried = load.carriedDeltaParts.get(props.messageID)
|
||||
carried?.delete(props.partID)
|
||||
if (carried?.size === 0) load.carriedDeltaParts.delete(props.messageID)
|
||||
}
|
||||
const field = props.field as keyof (typeof parts)[number]
|
||||
const current = parts[result.index]?.[field]
|
||||
if (!deltaBases.has(props.partID) && typeof current === "string")
|
||||
deltaBases.set(props.partID, { base: current, sessionID: props.sessionID })
|
||||
setData(
|
||||
"part_text_accum_delta",
|
||||
props.partID,
|
||||
@@ -992,70 +559,32 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
||||
},
|
||||
optimistic: {
|
||||
add(input: { sessionID: string; message: Message; parts: Part[] }) {
|
||||
const parts = input.parts
|
||||
.filter((part) => !!part?.id && !SKIP_PARTS.has(part.type))
|
||||
.sort((a, b) => cmp(a.id, b.id))
|
||||
const load = messageLoads.get(input.sessionID)
|
||||
if (load?.clearedMessageParts.has(input.message.id)) {
|
||||
const touched = load.touchedParts.get(input.message.id) ?? new Set<string>()
|
||||
parts.forEach((part) => touched.add(part.id))
|
||||
load.touchedParts.set(input.message.id, touched)
|
||||
}
|
||||
if (load) {
|
||||
load.removedMessages.delete(input.message.id)
|
||||
load.optimisticParts.set(input.message.id, new Set(parts.map((part) => part.id)))
|
||||
}
|
||||
const items = optimistic.get(input.sessionID)
|
||||
const removedMessagesForSession = removedMessages.get(input.sessionID)
|
||||
removedMessagesForSession?.delete(input.message.id)
|
||||
if (removedMessagesForSession?.size === 0) removedMessages.delete(input.sessionID)
|
||||
if (items) items.set(input.message.id, { ...input, parts, confirmedParts: [] })
|
||||
if (!items)
|
||||
optimistic.set(input.sessionID, new Map([[input.message.id, { ...input, parts, confirmedParts: [] }]]))
|
||||
if (items) items.set(input.message.id, input)
|
||||
if (!items) optimistic.set(input.sessionID, new Map([[input.message.id, input]]))
|
||||
setData("message", input.sessionID, (messages = []) => merge(messages, [input.message]))
|
||||
setData(
|
||||
"part_text_accum_delta",
|
||||
produce((draft) => {
|
||||
for (const part of [...(data.part[input.message.id] ?? []), ...parts]) {
|
||||
delete draft[part.id]
|
||||
deltaBases.delete(part.id)
|
||||
}
|
||||
}),
|
||||
"part",
|
||||
input.message.id,
|
||||
input.parts.filter((part) => !!part?.id).sort((a, b) => cmp(a.id, b.id)),
|
||||
)
|
||||
setData("part", input.message.id, parts)
|
||||
},
|
||||
remove(input: { sessionID: string; messageID: string }) {
|
||||
const item = optimistic.get(input.sessionID)?.get(input.messageID)
|
||||
if (!item) return
|
||||
messageLoads.get(input.sessionID)?.optimisticParts.delete(input.messageID)
|
||||
clearOptimistic(input.sessionID, input.messageID)
|
||||
if (item.confirmedMessage) {
|
||||
const partIDs = new Set(item.parts.map((part) => part.id))
|
||||
setData(
|
||||
produce((draft) => {
|
||||
for (const part of item.parts) {
|
||||
delete draft.part_text_accum_delta[part.id]
|
||||
deltaBases.delete(part.id)
|
||||
}
|
||||
const parts = draft.part[input.messageID]
|
||||
if (!parts) return
|
||||
draft.part[input.messageID] = parts.filter((part) => !partIDs.has(part.id))
|
||||
if (draft.part[input.messageID]?.length === 0) delete draft.part[input.messageID]
|
||||
}),
|
||||
)
|
||||
return
|
||||
}
|
||||
setData("message", input.sessionID, (messages) => messages?.filter((message) => message.id !== input.messageID))
|
||||
setData(produce((draft) => deleteMessageParts(draft, input.messageID)))
|
||||
setData(
|
||||
"part",
|
||||
produce((draft) => void delete draft[input.messageID]),
|
||||
)
|
||||
},
|
||||
},
|
||||
diff(sessionID: string, options?: { force?: boolean }) {
|
||||
touch(sessionID)
|
||||
if (data.session_diff[sessionID] !== undefined && !options?.force) return Promise.resolve()
|
||||
return runInflight(inflightDiff, sessionID, () => {
|
||||
const active = generation(sessionID)
|
||||
const generation = generations.get(sessionID) ?? 0
|
||||
return retry(() => client.session.diff({ sessionID })).then((result) => {
|
||||
if (generations.get(sessionID) !== active) return
|
||||
if ((generations.get(sessionID) ?? 0) !== generation) return
|
||||
setData("session_diff", sessionID, reconcile(cleanDiffs(result.data), { key: "file" }))
|
||||
})
|
||||
})
|
||||
@@ -1064,9 +593,9 @@ export function createServerSession(client: OpencodeClient, options?: { retry?:
|
||||
touch(sessionID)
|
||||
if (data.todo[sessionID] !== undefined && !options?.force) return Promise.resolve()
|
||||
return runInflight(inflightTodo, sessionID, () => {
|
||||
const active = generation(sessionID)
|
||||
const generation = generations.get(sessionID) ?? 0
|
||||
return retry(() => client.session.todo({ sessionID })).then((result) => {
|
||||
if (generations.get(sessionID) !== active) return
|
||||
if ((generations.get(sessionID) ?? 0) !== generation) return
|
||||
setData("todo", sessionID, reconcile(result.data ?? [], { key: "id" }))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -163,29 +163,27 @@ export const { use: useTabs, provider: TabsProvider } = createSimpleContext({
|
||||
},
|
||||
newDraft(draft: Omit<DraftTab, "type" | "draftID">, prompt?: string) {
|
||||
const draftID = uuid()
|
||||
void startTransition(() => {
|
||||
setStore(
|
||||
produce((tabs) => {
|
||||
tabs.push({ type: "draft", draftID, ...draft })
|
||||
}),
|
||||
)
|
||||
navigate(prompt ? `${draftHref(draftID)}&prompt=${encodeURIComponent(prompt)}` : draftHref(draftID))
|
||||
})
|
||||
setStore(
|
||||
produce((tabs) => {
|
||||
tabs.push({ type: "draft", draftID, ...draft })
|
||||
}),
|
||||
)
|
||||
navigate(prompt ? `${draftHref(draftID)}&prompt=${encodeURIComponent(prompt)}` : draftHref(draftID))
|
||||
},
|
||||
updateDraft(draftID: string, draft: Partial<Omit<DraftTab, "type" | "draftID">>) {
|
||||
void startTransition(() => {
|
||||
setStore(
|
||||
(tab) => tab.type === "draft" && tab.draftID === draftID,
|
||||
produce((tab) => Object.assign(tab, draft)),
|
||||
)
|
||||
})
|
||||
setStore(
|
||||
(tab) => tab.type === "draft" && tab.draftID === draftID,
|
||||
produce((tab) => Object.assign(tab, draft)),
|
||||
)
|
||||
},
|
||||
promoteDraft(draftID: string, session: Omit<SessionTab, "type">) {
|
||||
// Keep the replacement and navigation atomic so /new-session never renders
|
||||
// after its backing draft tab has been removed from the store.
|
||||
// We're viewing this draft when /new-session?draftId=… points at it. Promoting
|
||||
// replaces the draft tab with a session tab, so the draft route would stop resolving
|
||||
// and fall back home. Navigate to the new session first so we leave /new-session
|
||||
// before the draft is removed from the store.
|
||||
const active = location.pathname === "/new-session" && location.query.draftId === draftID
|
||||
const next = { type: "session" as const, ...session }
|
||||
void startTransition(() => {
|
||||
startTransition(() => {
|
||||
setStore(
|
||||
produce((tabs) => {
|
||||
const index = tabs.findIndex((tab) => tab.type === "draft" && tab.draftID === draftID)
|
||||
|
||||
@@ -302,7 +302,6 @@ export function NewHome() {
|
||||
|
||||
function selectProject(conn: ServerConnection.Any, directory: string) {
|
||||
const key = ServerConnection.key(conn)
|
||||
if (global.servers.health[key]?.healthy === false) return
|
||||
if (
|
||||
!global
|
||||
.ensureServerCtx(conn)
|
||||
@@ -409,7 +408,7 @@ export function NewHome() {
|
||||
|
||||
return (
|
||||
<div class="rounded-[10px] shadow-[var(--v2-elevation-raised)] m-2 min-h-0 lg:overflow-hidden bg-v2-background-bg-base self-stretch flex-1">
|
||||
<div class="mx-auto grid h-full w-full max-w-[1080px] grid-rows-[auto_minmax(0,1fr)_auto] gap-4 px-3 lg:grid-cols-[280px_minmax(0,720px)] lg:grid-rows-1 lg:gap-8 lg:px-6">
|
||||
<div class="mx-auto grid h-full w-full max-w-[1080px] grid-rows-[auto_minmax(0,1fr)_auto] gap-4 px-3 pb-3 lg:grid-cols-[280px_minmax(0,720px)] lg:grid-rows-1 lg:gap-8 lg:px-6 lg:pb-16">
|
||||
<HomeProjectColumn
|
||||
projects={projects()}
|
||||
selected={selection()}
|
||||
@@ -456,7 +455,7 @@ export function NewHome() {
|
||||
onClose={closeSearch}
|
||||
onSelect={selectSearchSession}
|
||||
/>
|
||||
<ScrollView class="mt-3 -mr-3 min-h-0 flex-1">
|
||||
<ScrollView class="mt-3 min-h-0 flex-1">
|
||||
<Show
|
||||
when={!sessionLoad.isLoading}
|
||||
fallback={
|
||||
@@ -469,7 +468,7 @@ export function NewHome() {
|
||||
when={groups().length > 0}
|
||||
fallback={<HomeSessionsEmpty onNewSession={newSessionProject() ? openNewSession : undefined} />}
|
||||
>
|
||||
<div class="flex flex-col gap-6 pt-3 pr-3 pb-16">
|
||||
<div class="pt-3 flex flex-col gap-6">
|
||||
<For each={groups()}>
|
||||
{(group, index) => (
|
||||
<div class="flex min-w-0 flex-col gap-4">
|
||||
@@ -768,18 +767,15 @@ function HomeProjectRow(props: {
|
||||
clearNotifications: (server: ServerConnection.Any, project: LocalProject) => void
|
||||
language: ReturnType<typeof useLanguage>
|
||||
}) {
|
||||
const global = useGlobal()
|
||||
const serverUnreachable = () => global.servers.health[ServerConnection.key(props.server)]?.healthy === false
|
||||
const [state, setState] = createStore({ menuOpen: false })
|
||||
return (
|
||||
<div class="group/project relative flex h-7 min-w-0 items-center rounded-[6px]">
|
||||
<button
|
||||
type="button"
|
||||
data-component="home-project-row"
|
||||
class={`${HOME_PROJECT_NAV_ROW} pr-16 disabled:opacity-60`}
|
||||
class={`${HOME_PROJECT_NAV_ROW} pr-16`}
|
||||
data-selected={props.selected ? "" : undefined}
|
||||
aria-current={props.selected ? "page" : undefined}
|
||||
disabled={serverUnreachable()}
|
||||
onClick={() => props.selectProject(props.server, props.project.worktree)}
|
||||
>
|
||||
<HomeProjectAvatar project={props.project} />
|
||||
@@ -969,7 +965,7 @@ function HomeSessionSearch(props: {
|
||||
style={{
|
||||
top: "-6px",
|
||||
left: "-6px",
|
||||
width: "calc(100% + 12px)",
|
||||
width: "calc(100% + 14px)",
|
||||
}}
|
||||
>
|
||||
<div class="flex flex-col pt-9">
|
||||
@@ -1137,7 +1133,7 @@ function HomeSessionSearchResultRow(props: {
|
||||
function HomeSessionGroupHeader(props: { title: string; onNewSession?: () => void }) {
|
||||
const language = useLanguage()
|
||||
return (
|
||||
<div class="flex h-7 min-w-0 items-center justify-between pl-3">
|
||||
<div class="flex h-7 min-w-0 items-center justify-between pl-[18px]">
|
||||
<div class={HOME_SECTION_LABEL}>{props.title}</div>
|
||||
<Show when={props.onNewSession}>
|
||||
{(onNewSession) => (
|
||||
|
||||
@@ -36,7 +36,7 @@ export default function NewLayout(props: ParentProps) {
|
||||
<main class="flex-1 min-h-0 min-w-0 overflow-x-hidden flex flex-col items-start contain-strict">
|
||||
<Suspense>{props.children}</Suspense>
|
||||
</main>
|
||||
{import.meta.env.DEV && <DebugBar inline />}
|
||||
{import.meta.env.DEV && <DebugBar />}
|
||||
<HelpButton />
|
||||
<ToastRegion v2 />
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@ Private generation target for clients derived directly from OpenCode's authorita
|
||||
- `@opencode-ai/client`: zero-Effect Promise client using `fetch`.
|
||||
- `@opencode-ai/client/effect`: rich Effect network client using an environment-provided `HttpClient`.
|
||||
|
||||
The generated surface includes every standard HTTP group from Server's concrete API. The build compiler reads `@opencode-ai/server/api`; the generated Effect runtime imports a client-local projection built from Protocol, with a generation-equivalence test preventing transport drift. Custom transports such as the PTY WebSocket connection remain outside the generic HTTP client. Run `bun run generate` after changing the contract and `bun run check:generated` to detect committed-output drift.
|
||||
The generated surface starts with the Session group from Server's concrete API. The build compiler reads `@opencode-ai/server/api`; the generated Effect runtime imports a client-local projection built from Protocol, with a generation-equivalence test preventing transport drift. Run `bun run generate` after changing the contract and `bun run check:generated` to detect committed-output drift.
|
||||
|
||||
The Effect entrypoint uses canonical decoded values such as `Session.ID`, `Location.Ref`, and `Prompt`. These datatypes come from the lightweight `@opencode-ai/schema` package and are re-exported so callers depend only on the client surface. Protocol owns endpoint construction and middleware placement; Server supplies the concrete middleware keys used by the build-time API.
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { NodeFileSystem } from "@effect/platform-node"
|
||||
import { compile, emitEffectImported, emitPromise, write } from "@opencode-ai/httpapi-codegen"
|
||||
import { ClientApi, endpointNames, groupNames, omitEndpoints } from "../src/contract"
|
||||
import { ClientApi } from "../src/contract"
|
||||
import { Effect } from "effect"
|
||||
import { fileURLToPath } from "url"
|
||||
|
||||
const contract = compile(ClientApi, { groupNames, endpointNames, omitEndpoints })
|
||||
const contract = compile(ClientApi, {
|
||||
groupNames: { "server.session": "sessions", "server.event": "events" },
|
||||
})
|
||||
|
||||
await Effect.runPromise(
|
||||
Effect.all(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { makeDefaultApi } from "@opencode-ai/protocol/api"
|
||||
import { InvalidRequestError, SessionNotFoundError } from "@opencode-ai/protocol/errors"
|
||||
import { HttpApiMiddleware } from "effect/unstable/httpapi"
|
||||
import { HttpApi, HttpApiMiddleware } from "effect/unstable/httpapi"
|
||||
|
||||
class LocationMiddleware extends HttpApiMiddleware.Service<LocationMiddleware>()(
|
||||
"@opencode-ai/client/LocationMiddleware",
|
||||
@@ -11,43 +11,11 @@ class SessionLocationMiddleware extends HttpApiMiddleware.Service<SessionLocatio
|
||||
{ error: [InvalidRequestError, SessionNotFoundError] },
|
||||
) {}
|
||||
|
||||
export const ClientApi = makeDefaultApi({
|
||||
const Api = makeDefaultApi({
|
||||
locationMiddleware: LocationMiddleware,
|
||||
sessionLocationMiddleware: SessionLocationMiddleware,
|
||||
})
|
||||
|
||||
export const groupNames = {
|
||||
"server.health": "health",
|
||||
"server.location": "location",
|
||||
"server.agent": "agents",
|
||||
"server.session": "sessions",
|
||||
"server.message": "messages",
|
||||
"server.model": "models",
|
||||
"server.provider": "providers",
|
||||
"server.integration": "integrations",
|
||||
"server.credential": "credentials",
|
||||
"server.permission": "permissions",
|
||||
"server.fs": "files",
|
||||
"server.command": "commands",
|
||||
"server.skill": "skills",
|
||||
"server.event": "events",
|
||||
"server.pty": "ptys",
|
||||
"server.question": "questions",
|
||||
"server.reference": "references",
|
||||
"server.projectCopy": "projectCopies",
|
||||
} as const
|
||||
|
||||
export const endpointNames = {
|
||||
"session.messages": "list",
|
||||
"integration.connect.key": "connectKey",
|
||||
"integration.connect.oauth": "connectOauth",
|
||||
"integration.attempt.status": "attemptStatus",
|
||||
"integration.attempt.complete": "attemptComplete",
|
||||
"integration.attempt.cancel": "attemptCancel",
|
||||
"permission.request.list": "listRequests",
|
||||
"permission.saved.list": "listSaved",
|
||||
"permission.saved.remove": "removeSaved",
|
||||
"question.request.list": "listRequests",
|
||||
} as const
|
||||
|
||||
export const omitEndpoints = new Set(["fs.read", "pty.connect", "pty.connectToken"])
|
||||
export const SessionGroup = Api.groups["server.session"]
|
||||
export const EventGroup = Api.groups["server.event"]
|
||||
export const ClientApi = HttpApi.make("opencode-client").add(SessionGroup).add(EventGroup)
|
||||
|
||||
@@ -2,24 +2,12 @@
|
||||
// Core or Server. Preserve these datatype exports so internal model reorganizations do not require caller migrations.
|
||||
export * from "./generated-effect/index"
|
||||
export { Agent } from "@opencode-ai/schema/agent"
|
||||
export { Command } from "@opencode-ai/schema/command"
|
||||
export { Credential } from "@opencode-ai/schema/credential"
|
||||
export { FileSystem } from "@opencode-ai/schema/filesystem"
|
||||
export { Integration } from "@opencode-ai/schema/integration"
|
||||
export { Location } from "@opencode-ai/schema/location"
|
||||
export { Model } from "@opencode-ai/schema/model"
|
||||
export { Permission } from "@opencode-ai/schema/permission"
|
||||
export { PermissionSaved } from "@opencode-ai/schema/permission-saved"
|
||||
export { Project } from "@opencode-ai/schema/project"
|
||||
export { ProjectCopy } from "@opencode-ai/schema/project-copy"
|
||||
export { Provider } from "@opencode-ai/schema/provider"
|
||||
export { Pty } from "@opencode-ai/schema/pty"
|
||||
export { Question } from "@opencode-ai/schema/question"
|
||||
export { Reference } from "@opencode-ai/schema/reference"
|
||||
export { AbsolutePath, RelativePath } from "@opencode-ai/schema/schema"
|
||||
export { Session } from "@opencode-ai/schema/session"
|
||||
export { SessionInput } from "@opencode-ai/schema/session-input"
|
||||
export { SessionMessage } from "@opencode-ai/schema/session-message"
|
||||
export { Skill } from "@opencode-ai/schema/skill"
|
||||
export { Prompt } from "@opencode-ai/schema/prompt"
|
||||
export type { OpenCodeEvent } from "@opencode-ai/protocol/groups/event"
|
||||
|
||||
@@ -13,502 +13,202 @@ const mapClientError = <E>(error: E) =>
|
||||
? new ClientError({ cause: error })
|
||||
: error
|
||||
|
||||
const Endpoint0_0 = (raw: RawClient["server.health"]) => () =>
|
||||
raw["health.get"]({}).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
const adaptGroup0 = (raw: RawClient["server.health"]) => ({ get: Endpoint0_0(raw) })
|
||||
|
||||
type Endpoint1_0Request = Parameters<RawClient["server.location"]["location.get"]>[0]
|
||||
type Endpoint1_0Input = { readonly location?: Endpoint1_0Request["query"]["location"] }
|
||||
const Endpoint1_0 = (raw: RawClient["server.location"]) => (input?: Endpoint1_0Input) =>
|
||||
raw["location.get"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
const adaptGroup1 = (raw: RawClient["server.location"]) => ({ get: Endpoint1_0(raw) })
|
||||
|
||||
type Endpoint2_0Request = Parameters<RawClient["server.agent"]["agent.list"]>[0]
|
||||
type Endpoint2_0Input = { readonly location?: Endpoint2_0Request["query"]["location"] }
|
||||
const Endpoint2_0 = (raw: RawClient["server.agent"]) => (input?: Endpoint2_0Input) =>
|
||||
raw["agent.list"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
const adaptGroup2 = (raw: RawClient["server.agent"]) => ({ list: Endpoint2_0(raw) })
|
||||
|
||||
type Endpoint3_0Request = Parameters<RawClient["server.session"]["session.list"]>[0]
|
||||
type Endpoint3_0Input = {
|
||||
readonly workspace?: Endpoint3_0Request["query"]["workspace"]
|
||||
readonly limit?: Endpoint3_0Request["query"]["limit"]
|
||||
readonly order?: Endpoint3_0Request["query"]["order"]
|
||||
readonly search?: Endpoint3_0Request["query"]["search"]
|
||||
readonly directory?: Endpoint3_0Request["query"]["directory"]
|
||||
readonly project?: Endpoint3_0Request["query"]["project"]
|
||||
readonly subpath?: Endpoint3_0Request["query"]["subpath"]
|
||||
readonly cursor?: Endpoint3_0Request["query"]["cursor"]
|
||||
type Endpoint0_0Request = Parameters<RawClient["server.session"]["session.list"]>[0]
|
||||
type Endpoint0_0Input = {
|
||||
readonly workspace?: Endpoint0_0Request["query"]["workspace"]
|
||||
readonly limit?: Endpoint0_0Request["query"]["limit"]
|
||||
readonly order?: Endpoint0_0Request["query"]["order"]
|
||||
readonly search?: Endpoint0_0Request["query"]["search"]
|
||||
readonly directory?: Endpoint0_0Request["query"]["directory"]
|
||||
readonly project?: Endpoint0_0Request["query"]["project"]
|
||||
readonly subpath?: Endpoint0_0Request["query"]["subpath"]
|
||||
readonly cursor?: Endpoint0_0Request["query"]["cursor"]
|
||||
}
|
||||
const Endpoint3_0 = (raw: RawClient["server.session"]) => (input?: Endpoint3_0Input) =>
|
||||
const Endpoint0_0 = (raw: RawClient["server.session"]) => (input?: Endpoint0_0Input) =>
|
||||
raw["session.list"]({
|
||||
query: {
|
||||
workspace: input?.["workspace"],
|
||||
limit: input?.["limit"],
|
||||
order: input?.["order"],
|
||||
search: input?.["search"],
|
||||
directory: input?.["directory"],
|
||||
project: input?.["project"],
|
||||
subpath: input?.["subpath"],
|
||||
cursor: input?.["cursor"],
|
||||
workspace: input?.workspace,
|
||||
limit: input?.limit,
|
||||
order: input?.order,
|
||||
search: input?.search,
|
||||
directory: input?.directory,
|
||||
project: input?.project,
|
||||
subpath: input?.subpath,
|
||||
cursor: input?.cursor,
|
||||
},
|
||||
}).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
type Endpoint3_1Request = Parameters<RawClient["server.session"]["session.create"]>[0]
|
||||
type Endpoint3_1Input = {
|
||||
readonly id?: Endpoint3_1Request["payload"]["id"]
|
||||
readonly agent?: Endpoint3_1Request["payload"]["agent"]
|
||||
readonly model?: Endpoint3_1Request["payload"]["model"]
|
||||
readonly location?: Endpoint3_1Request["payload"]["location"]
|
||||
type Endpoint0_1Request = Parameters<RawClient["server.session"]["session.create"]>[0]
|
||||
type Endpoint0_1Input = {
|
||||
readonly id?: Endpoint0_1Request["payload"]["id"]
|
||||
readonly agent?: Endpoint0_1Request["payload"]["agent"]
|
||||
readonly model?: Endpoint0_1Request["payload"]["model"]
|
||||
readonly location?: Endpoint0_1Request["payload"]["location"]
|
||||
}
|
||||
const Endpoint3_1 = (raw: RawClient["server.session"]) => (input?: Endpoint3_1Input) =>
|
||||
const Endpoint0_1 = (raw: RawClient["server.session"]) => (input?: Endpoint0_1Input) =>
|
||||
raw["session.create"]({
|
||||
payload: { id: input?.["id"], agent: input?.["agent"], model: input?.["model"], location: input?.["location"] },
|
||||
payload: { id: input?.id, agent: input?.agent, model: input?.model, location: input?.location },
|
||||
}).pipe(
|
||||
Effect.mapError(mapClientError),
|
||||
Effect.map((value) => value.data),
|
||||
)
|
||||
|
||||
const Endpoint3_2 = (raw: RawClient["server.session"]) => () =>
|
||||
const Endpoint0_2 = (raw: RawClient["server.session"]) => () =>
|
||||
raw["session.active"]({}).pipe(
|
||||
Effect.mapError(mapClientError),
|
||||
Effect.map((value) => value.data),
|
||||
)
|
||||
|
||||
type Endpoint3_3Request = Parameters<RawClient["server.session"]["session.get"]>[0]
|
||||
type Endpoint3_3Input = { readonly sessionID: Endpoint3_3Request["params"]["sessionID"] }
|
||||
const Endpoint3_3 = (raw: RawClient["server.session"]) => (input: Endpoint3_3Input) =>
|
||||
raw["session.get"]({ params: { sessionID: input["sessionID"] } }).pipe(
|
||||
type Endpoint0_3Request = Parameters<RawClient["server.session"]["session.get"]>[0]
|
||||
type Endpoint0_3Input = { readonly sessionID: Endpoint0_3Request["params"]["sessionID"] }
|
||||
const Endpoint0_3 = (raw: RawClient["server.session"]) => (input: Endpoint0_3Input) =>
|
||||
raw["session.get"]({ params: { sessionID: input.sessionID } }).pipe(
|
||||
Effect.mapError(mapClientError),
|
||||
Effect.map((value) => value.data),
|
||||
)
|
||||
|
||||
type Endpoint3_4Request = Parameters<RawClient["server.session"]["session.switchAgent"]>[0]
|
||||
type Endpoint3_4Input = {
|
||||
readonly sessionID: Endpoint3_4Request["params"]["sessionID"]
|
||||
readonly agent: Endpoint3_4Request["payload"]["agent"]
|
||||
type Endpoint0_4Request = Parameters<RawClient["server.session"]["session.switchAgent"]>[0]
|
||||
type Endpoint0_4Input = {
|
||||
readonly sessionID: Endpoint0_4Request["params"]["sessionID"]
|
||||
readonly agent: Endpoint0_4Request["payload"]["agent"]
|
||||
}
|
||||
const Endpoint3_4 = (raw: RawClient["server.session"]) => (input: Endpoint3_4Input) =>
|
||||
raw["session.switchAgent"]({ params: { sessionID: input["sessionID"] }, payload: { agent: input["agent"] } }).pipe(
|
||||
const Endpoint0_4 = (raw: RawClient["server.session"]) => (input: Endpoint0_4Input) =>
|
||||
raw["session.switchAgent"]({ params: { sessionID: input.sessionID }, payload: { agent: input.agent } }).pipe(
|
||||
Effect.mapError(mapClientError),
|
||||
)
|
||||
|
||||
type Endpoint3_5Request = Parameters<RawClient["server.session"]["session.switchModel"]>[0]
|
||||
type Endpoint3_5Input = {
|
||||
readonly sessionID: Endpoint3_5Request["params"]["sessionID"]
|
||||
readonly model: Endpoint3_5Request["payload"]["model"]
|
||||
type Endpoint0_5Request = Parameters<RawClient["server.session"]["session.switchModel"]>[0]
|
||||
type Endpoint0_5Input = {
|
||||
readonly sessionID: Endpoint0_5Request["params"]["sessionID"]
|
||||
readonly model: Endpoint0_5Request["payload"]["model"]
|
||||
}
|
||||
const Endpoint3_5 = (raw: RawClient["server.session"]) => (input: Endpoint3_5Input) =>
|
||||
raw["session.switchModel"]({ params: { sessionID: input["sessionID"] }, payload: { model: input["model"] } }).pipe(
|
||||
const Endpoint0_5 = (raw: RawClient["server.session"]) => (input: Endpoint0_5Input) =>
|
||||
raw["session.switchModel"]({ params: { sessionID: input.sessionID }, payload: { model: input.model } }).pipe(
|
||||
Effect.mapError(mapClientError),
|
||||
)
|
||||
|
||||
type Endpoint3_6Request = Parameters<RawClient["server.session"]["session.prompt"]>[0]
|
||||
type Endpoint3_6Input = {
|
||||
readonly sessionID: Endpoint3_6Request["params"]["sessionID"]
|
||||
readonly id?: Endpoint3_6Request["payload"]["id"]
|
||||
readonly prompt: Endpoint3_6Request["payload"]["prompt"]
|
||||
readonly delivery?: Endpoint3_6Request["payload"]["delivery"]
|
||||
readonly resume?: Endpoint3_6Request["payload"]["resume"]
|
||||
type Endpoint0_6Request = Parameters<RawClient["server.session"]["session.prompt"]>[0]
|
||||
type Endpoint0_6Input = {
|
||||
readonly sessionID: Endpoint0_6Request["params"]["sessionID"]
|
||||
readonly id?: Endpoint0_6Request["payload"]["id"]
|
||||
readonly prompt: Endpoint0_6Request["payload"]["prompt"]
|
||||
readonly delivery?: Endpoint0_6Request["payload"]["delivery"]
|
||||
readonly resume?: Endpoint0_6Request["payload"]["resume"]
|
||||
}
|
||||
const Endpoint3_6 = (raw: RawClient["server.session"]) => (input: Endpoint3_6Input) =>
|
||||
const Endpoint0_6 = (raw: RawClient["server.session"]) => (input: Endpoint0_6Input) =>
|
||||
raw["session.prompt"]({
|
||||
params: { sessionID: input["sessionID"] },
|
||||
payload: { id: input["id"], prompt: input["prompt"], delivery: input["delivery"], resume: input["resume"] },
|
||||
params: { sessionID: input.sessionID },
|
||||
payload: { id: input.id, prompt: input.prompt, delivery: input.delivery, resume: input.resume },
|
||||
}).pipe(
|
||||
Effect.mapError(mapClientError),
|
||||
Effect.map((value) => value.data),
|
||||
)
|
||||
|
||||
type Endpoint3_7Request = Parameters<RawClient["server.session"]["session.compact"]>[0]
|
||||
type Endpoint3_7Input = { readonly sessionID: Endpoint3_7Request["params"]["sessionID"] }
|
||||
const Endpoint3_7 = (raw: RawClient["server.session"]) => (input: Endpoint3_7Input) =>
|
||||
raw["session.compact"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
|
||||
type Endpoint0_7Request = Parameters<RawClient["server.session"]["session.compact"]>[0]
|
||||
type Endpoint0_7Input = { readonly sessionID: Endpoint0_7Request["params"]["sessionID"] }
|
||||
const Endpoint0_7 = (raw: RawClient["server.session"]) => (input: Endpoint0_7Input) =>
|
||||
raw["session.compact"]({ params: { sessionID: input.sessionID } }).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
type Endpoint3_8Request = Parameters<RawClient["server.session"]["session.wait"]>[0]
|
||||
type Endpoint3_8Input = { readonly sessionID: Endpoint3_8Request["params"]["sessionID"] }
|
||||
const Endpoint3_8 = (raw: RawClient["server.session"]) => (input: Endpoint3_8Input) =>
|
||||
raw["session.wait"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
|
||||
type Endpoint0_8Request = Parameters<RawClient["server.session"]["session.wait"]>[0]
|
||||
type Endpoint0_8Input = { readonly sessionID: Endpoint0_8Request["params"]["sessionID"] }
|
||||
const Endpoint0_8 = (raw: RawClient["server.session"]) => (input: Endpoint0_8Input) =>
|
||||
raw["session.wait"]({ params: { sessionID: input.sessionID } }).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
type Endpoint3_9Request = Parameters<RawClient["server.session"]["session.revert.stage"]>[0]
|
||||
type Endpoint3_9Input = {
|
||||
readonly sessionID: Endpoint3_9Request["params"]["sessionID"]
|
||||
readonly messageID: Endpoint3_9Request["payload"]["messageID"]
|
||||
readonly files?: Endpoint3_9Request["payload"]["files"]
|
||||
type Endpoint0_9Request = Parameters<RawClient["server.session"]["session.revert.stage"]>[0]
|
||||
type Endpoint0_9Input = {
|
||||
readonly sessionID: Endpoint0_9Request["params"]["sessionID"]
|
||||
readonly messageID: Endpoint0_9Request["payload"]["messageID"]
|
||||
readonly files?: Endpoint0_9Request["payload"]["files"]
|
||||
}
|
||||
const Endpoint3_9 = (raw: RawClient["server.session"]) => (input: Endpoint3_9Input) =>
|
||||
const Endpoint0_9 = (raw: RawClient["server.session"]) => (input: Endpoint0_9Input) =>
|
||||
raw["session.revert.stage"]({
|
||||
params: { sessionID: input["sessionID"] },
|
||||
payload: { messageID: input["messageID"], files: input["files"] },
|
||||
params: { sessionID: input.sessionID },
|
||||
payload: { messageID: input.messageID, files: input.files },
|
||||
}).pipe(
|
||||
Effect.mapError(mapClientError),
|
||||
Effect.map((value) => value.data),
|
||||
)
|
||||
|
||||
type Endpoint3_10Request = Parameters<RawClient["server.session"]["session.revert.clear"]>[0]
|
||||
type Endpoint3_10Input = { readonly sessionID: Endpoint3_10Request["params"]["sessionID"] }
|
||||
const Endpoint3_10 = (raw: RawClient["server.session"]) => (input: Endpoint3_10Input) =>
|
||||
raw["session.revert.clear"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
|
||||
type Endpoint0_10Request = Parameters<RawClient["server.session"]["session.revert.clear"]>[0]
|
||||
type Endpoint0_10Input = { readonly sessionID: Endpoint0_10Request["params"]["sessionID"] }
|
||||
const Endpoint0_10 = (raw: RawClient["server.session"]) => (input: Endpoint0_10Input) =>
|
||||
raw["session.revert.clear"]({ params: { sessionID: input.sessionID } }).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
type Endpoint3_11Request = Parameters<RawClient["server.session"]["session.revert.commit"]>[0]
|
||||
type Endpoint3_11Input = { readonly sessionID: Endpoint3_11Request["params"]["sessionID"] }
|
||||
const Endpoint3_11 = (raw: RawClient["server.session"]) => (input: Endpoint3_11Input) =>
|
||||
raw["session.revert.commit"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
|
||||
type Endpoint0_11Request = Parameters<RawClient["server.session"]["session.revert.commit"]>[0]
|
||||
type Endpoint0_11Input = { readonly sessionID: Endpoint0_11Request["params"]["sessionID"] }
|
||||
const Endpoint0_11 = (raw: RawClient["server.session"]) => (input: Endpoint0_11Input) =>
|
||||
raw["session.revert.commit"]({ params: { sessionID: input.sessionID } }).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
type Endpoint3_12Request = Parameters<RawClient["server.session"]["session.context"]>[0]
|
||||
type Endpoint3_12Input = { readonly sessionID: Endpoint3_12Request["params"]["sessionID"] }
|
||||
const Endpoint3_12 = (raw: RawClient["server.session"]) => (input: Endpoint3_12Input) =>
|
||||
raw["session.context"]({ params: { sessionID: input["sessionID"] } }).pipe(
|
||||
type Endpoint0_12Request = Parameters<RawClient["server.session"]["session.context"]>[0]
|
||||
type Endpoint0_12Input = { readonly sessionID: Endpoint0_12Request["params"]["sessionID"] }
|
||||
const Endpoint0_12 = (raw: RawClient["server.session"]) => (input: Endpoint0_12Input) =>
|
||||
raw["session.context"]({ params: { sessionID: input.sessionID } }).pipe(
|
||||
Effect.mapError(mapClientError),
|
||||
Effect.map((value) => value.data),
|
||||
)
|
||||
|
||||
type Endpoint3_13Request = Parameters<RawClient["server.session"]["session.history"]>[0]
|
||||
type Endpoint3_13Input = {
|
||||
readonly sessionID: Endpoint3_13Request["params"]["sessionID"]
|
||||
readonly limit?: Endpoint3_13Request["query"]["limit"]
|
||||
readonly after?: Endpoint3_13Request["query"]["after"]
|
||||
type Endpoint0_13Request = Parameters<RawClient["server.session"]["session.history"]>[0]
|
||||
type Endpoint0_13Input = {
|
||||
readonly sessionID: Endpoint0_13Request["params"]["sessionID"]
|
||||
readonly limit?: Endpoint0_13Request["query"]["limit"]
|
||||
readonly after?: Endpoint0_13Request["query"]["after"]
|
||||
}
|
||||
const Endpoint3_13 = (raw: RawClient["server.session"]) => (input: Endpoint3_13Input) =>
|
||||
const Endpoint0_13 = (raw: RawClient["server.session"]) => (input: Endpoint0_13Input) =>
|
||||
raw["session.history"]({
|
||||
params: { sessionID: input["sessionID"] },
|
||||
query: { limit: input["limit"], after: input["after"] },
|
||||
params: { sessionID: input.sessionID },
|
||||
query: { limit: input.limit, after: input.after },
|
||||
}).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
type Endpoint3_14Request = Parameters<RawClient["server.session"]["session.events"]>[0]
|
||||
type Endpoint3_14Input = {
|
||||
readonly sessionID: Endpoint3_14Request["params"]["sessionID"]
|
||||
readonly after?: Endpoint3_14Request["query"]["after"]
|
||||
type Endpoint0_14Request = Parameters<RawClient["server.session"]["session.events"]>[0]
|
||||
type Endpoint0_14Input = {
|
||||
readonly sessionID: Endpoint0_14Request["params"]["sessionID"]
|
||||
readonly after?: Endpoint0_14Request["query"]["after"]
|
||||
}
|
||||
const Endpoint3_14 = (raw: RawClient["server.session"]) => (input: Endpoint3_14Input) =>
|
||||
const Endpoint0_14 = (raw: RawClient["server.session"]) => (input: Endpoint0_14Input) =>
|
||||
Stream.unwrap(
|
||||
raw["session.events"]({ params: { sessionID: input["sessionID"] }, query: { after: input["after"] } }).pipe(
|
||||
raw["session.events"]({ params: { sessionID: input.sessionID }, query: { after: input.after } }).pipe(
|
||||
Effect.mapError(mapClientError),
|
||||
Effect.map((stream) => stream.pipe(Stream.mapError(mapClientError))),
|
||||
),
|
||||
)
|
||||
|
||||
type Endpoint3_15Request = Parameters<RawClient["server.session"]["session.interrupt"]>[0]
|
||||
type Endpoint3_15Input = { readonly sessionID: Endpoint3_15Request["params"]["sessionID"] }
|
||||
const Endpoint3_15 = (raw: RawClient["server.session"]) => (input: Endpoint3_15Input) =>
|
||||
raw["session.interrupt"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
|
||||
type Endpoint0_15Request = Parameters<RawClient["server.session"]["session.interrupt"]>[0]
|
||||
type Endpoint0_15Input = { readonly sessionID: Endpoint0_15Request["params"]["sessionID"] }
|
||||
const Endpoint0_15 = (raw: RawClient["server.session"]) => (input: Endpoint0_15Input) =>
|
||||
raw["session.interrupt"]({ params: { sessionID: input.sessionID } }).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
type Endpoint3_16Request = Parameters<RawClient["server.session"]["session.message"]>[0]
|
||||
type Endpoint3_16Input = {
|
||||
readonly sessionID: Endpoint3_16Request["params"]["sessionID"]
|
||||
readonly messageID: Endpoint3_16Request["params"]["messageID"]
|
||||
type Endpoint0_16Request = Parameters<RawClient["server.session"]["session.message"]>[0]
|
||||
type Endpoint0_16Input = {
|
||||
readonly sessionID: Endpoint0_16Request["params"]["sessionID"]
|
||||
readonly messageID: Endpoint0_16Request["params"]["messageID"]
|
||||
}
|
||||
const Endpoint3_16 = (raw: RawClient["server.session"]) => (input: Endpoint3_16Input) =>
|
||||
raw["session.message"]({ params: { sessionID: input["sessionID"], messageID: input["messageID"] } }).pipe(
|
||||
const Endpoint0_16 = (raw: RawClient["server.session"]) => (input: Endpoint0_16Input) =>
|
||||
raw["session.message"]({ params: { sessionID: input.sessionID, messageID: input.messageID } }).pipe(
|
||||
Effect.mapError(mapClientError),
|
||||
Effect.map((value) => value.data),
|
||||
)
|
||||
|
||||
const adaptGroup3 = (raw: RawClient["server.session"]) => ({
|
||||
list: Endpoint3_0(raw),
|
||||
create: Endpoint3_1(raw),
|
||||
active: Endpoint3_2(raw),
|
||||
get: Endpoint3_3(raw),
|
||||
switchAgent: Endpoint3_4(raw),
|
||||
switchModel: Endpoint3_5(raw),
|
||||
prompt: Endpoint3_6(raw),
|
||||
compact: Endpoint3_7(raw),
|
||||
wait: Endpoint3_8(raw),
|
||||
stage: Endpoint3_9(raw),
|
||||
clear: Endpoint3_10(raw),
|
||||
commit: Endpoint3_11(raw),
|
||||
context: Endpoint3_12(raw),
|
||||
history: Endpoint3_13(raw),
|
||||
events: Endpoint3_14(raw),
|
||||
interrupt: Endpoint3_15(raw),
|
||||
message: Endpoint3_16(raw),
|
||||
const adaptGroup0 = (raw: RawClient["server.session"]) => ({
|
||||
list: Endpoint0_0(raw),
|
||||
create: Endpoint0_1(raw),
|
||||
active: Endpoint0_2(raw),
|
||||
get: Endpoint0_3(raw),
|
||||
switchAgent: Endpoint0_4(raw),
|
||||
switchModel: Endpoint0_5(raw),
|
||||
prompt: Endpoint0_6(raw),
|
||||
compact: Endpoint0_7(raw),
|
||||
wait: Endpoint0_8(raw),
|
||||
stage: Endpoint0_9(raw),
|
||||
clear: Endpoint0_10(raw),
|
||||
commit: Endpoint0_11(raw),
|
||||
context: Endpoint0_12(raw),
|
||||
history: Endpoint0_13(raw),
|
||||
events: Endpoint0_14(raw),
|
||||
interrupt: Endpoint0_15(raw),
|
||||
message: Endpoint0_16(raw),
|
||||
})
|
||||
|
||||
type Endpoint4_0Request = Parameters<RawClient["server.message"]["session.messages"]>[0]
|
||||
type Endpoint4_0Input = {
|
||||
readonly sessionID: Endpoint4_0Request["params"]["sessionID"]
|
||||
readonly limit?: Endpoint4_0Request["query"]["limit"]
|
||||
readonly order?: Endpoint4_0Request["query"]["order"]
|
||||
readonly cursor?: Endpoint4_0Request["query"]["cursor"]
|
||||
}
|
||||
const Endpoint4_0 = (raw: RawClient["server.message"]) => (input: Endpoint4_0Input) =>
|
||||
raw["session.messages"]({
|
||||
params: { sessionID: input["sessionID"] },
|
||||
query: { limit: input["limit"], order: input["order"], cursor: input["cursor"] },
|
||||
}).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
const adaptGroup4 = (raw: RawClient["server.message"]) => ({ list: Endpoint4_0(raw) })
|
||||
|
||||
type Endpoint5_0Request = Parameters<RawClient["server.model"]["model.list"]>[0]
|
||||
type Endpoint5_0Input = { readonly location?: Endpoint5_0Request["query"]["location"] }
|
||||
const Endpoint5_0 = (raw: RawClient["server.model"]) => (input?: Endpoint5_0Input) =>
|
||||
raw["model.list"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
const adaptGroup5 = (raw: RawClient["server.model"]) => ({ list: Endpoint5_0(raw) })
|
||||
|
||||
type Endpoint6_0Request = Parameters<RawClient["server.provider"]["provider.list"]>[0]
|
||||
type Endpoint6_0Input = { readonly location?: Endpoint6_0Request["query"]["location"] }
|
||||
const Endpoint6_0 = (raw: RawClient["server.provider"]) => (input?: Endpoint6_0Input) =>
|
||||
raw["provider.list"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
type Endpoint6_1Request = Parameters<RawClient["server.provider"]["provider.get"]>[0]
|
||||
type Endpoint6_1Input = {
|
||||
readonly providerID: Endpoint6_1Request["params"]["providerID"]
|
||||
readonly location?: Endpoint6_1Request["query"]["location"]
|
||||
}
|
||||
const Endpoint6_1 = (raw: RawClient["server.provider"]) => (input: Endpoint6_1Input) =>
|
||||
raw["provider.get"]({ params: { providerID: input["providerID"] }, query: { location: input["location"] } }).pipe(
|
||||
Effect.mapError(mapClientError),
|
||||
)
|
||||
|
||||
const adaptGroup6 = (raw: RawClient["server.provider"]) => ({ list: Endpoint6_0(raw), get: Endpoint6_1(raw) })
|
||||
|
||||
type Endpoint7_0Request = Parameters<RawClient["server.integration"]["integration.list"]>[0]
|
||||
type Endpoint7_0Input = { readonly location?: Endpoint7_0Request["query"]["location"] }
|
||||
const Endpoint7_0 = (raw: RawClient["server.integration"]) => (input?: Endpoint7_0Input) =>
|
||||
raw["integration.list"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
type Endpoint7_1Request = Parameters<RawClient["server.integration"]["integration.get"]>[0]
|
||||
type Endpoint7_1Input = {
|
||||
readonly integrationID: Endpoint7_1Request["params"]["integrationID"]
|
||||
readonly location?: Endpoint7_1Request["query"]["location"]
|
||||
}
|
||||
const Endpoint7_1 = (raw: RawClient["server.integration"]) => (input: Endpoint7_1Input) =>
|
||||
raw["integration.get"]({
|
||||
params: { integrationID: input["integrationID"] },
|
||||
query: { location: input["location"] },
|
||||
}).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
type Endpoint7_2Request = Parameters<RawClient["server.integration"]["integration.connect.key"]>[0]
|
||||
type Endpoint7_2Input = {
|
||||
readonly integrationID: Endpoint7_2Request["params"]["integrationID"]
|
||||
readonly location?: Endpoint7_2Request["query"]["location"]
|
||||
readonly key: Endpoint7_2Request["payload"]["key"]
|
||||
readonly label?: Endpoint7_2Request["payload"]["label"]
|
||||
}
|
||||
const Endpoint7_2 = (raw: RawClient["server.integration"]) => (input: Endpoint7_2Input) =>
|
||||
raw["integration.connect.key"]({
|
||||
params: { integrationID: input["integrationID"] },
|
||||
query: { location: input["location"] },
|
||||
payload: { key: input["key"], label: input["label"] },
|
||||
}).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
type Endpoint7_3Request = Parameters<RawClient["server.integration"]["integration.connect.oauth"]>[0]
|
||||
type Endpoint7_3Input = {
|
||||
readonly integrationID: Endpoint7_3Request["params"]["integrationID"]
|
||||
readonly location?: Endpoint7_3Request["query"]["location"]
|
||||
readonly methodID: Endpoint7_3Request["payload"]["methodID"]
|
||||
readonly inputs: Endpoint7_3Request["payload"]["inputs"]
|
||||
readonly label?: Endpoint7_3Request["payload"]["label"]
|
||||
}
|
||||
const Endpoint7_3 = (raw: RawClient["server.integration"]) => (input: Endpoint7_3Input) =>
|
||||
raw["integration.connect.oauth"]({
|
||||
params: { integrationID: input["integrationID"] },
|
||||
query: { location: input["location"] },
|
||||
payload: { methodID: input["methodID"], inputs: input["inputs"], label: input["label"] },
|
||||
}).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
type Endpoint7_4Request = Parameters<RawClient["server.integration"]["integration.attempt.status"]>[0]
|
||||
type Endpoint7_4Input = {
|
||||
readonly attemptID: Endpoint7_4Request["params"]["attemptID"]
|
||||
readonly location?: Endpoint7_4Request["query"]["location"]
|
||||
}
|
||||
const Endpoint7_4 = (raw: RawClient["server.integration"]) => (input: Endpoint7_4Input) =>
|
||||
raw["integration.attempt.status"]({
|
||||
params: { attemptID: input["attemptID"] },
|
||||
query: { location: input["location"] },
|
||||
}).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
type Endpoint7_5Request = Parameters<RawClient["server.integration"]["integration.attempt.complete"]>[0]
|
||||
type Endpoint7_5Input = {
|
||||
readonly attemptID: Endpoint7_5Request["params"]["attemptID"]
|
||||
readonly location?: Endpoint7_5Request["query"]["location"]
|
||||
readonly code?: Endpoint7_5Request["payload"]["code"]
|
||||
}
|
||||
const Endpoint7_5 = (raw: RawClient["server.integration"]) => (input: Endpoint7_5Input) =>
|
||||
raw["integration.attempt.complete"]({
|
||||
params: { attemptID: input["attemptID"] },
|
||||
query: { location: input["location"] },
|
||||
payload: { code: input["code"] },
|
||||
}).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
type Endpoint7_6Request = Parameters<RawClient["server.integration"]["integration.attempt.cancel"]>[0]
|
||||
type Endpoint7_6Input = {
|
||||
readonly attemptID: Endpoint7_6Request["params"]["attemptID"]
|
||||
readonly location?: Endpoint7_6Request["query"]["location"]
|
||||
}
|
||||
const Endpoint7_6 = (raw: RawClient["server.integration"]) => (input: Endpoint7_6Input) =>
|
||||
raw["integration.attempt.cancel"]({
|
||||
params: { attemptID: input["attemptID"] },
|
||||
query: { location: input["location"] },
|
||||
}).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
const adaptGroup7 = (raw: RawClient["server.integration"]) => ({
|
||||
list: Endpoint7_0(raw),
|
||||
get: Endpoint7_1(raw),
|
||||
connectKey: Endpoint7_2(raw),
|
||||
connectOauth: Endpoint7_3(raw),
|
||||
attemptStatus: Endpoint7_4(raw),
|
||||
attemptComplete: Endpoint7_5(raw),
|
||||
attemptCancel: Endpoint7_6(raw),
|
||||
})
|
||||
|
||||
type Endpoint8_0Request = Parameters<RawClient["server.credential"]["credential.update"]>[0]
|
||||
type Endpoint8_0Input = {
|
||||
readonly credentialID: Endpoint8_0Request["params"]["credentialID"]
|
||||
readonly location?: Endpoint8_0Request["query"]["location"]
|
||||
readonly label: Endpoint8_0Request["payload"]["label"]
|
||||
}
|
||||
const Endpoint8_0 = (raw: RawClient["server.credential"]) => (input: Endpoint8_0Input) =>
|
||||
raw["credential.update"]({
|
||||
params: { credentialID: input["credentialID"] },
|
||||
query: { location: input["location"] },
|
||||
payload: { label: input["label"] },
|
||||
}).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
type Endpoint8_1Request = Parameters<RawClient["server.credential"]["credential.remove"]>[0]
|
||||
type Endpoint8_1Input = {
|
||||
readonly credentialID: Endpoint8_1Request["params"]["credentialID"]
|
||||
readonly location?: Endpoint8_1Request["query"]["location"]
|
||||
}
|
||||
const Endpoint8_1 = (raw: RawClient["server.credential"]) => (input: Endpoint8_1Input) =>
|
||||
raw["credential.remove"]({
|
||||
params: { credentialID: input["credentialID"] },
|
||||
query: { location: input["location"] },
|
||||
}).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
const adaptGroup8 = (raw: RawClient["server.credential"]) => ({ update: Endpoint8_0(raw), remove: Endpoint8_1(raw) })
|
||||
|
||||
type Endpoint9_0Request = Parameters<RawClient["server.permission"]["permission.request.list"]>[0]
|
||||
type Endpoint9_0Input = { readonly location?: Endpoint9_0Request["query"]["location"] }
|
||||
const Endpoint9_0 = (raw: RawClient["server.permission"]) => (input?: Endpoint9_0Input) =>
|
||||
raw["permission.request.list"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
type Endpoint9_1Request = Parameters<RawClient["server.permission"]["permission.saved.list"]>[0]
|
||||
type Endpoint9_1Input = { readonly projectID?: Endpoint9_1Request["query"]["projectID"] }
|
||||
const Endpoint9_1 = (raw: RawClient["server.permission"]) => (input?: Endpoint9_1Input) =>
|
||||
raw["permission.saved.list"]({ query: { projectID: input?.["projectID"] } }).pipe(
|
||||
Effect.mapError(mapClientError),
|
||||
Effect.map((value) => value.data),
|
||||
)
|
||||
|
||||
type Endpoint9_2Request = Parameters<RawClient["server.permission"]["permission.saved.remove"]>[0]
|
||||
type Endpoint9_2Input = { readonly id: Endpoint9_2Request["params"]["id"] }
|
||||
const Endpoint9_2 = (raw: RawClient["server.permission"]) => (input: Endpoint9_2Input) =>
|
||||
raw["permission.saved.remove"]({ params: { id: input["id"] } }).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
type Endpoint9_3Request = Parameters<RawClient["server.permission"]["session.permission.create"]>[0]
|
||||
type Endpoint9_3Input = {
|
||||
readonly sessionID: Endpoint9_3Request["params"]["sessionID"]
|
||||
readonly id?: Endpoint9_3Request["payload"]["id"]
|
||||
readonly action: Endpoint9_3Request["payload"]["action"]
|
||||
readonly resources: Endpoint9_3Request["payload"]["resources"]
|
||||
readonly save?: Endpoint9_3Request["payload"]["save"]
|
||||
readonly metadata?: Endpoint9_3Request["payload"]["metadata"]
|
||||
readonly source?: Endpoint9_3Request["payload"]["source"]
|
||||
readonly agent?: Endpoint9_3Request["payload"]["agent"]
|
||||
}
|
||||
const Endpoint9_3 = (raw: RawClient["server.permission"]) => (input: Endpoint9_3Input) =>
|
||||
raw["session.permission.create"]({
|
||||
params: { sessionID: input["sessionID"] },
|
||||
payload: {
|
||||
id: input["id"],
|
||||
action: input["action"],
|
||||
resources: input["resources"],
|
||||
save: input["save"],
|
||||
metadata: input["metadata"],
|
||||
source: input["source"],
|
||||
agent: input["agent"],
|
||||
},
|
||||
}).pipe(
|
||||
Effect.mapError(mapClientError),
|
||||
Effect.map((value) => value.data),
|
||||
)
|
||||
|
||||
type Endpoint9_4Request = Parameters<RawClient["server.permission"]["session.permission.list"]>[0]
|
||||
type Endpoint9_4Input = { readonly sessionID: Endpoint9_4Request["params"]["sessionID"] }
|
||||
const Endpoint9_4 = (raw: RawClient["server.permission"]) => (input: Endpoint9_4Input) =>
|
||||
raw["session.permission.list"]({ params: { sessionID: input["sessionID"] } }).pipe(
|
||||
Effect.mapError(mapClientError),
|
||||
Effect.map((value) => value.data),
|
||||
)
|
||||
|
||||
type Endpoint9_5Request = Parameters<RawClient["server.permission"]["session.permission.get"]>[0]
|
||||
type Endpoint9_5Input = {
|
||||
readonly sessionID: Endpoint9_5Request["params"]["sessionID"]
|
||||
readonly requestID: Endpoint9_5Request["params"]["requestID"]
|
||||
}
|
||||
const Endpoint9_5 = (raw: RawClient["server.permission"]) => (input: Endpoint9_5Input) =>
|
||||
raw["session.permission.get"]({ params: { sessionID: input["sessionID"], requestID: input["requestID"] } }).pipe(
|
||||
Effect.mapError(mapClientError),
|
||||
Effect.map((value) => value.data),
|
||||
)
|
||||
|
||||
type Endpoint9_6Request = Parameters<RawClient["server.permission"]["session.permission.reply"]>[0]
|
||||
type Endpoint9_6Input = {
|
||||
readonly sessionID: Endpoint9_6Request["params"]["sessionID"]
|
||||
readonly requestID: Endpoint9_6Request["params"]["requestID"]
|
||||
readonly reply: Endpoint9_6Request["payload"]["reply"]
|
||||
readonly message?: Endpoint9_6Request["payload"]["message"]
|
||||
}
|
||||
const Endpoint9_6 = (raw: RawClient["server.permission"]) => (input: Endpoint9_6Input) =>
|
||||
raw["session.permission.reply"]({
|
||||
params: { sessionID: input["sessionID"], requestID: input["requestID"] },
|
||||
payload: { reply: input["reply"], message: input["message"] },
|
||||
}).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
const adaptGroup9 = (raw: RawClient["server.permission"]) => ({
|
||||
listRequests: Endpoint9_0(raw),
|
||||
listSaved: Endpoint9_1(raw),
|
||||
removeSaved: Endpoint9_2(raw),
|
||||
create: Endpoint9_3(raw),
|
||||
list: Endpoint9_4(raw),
|
||||
get: Endpoint9_5(raw),
|
||||
reply: Endpoint9_6(raw),
|
||||
})
|
||||
|
||||
type Endpoint10_0Request = Parameters<RawClient["server.fs"]["fs.list"]>[0]
|
||||
type Endpoint10_0Input = {
|
||||
readonly location?: Endpoint10_0Request["query"]["location"]
|
||||
readonly path?: Endpoint10_0Request["query"]["path"]
|
||||
}
|
||||
const Endpoint10_0 = (raw: RawClient["server.fs"]) => (input?: Endpoint10_0Input) =>
|
||||
raw["fs.list"]({ query: { location: input?.["location"], path: input?.["path"] } }).pipe(
|
||||
Effect.mapError(mapClientError),
|
||||
)
|
||||
|
||||
type Endpoint10_1Request = Parameters<RawClient["server.fs"]["fs.find"]>[0]
|
||||
type Endpoint10_1Input = {
|
||||
readonly location?: Endpoint10_1Request["query"]["location"]
|
||||
readonly query: Endpoint10_1Request["query"]["query"]
|
||||
readonly type?: Endpoint10_1Request["query"]["type"]
|
||||
readonly limit?: Endpoint10_1Request["query"]["limit"]
|
||||
}
|
||||
const Endpoint10_1 = (raw: RawClient["server.fs"]) => (input: Endpoint10_1Input) =>
|
||||
raw["fs.find"]({
|
||||
query: { location: input["location"], query: input["query"], type: input["type"], limit: input["limit"] },
|
||||
}).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
const adaptGroup10 = (raw: RawClient["server.fs"]) => ({ list: Endpoint10_0(raw), find: Endpoint10_1(raw) })
|
||||
|
||||
type Endpoint11_0Request = Parameters<RawClient["server.command"]["command.list"]>[0]
|
||||
type Endpoint11_0Input = { readonly location?: Endpoint11_0Request["query"]["location"] }
|
||||
const Endpoint11_0 = (raw: RawClient["server.command"]) => (input?: Endpoint11_0Input) =>
|
||||
raw["command.list"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
const adaptGroup11 = (raw: RawClient["server.command"]) => ({ list: Endpoint11_0(raw) })
|
||||
|
||||
type Endpoint12_0Request = Parameters<RawClient["server.skill"]["skill.list"]>[0]
|
||||
type Endpoint12_0Input = { readonly location?: Endpoint12_0Request["query"]["location"] }
|
||||
const Endpoint12_0 = (raw: RawClient["server.skill"]) => (input?: Endpoint12_0Input) =>
|
||||
raw["skill.list"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
const adaptGroup12 = (raw: RawClient["server.skill"]) => ({ list: Endpoint12_0(raw) })
|
||||
|
||||
const Endpoint13_0 = (raw: RawClient["server.event"]) => () =>
|
||||
const Endpoint1_0 = (raw: RawClient["server.event"]) => () =>
|
||||
Stream.unwrap(
|
||||
raw["event.subscribe"]({}).pipe(
|
||||
Effect.mapError(mapClientError),
|
||||
@@ -516,190 +216,11 @@ const Endpoint13_0 = (raw: RawClient["server.event"]) => () =>
|
||||
),
|
||||
)
|
||||
|
||||
const adaptGroup13 = (raw: RawClient["server.event"]) => ({ subscribe: Endpoint13_0(raw) })
|
||||
|
||||
type Endpoint14_0Request = Parameters<RawClient["server.pty"]["pty.list"]>[0]
|
||||
type Endpoint14_0Input = { readonly location?: Endpoint14_0Request["query"]["location"] }
|
||||
const Endpoint14_0 = (raw: RawClient["server.pty"]) => (input?: Endpoint14_0Input) =>
|
||||
raw["pty.list"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
type Endpoint14_1Request = Parameters<RawClient["server.pty"]["pty.create"]>[0]
|
||||
type Endpoint14_1Input = {
|
||||
readonly location?: Endpoint14_1Request["query"]["location"]
|
||||
readonly command?: Endpoint14_1Request["payload"]["command"]
|
||||
readonly args?: Endpoint14_1Request["payload"]["args"]
|
||||
readonly cwd?: Endpoint14_1Request["payload"]["cwd"]
|
||||
readonly title?: Endpoint14_1Request["payload"]["title"]
|
||||
readonly env?: Endpoint14_1Request["payload"]["env"]
|
||||
}
|
||||
const Endpoint14_1 = (raw: RawClient["server.pty"]) => (input?: Endpoint14_1Input) =>
|
||||
raw["pty.create"]({
|
||||
query: { location: input?.["location"] },
|
||||
payload: {
|
||||
command: input?.["command"],
|
||||
args: input?.["args"],
|
||||
cwd: input?.["cwd"],
|
||||
title: input?.["title"],
|
||||
env: input?.["env"],
|
||||
},
|
||||
}).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
type Endpoint14_2Request = Parameters<RawClient["server.pty"]["pty.get"]>[0]
|
||||
type Endpoint14_2Input = {
|
||||
readonly ptyID: Endpoint14_2Request["params"]["ptyID"]
|
||||
readonly location?: Endpoint14_2Request["query"]["location"]
|
||||
}
|
||||
const Endpoint14_2 = (raw: RawClient["server.pty"]) => (input: Endpoint14_2Input) =>
|
||||
raw["pty.get"]({ params: { ptyID: input["ptyID"] }, query: { location: input["location"] } }).pipe(
|
||||
Effect.mapError(mapClientError),
|
||||
)
|
||||
|
||||
type Endpoint14_3Request = Parameters<RawClient["server.pty"]["pty.update"]>[0]
|
||||
type Endpoint14_3Input = {
|
||||
readonly ptyID: Endpoint14_3Request["params"]["ptyID"]
|
||||
readonly location?: Endpoint14_3Request["query"]["location"]
|
||||
readonly title?: Endpoint14_3Request["payload"]["title"]
|
||||
readonly size?: Endpoint14_3Request["payload"]["size"]
|
||||
}
|
||||
const Endpoint14_3 = (raw: RawClient["server.pty"]) => (input: Endpoint14_3Input) =>
|
||||
raw["pty.update"]({
|
||||
params: { ptyID: input["ptyID"] },
|
||||
query: { location: input["location"] },
|
||||
payload: { title: input["title"], size: input["size"] },
|
||||
}).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
type Endpoint14_4Request = Parameters<RawClient["server.pty"]["pty.remove"]>[0]
|
||||
type Endpoint14_4Input = {
|
||||
readonly ptyID: Endpoint14_4Request["params"]["ptyID"]
|
||||
readonly location?: Endpoint14_4Request["query"]["location"]
|
||||
}
|
||||
const Endpoint14_4 = (raw: RawClient["server.pty"]) => (input: Endpoint14_4Input) =>
|
||||
raw["pty.remove"]({ params: { ptyID: input["ptyID"] }, query: { location: input["location"] } }).pipe(
|
||||
Effect.mapError(mapClientError),
|
||||
)
|
||||
|
||||
const adaptGroup14 = (raw: RawClient["server.pty"]) => ({
|
||||
list: Endpoint14_0(raw),
|
||||
create: Endpoint14_1(raw),
|
||||
get: Endpoint14_2(raw),
|
||||
update: Endpoint14_3(raw),
|
||||
remove: Endpoint14_4(raw),
|
||||
})
|
||||
|
||||
type Endpoint15_0Request = Parameters<RawClient["server.question"]["question.request.list"]>[0]
|
||||
type Endpoint15_0Input = { readonly location?: Endpoint15_0Request["query"]["location"] }
|
||||
const Endpoint15_0 = (raw: RawClient["server.question"]) => (input?: Endpoint15_0Input) =>
|
||||
raw["question.request.list"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
type Endpoint15_1Request = Parameters<RawClient["server.question"]["session.question.list"]>[0]
|
||||
type Endpoint15_1Input = { readonly sessionID: Endpoint15_1Request["params"]["sessionID"] }
|
||||
const Endpoint15_1 = (raw: RawClient["server.question"]) => (input: Endpoint15_1Input) =>
|
||||
raw["session.question.list"]({ params: { sessionID: input["sessionID"] } }).pipe(
|
||||
Effect.mapError(mapClientError),
|
||||
Effect.map((value) => value.data),
|
||||
)
|
||||
|
||||
type Endpoint15_2Request = Parameters<RawClient["server.question"]["session.question.reply"]>[0]
|
||||
type Endpoint15_2Input = {
|
||||
readonly sessionID: Endpoint15_2Request["params"]["sessionID"]
|
||||
readonly requestID: Endpoint15_2Request["params"]["requestID"]
|
||||
readonly answers: Endpoint15_2Request["payload"]["answers"]
|
||||
}
|
||||
const Endpoint15_2 = (raw: RawClient["server.question"]) => (input: Endpoint15_2Input) =>
|
||||
raw["session.question.reply"]({
|
||||
params: { sessionID: input["sessionID"], requestID: input["requestID"] },
|
||||
payload: { answers: input["answers"] },
|
||||
}).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
type Endpoint15_3Request = Parameters<RawClient["server.question"]["session.question.reject"]>[0]
|
||||
type Endpoint15_3Input = {
|
||||
readonly sessionID: Endpoint15_3Request["params"]["sessionID"]
|
||||
readonly requestID: Endpoint15_3Request["params"]["requestID"]
|
||||
}
|
||||
const Endpoint15_3 = (raw: RawClient["server.question"]) => (input: Endpoint15_3Input) =>
|
||||
raw["session.question.reject"]({ params: { sessionID: input["sessionID"], requestID: input["requestID"] } }).pipe(
|
||||
Effect.mapError(mapClientError),
|
||||
)
|
||||
|
||||
const adaptGroup15 = (raw: RawClient["server.question"]) => ({
|
||||
listRequests: Endpoint15_0(raw),
|
||||
list: Endpoint15_1(raw),
|
||||
reply: Endpoint15_2(raw),
|
||||
reject: Endpoint15_3(raw),
|
||||
})
|
||||
|
||||
type Endpoint16_0Request = Parameters<RawClient["server.reference"]["reference.list"]>[0]
|
||||
type Endpoint16_0Input = { readonly location?: Endpoint16_0Request["query"]["location"] }
|
||||
const Endpoint16_0 = (raw: RawClient["server.reference"]) => (input?: Endpoint16_0Input) =>
|
||||
raw["reference.list"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
const adaptGroup16 = (raw: RawClient["server.reference"]) => ({ list: Endpoint16_0(raw) })
|
||||
|
||||
type Endpoint17_0Request = Parameters<RawClient["server.projectCopy"]["projectCopy.create"]>[0]
|
||||
type Endpoint17_0Input = {
|
||||
readonly projectID: Endpoint17_0Request["params"]["projectID"]
|
||||
readonly location?: Endpoint17_0Request["query"]["location"]
|
||||
readonly strategy: Endpoint17_0Request["payload"]["strategy"]
|
||||
readonly directory: Endpoint17_0Request["payload"]["directory"]
|
||||
readonly name?: Endpoint17_0Request["payload"]["name"]
|
||||
}
|
||||
const Endpoint17_0 = (raw: RawClient["server.projectCopy"]) => (input: Endpoint17_0Input) =>
|
||||
raw["projectCopy.create"]({
|
||||
params: { projectID: input["projectID"] },
|
||||
query: { location: input["location"] },
|
||||
payload: { strategy: input["strategy"], directory: input["directory"], name: input["name"] },
|
||||
}).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
type Endpoint17_1Request = Parameters<RawClient["server.projectCopy"]["projectCopy.remove"]>[0]
|
||||
type Endpoint17_1Input = {
|
||||
readonly projectID: Endpoint17_1Request["params"]["projectID"]
|
||||
readonly location?: Endpoint17_1Request["query"]["location"]
|
||||
readonly directory: Endpoint17_1Request["payload"]["directory"]
|
||||
readonly force: Endpoint17_1Request["payload"]["force"]
|
||||
}
|
||||
const Endpoint17_1 = (raw: RawClient["server.projectCopy"]) => (input: Endpoint17_1Input) =>
|
||||
raw["projectCopy.remove"]({
|
||||
params: { projectID: input["projectID"] },
|
||||
query: { location: input["location"] },
|
||||
payload: { directory: input["directory"], force: input["force"] },
|
||||
}).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
type Endpoint17_2Request = Parameters<RawClient["server.projectCopy"]["projectCopy.refresh"]>[0]
|
||||
type Endpoint17_2Input = {
|
||||
readonly projectID: Endpoint17_2Request["params"]["projectID"]
|
||||
readonly location?: Endpoint17_2Request["query"]["location"]
|
||||
}
|
||||
const Endpoint17_2 = (raw: RawClient["server.projectCopy"]) => (input: Endpoint17_2Input) =>
|
||||
raw["projectCopy.refresh"]({
|
||||
params: { projectID: input["projectID"] },
|
||||
query: { location: input["location"] },
|
||||
}).pipe(Effect.mapError(mapClientError))
|
||||
|
||||
const adaptGroup17 = (raw: RawClient["server.projectCopy"]) => ({
|
||||
create: Endpoint17_0(raw),
|
||||
remove: Endpoint17_1(raw),
|
||||
refresh: Endpoint17_2(raw),
|
||||
})
|
||||
const adaptGroup1 = (raw: RawClient["server.event"]) => ({ subscribe: Endpoint1_0(raw) })
|
||||
|
||||
const adaptClient = (raw: RawClient) => ({
|
||||
health: adaptGroup0(raw["server.health"]),
|
||||
location: adaptGroup1(raw["server.location"]),
|
||||
agents: adaptGroup2(raw["server.agent"]),
|
||||
sessions: adaptGroup3(raw["server.session"]),
|
||||
messages: adaptGroup4(raw["server.message"]),
|
||||
models: adaptGroup5(raw["server.model"]),
|
||||
providers: adaptGroup6(raw["server.provider"]),
|
||||
integrations: adaptGroup7(raw["server.integration"]),
|
||||
credentials: adaptGroup8(raw["server.credential"]),
|
||||
permissions: adaptGroup9(raw["server.permission"]),
|
||||
files: adaptGroup10(raw["server.fs"]),
|
||||
commands: adaptGroup11(raw["server.command"]),
|
||||
skills: adaptGroup12(raw["server.skill"]),
|
||||
events: adaptGroup13(raw["server.event"]),
|
||||
ptys: adaptGroup14(raw["server.pty"]),
|
||||
questions: adaptGroup15(raw["server.question"]),
|
||||
references: adaptGroup16(raw["server.reference"]),
|
||||
projectCopies: adaptGroup17(raw["server.projectCopy"]),
|
||||
sessions: adaptGroup0(raw["server.session"]),
|
||||
events: adaptGroup1(raw["server.event"]),
|
||||
})
|
||||
|
||||
export const make = (options?: { readonly baseUrl?: URL | string }) =>
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import type {
|
||||
HealthGetOutput,
|
||||
LocationGetInput,
|
||||
LocationGetOutput,
|
||||
AgentsListInput,
|
||||
AgentsListOutput,
|
||||
SessionsListInput,
|
||||
SessionsListOutput,
|
||||
SessionsCreateInput,
|
||||
@@ -37,81 +32,7 @@ import type {
|
||||
SessionsInterruptOutput,
|
||||
SessionsMessageInput,
|
||||
SessionsMessageOutput,
|
||||
MessagesListInput,
|
||||
MessagesListOutput,
|
||||
ModelsListInput,
|
||||
ModelsListOutput,
|
||||
ProvidersListInput,
|
||||
ProvidersListOutput,
|
||||
ProvidersGetInput,
|
||||
ProvidersGetOutput,
|
||||
IntegrationsListInput,
|
||||
IntegrationsListOutput,
|
||||
IntegrationsGetInput,
|
||||
IntegrationsGetOutput,
|
||||
IntegrationsConnectKeyInput,
|
||||
IntegrationsConnectKeyOutput,
|
||||
IntegrationsConnectOauthInput,
|
||||
IntegrationsConnectOauthOutput,
|
||||
IntegrationsAttemptStatusInput,
|
||||
IntegrationsAttemptStatusOutput,
|
||||
IntegrationsAttemptCompleteInput,
|
||||
IntegrationsAttemptCompleteOutput,
|
||||
IntegrationsAttemptCancelInput,
|
||||
IntegrationsAttemptCancelOutput,
|
||||
CredentialsUpdateInput,
|
||||
CredentialsUpdateOutput,
|
||||
CredentialsRemoveInput,
|
||||
CredentialsRemoveOutput,
|
||||
PermissionsListRequestsInput,
|
||||
PermissionsListRequestsOutput,
|
||||
PermissionsListSavedInput,
|
||||
PermissionsListSavedOutput,
|
||||
PermissionsRemoveSavedInput,
|
||||
PermissionsRemoveSavedOutput,
|
||||
PermissionsCreateInput,
|
||||
PermissionsCreateOutput,
|
||||
PermissionsListInput,
|
||||
PermissionsListOutput,
|
||||
PermissionsGetInput,
|
||||
PermissionsGetOutput,
|
||||
PermissionsReplyInput,
|
||||
PermissionsReplyOutput,
|
||||
FilesListInput,
|
||||
FilesListOutput,
|
||||
FilesFindInput,
|
||||
FilesFindOutput,
|
||||
CommandsListInput,
|
||||
CommandsListOutput,
|
||||
SkillsListInput,
|
||||
SkillsListOutput,
|
||||
EventsSubscribeOutput,
|
||||
PtysListInput,
|
||||
PtysListOutput,
|
||||
PtysCreateInput,
|
||||
PtysCreateOutput,
|
||||
PtysGetInput,
|
||||
PtysGetOutput,
|
||||
PtysUpdateInput,
|
||||
PtysUpdateOutput,
|
||||
PtysRemoveInput,
|
||||
PtysRemoveOutput,
|
||||
QuestionsListRequestsInput,
|
||||
QuestionsListRequestsOutput,
|
||||
QuestionsListInput,
|
||||
QuestionsListOutput,
|
||||
QuestionsReplyInput,
|
||||
QuestionsReplyOutput,
|
||||
QuestionsRejectInput,
|
||||
QuestionsRejectOutput,
|
||||
ReferencesListInput,
|
||||
ReferencesListOutput,
|
||||
ProjectCopiesCreateInput,
|
||||
ProjectCopiesCreateOutput,
|
||||
ProjectCopiesRemoveInput,
|
||||
ProjectCopiesRemoveOutput,
|
||||
ProjectCopiesRefreshInput,
|
||||
ProjectCopiesRefreshOutput,
|
||||
} from "./types"
|
||||
import { ClientError } from "./client-error"
|
||||
|
||||
@@ -247,41 +168,6 @@ export function make(options: ClientOptions) {
|
||||
})
|
||||
|
||||
return {
|
||||
health: {
|
||||
get: (requestOptions?: RequestOptions) =>
|
||||
request<HealthGetOutput>(
|
||||
{ method: "GET", path: `/api/health`, successStatus: 200, declaredStatuses: [401, 400], empty: false },
|
||||
requestOptions,
|
||||
),
|
||||
},
|
||||
location: {
|
||||
get: (input?: LocationGetInput, requestOptions?: RequestOptions) =>
|
||||
request<LocationGetOutput>(
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/location`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [401, 400],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
},
|
||||
agents: {
|
||||
list: (input?: AgentsListInput, requestOptions?: RequestOptions) =>
|
||||
request<AgentsListOutput>(
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/agent`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [401, 400],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
},
|
||||
sessions: {
|
||||
list: (input?: SessionsListInput, requestOptions?: RequestOptions) =>
|
||||
request<SessionsListOutput>(
|
||||
@@ -289,14 +175,14 @@ export function make(options: ClientOptions) {
|
||||
method: "GET",
|
||||
path: `/api/session`,
|
||||
query: {
|
||||
workspace: input?.["workspace"],
|
||||
limit: input?.["limit"],
|
||||
order: input?.["order"],
|
||||
search: input?.["search"],
|
||||
directory: input?.["directory"],
|
||||
project: input?.["project"],
|
||||
subpath: input?.["subpath"],
|
||||
cursor: input?.["cursor"],
|
||||
workspace: input?.workspace,
|
||||
limit: input?.limit,
|
||||
order: input?.order,
|
||||
search: input?.search,
|
||||
directory: input?.directory,
|
||||
project: input?.project,
|
||||
subpath: input?.subpath,
|
||||
cursor: input?.cursor,
|
||||
},
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
@@ -309,12 +195,7 @@ export function make(options: ClientOptions) {
|
||||
{
|
||||
method: "POST",
|
||||
path: `/api/session`,
|
||||
body: {
|
||||
id: input?.["id"],
|
||||
agent: input?.["agent"],
|
||||
model: input?.["model"],
|
||||
location: input?.["location"],
|
||||
},
|
||||
body: { id: input?.id, agent: input?.agent, model: input?.model, location: input?.location },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [401, 400],
|
||||
empty: false,
|
||||
@@ -348,7 +229,7 @@ export function make(options: ClientOptions) {
|
||||
{
|
||||
method: "POST",
|
||||
path: `/api/session/${encodeURIComponent(input.sessionID)}/agent`,
|
||||
body: { agent: input["agent"] },
|
||||
body: { agent: input.agent },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [404, 400, 401],
|
||||
empty: true,
|
||||
@@ -360,7 +241,7 @@ export function make(options: ClientOptions) {
|
||||
{
|
||||
method: "POST",
|
||||
path: `/api/session/${encodeURIComponent(input.sessionID)}/model`,
|
||||
body: { model: input["model"] },
|
||||
body: { model: input.model },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [404, 400, 401],
|
||||
empty: true,
|
||||
@@ -372,7 +253,7 @@ export function make(options: ClientOptions) {
|
||||
{
|
||||
method: "POST",
|
||||
path: `/api/session/${encodeURIComponent(input.sessionID)}/prompt`,
|
||||
body: { id: input["id"], prompt: input["prompt"], delivery: input["delivery"], resume: input["resume"] },
|
||||
body: { id: input.id, prompt: input.prompt, delivery: input.delivery, resume: input.resume },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [409, 404, 400, 401],
|
||||
empty: false,
|
||||
@@ -406,7 +287,7 @@ export function make(options: ClientOptions) {
|
||||
{
|
||||
method: "POST",
|
||||
path: `/api/session/${encodeURIComponent(input.sessionID)}/revert/stage`,
|
||||
body: { messageID: input["messageID"], files: input["files"] },
|
||||
body: { messageID: input.messageID, files: input.files },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [404, 500, 400, 401],
|
||||
empty: false,
|
||||
@@ -451,7 +332,7 @@ export function make(options: ClientOptions) {
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/session/${encodeURIComponent(input.sessionID)}/history`,
|
||||
query: { limit: input["limit"], after: input["after"] },
|
||||
query: { limit: input.limit, after: input.after },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [404, 400, 401],
|
||||
empty: false,
|
||||
@@ -463,7 +344,7 @@ export function make(options: ClientOptions) {
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/session/${encodeURIComponent(input.sessionID)}/event`,
|
||||
query: { after: input["after"] },
|
||||
query: { after: input.after },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [404, 400, 401],
|
||||
empty: false,
|
||||
@@ -493,321 +374,6 @@ export function make(options: ClientOptions) {
|
||||
requestOptions,
|
||||
).then((value) => value.data),
|
||||
},
|
||||
messages: {
|
||||
list: (input: MessagesListInput, requestOptions?: RequestOptions) =>
|
||||
request<MessagesListOutput>(
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/session/${encodeURIComponent(input.sessionID)}/message`,
|
||||
query: { limit: input["limit"], order: input["order"], cursor: input["cursor"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 404, 500, 401],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
},
|
||||
models: {
|
||||
list: (input?: ModelsListInput, requestOptions?: RequestOptions) =>
|
||||
request<ModelsListOutput>(
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/model`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [503, 401, 400],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
},
|
||||
providers: {
|
||||
list: (input?: ProvidersListInput, requestOptions?: RequestOptions) =>
|
||||
request<ProvidersListOutput>(
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/provider`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [503, 401, 400],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
get: (input: ProvidersGetInput, requestOptions?: RequestOptions) =>
|
||||
request<ProvidersGetOutput>(
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/provider/${encodeURIComponent(input.providerID)}`,
|
||||
query: { location: input["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [404, 503, 401, 400],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
},
|
||||
integrations: {
|
||||
list: (input?: IntegrationsListInput, requestOptions?: RequestOptions) =>
|
||||
request<IntegrationsListOutput>(
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/integration`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [401, 400],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
get: (input: IntegrationsGetInput, requestOptions?: RequestOptions) =>
|
||||
request<IntegrationsGetOutput>(
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/integration/${encodeURIComponent(input.integrationID)}`,
|
||||
query: { location: input["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [401, 400],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
connectKey: (input: IntegrationsConnectKeyInput, requestOptions?: RequestOptions) =>
|
||||
request<IntegrationsConnectKeyOutput>(
|
||||
{
|
||||
method: "POST",
|
||||
path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/key`,
|
||||
query: { location: input["location"] },
|
||||
body: { key: input["key"], label: input["label"] },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [400, 401],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
connectOauth: (input: IntegrationsConnectOauthInput, requestOptions?: RequestOptions) =>
|
||||
request<IntegrationsConnectOauthOutput>(
|
||||
{
|
||||
method: "POST",
|
||||
path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/oauth`,
|
||||
query: { location: input["location"] },
|
||||
body: { methodID: input["methodID"], inputs: input["inputs"], label: input["label"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
attemptStatus: (input: IntegrationsAttemptStatusInput, requestOptions?: RequestOptions) =>
|
||||
request<IntegrationsAttemptStatusOutput>(
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/integration/attempt/${encodeURIComponent(input.attemptID)}`,
|
||||
query: { location: input["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [401, 400],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
attemptComplete: (input: IntegrationsAttemptCompleteInput, requestOptions?: RequestOptions) =>
|
||||
request<IntegrationsAttemptCompleteOutput>(
|
||||
{
|
||||
method: "POST",
|
||||
path: `/api/integration/attempt/${encodeURIComponent(input.attemptID)}/complete`,
|
||||
query: { location: input["location"] },
|
||||
body: { code: input["code"] },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [400, 401],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
attemptCancel: (input: IntegrationsAttemptCancelInput, requestOptions?: RequestOptions) =>
|
||||
request<IntegrationsAttemptCancelOutput>(
|
||||
{
|
||||
method: "DELETE",
|
||||
path: `/api/integration/attempt/${encodeURIComponent(input.attemptID)}`,
|
||||
query: { location: input["location"] },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [401, 400],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
},
|
||||
credentials: {
|
||||
update: (input: CredentialsUpdateInput, requestOptions?: RequestOptions) =>
|
||||
request<CredentialsUpdateOutput>(
|
||||
{
|
||||
method: "PATCH",
|
||||
path: `/api/credential/${encodeURIComponent(input.credentialID)}`,
|
||||
query: { location: input["location"] },
|
||||
body: { label: input["label"] },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [401, 400],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
remove: (input: CredentialsRemoveInput, requestOptions?: RequestOptions) =>
|
||||
request<CredentialsRemoveOutput>(
|
||||
{
|
||||
method: "DELETE",
|
||||
path: `/api/credential/${encodeURIComponent(input.credentialID)}`,
|
||||
query: { location: input["location"] },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [401, 400],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
},
|
||||
permissions: {
|
||||
listRequests: (input?: PermissionsListRequestsInput, requestOptions?: RequestOptions) =>
|
||||
request<PermissionsListRequestsOutput>(
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/permission/request`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [401, 400],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
listSaved: (input?: PermissionsListSavedInput, requestOptions?: RequestOptions) =>
|
||||
request<{ readonly data: PermissionsListSavedOutput }>(
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/permission/saved`,
|
||||
query: { projectID: input?.["projectID"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [401, 400],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
).then((value) => value.data),
|
||||
removeSaved: (input: PermissionsRemoveSavedInput, requestOptions?: RequestOptions) =>
|
||||
request<PermissionsRemoveSavedOutput>(
|
||||
{
|
||||
method: "DELETE",
|
||||
path: `/api/permission/saved/${encodeURIComponent(input.id)}`,
|
||||
successStatus: 204,
|
||||
declaredStatuses: [401, 400],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
create: (input: PermissionsCreateInput, requestOptions?: RequestOptions) =>
|
||||
request<{ readonly data: PermissionsCreateOutput }>(
|
||||
{
|
||||
method: "POST",
|
||||
path: `/api/session/${encodeURIComponent(input.sessionID)}/permission`,
|
||||
body: {
|
||||
id: input["id"],
|
||||
action: input["action"],
|
||||
resources: input["resources"],
|
||||
save: input["save"],
|
||||
metadata: input["metadata"],
|
||||
source: input["source"],
|
||||
agent: input["agent"],
|
||||
},
|
||||
successStatus: 200,
|
||||
declaredStatuses: [404, 400, 401],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
).then((value) => value.data),
|
||||
list: (input: PermissionsListInput, requestOptions?: RequestOptions) =>
|
||||
request<{ readonly data: PermissionsListOutput }>(
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/session/${encodeURIComponent(input.sessionID)}/permission`,
|
||||
successStatus: 200,
|
||||
declaredStatuses: [404, 400, 401],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
).then((value) => value.data),
|
||||
get: (input: PermissionsGetInput, requestOptions?: RequestOptions) =>
|
||||
request<{ readonly data: PermissionsGetOutput }>(
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/session/${encodeURIComponent(input.sessionID)}/permission/${encodeURIComponent(input.requestID)}`,
|
||||
successStatus: 200,
|
||||
declaredStatuses: [404, 400, 401],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
).then((value) => value.data),
|
||||
reply: (input: PermissionsReplyInput, requestOptions?: RequestOptions) =>
|
||||
request<PermissionsReplyOutput>(
|
||||
{
|
||||
method: "POST",
|
||||
path: `/api/session/${encodeURIComponent(input.sessionID)}/permission/${encodeURIComponent(input.requestID)}/reply`,
|
||||
body: { reply: input["reply"], message: input["message"] },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [404, 400, 401],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
},
|
||||
files: {
|
||||
list: (input?: FilesListInput, requestOptions?: RequestOptions) =>
|
||||
request<FilesListOutput>(
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/fs/list`,
|
||||
query: { location: input?.["location"], path: input?.["path"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [401, 400],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
find: (input: FilesFindInput, requestOptions?: RequestOptions) =>
|
||||
request<FilesFindOutput>(
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/fs/find`,
|
||||
query: { location: input["location"], query: input["query"], type: input["type"], limit: input["limit"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [401, 400],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
},
|
||||
commands: {
|
||||
list: (input?: CommandsListInput, requestOptions?: RequestOptions) =>
|
||||
request<CommandsListOutput>(
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/command`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [401, 400],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
},
|
||||
skills: {
|
||||
list: (input?: SkillsListInput, requestOptions?: RequestOptions) =>
|
||||
request<SkillsListOutput>(
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/skill`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [401, 400],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
},
|
||||
events: {
|
||||
subscribe: (requestOptions?: RequestOptions): AsyncIterable<EventsSubscribeOutput> =>
|
||||
sse<EventsSubscribeOutput>(
|
||||
@@ -815,178 +381,6 @@ export function make(options: ClientOptions) {
|
||||
requestOptions,
|
||||
),
|
||||
},
|
||||
ptys: {
|
||||
list: (input?: PtysListInput, requestOptions?: RequestOptions) =>
|
||||
request<PtysListOutput>(
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/pty`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [401, 400],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
create: (input?: PtysCreateInput, requestOptions?: RequestOptions) =>
|
||||
request<PtysCreateOutput>(
|
||||
{
|
||||
method: "POST",
|
||||
path: `/api/pty`,
|
||||
query: { location: input?.["location"] },
|
||||
body: {
|
||||
command: input?.["command"],
|
||||
args: input?.["args"],
|
||||
cwd: input?.["cwd"],
|
||||
title: input?.["title"],
|
||||
env: input?.["env"],
|
||||
},
|
||||
successStatus: 200,
|
||||
declaredStatuses: [401, 400],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
get: (input: PtysGetInput, requestOptions?: RequestOptions) =>
|
||||
request<PtysGetOutput>(
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/pty/${encodeURIComponent(input.ptyID)}`,
|
||||
query: { location: input["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [404, 401, 400],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
update: (input: PtysUpdateInput, requestOptions?: RequestOptions) =>
|
||||
request<PtysUpdateOutput>(
|
||||
{
|
||||
method: "PUT",
|
||||
path: `/api/pty/${encodeURIComponent(input.ptyID)}`,
|
||||
query: { location: input["location"] },
|
||||
body: { title: input["title"], size: input["size"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [404, 401, 400],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
remove: (input: PtysRemoveInput, requestOptions?: RequestOptions) =>
|
||||
request<PtysRemoveOutput>(
|
||||
{
|
||||
method: "DELETE",
|
||||
path: `/api/pty/${encodeURIComponent(input.ptyID)}`,
|
||||
query: { location: input["location"] },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [404, 401, 400],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
},
|
||||
questions: {
|
||||
listRequests: (input?: QuestionsListRequestsInput, requestOptions?: RequestOptions) =>
|
||||
request<QuestionsListRequestsOutput>(
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/question/request`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [401, 400],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
list: (input: QuestionsListInput, requestOptions?: RequestOptions) =>
|
||||
request<{ readonly data: QuestionsListOutput }>(
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/session/${encodeURIComponent(input.sessionID)}/question`,
|
||||
successStatus: 200,
|
||||
declaredStatuses: [404, 400, 401],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
).then((value) => value.data),
|
||||
reply: (input: QuestionsReplyInput, requestOptions?: RequestOptions) =>
|
||||
request<QuestionsReplyOutput>(
|
||||
{
|
||||
method: "POST",
|
||||
path: `/api/session/${encodeURIComponent(input.sessionID)}/question/${encodeURIComponent(input.requestID)}/reply`,
|
||||
body: { answers: input["answers"] },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [404, 400, 401],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
reject: (input: QuestionsRejectInput, requestOptions?: RequestOptions) =>
|
||||
request<QuestionsRejectOutput>(
|
||||
{
|
||||
method: "POST",
|
||||
path: `/api/session/${encodeURIComponent(input.sessionID)}/question/${encodeURIComponent(input.requestID)}/reject`,
|
||||
successStatus: 204,
|
||||
declaredStatuses: [404, 400, 401],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
},
|
||||
references: {
|
||||
list: (input?: ReferencesListInput, requestOptions?: RequestOptions) =>
|
||||
request<ReferencesListOutput>(
|
||||
{
|
||||
method: "GET",
|
||||
path: `/api/reference`,
|
||||
query: { location: input?.["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [401, 400],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
},
|
||||
projectCopies: {
|
||||
create: (input: ProjectCopiesCreateInput, requestOptions?: RequestOptions) =>
|
||||
request<ProjectCopiesCreateOutput>(
|
||||
{
|
||||
method: "POST",
|
||||
path: `/experimental/project/${encodeURIComponent(input.projectID)}/copy`,
|
||||
query: { location: input["location"] },
|
||||
body: { strategy: input["strategy"], directory: input["directory"], name: input["name"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
remove: (input: ProjectCopiesRemoveInput, requestOptions?: RequestOptions) =>
|
||||
request<ProjectCopiesRemoveOutput>(
|
||||
{
|
||||
method: "DELETE",
|
||||
path: `/experimental/project/${encodeURIComponent(input.projectID)}/copy`,
|
||||
query: { location: input["location"] },
|
||||
body: { directory: input["directory"], force: input["force"] },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [400, 401],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
refresh: (input: ProjectCopiesRefreshInput, requestOptions?: RequestOptions) =>
|
||||
request<ProjectCopiesRefreshOutput>(
|
||||
{
|
||||
method: "POST",
|
||||
path: `/experimental/project/${encodeURIComponent(input.projectID)}/copy/refresh`,
|
||||
query: { location: input["location"] },
|
||||
successStatus: 204,
|
||||
declaredStatuses: [400, 401],
|
||||
empty: true,
|
||||
},
|
||||
requestOptions,
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,7 +19,8 @@ import { SessionMessage } from "@opencode-ai/schema/session-message"
|
||||
import { Workspace } from "@opencode-ai/schema/workspace"
|
||||
import { Api } from "@opencode-ai/server/api"
|
||||
import { compile, emitPromise } from "@opencode-ai/httpapi-codegen"
|
||||
import { ClientApi, endpointNames, groupNames, omitEndpoints } from "../src/contract"
|
||||
import { HttpApi } from "effect/unstable/httpapi"
|
||||
import { EventGroup, SessionGroup } from "../src/contract"
|
||||
|
||||
test("Core and Server reuse the authoritative Schema and Protocol values", () => {
|
||||
expect(AgentV2.ID).toBe(Agent.ID)
|
||||
@@ -30,16 +31,18 @@ test("Core and Server reuse the authoritative Schema and Protocol values", () =>
|
||||
expect(CoreSessionMessage.Message).toBe(SessionMessage.Message)
|
||||
expect(CorePrompt).toBe(Prompt)
|
||||
expect(Api.groups["server.session"].identifier).toBe("server.session")
|
||||
expect(Object.keys(ClientApi.groups)).toEqual(Object.keys(Api.groups))
|
||||
expect(SessionGroup.identifier).toBe(Api.groups["server.session"].identifier)
|
||||
expect(EventGroup.identifier).toBe(Api.groups["server.event"].identifier)
|
||||
expect(Session.ID.create()).toStartWith("ses_")
|
||||
expect(Project.ID.global).toBe("global")
|
||||
expect(Provider.ID.anthropic).toBe("anthropic")
|
||||
expect(Workspace.ID.create()).toStartWith("wrk_")
|
||||
})
|
||||
|
||||
test("client and Server contracts generate identically", () => {
|
||||
const server = compile(Api, { groupNames, endpointNames, omitEndpoints })
|
||||
const client = compile(ClientApi, { groupNames, endpointNames, omitEndpoints })
|
||||
test("client and Server Session contracts generate identically", () => {
|
||||
const options = { groupNames: { "server.session": "sessions" } }
|
||||
const server = compile(HttpApi.make("server").add(Api.groups["server.session"]), options)
|
||||
const client = compile(HttpApi.make("client").add(SessionGroup), options)
|
||||
|
||||
expect(emitPromise(client)).toEqual(emitPromise(server))
|
||||
})
|
||||
|
||||
@@ -1,43 +1,6 @@
|
||||
import { expect, test } from "bun:test"
|
||||
import { isSessionNotFoundError, isUnauthorizedError, OpenCode } from "../src"
|
||||
|
||||
test("exposes every standard HTTP API group", () => {
|
||||
const client = OpenCode.make({ baseUrl: "http://localhost:3000" })
|
||||
|
||||
expect(Object.keys(client)).toEqual([
|
||||
"health",
|
||||
"location",
|
||||
"agents",
|
||||
"sessions",
|
||||
"messages",
|
||||
"models",
|
||||
"providers",
|
||||
"integrations",
|
||||
"credentials",
|
||||
"permissions",
|
||||
"files",
|
||||
"commands",
|
||||
"skills",
|
||||
"events",
|
||||
"ptys",
|
||||
"questions",
|
||||
"references",
|
||||
"projectCopies",
|
||||
])
|
||||
expect(Object.keys(client.messages)).toEqual(["list"])
|
||||
expect(Object.keys(client.integrations)).toEqual([
|
||||
"list",
|
||||
"get",
|
||||
"connectKey",
|
||||
"connectOauth",
|
||||
"attemptStatus",
|
||||
"attemptComplete",
|
||||
"attemptCancel",
|
||||
])
|
||||
expect(Object.keys(client.files)).toEqual(["list", "find"])
|
||||
expect(Object.keys(client.ptys)).toEqual(["list", "create", "get", "update", "remove"])
|
||||
})
|
||||
|
||||
test("sessions.get returns the wire projection", async () => {
|
||||
const client = OpenCode.make({
|
||||
baseUrl: "http://localhost:3000",
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
"@opencode-ai/console-mail": "workspace:*",
|
||||
"@opencode-ai/console-resource": "workspace:*",
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
"opencode": "./bin/opencode"
|
||||
},
|
||||
"exports": {
|
||||
"./effect/layer-node": "./src/effect/layer-node.ts",
|
||||
"./effect/app-node": "./src/effect/app-node.ts",
|
||||
"./effect/layer-node": "./src/effect/layer-node/index.ts",
|
||||
"./effect/node": "./src/effect/node.ts",
|
||||
"./session/runner": "./src/session/runner/index.ts",
|
||||
"./system-context": "./src/system-context/index.ts",
|
||||
"./*": "./src/*.ts"
|
||||
@@ -104,7 +104,6 @@
|
||||
"ai-gateway-provider": "3.1.2",
|
||||
"bun-pty": "0.4.8",
|
||||
"cross-spawn": "catalog:",
|
||||
"diff": "catalog:",
|
||||
"drizzle-orm": "catalog:",
|
||||
"effect": "catalog:",
|
||||
"fuzzysort": "3.1.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * as AgentV2 from "./agent"
|
||||
|
||||
import { makeLocationNode } from "./effect/app-node"
|
||||
import { makeLocationNode } from "./effect/node"
|
||||
import { Array, Context, Effect, Layer, Types } from "effect"
|
||||
import { Agent } from "@opencode-ai/schema/agent"
|
||||
import { State } from "./state"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * as AISDK from "./aisdk"
|
||||
|
||||
import { makeLocationNode } from "./effect/app-node"
|
||||
import { makeLocationNode } from "./effect/node"
|
||||
import type { LanguageModelV3 } from "@ai-sdk/provider"
|
||||
import { Cause, Context, Effect, Layer, Schema, Scope } from "effect"
|
||||
import { ModelV2 } from "./model"
|
||||
|
||||
@@ -2,7 +2,7 @@ export * as BackgroundJob from "./background-job"
|
||||
|
||||
import { Cause, Clock, Context, Deferred, Effect, Exit, Layer, Scope, SynchronizedRef } from "effect"
|
||||
import { Identifier } from "./id/id"
|
||||
import { makeGlobalNode } from "./effect/app-node"
|
||||
import { makeGlobalNode } from "./effect/node"
|
||||
|
||||
export type Status = "running" | "completed" | "error" | "cancelled"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * as Catalog from "./catalog"
|
||||
|
||||
import { makeLocationNode } from "./effect/app-node"
|
||||
import { makeLocationNode } from "./effect/node"
|
||||
import { Array, Context, Effect, Layer, Option, Order, pipe, Schema } from "effect"
|
||||
import { Catalog } from "@opencode-ai/schema/catalog"
|
||||
import { ModelV2 } from "./model"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * as CommandV2 from "./command"
|
||||
|
||||
import { makeLocationNode } from "./effect/app-node"
|
||||
import { makeLocationNode } from "./effect/node"
|
||||
import { Context, Effect, Layer, Types } from "effect"
|
||||
import { Command } from "@opencode-ai/schema/command"
|
||||
import { State } from "./state"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * as Config from "./config"
|
||||
|
||||
import { makeLocationNode } from "./effect/app-node"
|
||||
import { makeLocationNode } from "./effect/node"
|
||||
import path from "path"
|
||||
import { type ParseError, parse } from "jsonc-parser"
|
||||
import { Context, Effect, Layer, Option, Schema } from "effect"
|
||||
|
||||
@@ -66,7 +66,6 @@ export const Plugin = define({
|
||||
if (config.capabilities !== undefined) {
|
||||
model.capabilities = {
|
||||
tools: config.capabilities.tools,
|
||||
reasoning: config.capabilities.reasoning,
|
||||
input: [...config.capabilities.input],
|
||||
output: [...config.capabilities.output],
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Context, Effect, Layer, Schema } from "effect"
|
||||
import { Credential } from "@opencode-ai/schema/credential"
|
||||
import { Integration } from "@opencode-ai/schema/integration"
|
||||
import { Database } from "./database/database"
|
||||
import { makeGlobalNode } from "./effect/app-node"
|
||||
import { makeGlobalNode } from "./effect/node"
|
||||
import { CredentialTable } from "./credential/sql"
|
||||
|
||||
export const ID = Credential.ID
|
||||
|
||||
@@ -24,8 +24,8 @@ import {
|
||||
import * as NodeChildProcess from "node:child_process"
|
||||
import { PassThrough } from "node:stream"
|
||||
import launch from "cross-spawn"
|
||||
import { makeGlobalNode } from "./effect/app-node"
|
||||
import { filesystem, path } from "./effect/app-node-platform"
|
||||
import { makeGlobalNode } from "./effect/node"
|
||||
import { filesystem, path } from "./effect/layer-node-platform"
|
||||
|
||||
const toError = (err: unknown): Error => (err instanceof globalThis.Error ? err : new globalThis.Error(String(err)))
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Flag } from "../flag/flag"
|
||||
import { isAbsolute, join } from "path"
|
||||
import { DatabaseMigration } from "./migration"
|
||||
import { InstallationChannel } from "../installation/version"
|
||||
import { makeGlobalNode } from "../effect/app-node"
|
||||
import { makeGlobalNode } from "../effect/node"
|
||||
|
||||
const makeDatabase = EffectDrizzleSqlite.makeWithDefaults()
|
||||
type DatabaseShape = Effect.Success<typeof makeDatabase>
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@ import { LLMClient, RequestExecutor } from "@opencode-ai/llm/route"
|
||||
import { FileSystem, Path } from "effect"
|
||||
import { FetchHttpClient } from "effect/unstable/http"
|
||||
import { HttpClient } from "effect/unstable/http"
|
||||
import { makeGlobalNode } from "./app-node"
|
||||
import { makeGlobalNode } from "./node"
|
||||
|
||||
export const filesystem = makeGlobalNode({ service: FileSystem.FileSystem, layer: NodeFileSystem.layer, deps: [] })
|
||||
export const path = makeGlobalNode({ service: Path.Path, layer: NodePath.layer, deps: [] })
|
||||
@@ -15,4 +15,4 @@ export const requestExecutor = makeGlobalNode({
|
||||
})
|
||||
export const llmClient = makeGlobalNode({ service: LLMClient.Service, layer: LLMClient.layer, deps: [requestExecutor] })
|
||||
|
||||
export * as LayerNodePlatform from "./app-node-platform"
|
||||
export * as LayerNodePlatform from "./layer-node-platform"
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./layer-node"
|
||||
export * as LayerNodeTree from "./layer-node-tree"
|
||||
@@ -0,0 +1,118 @@
|
||||
import { Layer } from "effect"
|
||||
import { LayerNode } from "./layer-node"
|
||||
|
||||
type AnyNode = LayerNode.Node<unknown, unknown, any>
|
||||
type RuntimeLayer = Layer.Layer<never, unknown, unknown>
|
||||
|
||||
export function hoist<A, E, T extends LayerNode.Tag>(
|
||||
root: LayerNode.Node<A, E, any>,
|
||||
tag: T,
|
||||
): {
|
||||
readonly node: LayerNode.Node<A, E>
|
||||
readonly hoisted: LayerNode.Node<unknown, E>
|
||||
} {
|
||||
const visited = new Map<AnyNode, AnyNode>()
|
||||
const hoisted = new Map<string, AnyNode>()
|
||||
const visiting = new Set<AnyNode>()
|
||||
const stack: AnyNode[] = []
|
||||
|
||||
const visit = (node: AnyNode): AnyNode => {
|
||||
if (node.kind === "group") {
|
||||
return { ...node, dependencies: node.dependencies.map(visit) }
|
||||
}
|
||||
|
||||
const existingNode = visited.get(node)
|
||||
if (existingNode) return existingNode
|
||||
|
||||
if (node.tag === tag) {
|
||||
const existing = hoisted.get(node.name)
|
||||
if (existing && existing !== node) {
|
||||
throw new Error(`Tag ${tag} has conflicting implementations for ${node.name}`)
|
||||
}
|
||||
hoisted.set(node.name, node)
|
||||
const empty = LayerNode.group([])
|
||||
visited.set(node, empty)
|
||||
return empty
|
||||
}
|
||||
if (node.kind === "unbound") {
|
||||
return node
|
||||
}
|
||||
|
||||
if (visiting.has(node)) {
|
||||
const start = stack.indexOf(node)
|
||||
throw new Error(
|
||||
`Cycle detected in layer tree: ${[...stack.slice(start), node].map((item) => item.name).join(" -> ")}`,
|
||||
)
|
||||
}
|
||||
visiting.add(node)
|
||||
stack.push(node)
|
||||
try {
|
||||
const dependencies = node.dependencies.map(visit)
|
||||
const clone = { ...node, dependencies }
|
||||
visited.set(node, clone)
|
||||
return clone
|
||||
} finally {
|
||||
stack.pop()
|
||||
visiting.delete(node)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
node: visit(root) as LayerNode.Node<A, E>,
|
||||
hoisted: LayerNode.group(Array.from(hoisted.values())) as LayerNode.Node<unknown, E>,
|
||||
}
|
||||
}
|
||||
|
||||
export function compile<A, E>(
|
||||
root: LayerNode.Node<A, E, any>,
|
||||
replacements?: ReadonlyMap<Layer.Any, Layer.Any>,
|
||||
): Layer.Layer<A, E> {
|
||||
const cache = new Map<AnyNode, RuntimeLayer>()
|
||||
const compileNode = (node: AnyNode): RuntimeLayer => {
|
||||
if (node.kind === "unbound") throw new Error(`Unbound layer node: ${node.name}`)
|
||||
const cached = cache.get(node)
|
||||
if (cached) return cached
|
||||
const dependencies = node.dependencies.flatMap(flatten).map(compileNode)
|
||||
const implementation = (replacements?.get(node.implementation!) ?? node.implementation!) as RuntimeLayer
|
||||
const layer =
|
||||
dependencies.length === 0
|
||||
? implementation
|
||||
: implementation.pipe(Layer.provide(dependencies as [RuntimeLayer, ...RuntimeLayer[]]))
|
||||
cache.set(node, layer)
|
||||
return layer
|
||||
}
|
||||
const layers = flatten(root).map((node) => compileNode(node))
|
||||
const layer = layers.reduce<RuntimeLayer>((result, layer) => layer.pipe(Layer.provideMerge(result)), Layer.empty)
|
||||
return layer as Layer.Layer<A, E>
|
||||
}
|
||||
|
||||
export function bind<A, E, T extends LayerNode.Tag | undefined>(
|
||||
root: LayerNode.Node<A, E, T>,
|
||||
source: AnyNode,
|
||||
replacement: AnyNode,
|
||||
): LayerNode.Node<A, E, T> {
|
||||
if (source.kind !== "unbound") throw new Error(`Cannot bind non-unbound layer node: ${source.name}`)
|
||||
if (source.name !== replacement.name) {
|
||||
throw new Error(`Cannot bind ${source.name} to ${replacement.name}`)
|
||||
}
|
||||
if (source.tag !== replacement.tag) {
|
||||
throw new Error(`Cannot bind ${source.name} across tags`)
|
||||
}
|
||||
const visited = new Map<AnyNode, AnyNode>()
|
||||
const visit = (node: AnyNode): AnyNode => {
|
||||
if (node === source) return replacement
|
||||
const existing = visited.get(node)
|
||||
if (existing) return existing
|
||||
if (node.kind === "unbound") return node
|
||||
const clone = { ...node, dependencies: node.dependencies.map(visit) }
|
||||
visited.set(node, clone)
|
||||
return clone
|
||||
}
|
||||
return visit(root) as LayerNode.Node<A, E, T>
|
||||
}
|
||||
|
||||
function flatten(node: AnyNode): readonly AnyNode[] {
|
||||
return node.kind === "group" ? node.dependencies.flatMap(flatten) : [node]
|
||||
}
|
||||
|
||||
export * as LayerNodeTree from "./layer-node-tree"
|
||||
+31
-177
@@ -1,7 +1,6 @@
|
||||
import { Brand, Context, Layer } from "effect"
|
||||
|
||||
type AnyNode = Node<unknown, unknown, any>
|
||||
type RuntimeLayer = Layer.Layer<never, unknown, unknown>
|
||||
type NodeList<Item extends AnyNode = AnyNode> = readonly [] | readonly [Item, ...Item[]]
|
||||
export type Output<Item> = [Item] extends [never] ? never : Item extends Node<infer A, unknown, any> ? A : never
|
||||
export type Error<Item> = [Item] extends [never] ? never : Item extends Node<unknown, infer E, any> ? E : never
|
||||
@@ -35,39 +34,6 @@ type NodeIdentity =
|
||||
| { readonly name: string; readonly service?: never }
|
||||
type DistributiveOmit<A, K extends PropertyKey> = A extends unknown ? Omit<A, K> : never
|
||||
|
||||
export type TagConfig = Readonly<Record<string, readonly string[]>>
|
||||
type TagNames<Config extends TagConfig> = keyof Config & string
|
||||
type NodeInTags<Names extends string> = Node<unknown, unknown, Tag<Names> | undefined>
|
||||
type CheckTags<Items extends NodeList, Names extends string> = [Exclude<Items[number], NodeInTags<Names>>] extends [
|
||||
never,
|
||||
]
|
||||
? unknown
|
||||
: { readonly "Invalid tag dependencies": Exclude<Items[number], NodeInTags<Names>> }
|
||||
|
||||
export interface Tags<Config extends TagConfig> {
|
||||
readonly values: { readonly [Name in TagNames<Config>]: Tag<Name> }
|
||||
readonly make: <Name extends TagNames<Config>>(
|
||||
name: Name,
|
||||
) => <const Implementation extends Layer.Any, const Items extends NodeList>(
|
||||
input: DistributiveOmit<MakeInput<Implementation, Items, Tag<Name>>, "tag"> &
|
||||
CheckTags<Items, Name | Extract<Config[Name][number], string>>,
|
||||
) => Node<Layer.Success<Implementation>, Layer.Error<Implementation> | Error<Items[number]>, Tag<Name>>
|
||||
}
|
||||
|
||||
export function tags<const Config extends { readonly [Name in keyof Config]: readonly (keyof Config & string)[] }>(
|
||||
config: Config,
|
||||
): Tags<Config> {
|
||||
const names = Object.keys(config) as TagNames<Config>[]
|
||||
const values = Object.fromEntries(names.map((name) => [name, makeTag(name)])) as Tags<Config>["values"]
|
||||
return {
|
||||
values,
|
||||
make: ((name: TagNames<Config>) => (input: DistributiveOmit<MakeInput<Layer.Any, NodeList, Tag>, "tag">) =>
|
||||
make({ ...input, tag: values[name] })) as Tags<Config>["make"],
|
||||
}
|
||||
}
|
||||
|
||||
// Nodes ---------------------------------------------------------------------
|
||||
|
||||
type MakeInput<
|
||||
Implementation extends Layer.Any,
|
||||
Items extends NodeList,
|
||||
@@ -111,6 +77,37 @@ export function group<const Items extends readonly AnyNode[]>(
|
||||
return { kind: "group", name: "group", dependencies }
|
||||
}
|
||||
|
||||
export type TagConfig = Readonly<Record<string, readonly string[]>>
|
||||
type TagNames<Config extends TagConfig> = keyof Config & string
|
||||
type NodeInTags<Names extends string> = Node<unknown, unknown, Tag<Names> | undefined>
|
||||
type CheckTags<Items extends NodeList, Names extends string> = [Exclude<Items[number], NodeInTags<Names>>] extends [
|
||||
never,
|
||||
]
|
||||
? unknown
|
||||
: { readonly "Invalid tag dependencies": Exclude<Items[number], NodeInTags<Names>> }
|
||||
|
||||
export interface Tags<Config extends TagConfig> {
|
||||
readonly values: { readonly [Name in TagNames<Config>]: Tag<Name> }
|
||||
readonly make: <Name extends TagNames<Config>>(
|
||||
name: Name,
|
||||
) => <const Implementation extends Layer.Any, const Items extends NodeList>(
|
||||
input: DistributiveOmit<MakeInput<Implementation, Items, Tag<Name>>, "tag"> &
|
||||
CheckTags<Items, Name | Extract<Config[Name][number], string>>,
|
||||
) => Node<Layer.Success<Implementation>, Layer.Error<Implementation> | Error<Items[number]>, Tag<Name>>
|
||||
}
|
||||
|
||||
export function tags<const Config extends { readonly [Name in keyof Config]: readonly (keyof Config & string)[] }>(
|
||||
config: Config,
|
||||
): Tags<Config> {
|
||||
const names = Object.keys(config) as TagNames<Config>[]
|
||||
const values = Object.fromEntries(names.map((name) => [name, makeTag(name)])) as Tags<Config>["values"]
|
||||
return {
|
||||
values,
|
||||
make: ((name: TagNames<Config>) => (input: DistributiveOmit<MakeInput<Layer.Any, NodeList, Tag>, "tag">) =>
|
||||
make({ ...input, tag: values[name] })) as Tags<Config>["make"],
|
||||
}
|
||||
}
|
||||
|
||||
export type Replacement = {
|
||||
readonly source: Layer.Any
|
||||
readonly replacement: Layer.Any
|
||||
@@ -127,147 +124,4 @@ export function replace<A, E, R, E2>(
|
||||
return { source, replacement }
|
||||
}
|
||||
|
||||
// Tree -----------------------------------------------------------------------
|
||||
|
||||
type Visit<Result> = (node: AnyNode, context: VisitContext<Result>) => Result
|
||||
|
||||
type VisitContext<Result> = {
|
||||
readonly cache: Map<AnyNode, Result>
|
||||
readonly visit: (node: AnyNode) => Result
|
||||
}
|
||||
|
||||
function walk<Result>(
|
||||
root: AnyNode,
|
||||
visit: Visit<Result>,
|
||||
options: {
|
||||
readonly cache?: Map<AnyNode, Result>
|
||||
readonly resolve?: (node: AnyNode) => AnyNode
|
||||
readonly detectCycles?: boolean
|
||||
} = {},
|
||||
) {
|
||||
const cache = options.cache ?? new Map<AnyNode, Result>()
|
||||
const visiting = new Set<AnyNode>()
|
||||
const stack: AnyNode[] = []
|
||||
|
||||
const recur = (node: AnyNode): Result => {
|
||||
const target = options.resolve?.(node) ?? node
|
||||
const cached = cache.get(target)
|
||||
if (cached !== undefined || cache.has(target)) return cached!
|
||||
|
||||
if (options.detectCycles !== false && visiting.has(target)) {
|
||||
const start = stack.indexOf(target)
|
||||
throw new Error(
|
||||
`Cycle detected in layer tree: ${[...stack.slice(start), target].map((item) => item.name).join(" -> ")}`,
|
||||
)
|
||||
}
|
||||
|
||||
visiting.add(target)
|
||||
stack.push(target)
|
||||
try {
|
||||
const result = visit(target, { cache, visit: recur })
|
||||
if (!cache.has(target)) cache.set(target, result)
|
||||
return result
|
||||
} finally {
|
||||
stack.pop()
|
||||
visiting.delete(target)
|
||||
}
|
||||
}
|
||||
|
||||
return recur(root)
|
||||
}
|
||||
|
||||
export function hoist<A, E, T extends Tag>(
|
||||
root: Node<A, E, any>,
|
||||
tag: T,
|
||||
): {
|
||||
readonly node: Node<A, E>
|
||||
readonly hoisted: Node<unknown, E>
|
||||
} {
|
||||
const hoisted = new Map<string, AnyNode>()
|
||||
|
||||
const node = walk<AnyNode>(root, (node, context) => {
|
||||
if (node.kind === "group") {
|
||||
return { ...node, dependencies: node.dependencies.map(context.visit) }
|
||||
}
|
||||
if (node.tag === tag) {
|
||||
const existing = hoisted.get(node.name)
|
||||
if (existing && existing !== node) {
|
||||
throw new Error(`Tag ${tag} has conflicting implementations for ${node.name}`)
|
||||
}
|
||||
hoisted.set(node.name, node)
|
||||
return group([])
|
||||
}
|
||||
if (node.kind === "unbound") {
|
||||
return node
|
||||
}
|
||||
return { ...node, dependencies: node.dependencies.map(context.visit) }
|
||||
})
|
||||
|
||||
return {
|
||||
node: node as Node<A, E>,
|
||||
hoisted: group(Array.from(hoisted.values())) as Node<unknown, E>,
|
||||
}
|
||||
}
|
||||
|
||||
export function compile<A, E>(
|
||||
root: Node<A, E, any>,
|
||||
replacements?: ReadonlyMap<Layer.Any, Layer.Any>,
|
||||
): Layer.Layer<A, E> {
|
||||
const cache = new Map<AnyNode, RuntimeLayer>()
|
||||
const compileNode = (node: AnyNode) =>
|
||||
walk<RuntimeLayer>(
|
||||
node,
|
||||
(node, context) => {
|
||||
if (node.kind === "unbound") throw new Error(`Unbound layer node: ${node.name}`)
|
||||
const dependencies = node.dependencies.flatMap(flatten).map(context.visit)
|
||||
const implementation = (replacements?.get(node.implementation!) ?? node.implementation!) as RuntimeLayer
|
||||
return dependencies.length === 0
|
||||
? implementation
|
||||
: implementation.pipe(Layer.provide(dependencies as [RuntimeLayer, ...RuntimeLayer[]]))
|
||||
},
|
||||
{ cache },
|
||||
)
|
||||
const layers = flatten(root).map((node) => compileNode(node))
|
||||
const layer = layers.reduce<RuntimeLayer>((result, layer) => layer.pipe(Layer.provideMerge(result)), Layer.empty)
|
||||
return layer as Layer.Layer<A, E>
|
||||
}
|
||||
|
||||
export function hasUnbound(root: Node<unknown, unknown, any>, source: AnyNode): boolean {
|
||||
if (source.kind !== "unbound") throw new Error(`Cannot check non-unbound layer node: ${source.name}`)
|
||||
return walk<boolean>(root, (node, context) => {
|
||||
if (node === source) return true
|
||||
return node.dependencies.some(context.visit)
|
||||
})
|
||||
}
|
||||
|
||||
export function bind<A, E, T extends Tag | undefined>(
|
||||
root: Node<A, E, T>,
|
||||
source: AnyNode,
|
||||
replacement: AnyNode,
|
||||
): Node<A, E, T> {
|
||||
if (source.kind !== "unbound") throw new Error(`Cannot bind non-unbound layer node: ${source.name}`)
|
||||
if (source.name !== replacement.name) {
|
||||
throw new Error(`Cannot bind ${source.name} to ${replacement.name}`)
|
||||
}
|
||||
if (source.tag !== replacement.tag) {
|
||||
throw new Error(`Cannot bind ${source.name} across tags`)
|
||||
}
|
||||
return walk<AnyNode>(
|
||||
root,
|
||||
(target, context) => {
|
||||
if (target.kind === "unbound") return target
|
||||
const dependencies: AnyNode[] = []
|
||||
const clone = { ...target, dependencies }
|
||||
context.cache.set(target, clone)
|
||||
dependencies.push(...target.dependencies.map(context.visit))
|
||||
return clone
|
||||
},
|
||||
{ detectCycles: false, resolve: (node) => (node === source ? replacement : node) },
|
||||
) as Node<A, E, T>
|
||||
}
|
||||
|
||||
function flatten(node: AnyNode): readonly AnyNode[] {
|
||||
return node.kind === "group" ? node.dependencies.flatMap(flatten) : [node]
|
||||
}
|
||||
|
||||
export * as LayerNode from "./layer-node"
|
||||
+5
-11
@@ -1,24 +1,18 @@
|
||||
import { Layer } from "effect"
|
||||
import { buildLocationServiceMap } from "../location-services"
|
||||
import { LocationServiceMap } from "../location-service-map"
|
||||
import { LayerNode } from "./layer-node"
|
||||
import { makeGlobalNode } from "./app-node"
|
||||
import { LayerNode, LayerNodeTree } from "./layer-node"
|
||||
import { makeGlobalNode } from "./node"
|
||||
|
||||
export function build<A, E>(root: LayerNode.Node<A, E, any>, replacements?: readonly LayerNode.Replacement[]) {
|
||||
const replacementMap = new Map(replacements?.map((item) => [item.source, item.replacement]))
|
||||
|
||||
if (!LayerNode.hasUnbound(root, LocationServiceMap.node)) {
|
||||
// If the location service map is not needed, we shouldn't pull it
|
||||
// in. Compile the graph normally
|
||||
return LayerNode.compile(root, replacementMap)
|
||||
}
|
||||
|
||||
const locationMap = buildLocationServiceMap(replacementMap)
|
||||
const locationMapNode = makeGlobalNode({ service: LocationServiceMap.Service, layer: locationMap, deps: [] })
|
||||
|
||||
const app = LayerNode.bind(root, LocationServiceMap.node, locationMapNode)
|
||||
const app = LayerNodeTree.bind(root, LocationServiceMap.node, locationMapNode)
|
||||
|
||||
return LayerNode.compile(app, replacementMap)
|
||||
return LayerNodeTree.compile(app, replacementMap)
|
||||
}
|
||||
|
||||
export * as AppNodeBuilder from "./app-node-builder"
|
||||
export * as NodeBuild from "./node-build"
|
||||
@@ -11,4 +11,4 @@ export type LocationNode<A, E = never> = LayerNode.Node<A, E, (typeof tags.value
|
||||
export const makeGlobalNode = tags.make("global")
|
||||
export const makeLocationNode = tags.make("location")
|
||||
|
||||
export * as Node from "./app-node"
|
||||
export * as Node from "./node"
|
||||
@@ -7,7 +7,7 @@ import { and, asc, eq, gt, inArray } from "drizzle-orm"
|
||||
import { Database } from "./database/database"
|
||||
import { EventSequenceTable, EventTable } from "./event/sql"
|
||||
import { Location } from "./location"
|
||||
import { makeGlobalNode } from "./effect/app-node"
|
||||
import { makeGlobalNode } from "./effect/node"
|
||||
import { isDeepStrictEqual } from "node:util"
|
||||
import { Durable } from "@opencode-ai/schema/durable-event-manifest"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * as FileMutation from "./file-mutation"
|
||||
|
||||
import { makeLocationNode } from "./effect/app-node"
|
||||
import { makeLocationNode } from "./effect/node"
|
||||
import { Context, Effect, Layer, Schema } from "effect"
|
||||
import { dirname } from "path"
|
||||
import { KeyedMutex } from "./effect/keyed-mutex"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * as FileSystem from "./filesystem"
|
||||
|
||||
import { makeLocationNode } from "./effect/app-node"
|
||||
import { makeLocationNode } from "./effect/node"
|
||||
import path from "path"
|
||||
import { Context, Effect, Layer, Schema } from "effect"
|
||||
import { FSUtil } from "./fs-util"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * as FileSystemSearch from "./search"
|
||||
|
||||
import { makeLocationNode } from "../effect/app-node"
|
||||
import { makeLocationNode } from "../effect/node"
|
||||
import path from "path"
|
||||
import { Context, Effect, Layer, Scope } from "effect"
|
||||
import { Fff } from "#fff"
|
||||
|
||||
@@ -3,7 +3,7 @@ export * as Watcher from "./watcher"
|
||||
// @ts-ignore
|
||||
import { createWrapper } from "@parcel/watcher/wrapper"
|
||||
import type ParcelWatcher from "@parcel/watcher"
|
||||
import { makeLocationNode } from "../effect/app-node"
|
||||
import { makeLocationNode } from "../effect/node"
|
||||
import { Cause, Context, Effect, Layer } from "effect"
|
||||
import { FileSystemWatcher } from "@opencode-ai/schema/filesystem-watcher"
|
||||
import path from "path"
|
||||
|
||||
@@ -7,8 +7,8 @@ import { Context, Effect, FileSystem, Layer, Schema } from "effect"
|
||||
import type { PlatformError } from "effect/PlatformError"
|
||||
import { Glob } from "./util/glob"
|
||||
import { serviceUse } from "./effect/service-use"
|
||||
import { makeGlobalNode } from "./effect/app-node"
|
||||
import { filesystem } from "./effect/app-node-platform"
|
||||
import { makeGlobalNode } from "./effect/node"
|
||||
import { filesystem } from "./effect/layer-node-platform"
|
||||
|
||||
export namespace FSUtil {
|
||||
export class FileSystemError extends Schema.TaggedErrorClass<FileSystemError>()("FileSystemError", {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ChildProcess } from "effect/unstable/process"
|
||||
import { AbsolutePath, RelativePath } from "./schema"
|
||||
import { FSUtil } from "./fs-util"
|
||||
import { AppProcess } from "./process"
|
||||
import { makeGlobalNode } from "./effect/app-node"
|
||||
import { makeGlobalNode } from "./effect/node"
|
||||
import { File } from "./file"
|
||||
import { KeyedMutex } from "./effect/keyed-mutex"
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import os from "os"
|
||||
import { Context, Effect, Layer } from "effect"
|
||||
import { Flock } from "./util/flock"
|
||||
import { Flag } from "./flag/flag"
|
||||
import { makeGlobalNode } from "./effect/app-node"
|
||||
import { makeGlobalNode } from "./effect/node"
|
||||
|
||||
const app = "opencode"
|
||||
const data = path.join(xdgData!, app)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * as Image from "./image"
|
||||
|
||||
import { makeLocationNode } from "./effect/app-node"
|
||||
import { makeLocationNode } from "./effect/node"
|
||||
import { Context, Effect, Layer, Schema } from "effect"
|
||||
import { Config } from "./config"
|
||||
import { FileSystem } from "./filesystem"
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Location } from "./location"
|
||||
import { AbsolutePath } from "./schema"
|
||||
import { SystemContext } from "./system-context/index"
|
||||
import { SystemContextRegistry } from "./system-context/registry"
|
||||
import { makeLocationNode } from "./effect/app-node"
|
||||
import { makeLocationNode } from "./effect/node"
|
||||
|
||||
class File extends Schema.Class<File>("InstructionContext.File")({
|
||||
path: AbsolutePath,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * as Integration from "./integration"
|
||||
|
||||
import { makeLocationNode } from "./effect/app-node"
|
||||
import { makeLocationNode } from "./effect/node"
|
||||
import {
|
||||
Cause,
|
||||
Clock,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * as LocationMutation from "./location-mutation"
|
||||
|
||||
import { makeLocationNode } from "./effect/app-node"
|
||||
import { makeLocationNode } from "./effect/node"
|
||||
import path from "path"
|
||||
import { Context, Effect, Layer, Schema } from "effect"
|
||||
import { FSUtil } from "./fs-util"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Context, Effect, Layer, LayerMap } from "effect"
|
||||
import { LayerNode } from "./effect/layer-node"
|
||||
import { Node } from "./effect/app-node"
|
||||
import { Node } from "./effect/node"
|
||||
import { Location } from "./location"
|
||||
import type { LocationError, LocationServices } from "./location-services"
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ import { AISDK } from "./aisdk"
|
||||
import { Catalog } from "./catalog"
|
||||
import { CommandV2 } from "./command"
|
||||
import { Config } from "./config"
|
||||
import { LayerNode } from "./effect/layer-node"
|
||||
import { Node } from "./effect/app-node"
|
||||
import { LayerNode, LayerNodeTree } from "./effect/layer-node"
|
||||
import { Node } from "./effect/node"
|
||||
import { FileMutation } from "./file-mutation"
|
||||
import { FileSystem } from "./filesystem"
|
||||
import { FileSystemSearch } from "./filesystem/search"
|
||||
@@ -88,11 +88,11 @@ export function buildLocationServiceMap(
|
||||
LocationServiceMap.Service,
|
||||
LayerMap.make(
|
||||
(ref: Location.Ref) => {
|
||||
const location = LayerNode.hoist(
|
||||
LayerNode.bind(locationServices, Location.node, Location.boundNode(ref)),
|
||||
const location = LayerNodeTree.hoist(
|
||||
LayerNodeTree.bind(locationServices, Location.node, Location.boundNode(ref)),
|
||||
Node.tags.values.global,
|
||||
)
|
||||
return LayerNode.compile(location.node, replacements).pipe(
|
||||
return LayerNodeTree.compile(location.node, replacements).pipe(
|
||||
Layer.fresh,
|
||||
Layer.tap(() =>
|
||||
Effect.logInfo("booting location services", {
|
||||
@@ -100,7 +100,7 @@ export function buildLocationServiceMap(
|
||||
workspaceID: ref.workspaceID,
|
||||
}),
|
||||
),
|
||||
Layer.provide(LayerNode.compile(location.hoisted, replacements)),
|
||||
Layer.provide(LayerNodeTree.compile(location.hoisted, replacements)),
|
||||
)
|
||||
},
|
||||
{ idleTimeToLive: "60 minutes" },
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Context, Effect, Layer } from "effect"
|
||||
import { Info, Ref, response } from "@opencode-ai/schema/location"
|
||||
import { Project } from "./project"
|
||||
import { LayerNode } from "./effect/layer-node"
|
||||
import { makeLocationNode, tags } from "./effect/app-node"
|
||||
import { makeLocationNode, tags } from "./effect/node"
|
||||
|
||||
export * as Location from "./location"
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ import { Hash } from "./util/hash"
|
||||
import { FSUtil } from "./fs-util"
|
||||
import { InstallationChannel, InstallationVersion } from "./installation/version"
|
||||
import { EventV2 } from "./event"
|
||||
import { makeGlobalNode } from "./effect/app-node"
|
||||
import { httpClient } from "./effect/app-node-platform"
|
||||
import { makeGlobalNode } from "./effect/node"
|
||||
import { httpClient } from "./effect/layer-node-platform"
|
||||
|
||||
export const CatalogModelStatus = Schema.Literals(["alpha", "beta", "deprecated"])
|
||||
export type CatalogModelStatus = typeof CatalogModelStatus.Type
|
||||
|
||||
@@ -7,8 +7,8 @@ import { NodeFileSystem } from "@effect/platform-node"
|
||||
import { FSUtil } from "./fs-util"
|
||||
import { Global } from "./global"
|
||||
import { EffectFlock } from "./util/effect-flock"
|
||||
import { makeGlobalNode } from "./effect/app-node"
|
||||
import { filesystem } from "./effect/app-node-platform"
|
||||
import { makeGlobalNode } from "./effect/node"
|
||||
import { filesystem } from "./effect/layer-node-platform"
|
||||
import { makeRuntime } from "./effect/runtime"
|
||||
import { NpmConfig } from "./npm-config"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * as PermissionV2 from "./permission"
|
||||
|
||||
import { makeLocationNode } from "./effect/app-node"
|
||||
import { makeLocationNode } from "./effect/node"
|
||||
import { Context, Deferred, Effect as EffectRuntime, Layer, Schema } from "effect"
|
||||
import { Permission } from "@opencode-ai/schema/permission"
|
||||
import { EventV2 } from "./event"
|
||||
|
||||
@@ -3,7 +3,7 @@ export * as PermissionSaved from "./saved"
|
||||
import { eq } from "drizzle-orm"
|
||||
import { Context, Effect, Layer, Schema } from "effect"
|
||||
import { Database } from "../database/database"
|
||||
import { makeGlobalNode } from "../effect/app-node"
|
||||
import { makeGlobalNode } from "../effect/node"
|
||||
import { ProjectV2 } from "../project"
|
||||
import { PermissionTable } from "./sql"
|
||||
import { PermissionSaved } from "@opencode-ai/schema/permission-saved"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * as PluginV2 from "./plugin"
|
||||
|
||||
import { makeLocationNode } from "./effect/app-node"
|
||||
import { makeLocationNode } from "./effect/node"
|
||||
import { Context, Deferred, Effect, Exit, Layer, Scope } from "effect"
|
||||
import type { Plugin as PluginRuntime } from "@opencode-ai/plugin/v2/effect"
|
||||
import { Plugin } from "@opencode-ai/schema/plugin"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export * as PluginInternal from "./internal"
|
||||
|
||||
import { makeLocationNode } from "../effect/app-node"
|
||||
import { httpClient } from "../effect/app-node-platform"
|
||||
import { makeLocationNode } from "../effect/node"
|
||||
import { httpClient } from "../effect/layer-node-platform"
|
||||
import type { PluginContext } from "@opencode-ai/plugin/v2/effect"
|
||||
import { Effect, Layer, Scope } from "effect"
|
||||
import { AgentV2 } from "../agent"
|
||||
@@ -25,7 +25,6 @@ import { Npm } from "../npm"
|
||||
import { PluginV2 } from "../plugin"
|
||||
import { Reference } from "../reference"
|
||||
import { SkillV2 } from "../skill"
|
||||
import { State } from "../state"
|
||||
import { FetchHttpClient, HttpClient } from "effect/unstable/http"
|
||||
import { AgentPlugin } from "./agent"
|
||||
import { CommandPlugin } from "./command"
|
||||
@@ -105,22 +104,20 @@ const layer = Layer.effectDiscard(
|
||||
return plugin.add(PluginV2.ID.make(loaded.id), loaded.effect)
|
||||
}
|
||||
|
||||
yield* State.batch(
|
||||
Effect.gen(function* () {
|
||||
yield* add(ConfigReferencePlugin.Plugin)
|
||||
yield* add(AgentPlugin.Plugin)
|
||||
yield* add(CommandPlugin.Plugin)
|
||||
yield* add(SkillPlugin.Plugin)
|
||||
yield* add(ModelsDevPlugin)
|
||||
yield* add(ConfigAgentPlugin.Plugin)
|
||||
yield* add(ConfigCommandPlugin.Plugin)
|
||||
yield* add(ConfigSkillPlugin.Plugin)
|
||||
for (const item of ProviderPlugins) yield* add(item)
|
||||
yield* add(ConfigExternalPlugin.Plugin)
|
||||
yield* add(ConfigProviderPlugin.Plugin)
|
||||
yield* add(VariantPlugin.Plugin)
|
||||
}),
|
||||
).pipe(Effect.withSpan("PluginInternal.boot"), Effect.forkScoped({ startImmediately: true }))
|
||||
yield* Effect.gen(function* () {
|
||||
yield* add(ConfigReferencePlugin.Plugin)
|
||||
yield* add(AgentPlugin.Plugin)
|
||||
yield* add(CommandPlugin.Plugin)
|
||||
yield* add(SkillPlugin.Plugin)
|
||||
yield* add(ModelsDevPlugin)
|
||||
yield* add(ConfigAgentPlugin.Plugin)
|
||||
yield* add(ConfigCommandPlugin.Plugin)
|
||||
yield* add(ConfigSkillPlugin.Plugin)
|
||||
for (const item of ProviderPlugins) yield* add(item)
|
||||
yield* add(ConfigExternalPlugin.Plugin)
|
||||
yield* add(ConfigProviderPlugin.Plugin)
|
||||
yield* add(VariantPlugin.Plugin)
|
||||
}).pipe(Effect.withSpan("PluginInternal.boot"), Effect.forkScoped({ startImmediately: true }))
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
@@ -108,7 +108,6 @@ export const ModelsDevPlugin = define({
|
||||
}
|
||||
draft.capabilities = {
|
||||
tools: model.tool_call,
|
||||
reasoning: model.reasoning,
|
||||
input: [...(model.modalities?.input ?? [])],
|
||||
output: [...(model.modalities?.output ?? [])],
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Duration, Effect, Schema, Semaphore, Stream } from "effect"
|
||||
import { Duration, Effect, Schema, Stream } from "effect"
|
||||
import type { Scope } from "effect"
|
||||
import type { IntegrationOAuthMethodRegistration } from "@opencode-ai/plugin/v2/effect/integration"
|
||||
import { define } from "@opencode-ai/plugin/v2/effect/plugin"
|
||||
@@ -79,7 +79,6 @@ export const OpencodePlugin = define<HttpClient.HttpClient | EventV2.Service | S
|
||||
effect: Effect.fn(function* (ctx) {
|
||||
const events = yield* EventV2.Service
|
||||
const http = yield* HttpClient.HttpClient
|
||||
const loading = Semaphore.makeUnsafe(1)
|
||||
let connected = false
|
||||
let providers: typeof ConfigV1.Info.Type.provider | undefined
|
||||
|
||||
@@ -106,7 +105,7 @@ export const OpencodePlugin = define<HttpClient.HttpClient | EventV2.Service | S
|
||||
draft.method.update({ integrationID: "opencode", method: { type: "key", label: "API key (service account)" } })
|
||||
})
|
||||
|
||||
connected = (yield* ctx.integration.connection.active("opencode")) !== undefined
|
||||
yield* load()
|
||||
yield* ctx.catalog.transform((catalog) => {
|
||||
for (const [providerID, item] of Object.entries(providers ?? {})) {
|
||||
catalog.provider.update(providerID, (provider) => {
|
||||
@@ -135,7 +134,6 @@ export const OpencodePlugin = define<HttpClient.HttpClient | EventV2.Service | S
|
||||
: { id: model.api.id, type: "native", url: config.provider.api, settings: {} }
|
||||
}
|
||||
if (config.tool_call !== undefined) model.capabilities.tools = config.tool_call
|
||||
if (config.reasoning !== undefined) model.capabilities.reasoning = config.reasoning
|
||||
if (config.modalities?.input !== undefined) model.capabilities.input = [...config.modalities.input]
|
||||
if (config.modalities?.output !== undefined) model.capabilities.output = [...config.modalities.output]
|
||||
const packageName = config.provider?.npm ?? item.npm
|
||||
@@ -178,13 +176,11 @@ export const OpencodePlugin = define<HttpClient.HttpClient | EventV2.Service | S
|
||||
}
|
||||
})
|
||||
|
||||
const refresh = () => loading.withPermit(load().pipe(Effect.andThen(ctx.catalog.reload())))
|
||||
yield* events.subscribe(Integration.Event.ConnectionUpdated).pipe(
|
||||
Stream.filter((event) => event.data.integrationID === Integration.ID.make("opencode")),
|
||||
Stream.runForEach(refresh),
|
||||
Stream.runForEach(() => load().pipe(Effect.andThen(ctx.catalog.reload()))),
|
||||
Effect.forkScoped({ startImmediately: true }),
|
||||
)
|
||||
yield* refresh().pipe(Effect.forkScoped)
|
||||
}),
|
||||
})
|
||||
|
||||
|
||||
@@ -28,36 +28,12 @@ export const Plugin = define({
|
||||
})
|
||||
|
||||
export function generate(model: ModelV2Info): ModelV2Info["variants"] {
|
||||
if (model.api.type !== "aisdk") return []
|
||||
|
||||
if (model.api.package === "@ai-sdk/openai-compatible") {
|
||||
const ids = `${model.id} ${model.api.id}`.toLowerCase()
|
||||
if (["glm-5.2", "glm-5-2", "glm-5p2"].some((name) => ids.includes(name)))
|
||||
return ["high", "max"].map((id) => ({
|
||||
id,
|
||||
headers: {},
|
||||
body: { reasoning_effort: id },
|
||||
}))
|
||||
return []
|
||||
}
|
||||
|
||||
if (model.api.package === "@ai-sdk/anthropic") return anthropicThinking(model)
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
// Anthropic thinking levels lower to the Messages API `thinking` body, which the
|
||||
// v2 Anthropic protocol only accepts as `{ type: "enabled", budget_tokens }`.
|
||||
// Budgets scale with the model output limit so the larger tier stays under the cap.
|
||||
function anthropicThinking(model: ModelV2Info): ModelV2Info["variants"] {
|
||||
if (!model.capabilities.reasoning) return []
|
||||
const budget = (target: number) => Math.max(1024, Math.min(target, model.limit.output - 1))
|
||||
return [
|
||||
{ id: "high", target: 16_000 },
|
||||
{ id: "max", target: 31_999 },
|
||||
].map((tier) => ({
|
||||
id: tier.id,
|
||||
if (model.api.type !== "aisdk" || model.api.package !== "@ai-sdk/openai-compatible") return []
|
||||
const ids = `${model.id} ${model.api.id}`.toLowerCase()
|
||||
if (!["glm-5.2", "glm-5-2", "glm-5p2"].some((name) => ids.includes(name))) return []
|
||||
return ["high", "max"].map((id) => ({
|
||||
id,
|
||||
headers: {},
|
||||
body: { thinking: { type: "enabled", budget_tokens: budget(tier.target) } },
|
||||
body: { reasoning_effort: id },
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * as Policy from "./policy"
|
||||
|
||||
import { makeLocationNode } from "./effect/app-node"
|
||||
import { makeLocationNode } from "./effect/node"
|
||||
import { Context, Effect as EffectRuntime, Layer, Schema } from "effect"
|
||||
import { Wildcard } from "./util/wildcard"
|
||||
import { Location } from "./location"
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { PlatformError } from "effect/PlatformError"
|
||||
import { ChildProcess } from "effect/unstable/process"
|
||||
import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner"
|
||||
import { CrossSpawnSpawner } from "./cross-spawn-spawner"
|
||||
import { makeGlobalNode } from "./effect/app-node"
|
||||
import { makeGlobalNode } from "./effect/node"
|
||||
|
||||
export class AppProcessError extends Schema.TaggedErrorClass<AppProcessError>()("AppProcessError", {
|
||||
command: Schema.String,
|
||||
@@ -20,7 +20,6 @@ export class AppProcessError extends Schema.TaggedErrorClass<AppProcessError>()(
|
||||
}
|
||||
|
||||
export interface RunOptions {
|
||||
readonly combineOutput?: boolean
|
||||
readonly maxOutputBytes?: number
|
||||
readonly maxErrorBytes?: number
|
||||
readonly signal?: AbortSignal
|
||||
@@ -38,10 +37,8 @@ export interface RunStreamOptions {
|
||||
export interface RunResult {
|
||||
readonly command: string
|
||||
readonly exitCode: number
|
||||
readonly output?: Buffer
|
||||
readonly stdout: Buffer
|
||||
readonly stderr: Buffer
|
||||
readonly outputTruncated?: boolean
|
||||
readonly stdoutTruncated: boolean
|
||||
readonly stderrTruncated: boolean
|
||||
}
|
||||
@@ -146,22 +143,6 @@ export const layer = Layer.effect(
|
||||
const collect = Effect.scoped(
|
||||
Effect.gen(function* () {
|
||||
const handle = yield* spawner.spawn(command)
|
||||
if (options?.combineOutput) {
|
||||
const [output, exitCode] = yield* Effect.all(
|
||||
[collectStream(handle.all, options.maxOutputBytes), handle.exitCode],
|
||||
{ concurrency: "unbounded" },
|
||||
)
|
||||
return {
|
||||
command: description,
|
||||
exitCode,
|
||||
output: output.buffer,
|
||||
stdout: Buffer.alloc(0),
|
||||
stderr: Buffer.alloc(0),
|
||||
outputTruncated: output.truncated,
|
||||
stdoutTruncated: false,
|
||||
stderrTruncated: false,
|
||||
} satisfies RunResult
|
||||
}
|
||||
const [stdout, stderr, exitCode] = yield* Effect.all(
|
||||
[
|
||||
collectStream(handle.stdout, options?.maxOutputBytes),
|
||||
|
||||
@@ -6,7 +6,7 @@ import path from "path"
|
||||
import { AbsolutePath } from "./schema"
|
||||
import { FSUtil } from "./fs-util"
|
||||
import { Git } from "./git"
|
||||
import { makeGlobalNode } from "./effect/app-node"
|
||||
import { makeGlobalNode } from "./effect/node"
|
||||
import { Hash } from "./util/hash"
|
||||
import { ProjectDirectories } from "./project/directories"
|
||||
import { ProjectSchema } from "./project/schema"
|
||||
|
||||
@@ -5,7 +5,7 @@ import path from "path"
|
||||
import { AbsolutePath } from "../schema"
|
||||
import { FSUtil } from "../fs-util"
|
||||
import { Git } from "../git"
|
||||
import { makeLocationNode } from "../effect/app-node"
|
||||
import { makeLocationNode } from "../effect/node"
|
||||
import { Project } from "../project"
|
||||
import { ProjectDirectories } from "./directories"
|
||||
import { makeGitWorktreeStrategy } from "./copy-strategies"
|
||||
|
||||
@@ -3,7 +3,7 @@ export * as ProjectDirectories from "./directories"
|
||||
import { and, asc, desc, eq, isNotNull, isNull, ne, or } from "drizzle-orm"
|
||||
import { Context, Effect, Layer, Schema } from "effect"
|
||||
import { Database } from "../database/database"
|
||||
import { makeGlobalNode } from "../effect/app-node"
|
||||
import { makeGlobalNode } from "../effect/node"
|
||||
import { AbsolutePath, optional } from "../schema"
|
||||
import { ProjectSchema } from "./schema"
|
||||
import { ProjectDirectoryTable } from "./sql"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * as Pty from "./pty"
|
||||
|
||||
import { makeLocationNode } from "./effect/app-node"
|
||||
import { makeLocationNode } from "./effect/node"
|
||||
import type { Disp, Proc } from "#pty"
|
||||
import { Context, Effect, Layer, Schema, Types } from "effect"
|
||||
import { Pty } from "@opencode-ai/schema/pty"
|
||||
|
||||
@@ -4,7 +4,7 @@ import { WorkspaceV2 } from "../workspace"
|
||||
import { PtyTicket } from "@opencode-ai/schema/pty-ticket"
|
||||
import { PtyID } from "./schema"
|
||||
import { Cache, Context, Duration, Effect, Layer } from "effect"
|
||||
import { makeGlobalNode } from "../effect/app-node"
|
||||
import { makeGlobalNode } from "../effect/node"
|
||||
|
||||
const DEFAULT_TTL = Duration.seconds(60)
|
||||
const CAPACITY = 10_000
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
export * as PublicEventManifest from "./public-event-manifest"
|
||||
|
||||
import { Event } from "@opencode-ai/schema/event"
|
||||
import { EventManifest } from "@opencode-ai/schema/event-manifest"
|
||||
|
||||
export const Definitions = EventManifest.ServerDefinitions
|
||||
export const Latest = Event.latest(Definitions)
|
||||
export { ServerDefinitions as Definitions } from "@opencode-ai/schema/event-manifest"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * as QuestionV2 from "./question"
|
||||
|
||||
import { makeLocationNode } from "./effect/app-node"
|
||||
import { makeLocationNode } from "./effect/node"
|
||||
import { Context, Deferred, Effect, Layer, Schema } from "effect"
|
||||
import { Question } from "@opencode-ai/schema/question"
|
||||
import { EventV2 } from "./event"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * as Reference from "./reference"
|
||||
|
||||
import { makeLocationNode } from "./effect/app-node"
|
||||
import { makeLocationNode } from "./effect/node"
|
||||
import { Context, Effect, Layer, Scope, Types } from "effect"
|
||||
import { Reference } from "@opencode-ai/schema/reference"
|
||||
import { Global } from "./global"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * as ReferenceGuidance from "./guidance"
|
||||
|
||||
import { makeLocationNode } from "../effect/app-node"
|
||||
import { makeLocationNode } from "../effect/node"
|
||||
import { Context, Effect, Layer, Schema } from "effect"
|
||||
import { Reference } from "../reference"
|
||||
import { SystemContext } from "../system-context/index"
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Git } from "./git"
|
||||
import { Global } from "./global"
|
||||
import { Repository } from "./repository"
|
||||
import { AbsolutePath } from "./schema"
|
||||
import { makeGlobalNode } from "./effect/app-node"
|
||||
import { makeGlobalNode } from "./effect/node"
|
||||
import { EffectFlock } from "./util/effect-flock"
|
||||
|
||||
export type Result = {
|
||||
|
||||
@@ -3,7 +3,7 @@ export * as Ripgrep from "./ripgrep"
|
||||
import { Context, Effect, Fiber, Layer, Schema, Stream } from "effect"
|
||||
import { ChildProcess } from "effect/unstable/process"
|
||||
import { Entry, Match } from "@opencode-ai/schema/filesystem"
|
||||
import { makeGlobalNode } from "./effect/app-node"
|
||||
import { makeGlobalNode } from "./effect/node"
|
||||
import { AppProcess, collectStream, waitForAbort } from "./process"
|
||||
import { NonNegativeInt, PositiveInt, RelativePath } from "./schema"
|
||||
import { RipgrepBinary } from "./ripgrep/binary"
|
||||
|
||||
@@ -4,8 +4,8 @@ import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/unstable/
|
||||
import { ChildProcess } from "effect/unstable/process"
|
||||
import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner"
|
||||
import { CrossSpawnSpawner } from "../cross-spawn-spawner"
|
||||
import { makeGlobalNode } from "../effect/app-node"
|
||||
import { httpClient } from "../effect/app-node-platform"
|
||||
import { makeGlobalNode } from "../effect/node"
|
||||
import { httpClient } from "../effect/layer-node-platform"
|
||||
import { FSUtil } from "../fs-util"
|
||||
import { Global } from "../global"
|
||||
import { which } from "../util/which"
|
||||
|
||||
@@ -27,7 +27,7 @@ import { fromRow } from "./session/info"
|
||||
import { SessionRunner } from "./session/runner/index"
|
||||
import { SessionStore } from "./session/store"
|
||||
import { SessionExecution } from "./session/execution"
|
||||
import { makeGlobalNode } from "./effect/app-node"
|
||||
import { makeGlobalNode } from "./effect/node"
|
||||
import { LocationServiceMap } from "./location-service-map"
|
||||
import { MessageDecodeError } from "./session/error"
|
||||
import { SessionEvent } from "./session/event"
|
||||
@@ -400,13 +400,7 @@ export const layer = Layer.effect(
|
||||
})
|
||||
}),
|
||||
switchModel: Effect.fn("V2Session.switchModel")(function* (input) {
|
||||
const session = yield* result.get(input.sessionID)
|
||||
if (
|
||||
session.model?.providerID === input.model.providerID &&
|
||||
session.model.id === input.model.id &&
|
||||
(session.model.variant ?? "default") === (input.model.variant ?? "default")
|
||||
)
|
||||
return
|
||||
yield* result.get(input.sessionID)
|
||||
yield* events.publish(SessionEvent.ModelSwitched, {
|
||||
sessionID: input.sessionID,
|
||||
messageID: SessionMessage.ID.create(),
|
||||
|
||||
@@ -2,7 +2,7 @@ export * as SessionExecution from "./execution"
|
||||
|
||||
import { Context, Effect, Layer } from "effect"
|
||||
import { LayerNode } from "../effect/layer-node"
|
||||
import { Node } from "../effect/app-node"
|
||||
import { Node } from "../effect/node"
|
||||
import { SessionRunner } from "./runner/index"
|
||||
import { SessionSchema } from "./schema"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Cause, Effect, Layer } from "effect"
|
||||
import { LocationServiceMap } from "../../location-service-map"
|
||||
import { makeGlobalNode } from "../../effect/app-node"
|
||||
import { makeGlobalNode } from "../../effect/node"
|
||||
import { SessionRunCoordinator } from "../run-coordinator"
|
||||
import { SessionRunner } from "../runner"
|
||||
import { SessionSchema } from "../schema"
|
||||
|
||||
@@ -349,7 +349,6 @@ export function update(adapter: Adapter, event: SessionEvent.Event) {
|
||||
id: event.data.reasoningID,
|
||||
text: "",
|
||||
providerMetadata: event.data.providerMetadata,
|
||||
time: { created: event.data.timestamp },
|
||||
}),
|
||||
),
|
||||
)
|
||||
@@ -366,7 +365,6 @@ export function update(adapter: Adapter, event: SessionEvent.Event) {
|
||||
const match = latestReasoning(draft, event.data.reasoningID)
|
||||
if (match) {
|
||||
match.text = event.data.text
|
||||
match.time = { created: match.time?.created ?? event.data.timestamp, completed: event.data.timestamp }
|
||||
if (event.data.providerMetadata !== undefined) match.providerMetadata = event.data.providerMetadata
|
||||
}
|
||||
})
|
||||
|
||||
@@ -4,7 +4,7 @@ import { and, desc, eq, gt, or, sql } from "drizzle-orm"
|
||||
import { DateTime, Effect, Layer, Schema } from "effect"
|
||||
import { Database } from "../database/database"
|
||||
import { EventV2 } from "../event"
|
||||
import { makeGlobalNode } from "../effect/app-node"
|
||||
import { makeGlobalNode } from "../effect/node"
|
||||
import { SessionEvent } from "./event"
|
||||
import { SessionV1 } from "../v1/session"
|
||||
import { WorkspaceTable } from "../control-plane/workspace.sql"
|
||||
|
||||
@@ -36,8 +36,8 @@ import { createLLMEventPublisher } from "./publish-llm-event"
|
||||
import { toLLMMessages } from "./to-llm-message"
|
||||
import { MAX_STEPS_PROMPT } from "./max-steps"
|
||||
import { Snapshot } from "../../snapshot"
|
||||
import { makeLocationNode } from "../../effect/app-node"
|
||||
import { llmClient } from "../../effect/app-node-platform"
|
||||
import { makeLocationNode } from "../../effect/node"
|
||||
import { llmClient } from "../../effect/layer-node-platform"
|
||||
|
||||
/**
|
||||
* Runs one durable coding-agent Session until it settles.
|
||||
@@ -335,8 +335,7 @@ export const layer = Layer.effect(
|
||||
if (stream._tag === "Success" && !publisher.hasProviderError())
|
||||
yield* withPublication(publisher.failUnsettledTools("Provider did not return a tool result", true))
|
||||
if (stream._tag === "Failure") return yield* Effect.failCause(stream.cause)
|
||||
if (settled._tag === "Failure" && Cause.hasInterrupts(settled.cause))
|
||||
return yield* Effect.failCause(settled.cause)
|
||||
if (settled._tag === "Failure") return yield* Effect.failCause(settled.cause)
|
||||
return { needsContinuation: !publisher.hasProviderError() && needsContinuation, step: currentStep }
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * as SessionRunnerModel from "./model"
|
||||
|
||||
import { makeLocationNode } from "../../effect/app-node"
|
||||
import { makeLocationNode } from "../../effect/node"
|
||||
import { type Model } from "@opencode-ai/llm"
|
||||
import * as AnthropicMessages from "@opencode-ai/llm/protocols/anthropic-messages"
|
||||
import * as OpenAICompatibleChat from "@opencode-ai/llm/protocols/openai-compatible-chat"
|
||||
@@ -133,7 +133,7 @@ export const fromCatalogModel = (
|
||||
credential?: Credential.Value,
|
||||
): Effect.Effect<Model, UnsupportedApiError> => {
|
||||
const resolved =
|
||||
credential?.type !== "key" || credential.metadata === undefined
|
||||
credential?.metadata === undefined
|
||||
? model
|
||||
: produce(model, (draft) => {
|
||||
Object.assign(draft.request.body, credential.metadata)
|
||||
|
||||
@@ -3,7 +3,7 @@ export * as SessionStore from "./store"
|
||||
import { eq } from "drizzle-orm"
|
||||
import { Context, Effect, Layer, Schema } from "effect"
|
||||
import { Database } from "../database/database"
|
||||
import { makeGlobalNode } from "../effect/app-node"
|
||||
import { makeGlobalNode } from "../effect/node"
|
||||
import { SessionHistory } from "./history"
|
||||
import { MessageDecodeError } from "./error"
|
||||
import { SessionMessage } from "./message"
|
||||
|
||||
@@ -4,7 +4,7 @@ import { asc, eq } from "drizzle-orm"
|
||||
import { Context, Effect, Layer } from "effect"
|
||||
import { SessionTodo } from "@opencode-ai/schema/session-todo"
|
||||
import { Database } from "../database/database"
|
||||
import { makeLocationNode } from "../effect/app-node"
|
||||
import { makeLocationNode } from "../effect/node"
|
||||
import { EventV2 } from "../event"
|
||||
import { SessionSchema } from "./schema"
|
||||
import { TodoTable } from "./sql"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * as SkillV2 from "./skill"
|
||||
|
||||
import { makeLocationNode } from "./effect/app-node"
|
||||
import { makeLocationNode } from "./effect/node"
|
||||
import path from "path"
|
||||
import { Context, Effect, Layer, Schema, Types } from "effect"
|
||||
import { Skill } from "@opencode-ai/schema/skill"
|
||||
|
||||
@@ -5,8 +5,8 @@ import { Context, Effect, Layer, Schedule, Schema } from "effect"
|
||||
import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
|
||||
import { FSUtil } from "../fs-util"
|
||||
import { Global } from "../global"
|
||||
import { makeGlobalNode } from "../effect/app-node"
|
||||
import { httpClient } from "../effect/app-node-platform"
|
||||
import { makeGlobalNode } from "../effect/node"
|
||||
import { httpClient } from "../effect/layer-node-platform"
|
||||
import { AbsolutePath } from "../schema"
|
||||
|
||||
const skillConcurrency = 4
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * as SkillGuidance from "./guidance"
|
||||
|
||||
import { makeLocationNode } from "../effect/app-node"
|
||||
import { makeLocationNode } from "../effect/node"
|
||||
import { Context, Effect, Layer, Schema } from "effect"
|
||||
import { AgentV2 } from "../agent"
|
||||
import { PermissionV2 } from "../permission"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * as Snapshot from "./snapshot"
|
||||
|
||||
import { makeLocationNode } from "./effect/app-node"
|
||||
import { makeLocationNode } from "./effect/node"
|
||||
import path from "path"
|
||||
import { Context, Effect, Layer, Schema } from "effect"
|
||||
import { Config } from "./config"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * as SystemContextBuiltIns from "./builtins"
|
||||
|
||||
import { makeLocationNode } from "../effect/app-node"
|
||||
import { makeLocationNode } from "../effect/node"
|
||||
import { DateTime, Effect, Layer, Schema } from "effect"
|
||||
import { Location } from "../location"
|
||||
import { SystemContext } from "./index"
|
||||
|
||||
@@ -2,7 +2,7 @@ export * as SystemContextRegistry from "./registry"
|
||||
|
||||
import { Context, Effect, Layer, Ref, Scope } from "effect"
|
||||
import { SystemContext } from "./index"
|
||||
import { makeLocationNode } from "../effect/app-node"
|
||||
import { makeLocationNode } from "../effect/node"
|
||||
|
||||
export interface Entry {
|
||||
readonly key: SystemContext.Key
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Context, Duration, Effect, Layer, Option, Schedule, Schema } from "effe
|
||||
import { Config } from "./config"
|
||||
import { FSUtil } from "./fs-util"
|
||||
import { Global } from "./global"
|
||||
import { makeGlobalNode, makeLocationNode } from "./effect/app-node"
|
||||
import { makeGlobalNode, makeLocationNode } from "./effect/node"
|
||||
import { SessionSchema } from "./session/schema"
|
||||
import { Identifier } from "./util/identifier"
|
||||
import type { ToolOutput } from "@opencode-ai/llm"
|
||||
|
||||
@@ -3,7 +3,7 @@ export * as ApplicationTools from "./application-tools"
|
||||
import { Context, Effect, Layer, Scope } from "effect"
|
||||
import { State } from "../state"
|
||||
import { Tool } from "./tool"
|
||||
import { makeGlobalNode } from "../effect/app-node"
|
||||
import { makeGlobalNode } from "../effect/node"
|
||||
|
||||
type Data = {
|
||||
readonly entries: Map<string, Entry>
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
export * as ApplyPatchTool from "./apply-patch"
|
||||
|
||||
import { ToolFailure } from "@opencode-ai/llm"
|
||||
import { FileDiff } from "@opencode-ai/schema/file-diff"
|
||||
import { createTwoFilesPatch, diffLines } from "diff"
|
||||
import { Effect, Layer, Schema } from "effect"
|
||||
import { FileMutation } from "../file-mutation"
|
||||
import { FSUtil } from "../fs-util"
|
||||
@@ -26,10 +24,7 @@ export const Applied = Schema.Struct({
|
||||
target: Schema.String,
|
||||
})
|
||||
|
||||
export const Output = Schema.Struct({
|
||||
applied: Schema.Array(Applied),
|
||||
files: Schema.Array(FileDiff.Info),
|
||||
})
|
||||
export const Output = Schema.Struct({ applied: Schema.Array(Applied) })
|
||||
export type Output = typeof Output.Type
|
||||
|
||||
export const toModelOutput = (output: Output) =>
|
||||
@@ -41,17 +36,11 @@ export const toModelOutput = (output: Output) =>
|
||||
].join("\n")
|
||||
|
||||
type Prepared =
|
||||
| (Extract<Patch.Hunk, { readonly type: "add" | "delete" }> & {
|
||||
readonly target: LocationMutation.Target
|
||||
readonly before: string
|
||||
readonly after: string
|
||||
})
|
||||
| (Extract<Patch.Hunk, { readonly type: "add" | "delete" }> & { readonly target: LocationMutation.Target })
|
||||
| (Extract<Patch.Hunk, { readonly type: "update" }> & {
|
||||
readonly target: LocationMutation.Target
|
||||
readonly source: Uint8Array
|
||||
readonly content: string
|
||||
readonly before: string
|
||||
readonly after: string
|
||||
})
|
||||
|
||||
export const layer = Layer.effectDiscard(
|
||||
@@ -124,36 +113,29 @@ export const layer = Layer.effectDiscard(
|
||||
for (const { hunk, target } of targets) {
|
||||
yield* Effect.gen(function* () {
|
||||
if (hunk.type === "add") {
|
||||
prepared.push({
|
||||
...hunk,
|
||||
target,
|
||||
before: "",
|
||||
after:
|
||||
hunk.contents.endsWith("\n") || hunk.contents === "" ? hunk.contents : `${hunk.contents}\n`,
|
||||
})
|
||||
prepared.push({ ...hunk, target })
|
||||
return
|
||||
}
|
||||
if ((yield* fs.stat(target.canonical)).type !== "File") yield* fail(hunk.path)
|
||||
const source = yield* fs.readFile(target.canonical)
|
||||
const original = new TextDecoder("utf-8", { ignoreBOM: true }).decode(source)
|
||||
const before = original.replace(/^\uFEFF/, "")
|
||||
if (hunk.type === "delete") {
|
||||
prepared.push({ ...hunk, target, before, after: "" })
|
||||
prepared.push({ ...hunk, target })
|
||||
return
|
||||
}
|
||||
const update = Patch.derive(hunk.path, hunk.chunks, original)
|
||||
const source = yield* fs.readFile(target.canonical)
|
||||
const update = Patch.derive(
|
||||
hunk.path,
|
||||
hunk.chunks,
|
||||
new TextDecoder("utf-8", { ignoreBOM: true }).decode(source),
|
||||
)
|
||||
prepared.push({
|
||||
...hunk,
|
||||
target,
|
||||
source,
|
||||
content: Patch.joinBom(update.content, update.bom),
|
||||
before,
|
||||
after: update.content,
|
||||
})
|
||||
}).pipe(Effect.mapError(() => fail(hunk.path)))
|
||||
}
|
||||
|
||||
const patchFiles = prepared.map(patchFile)
|
||||
yield* Effect.forEach(
|
||||
prepared,
|
||||
(change) =>
|
||||
@@ -183,7 +165,7 @@ export const layer = Layer.effectDiscard(
|
||||
}).pipe(Effect.mapError(() => fail(change.path))),
|
||||
{ discard: true },
|
||||
)
|
||||
return { applied, files: patchFiles }
|
||||
return { applied }
|
||||
}).pipe(Effect.mapError((error) => (error instanceof ToolFailure ? error : fail("patch"))))
|
||||
},
|
||||
}),
|
||||
@@ -193,19 +175,3 @@ export const layer = Layer.effectDiscard(
|
||||
.pipe(Effect.orDie)
|
||||
}),
|
||||
)
|
||||
|
||||
function patchFile(change: Prepared): typeof FileDiff.Info.Type {
|
||||
const counts = diffLines(change.before, change.after).reduce(
|
||||
(result, item) => ({
|
||||
additions: result.additions + (item.added ? (item.count ?? 0) : 0),
|
||||
deletions: result.deletions + (item.removed ? (item.count ?? 0) : 0),
|
||||
}),
|
||||
{ additions: 0, deletions: 0 },
|
||||
)
|
||||
return {
|
||||
file: change.target.resource,
|
||||
patch: createTwoFilesPatch(change.target.resource, change.target.resource, change.before, change.after),
|
||||
status: change.type === "add" ? "added" : change.type === "delete" ? "deleted" : "modified",
|
||||
...counts,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,15 +30,16 @@ export const Input = Schema.Struct({
|
||||
}),
|
||||
})
|
||||
|
||||
const StructuredOutput = Schema.Struct({
|
||||
exit: Schema.Number.pipe(Schema.optional),
|
||||
truncated: Schema.Boolean,
|
||||
timeout: Schema.Boolean.pipe(Schema.optional),
|
||||
})
|
||||
|
||||
const Output = Schema.Struct({
|
||||
...StructuredOutput.fields,
|
||||
command: Schema.String,
|
||||
cwd: Schema.String,
|
||||
exitCode: Schema.Number.pipe(Schema.optional),
|
||||
/** Bounded compact equivalent of stdout/stderr: stderr is labeled when present. */
|
||||
output: Schema.String,
|
||||
truncated: Schema.Boolean,
|
||||
stdoutTruncated: Schema.Boolean.pipe(Schema.optional),
|
||||
stderrTruncated: Schema.Boolean.pipe(Schema.optional),
|
||||
timedOut: Schema.Boolean.pipe(Schema.optional),
|
||||
warnings: Schema.Array(Schema.String).pipe(Schema.optional),
|
||||
})
|
||||
|
||||
@@ -46,12 +47,24 @@ type Output = typeof Output.Type
|
||||
|
||||
const defaultShell = () => (process.platform === "win32" ? (process.env.COMSPEC ?? "cmd.exe") : "/bin/sh")
|
||||
|
||||
const compactOutput = (stdout: string, stderr: string) => {
|
||||
const output = stdout && stderr ? `${stdout}\n\nstderr:\n${stderr}` : stderr ? `stderr:\n${stderr}` : stdout
|
||||
return output || "(no output)"
|
||||
}
|
||||
|
||||
const captureNotice = (stdoutTruncated: boolean, stderrTruncated: boolean) => {
|
||||
if (stdoutTruncated && stderrTruncated) return "[stdout and stderr capture truncated at the in-memory safety limit]"
|
||||
if (stdoutTruncated) return "[stdout capture truncated at the in-memory safety limit]"
|
||||
if (stderrTruncated) return "[stderr capture truncated at the in-memory safety limit]"
|
||||
return undefined
|
||||
}
|
||||
|
||||
const modelOutput = (output: Output) => {
|
||||
const warnings = output.warnings?.length
|
||||
? `\n\nWarnings:\n${output.warnings.map((warning) => `- ${warning}`).join("\n")}`
|
||||
: ""
|
||||
if (output.timeout) return `${warnings.trimStart()}${warnings ? "\n\n" : ""}Command timed out before completion.`
|
||||
return `${warnings.trimStart()}${warnings ? "\n\n" : ""}Command exited with code ${output.exit}.`
|
||||
if (output.timedOut) return `${output.output}${warnings}\n\nCommand timed out before completion.`
|
||||
return `${output.output}${warnings}\n\nCommand exited with code ${output.exitCode}.`
|
||||
}
|
||||
|
||||
const isTimeout = (error: AppProcess.AppProcessError) =>
|
||||
@@ -103,16 +116,7 @@ export const layer = Layer.effectDiscard(
|
||||
description: `Execute one shell command string with the host user's filesystem, process, and network authority. The active Location is the default working directory. Relative workdir values resolve from that Location. External workdir values require external_directory approval; best-effort command-argument path warnings are advisory only. Timeout values are milliseconds (default: ${DEFAULT_TIMEOUT_MS}; maximum: ${MAX_TIMEOUT_MS}). Uses the configured shell when set; otherwise uses /bin/sh on POSIX and COMSPEC or cmd.exe on Windows.`,
|
||||
input: Input,
|
||||
output: Output,
|
||||
structured: StructuredOutput,
|
||||
toStructuredOutput: ({ output }) => ({
|
||||
truncated: output.truncated,
|
||||
...(output.exit === undefined ? {} : { exit: output.exit }),
|
||||
...(output.timeout === undefined ? {} : { timeout: output.timeout }),
|
||||
}),
|
||||
toModelOutput: ({ output }) => [
|
||||
{ type: "text", text: output.output },
|
||||
{ type: "text", text: modelOutput(output) },
|
||||
],
|
||||
toModelOutput: ({ output }) => [{ type: "text", text: modelOutput(output) }],
|
||||
execute: (input, context) =>
|
||||
Effect.gen(function* () {
|
||||
const source = {
|
||||
@@ -159,9 +163,9 @@ export const layer = Layer.effectDiscard(
|
||||
const timeout = input.timeout ?? DEFAULT_TIMEOUT_MS
|
||||
const result = yield* appProcess
|
||||
.run(command, {
|
||||
combineOutput: true,
|
||||
timeout: Duration.millis(timeout),
|
||||
maxOutputBytes: MAX_CAPTURE_BYTES,
|
||||
maxErrorBytes: MAX_CAPTURE_BYTES,
|
||||
})
|
||||
.pipe(
|
||||
Effect.catchTag("AppProcessError", (error) =>
|
||||
@@ -170,22 +174,26 @@ export const layer = Layer.effectDiscard(
|
||||
)
|
||||
if (!result) {
|
||||
return {
|
||||
command: input.command,
|
||||
cwd: target.canonical,
|
||||
output: `Command exceeded timeout of ${timeout} ms. Retry with a larger timeout if the command is expected to take longer.`,
|
||||
truncated: false,
|
||||
timeout: true,
|
||||
timedOut: true,
|
||||
...(warnings.length ? { warnings } : {}),
|
||||
}
|
||||
}
|
||||
|
||||
const output = result.output?.toString("utf8") || "(no output)"
|
||||
const notice = result.outputTruncated
|
||||
? "[output capture truncated at the in-memory safety limit]"
|
||||
: undefined
|
||||
const compact = compactOutput(result.stdout.toString("utf8"), result.stderr.toString("utf8"))
|
||||
const notice = captureNotice(result.stdoutTruncated, result.stderrTruncated)
|
||||
return {
|
||||
exit: result.exitCode,
|
||||
output: notice ? `${output}\n\n${notice}` : output,
|
||||
truncated: result.outputTruncated === true,
|
||||
command: input.command,
|
||||
cwd: target.canonical,
|
||||
exitCode: result.exitCode,
|
||||
output: notice ? `${compact}\n\n${notice}` : compact,
|
||||
truncated: result.stdoutTruncated || result.stderrTruncated,
|
||||
...(warnings.length ? { warnings } : {}),
|
||||
...(result.stdoutTruncated ? { stdoutTruncated: true } : {}),
|
||||
...(result.stderrTruncated ? { stderrTruncated: true } : {}),
|
||||
}
|
||||
}).pipe(Effect.mapError(() => new ToolFailure({ message: `Unable to execute command: ${input.command}` }))),
|
||||
}),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * as BuiltInTools from "./builtins"
|
||||
|
||||
import { makeLocationNode } from "../effect/app-node"
|
||||
import { makeLocationNode } from "../effect/node"
|
||||
import { Layer } from "effect"
|
||||
import { BashTool } from "./bash"
|
||||
import { ApplyPatchTool } from "./apply-patch"
|
||||
@@ -28,7 +28,7 @@ import { QuestionV2 } from "../question"
|
||||
import { SkillV2 } from "../skill"
|
||||
import { SessionTodo } from "../session/todo"
|
||||
import { ToolRegistry } from "./registry"
|
||||
import { httpClient } from "../effect/app-node-platform"
|
||||
import { httpClient } from "../effect/layer-node-platform"
|
||||
|
||||
/**
|
||||
* Composes only the shipped Location-scoped built-in tool transforms.
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
export * as EditTool from "./edit"
|
||||
|
||||
import { ToolFailure } from "@opencode-ai/llm"
|
||||
import { FileDiff } from "@opencode-ai/schema/file-diff"
|
||||
import { createTwoFilesPatch, diffLines } from "diff"
|
||||
import { Effect, Layer, Schema } from "effect"
|
||||
import { FileMutation } from "../file-mutation"
|
||||
import { FSUtil } from "../fs-util"
|
||||
@@ -32,7 +30,10 @@ export const Input = Schema.Struct({
|
||||
})
|
||||
|
||||
export const Output = Schema.Struct({
|
||||
files: Schema.Array(FileDiff.Info),
|
||||
operation: Schema.Literal("write"),
|
||||
target: Schema.String,
|
||||
resource: Schema.String,
|
||||
existed: Schema.Boolean,
|
||||
replacements: Schema.Number,
|
||||
})
|
||||
export type Output = typeof Output.Type
|
||||
@@ -70,7 +71,7 @@ const previewLines = (value: string, prefix: "+" | "-") => {
|
||||
|
||||
export const toModelOutput = (output: Output, oldString: string, newString: string) =>
|
||||
[
|
||||
`Edited file successfully: ${output.files[0]?.file}`,
|
||||
`Edited file successfully: ${output.resource}`,
|
||||
`Replacements: ${output.replacements}`,
|
||||
"```diff",
|
||||
...previewLines(oldString, "-"),
|
||||
@@ -178,13 +179,6 @@ export const layer = Layer.effectDiscard(
|
||||
input.replaceAll === true
|
||||
? source.text.replaceAll(oldString, newString)
|
||||
: source.text.replace(oldString, newString)
|
||||
const counts = diffLines(source.text, replaced).reduce(
|
||||
(result, item) => ({
|
||||
additions: result.additions + (item.added ? (item.count ?? 0) : 0),
|
||||
deletions: result.deletions + (item.removed ? (item.count ?? 0) : 0),
|
||||
}),
|
||||
{ additions: 0, deletions: 0 },
|
||||
)
|
||||
const next = splitBom(replaced)
|
||||
const result = yield* unableToEdit(
|
||||
files.writeIfUnchanged({
|
||||
@@ -193,17 +187,7 @@ export const layer = Layer.effectDiscard(
|
||||
content: joinBom(next.text, source.bom || next.bom),
|
||||
}),
|
||||
)
|
||||
return {
|
||||
files: [
|
||||
{
|
||||
file: result.resource,
|
||||
patch: createTwoFilesPatch(result.resource, result.resource, source.text, replaced),
|
||||
status: "modified" as const,
|
||||
...counts,
|
||||
},
|
||||
],
|
||||
replacements,
|
||||
} satisfies Output
|
||||
return { ...result, replacements } satisfies Output
|
||||
})
|
||||
},
|
||||
}),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user