Compare commits
53 commits
dev
...
feature/wo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fcc903489b | ||
|
|
949e69a9bf |
||
|
|
8c30f551e2 |
||
|
|
cb721497c1 | ||
|
|
4ec6293054 | ||
|
|
b7a323355c | ||
|
|
d4f053042c | ||
|
|
5f552534c7 | ||
|
|
ad5b790bb3 | ||
|
|
ed87341c4f | ||
|
|
794ecab028 | ||
|
|
eeb235724b | ||
|
|
61084e7f6f | ||
|
|
200aef2eb3 | ||
|
|
f6e375a555 | ||
|
|
db908deee5 | ||
|
|
7b72cc3a48 | ||
|
|
b8cbfd48ec | ||
|
|
498cbb2c26 | ||
|
|
d6fbd255b6 | ||
|
|
2de1c82bf7 | ||
|
|
34ebb3d051 | ||
|
|
9c3e3c1ab5 | ||
|
|
3ea499f04e | ||
|
|
ab13c1d1c4 | ||
|
|
53b610c331 | ||
|
|
e3519356f2 | ||
|
|
2619acc0ff | ||
|
|
1bc45dc266 | ||
|
|
2e8feb1c78 | ||
|
|
00e60899cc | ||
|
|
30a918e9d4 | ||
|
|
ac16068140 | ||
|
|
19a41ab297 | ||
|
|
cd174d8cba | ||
|
|
246e901e42 | ||
|
|
0ccef1b31f | ||
|
|
7706f5b6a8 | ||
|
|
63e38555c9 | ||
|
|
f40685ab13 | ||
|
|
a48a5a3462 | ||
|
|
5e1639de2b | ||
|
|
2b05833c32 | ||
|
|
acdcf7fa88 | ||
|
|
bf0754caeb | ||
|
|
4d50a32979 | ||
|
|
57edb0ddc5 | ||
|
|
a614b78c6d | ||
|
|
b9f5a34247 | ||
|
|
81b47a44e2 | ||
|
|
0c1c07467e | ||
|
|
105688bf90 | ||
|
|
1e7b4768b1 |
52 changed files with 3221 additions and 797 deletions
|
|
@ -16,15 +16,12 @@ wip:
|
||||||
|
|
||||||
For anything in the packages/web use the docs: prefix.
|
For anything in the packages/web use the docs: prefix.
|
||||||
|
|
||||||
For anything in the packages/app use the ignore: prefix.
|
|
||||||
|
|
||||||
prefer to explain WHY something was done from an end user perspective instead of
|
prefer to explain WHY something was done from an end user perspective instead of
|
||||||
WHAT was done.
|
WHAT was done.
|
||||||
|
|
||||||
do not do generic messages like "improved agent experience" be very specific
|
do not do generic messages like "improved agent experience" be very specific
|
||||||
about what user facing changes were made
|
about what user facing changes were made
|
||||||
|
|
||||||
if there are changes do a git pull --rebase
|
|
||||||
if there are conflicts DO NOT FIX THEM. notify me and I will fix them
|
if there are conflicts DO NOT FIX THEM. notify me and I will fix them
|
||||||
|
|
||||||
## GIT DIFF
|
## GIT DIFF
|
||||||
|
|
|
||||||
243
bun.lock
243
bun.lock
|
|
@ -115,7 +115,7 @@
|
||||||
"@opencode-ai/console-resource": "workspace:*",
|
"@opencode-ai/console-resource": "workspace:*",
|
||||||
"@planetscale/database": "1.19.0",
|
"@planetscale/database": "1.19.0",
|
||||||
"aws4fetch": "1.0.20",
|
"aws4fetch": "1.0.20",
|
||||||
"drizzle-orm": "0.41.0",
|
"drizzle-orm": "catalog:",
|
||||||
"postgres": "3.4.7",
|
"postgres": "3.4.7",
|
||||||
"stripe": "18.0.0",
|
"stripe": "18.0.0",
|
||||||
"ulid": "catalog:",
|
"ulid": "catalog:",
|
||||||
|
|
@ -127,7 +127,7 @@
|
||||||
"@types/bun": "1.3.0",
|
"@types/bun": "1.3.0",
|
||||||
"@types/node": "catalog:",
|
"@types/node": "catalog:",
|
||||||
"@typescript/native-preview": "catalog:",
|
"@typescript/native-preview": "catalog:",
|
||||||
"drizzle-kit": "0.30.5",
|
"drizzle-kit": "catalog:",
|
||||||
"mysql2": "3.14.4",
|
"mysql2": "3.14.4",
|
||||||
"typescript": "catalog:",
|
"typescript": "catalog:",
|
||||||
},
|
},
|
||||||
|
|
@ -313,6 +313,7 @@
|
||||||
"clipboardy": "4.0.0",
|
"clipboardy": "4.0.0",
|
||||||
"decimal.js": "10.5.0",
|
"decimal.js": "10.5.0",
|
||||||
"diff": "catalog:",
|
"diff": "catalog:",
|
||||||
|
"drizzle-orm": "1.0.0-beta.12-a5629fb",
|
||||||
"fuzzysort": "3.1.0",
|
"fuzzysort": "3.1.0",
|
||||||
"gray-matter": "4.0.3",
|
"gray-matter": "4.0.3",
|
||||||
"hono": "catalog:",
|
"hono": "catalog:",
|
||||||
|
|
@ -354,6 +355,8 @@
|
||||||
"@types/turndown": "5.0.5",
|
"@types/turndown": "5.0.5",
|
||||||
"@types/yargs": "17.0.33",
|
"@types/yargs": "17.0.33",
|
||||||
"@typescript/native-preview": "catalog:",
|
"@typescript/native-preview": "catalog:",
|
||||||
|
"drizzle-kit": "1.0.0-beta.12-a5629fb",
|
||||||
|
"drizzle-orm": "1.0.0-beta.12-a5629fb",
|
||||||
"typescript": "catalog:",
|
"typescript": "catalog:",
|
||||||
"vscode-languageserver-types": "3.17.5",
|
"vscode-languageserver-types": "3.17.5",
|
||||||
"why-is-node-running": "3.2.2",
|
"why-is-node-running": "3.2.2",
|
||||||
|
|
@ -524,6 +527,8 @@
|
||||||
"ai": "5.0.124",
|
"ai": "5.0.124",
|
||||||
"diff": "8.0.2",
|
"diff": "8.0.2",
|
||||||
"dompurify": "3.3.1",
|
"dompurify": "3.3.1",
|
||||||
|
"drizzle-kit": "1.0.0-beta.12-a5629fb",
|
||||||
|
"drizzle-orm": "1.0.0-beta.12-a5629fb",
|
||||||
"fuzzysort": "3.1.0",
|
"fuzzysort": "3.1.0",
|
||||||
"hono": "4.10.7",
|
"hono": "4.10.7",
|
||||||
"hono-openapi": "1.1.2",
|
"hono-openapi": "1.1.2",
|
||||||
|
|
@ -707,6 +712,8 @@
|
||||||
|
|
||||||
"@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.3", "", {}, "sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw=="],
|
"@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.3", "", {}, "sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw=="],
|
||||||
|
|
||||||
|
"@azure-rest/core-client": ["@azure-rest/core-client@2.5.1", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.10.0", "@azure/core-rest-pipeline": "^1.22.0", "@azure/core-tracing": "^1.3.0", "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-EHaOXW0RYDKS5CFffnixdyRPak5ytiCtU7uXDcP/uiY+A6jFRwNGzzJBiznkCzvi5EYpY+YWinieqHb0oY916A=="],
|
||||||
|
|
||||||
"@azure/abort-controller": ["@azure/abort-controller@2.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="],
|
"@azure/abort-controller": ["@azure/abort-controller@2.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="],
|
||||||
|
|
||||||
"@azure/core-auth": ["@azure/core-auth@1.10.1", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-util": "^1.13.0", "tslib": "^2.6.2" } }, "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg=="],
|
"@azure/core-auth": ["@azure/core-auth@1.10.1", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-util": "^1.13.0", "tslib": "^2.6.2" } }, "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg=="],
|
||||||
|
|
@ -729,8 +736,20 @@
|
||||||
|
|
||||||
"@azure/core-xml": ["@azure/core-xml@1.5.0", "", { "dependencies": { "fast-xml-parser": "^5.0.7", "tslib": "^2.8.1" } }, "sha512-D/sdlJBMJfx7gqoj66PKVmhDDaU6TKA49ptcolxdas29X7AfvLTmfAGLjAcIMBK7UZ2o4lygHIqVckOlQU3xWw=="],
|
"@azure/core-xml": ["@azure/core-xml@1.5.0", "", { "dependencies": { "fast-xml-parser": "^5.0.7", "tslib": "^2.8.1" } }, "sha512-D/sdlJBMJfx7gqoj66PKVmhDDaU6TKA49ptcolxdas29X7AfvLTmfAGLjAcIMBK7UZ2o4lygHIqVckOlQU3xWw=="],
|
||||||
|
|
||||||
|
"@azure/identity": ["@azure/identity@4.13.0", "", { "dependencies": { "@azure/abort-controller": "^2.0.0", "@azure/core-auth": "^1.9.0", "@azure/core-client": "^1.9.2", "@azure/core-rest-pipeline": "^1.17.0", "@azure/core-tracing": "^1.0.0", "@azure/core-util": "^1.11.0", "@azure/logger": "^1.0.0", "@azure/msal-browser": "^4.2.0", "@azure/msal-node": "^3.5.0", "open": "^10.1.0", "tslib": "^2.2.0" } }, "sha512-uWC0fssc+hs1TGGVkkghiaFkkS7NkTxfnCH+Hdg+yTehTpMcehpok4PgUKKdyCH+9ldu6FhiHRv84Ntqj1vVcw=="],
|
||||||
|
|
||||||
|
"@azure/keyvault-common": ["@azure/keyvault-common@2.0.0", "", { "dependencies": { "@azure/abort-controller": "^2.0.0", "@azure/core-auth": "^1.3.0", "@azure/core-client": "^1.5.0", "@azure/core-rest-pipeline": "^1.8.0", "@azure/core-tracing": "^1.0.0", "@azure/core-util": "^1.10.0", "@azure/logger": "^1.1.4", "tslib": "^2.2.0" } }, "sha512-wRLVaroQtOqfg60cxkzUkGKrKMsCP6uYXAOomOIysSMyt1/YM0eUn9LqieAWM8DLcU4+07Fio2YGpPeqUbpP9w=="],
|
||||||
|
|
||||||
|
"@azure/keyvault-keys": ["@azure/keyvault-keys@4.10.0", "", { "dependencies": { "@azure-rest/core-client": "^2.3.3", "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.9.0", "@azure/core-http-compat": "^2.2.0", "@azure/core-lro": "^2.7.2", "@azure/core-paging": "^1.6.2", "@azure/core-rest-pipeline": "^1.19.0", "@azure/core-tracing": "^1.2.0", "@azure/core-util": "^1.11.0", "@azure/keyvault-common": "^2.0.0", "@azure/logger": "^1.1.4", "tslib": "^2.8.1" } }, "sha512-eDT7iXoBTRZ2n3fLiftuGJFD+yjkiB1GNqzU2KbY1TLYeXeSPVTVgn2eJ5vmRTZ11978jy2Kg2wI7xa9Tyr8ag=="],
|
||||||
|
|
||||||
"@azure/logger": ["@azure/logger@1.3.0", "", { "dependencies": { "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA=="],
|
"@azure/logger": ["@azure/logger@1.3.0", "", { "dependencies": { "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA=="],
|
||||||
|
|
||||||
|
"@azure/msal-browser": ["@azure/msal-browser@4.28.1", "", { "dependencies": { "@azure/msal-common": "15.14.1" } }, "sha512-al2u2fTchbClq3L4C1NlqLm+vwKfhYCPtZN2LR/9xJVaQ4Mnrwf5vANvuyPSJHcGvw50UBmhuVmYUAhTEetTpA=="],
|
||||||
|
|
||||||
|
"@azure/msal-common": ["@azure/msal-common@15.14.1", "", {}, "sha512-IkzF7Pywt6QKTS0kwdCv/XV8x8JXknZDvSjj/IccooxnP373T5jaadO3FnOrbWo3S0UqkfIDyZNTaQ/oAgRdXw=="],
|
||||||
|
|
||||||
|
"@azure/msal-node": ["@azure/msal-node@3.8.6", "", { "dependencies": { "@azure/msal-common": "15.14.1", "jsonwebtoken": "^9.0.0", "uuid": "^8.3.0" } }, "sha512-XTmhdItcBckcVVTy65Xp+42xG4LX5GK+9AqAsXPXk4IqUNv+LyQo5TMwNjuFYBfAB2GTG9iSQGk+QLc03vhf3w=="],
|
||||||
|
|
||||||
"@azure/storage-blob": ["@azure/storage-blob@12.30.0", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.9.0", "@azure/core-client": "^1.9.3", "@azure/core-http-compat": "^2.2.0", "@azure/core-lro": "^2.2.0", "@azure/core-paging": "^1.6.2", "@azure/core-rest-pipeline": "^1.19.1", "@azure/core-tracing": "^1.2.0", "@azure/core-util": "^1.11.0", "@azure/core-xml": "^1.4.5", "@azure/logger": "^1.1.4", "@azure/storage-common": "^12.2.0", "events": "^3.0.0", "tslib": "^2.8.1" } }, "sha512-peDCR8blSqhsAKDbpSP/o55S4sheNwSrblvCaHUZ5xUI73XA7ieUGGwrONgD/Fng0EoDe1VOa3fAQ7+WGB3Ocg=="],
|
"@azure/storage-blob": ["@azure/storage-blob@12.30.0", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.9.0", "@azure/core-client": "^1.9.3", "@azure/core-http-compat": "^2.2.0", "@azure/core-lro": "^2.2.0", "@azure/core-paging": "^1.6.2", "@azure/core-rest-pipeline": "^1.19.1", "@azure/core-tracing": "^1.2.0", "@azure/core-util": "^1.11.0", "@azure/core-xml": "^1.4.5", "@azure/logger": "^1.1.4", "@azure/storage-common": "^12.2.0", "events": "^3.0.0", "tslib": "^2.8.1" } }, "sha512-peDCR8blSqhsAKDbpSP/o55S4sheNwSrblvCaHUZ5xUI73XA7ieUGGwrONgD/Fng0EoDe1VOa3fAQ7+WGB3Ocg=="],
|
||||||
|
|
||||||
"@azure/storage-common": ["@azure/storage-common@12.2.0", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.9.0", "@azure/core-http-compat": "^2.2.0", "@azure/core-rest-pipeline": "^1.19.1", "@azure/core-tracing": "^1.2.0", "@azure/core-util": "^1.11.0", "@azure/logger": "^1.1.4", "events": "^3.3.0", "tslib": "^2.8.1" } }, "sha512-YZLxiJ3vBAAnFbG3TFuAMUlxZRexjQX5JDQxOkFGb6e2TpoxH3xyHI6idsMe/QrWtj41U/KoqBxlayzhS+LlwA=="],
|
"@azure/storage-common": ["@azure/storage-common@12.2.0", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.9.0", "@azure/core-http-compat": "^2.2.0", "@azure/core-rest-pipeline": "^1.19.1", "@azure/core-tracing": "^1.2.0", "@azure/core-util": "^1.11.0", "@azure/logger": "^1.1.4", "events": "^3.3.0", "tslib": "^2.8.1" } }, "sha512-YZLxiJ3vBAAnFbG3TFuAMUlxZRexjQX5JDQxOkFGb6e2TpoxH3xyHI6idsMe/QrWtj41U/KoqBxlayzhS+LlwA=="],
|
||||||
|
|
@ -835,7 +854,7 @@
|
||||||
|
|
||||||
"@dot/log": ["@dot/log@0.1.5", "", { "dependencies": { "chalk": "^4.1.2", "loglevelnext": "^6.0.0", "p-defer": "^3.0.0" } }, "sha512-ECraEVJWv2f2mWK93lYiefUkphStVlKD6yKDzisuoEmxuLKrxO9iGetHK2DoEAkj7sxjE886n0OUVVCUx0YPNg=="],
|
"@dot/log": ["@dot/log@0.1.5", "", { "dependencies": { "chalk": "^4.1.2", "loglevelnext": "^6.0.0", "p-defer": "^3.0.0" } }, "sha512-ECraEVJWv2f2mWK93lYiefUkphStVlKD6yKDzisuoEmxuLKrxO9iGetHK2DoEAkj7sxjE886n0OUVVCUx0YPNg=="],
|
||||||
|
|
||||||
"@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="],
|
"@drizzle-team/brocli": ["@drizzle-team/brocli@0.11.0", "", {}, "sha512-hD3pekGiPg0WPCCGAZmusBBJsDqGUR66Y452YgQsZOnkdQ7ViEPKuyP4huUGEZQefp8g34RRodXYmJ2TbCH+tg=="],
|
||||||
|
|
||||||
"@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="],
|
"@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="],
|
||||||
|
|
||||||
|
|
@ -847,10 +866,6 @@
|
||||||
|
|
||||||
"@emotion/memoize": ["@emotion/memoize@0.7.4", "", {}, "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw=="],
|
"@emotion/memoize": ["@emotion/memoize@0.7.4", "", {}, "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw=="],
|
||||||
|
|
||||||
"@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="],
|
|
||||||
|
|
||||||
"@esbuild-kit/esm-loader": ["@esbuild-kit/esm-loader@2.6.5", "", { "dependencies": { "@esbuild-kit/core-utils": "^3.3.2", "get-tsconfig": "^4.7.0" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="],
|
|
||||||
|
|
||||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="],
|
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="],
|
||||||
|
|
||||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="],
|
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="],
|
||||||
|
|
@ -1067,6 +1082,10 @@
|
||||||
|
|
||||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||||
|
|
||||||
|
"@js-joda/core": ["@js-joda/core@5.7.0", "", {}, "sha512-WBu4ULVVxySLLzK1Ppq+OdfP+adRS4ntmDQT915rzDJ++i95gc2jZkM5B6LWEAwN3lGXpfie3yPABozdD3K3Vg=="],
|
||||||
|
|
||||||
|
"@js-temporal/polyfill": ["@js-temporal/polyfill@0.5.1", "", { "dependencies": { "jsbi": "^4.3.0" } }, "sha512-hloP58zRVCRSpgDxmqCWJNlizAlUgJFqG2ypq79DCvyv9tHjRYMDOcPFjzfl/A1/YxDvRCZz8wvZvmapQnKwFQ=="],
|
||||||
|
|
||||||
"@jsdevtools/ono": ["@jsdevtools/ono@7.1.3", "", {}, "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg=="],
|
"@jsdevtools/ono": ["@jsdevtools/ono@7.1.3", "", {}, "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg=="],
|
||||||
|
|
||||||
"@jsx-email/all": ["@jsx-email/all@2.2.3", "", { "dependencies": { "@jsx-email/body": "1.0.2", "@jsx-email/button": "1.0.4", "@jsx-email/column": "1.0.3", "@jsx-email/container": "1.0.2", "@jsx-email/font": "1.0.3", "@jsx-email/head": "1.0.2", "@jsx-email/heading": "1.0.2", "@jsx-email/hr": "1.0.2", "@jsx-email/html": "1.0.2", "@jsx-email/img": "1.0.2", "@jsx-email/link": "1.0.2", "@jsx-email/markdown": "2.0.4", "@jsx-email/preview": "1.0.2", "@jsx-email/render": "1.1.1", "@jsx-email/row": "1.0.2", "@jsx-email/section": "1.0.2", "@jsx-email/tailwind": "2.4.4", "@jsx-email/text": "1.0.2" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-OBvLe/hVSQc0LlMSTJnkjFoqs3bmxcC4zpy/5pT5agPCSKMvAKQjzmsc2xJ2wO73jSpRV1K/g38GmvdCfrhSoQ=="],
|
"@jsx-email/all": ["@jsx-email/all@2.2.3", "", { "dependencies": { "@jsx-email/body": "1.0.2", "@jsx-email/button": "1.0.4", "@jsx-email/column": "1.0.3", "@jsx-email/container": "1.0.2", "@jsx-email/font": "1.0.3", "@jsx-email/head": "1.0.2", "@jsx-email/heading": "1.0.2", "@jsx-email/hr": "1.0.2", "@jsx-email/html": "1.0.2", "@jsx-email/img": "1.0.2", "@jsx-email/link": "1.0.2", "@jsx-email/markdown": "2.0.4", "@jsx-email/preview": "1.0.2", "@jsx-email/render": "1.1.1", "@jsx-email/row": "1.0.2", "@jsx-email/section": "1.0.2", "@jsx-email/tailwind": "2.4.4", "@jsx-email/text": "1.0.2" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-OBvLe/hVSQc0LlMSTJnkjFoqs3bmxcC4zpy/5pT5agPCSKMvAKQjzmsc2xJ2wO73jSpRV1K/g38GmvdCfrhSoQ=="],
|
||||||
|
|
@ -1355,8 +1374,6 @@
|
||||||
|
|
||||||
"@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="],
|
"@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="],
|
||||||
|
|
||||||
"@petamoriken/float16": ["@petamoriken/float16@3.9.3", "", {}, "sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g=="],
|
|
||||||
|
|
||||||
"@pierre/diffs": ["@pierre/diffs@1.0.2", "", { "dependencies": { "@shikijs/core": "^3.0.0", "@shikijs/engine-javascript": "3.19.0", "@shikijs/transformers": "3.19.0", "diff": "8.0.2", "hast-util-to-html": "9.0.5", "lru_map": "0.4.1", "shiki": "3.19.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-RkFSDD5X/U+8QjyilPViYGJfmJNWXR17zTL8zw48+DcVC1Ujbh6I1edyuRnFfgRzpft05x2DSCkz2cjoIAxPvQ=="],
|
"@pierre/diffs": ["@pierre/diffs@1.0.2", "", { "dependencies": { "@shikijs/core": "^3.0.0", "@shikijs/engine-javascript": "3.19.0", "@shikijs/transformers": "3.19.0", "diff": "8.0.2", "hast-util-to-html": "9.0.5", "lru_map": "0.4.1", "shiki": "3.19.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-RkFSDD5X/U+8QjyilPViYGJfmJNWXR17zTL8zw48+DcVC1Ujbh6I1edyuRnFfgRzpft05x2DSCkz2cjoIAxPvQ=="],
|
||||||
|
|
||||||
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
|
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
|
||||||
|
|
@ -1775,6 +1792,8 @@
|
||||||
|
|
||||||
"@tauri-apps/plugin-window-state": ["@tauri-apps/plugin-window-state@2.4.1", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-OuvdrzyY8Q5Dbzpj+GcrnV1iCeoZbcFdzMjanZMMcAEUNy/6PH5pxZPXpaZLOR7whlzXiuzx0L9EKZbH7zpdRw=="],
|
"@tauri-apps/plugin-window-state": ["@tauri-apps/plugin-window-state@2.4.1", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-OuvdrzyY8Q5Dbzpj+GcrnV1iCeoZbcFdzMjanZMMcAEUNy/6PH5pxZPXpaZLOR7whlzXiuzx0L9EKZbH7zpdRw=="],
|
||||||
|
|
||||||
|
"@tediousjs/connection-string": ["@tediousjs/connection-string@0.5.0", "", {}, "sha512-7qSgZbincDDDFyRweCIEvZULFAw5iz/DeunhvuxpL31nfntX3P4Yd4HkHBRg9H8CdqY1e5WFN1PZIz/REL9MVQ=="],
|
||||||
|
|
||||||
"@thisbeyond/solid-dnd": ["@thisbeyond/solid-dnd@0.7.5", "", { "peerDependencies": { "solid-js": "^1.5" } }, "sha512-DfI5ff+yYGpK9M21LhYwIPlbP2msKxN2ARwuu6GF8tT1GgNVDTI8VCQvH4TJFoVApP9d44izmAcTh/iTCH2UUw=="],
|
"@thisbeyond/solid-dnd": ["@thisbeyond/solid-dnd@0.7.5", "", { "peerDependencies": { "solid-js": "^1.5" } }, "sha512-DfI5ff+yYGpK9M21LhYwIPlbP2msKxN2ARwuu6GF8tT1GgNVDTI8VCQvH4TJFoVApP9d44izmAcTh/iTCH2UUw=="],
|
||||||
|
|
||||||
"@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
|
"@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
|
||||||
|
|
@ -1843,6 +1862,8 @@
|
||||||
|
|
||||||
"@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
|
"@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
|
||||||
|
|
||||||
|
"@types/mssql": ["@types/mssql@9.1.9", "", { "dependencies": { "@types/node": "*", "tarn": "^3.0.1", "tedious": "*" } }, "sha512-P0nCgw6vzY23UxZMnbI4N7fnLGANt4LI4yvxze1paPj+LuN28cFv5EI+QidP8udnId/BKhkcRhm/BleNsjK65A=="],
|
||||||
|
|
||||||
"@types/nlcst": ["@types/nlcst@2.0.3", "", { "dependencies": { "@types/unist": "*" } }, "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA=="],
|
"@types/nlcst": ["@types/nlcst@2.0.3", "", { "dependencies": { "@types/unist": "*" } }, "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA=="],
|
||||||
|
|
||||||
"@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
"@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||||
|
|
@ -1859,6 +1880,8 @@
|
||||||
|
|
||||||
"@types/react": ["@types/react@18.0.25", "", { "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", "csstype": "^3.0.2" } }, "sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g=="],
|
"@types/react": ["@types/react@18.0.25", "", { "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", "csstype": "^3.0.2" } }, "sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g=="],
|
||||||
|
|
||||||
|
"@types/readable-stream": ["@types/readable-stream@4.0.23", "", { "dependencies": { "@types/node": "*" } }, "sha512-wwXrtQvbMHxCbBgjHaMGEmImFTQxxpfMOR/ZoQnXxB1woqkUbdLGFDgauo00Py9IudiaqSeiBiulSV9i6XIPig=="],
|
||||||
|
|
||||||
"@types/retry": ["@types/retry@0.12.0", "", {}, "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="],
|
"@types/retry": ["@types/retry@0.12.0", "", {}, "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="],
|
||||||
|
|
||||||
"@types/sax": ["@types/sax@1.2.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A=="],
|
"@types/sax": ["@types/sax@1.2.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A=="],
|
||||||
|
|
@ -2059,6 +2082,8 @@
|
||||||
|
|
||||||
"binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
|
"binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
|
||||||
|
|
||||||
|
"bl": ["bl@6.1.6", "", { "dependencies": { "@types/readable-stream": "^4.0.0", "buffer": "^6.0.3", "inherits": "^2.0.4", "readable-stream": "^4.2.0" } }, "sha512-jLsPgN/YSvPUg9UX0Kd73CXpm2Psg9FxMeCSXnk3WBO3CMT10JMwijubhGfHCnFu6TPn1ei3b975dxv7K2pWVg=="],
|
||||||
|
|
||||||
"blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="],
|
"blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="],
|
||||||
|
|
||||||
"blob-to-buffer": ["blob-to-buffer@1.2.9", "", {}, "sha512-BF033y5fN6OCofD3vgHmNtwZWRcq9NLyyxyILx9hfMy1sXYy4ojFl765hJ2lP0YaN2fuxPaLO2Vzzoxy0FLFFA=="],
|
"blob-to-buffer": ["blob-to-buffer@1.2.9", "", {}, "sha512-BF033y5fN6OCofD3vgHmNtwZWRcq9NLyyxyILx9hfMy1sXYy4ojFl765hJ2lP0YaN2fuxPaLO2Vzzoxy0FLFFA=="],
|
||||||
|
|
@ -2331,9 +2356,9 @@
|
||||||
|
|
||||||
"dotenv": ["dotenv@17.2.3", "", {}, "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w=="],
|
"dotenv": ["dotenv@17.2.3", "", {}, "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w=="],
|
||||||
|
|
||||||
"drizzle-kit": ["drizzle-kit@0.30.5", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.19.7", "esbuild-register": "^3.5.0", "gel": "^2.0.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-l6dMSE100u7sDaTbLczibrQZjA35jLsHNqIV+jmhNVO3O8jzM6kywMOmV9uOz9ZVSCMPQhAZEFjL/qDPVrqpUA=="],
|
"drizzle-kit": ["drizzle-kit@1.0.0-beta.12-a5629fb", "", { "dependencies": { "@drizzle-team/brocli": "^0.11.0", "@js-temporal/polyfill": "^0.5.1", "esbuild": "^0.25.10", "tsx": "^4.20.6" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-l+p4QOMvPGYBYEE9NBlU7diu+NSlxuOUwi0I7i01Uj1PpfU0NxhPzaks/9q1MDw4FAPP8vdD0dOhoqosKtRWWQ=="],
|
||||||
|
|
||||||
"drizzle-orm": ["drizzle-orm@0.41.0", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-7A4ZxhHk9gdlXmTdPj/lREtP+3u8KvZ4yEN6MYVxBzZGex5Wtdc+CWSbu7btgF6TB0N+MNPrvW7RKBbxJchs/Q=="],
|
"drizzle-orm": ["drizzle-orm@1.0.0-beta.12-a5629fb", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@effect/sql": "^0.48.5", "@effect/sql-pg": "^0.49.7", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@sqlitecloud/drivers": ">=1.0.653", "@tidbcloud/serverless": "*", "@tursodatabase/database": ">=0.2.1", "@tursodatabase/database-common": ">=0.2.1", "@tursodatabase/database-wasm": ">=0.2.1", "@types/better-sqlite3": "*", "@types/mssql": "^9.1.4", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=9.3.0", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "mssql": "^11.0.1", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@effect/sql", "@effect/sql-pg", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@sqlitecloud/drivers", "@tidbcloud/serverless", "@tursodatabase/database", "@tursodatabase/database-common", "@tursodatabase/database-wasm", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-wyOAgr9Cy9oEN6z5S0JGhfipLKbRRJtQKgbDO9SXGR9swMBbGNIlXkeMqPRrqYQ8k70mh+7ZJ/eVmJ2F7zR3Vg=="],
|
||||||
|
|
||||||
"dset": ["dset@3.1.4", "", {}, "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA=="],
|
"dset": ["dset@3.1.4", "", {}, "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA=="],
|
||||||
|
|
||||||
|
|
@ -2363,8 +2388,6 @@
|
||||||
|
|
||||||
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
||||||
|
|
||||||
"env-paths": ["env-paths@3.0.0", "", {}, "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A=="],
|
|
||||||
|
|
||||||
"error-stack-parser": ["error-stack-parser@2.1.4", "", { "dependencies": { "stackframe": "^1.3.4" } }, "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ=="],
|
"error-stack-parser": ["error-stack-parser@2.1.4", "", { "dependencies": { "stackframe": "^1.3.4" } }, "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ=="],
|
||||||
|
|
||||||
"error-stack-parser-es": ["error-stack-parser-es@1.0.5", "", {}, "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA=="],
|
"error-stack-parser-es": ["error-stack-parser-es@1.0.5", "", {}, "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA=="],
|
||||||
|
|
@ -2395,8 +2418,6 @@
|
||||||
|
|
||||||
"esbuild-plugin-copy": ["esbuild-plugin-copy@2.1.1", "", { "dependencies": { "chalk": "^4.1.2", "chokidar": "^3.5.3", "fs-extra": "^10.0.1", "globby": "^11.0.3" }, "peerDependencies": { "esbuild": ">= 0.14.0" } }, "sha512-Bk66jpevTcV8KMFzZI1P7MZKZ+uDcrZm2G2egZ2jNIvVnivDpodZI+/KnpL3Jnap0PBdIHU7HwFGB8r+vV5CVw=="],
|
"esbuild-plugin-copy": ["esbuild-plugin-copy@2.1.1", "", { "dependencies": { "chalk": "^4.1.2", "chokidar": "^3.5.3", "fs-extra": "^10.0.1", "globby": "^11.0.3" }, "peerDependencies": { "esbuild": ">= 0.14.0" } }, "sha512-Bk66jpevTcV8KMFzZI1P7MZKZ+uDcrZm2G2egZ2jNIvVnivDpodZI+/KnpL3Jnap0PBdIHU7HwFGB8r+vV5CVw=="],
|
||||||
|
|
||||||
"esbuild-register": ["esbuild-register@3.6.0", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "esbuild": ">=0.12 <1" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="],
|
|
||||||
|
|
||||||
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
||||||
|
|
||||||
"escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="],
|
"escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="],
|
||||||
|
|
@ -2529,8 +2550,6 @@
|
||||||
|
|
||||||
"gcp-metadata": ["gcp-metadata@8.1.2", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="],
|
"gcp-metadata": ["gcp-metadata@8.1.2", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="],
|
||||||
|
|
||||||
"gel": ["gel@2.2.0", "", { "dependencies": { "@petamoriken/float16": "^3.8.7", "debug": "^4.3.4", "env-paths": "^3.0.0", "semver": "^7.6.2", "shell-quote": "^1.8.1", "which": "^4.0.0" }, "bin": { "gel": "dist/cli.mjs" } }, "sha512-q0ma7z2swmoamHQusey8ayo8+ilVdzDt4WTxSPzq/yRqvucWRfymRVMvNgmSC0XK7eNjjEZEcplxpgaNojKdmQ=="],
|
|
||||||
|
|
||||||
"generate-function": ["generate-function@2.3.1", "", { "dependencies": { "is-property": "^1.0.2" } }, "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ=="],
|
"generate-function": ["generate-function@2.3.1", "", { "dependencies": { "is-property": "^1.0.2" } }, "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ=="],
|
||||||
|
|
||||||
"generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="],
|
"generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="],
|
||||||
|
|
@ -2809,7 +2828,7 @@
|
||||||
|
|
||||||
"isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
|
"isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
|
||||||
|
|
||||||
"isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="],
|
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||||
|
|
||||||
"isomorphic-ws": ["isomorphic-ws@5.0.0", "", { "peerDependencies": { "ws": "*" } }, "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw=="],
|
"isomorphic-ws": ["isomorphic-ws@5.0.0", "", { "peerDependencies": { "ws": "*" } }, "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw=="],
|
||||||
|
|
||||||
|
|
@ -2835,10 +2854,14 @@
|
||||||
|
|
||||||
"js-cookie": ["js-cookie@3.0.5", "", {}, "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw=="],
|
"js-cookie": ["js-cookie@3.0.5", "", {}, "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw=="],
|
||||||
|
|
||||||
|
"js-md4": ["js-md4@0.3.2", "", {}, "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA=="],
|
||||||
|
|
||||||
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||||
|
|
||||||
"js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
|
"js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
|
||||||
|
|
||||||
|
"jsbi": ["jsbi@4.3.2", "", {}, "sha512-9fqMSQbhJykSeii05nxKl4m6Eqn2P6rOlYiS+C5Dr/HPIU/7yZxu5qzbs40tgaFORiw2Amd0mirjxatXYMkIew=="],
|
||||||
|
|
||||||
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
||||||
|
|
||||||
"json-bigint": ["json-bigint@1.0.0", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="],
|
"json-bigint": ["json-bigint@1.0.0", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="],
|
||||||
|
|
@ -3111,6 +3134,8 @@
|
||||||
|
|
||||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||||
|
|
||||||
|
"mssql": ["mssql@11.0.1", "", { "dependencies": { "@tediousjs/connection-string": "^0.5.0", "commander": "^11.0.0", "debug": "^4.3.3", "rfdc": "^1.3.0", "tarn": "^3.0.2", "tedious": "^18.2.1" }, "bin": { "mssql": "bin/mssql" } }, "sha512-KlGNsugoT90enKlR8/G36H0kTxPthDhmtNUCwEHvgRza5Cjpjoj+P2X6eMpFUDN7pFrJZsKadL4x990G8RBE1w=="],
|
||||||
|
|
||||||
"multicast-dns": ["multicast-dns@7.2.5", "", { "dependencies": { "dns-packet": "^5.2.2", "thunky": "^1.0.2" }, "bin": { "multicast-dns": "cli.js" } }, "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg=="],
|
"multicast-dns": ["multicast-dns@7.2.5", "", { "dependencies": { "dns-packet": "^5.2.2", "thunky": "^1.0.2" }, "bin": { "multicast-dns": "cli.js" } }, "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg=="],
|
||||||
|
|
||||||
"mustache": ["mustache@4.2.0", "", { "bin": { "mustache": "bin/mustache" } }, "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ=="],
|
"mustache": ["mustache@4.2.0", "", { "bin": { "mustache": "bin/mustache" } }, "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ=="],
|
||||||
|
|
@ -3125,6 +3150,8 @@
|
||||||
|
|
||||||
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||||
|
|
||||||
|
"native-duplexpair": ["native-duplexpair@1.0.0", "", {}, "sha512-E7QQoM+3jvNtlmyfqRZ0/U75VFgCls+fSkbml2MpgWkWyz3ox8Y58gNhfuziuQYGNNQAbFZJQck55LHCnCK6CA=="],
|
||||||
|
|
||||||
"negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
"negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
||||||
|
|
||||||
"neotraverse": ["neotraverse@0.6.18", "", {}, "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA=="],
|
"neotraverse": ["neotraverse@0.6.18", "", {}, "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA=="],
|
||||||
|
|
@ -3475,6 +3502,8 @@
|
||||||
|
|
||||||
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
||||||
|
|
||||||
|
"rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="],
|
||||||
|
|
||||||
"rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="],
|
"rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="],
|
||||||
|
|
||||||
"rollup": ["rollup@4.57.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.57.0", "@rollup/rollup-android-arm64": "4.57.0", "@rollup/rollup-darwin-arm64": "4.57.0", "@rollup/rollup-darwin-x64": "4.57.0", "@rollup/rollup-freebsd-arm64": "4.57.0", "@rollup/rollup-freebsd-x64": "4.57.0", "@rollup/rollup-linux-arm-gnueabihf": "4.57.0", "@rollup/rollup-linux-arm-musleabihf": "4.57.0", "@rollup/rollup-linux-arm64-gnu": "4.57.0", "@rollup/rollup-linux-arm64-musl": "4.57.0", "@rollup/rollup-linux-loong64-gnu": "4.57.0", "@rollup/rollup-linux-loong64-musl": "4.57.0", "@rollup/rollup-linux-ppc64-gnu": "4.57.0", "@rollup/rollup-linux-ppc64-musl": "4.57.0", "@rollup/rollup-linux-riscv64-gnu": "4.57.0", "@rollup/rollup-linux-riscv64-musl": "4.57.0", "@rollup/rollup-linux-s390x-gnu": "4.57.0", "@rollup/rollup-linux-x64-gnu": "4.57.0", "@rollup/rollup-linux-x64-musl": "4.57.0", "@rollup/rollup-openbsd-x64": "4.57.0", "@rollup/rollup-openharmony-arm64": "4.57.0", "@rollup/rollup-win32-arm64-msvc": "4.57.0", "@rollup/rollup-win32-ia32-msvc": "4.57.0", "@rollup/rollup-win32-x64-gnu": "4.57.0", "@rollup/rollup-win32-x64-msvc": "4.57.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-e5lPJi/aui4TO1LpAXIRLySmwXSE8k3b9zoGfd42p67wzxog4WHjiZF3M2uheQih4DGyc25QEV4yRBbpueNiUA=="],
|
"rollup": ["rollup@4.57.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.57.0", "@rollup/rollup-android-arm64": "4.57.0", "@rollup/rollup-darwin-arm64": "4.57.0", "@rollup/rollup-darwin-x64": "4.57.0", "@rollup/rollup-freebsd-arm64": "4.57.0", "@rollup/rollup-freebsd-x64": "4.57.0", "@rollup/rollup-linux-arm-gnueabihf": "4.57.0", "@rollup/rollup-linux-arm-musleabihf": "4.57.0", "@rollup/rollup-linux-arm64-gnu": "4.57.0", "@rollup/rollup-linux-arm64-musl": "4.57.0", "@rollup/rollup-linux-loong64-gnu": "4.57.0", "@rollup/rollup-linux-loong64-musl": "4.57.0", "@rollup/rollup-linux-ppc64-gnu": "4.57.0", "@rollup/rollup-linux-ppc64-musl": "4.57.0", "@rollup/rollup-linux-riscv64-gnu": "4.57.0", "@rollup/rollup-linux-riscv64-musl": "4.57.0", "@rollup/rollup-linux-s390x-gnu": "4.57.0", "@rollup/rollup-linux-x64-gnu": "4.57.0", "@rollup/rollup-linux-x64-musl": "4.57.0", "@rollup/rollup-openbsd-x64": "4.57.0", "@rollup/rollup-openharmony-arm64": "4.57.0", "@rollup/rollup-win32-arm64-msvc": "4.57.0", "@rollup/rollup-win32-ia32-msvc": "4.57.0", "@rollup/rollup-win32-x64-gnu": "4.57.0", "@rollup/rollup-win32-x64-msvc": "4.57.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-e5lPJi/aui4TO1LpAXIRLySmwXSE8k3b9zoGfd42p67wzxog4WHjiZF3M2uheQih4DGyc25QEV4yRBbpueNiUA=="],
|
||||||
|
|
@ -3533,8 +3562,6 @@
|
||||||
|
|
||||||
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||||
|
|
||||||
"shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="],
|
|
||||||
|
|
||||||
"shiki": ["shiki@3.20.0", "", { "dependencies": { "@shikijs/core": "3.20.0", "@shikijs/engine-javascript": "3.20.0", "@shikijs/engine-oniguruma": "3.20.0", "@shikijs/langs": "3.20.0", "@shikijs/themes": "3.20.0", "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-kgCOlsnyWb+p0WU+01RjkCH+eBVsjL1jOwUYWv0YDWkM2/A46+LDKVs5yZCUXjJG6bj4ndFoAg5iLIIue6dulg=="],
|
"shiki": ["shiki@3.20.0", "", { "dependencies": { "@shikijs/core": "3.20.0", "@shikijs/engine-javascript": "3.20.0", "@shikijs/engine-oniguruma": "3.20.0", "@shikijs/langs": "3.20.0", "@shikijs/themes": "3.20.0", "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-kgCOlsnyWb+p0WU+01RjkCH+eBVsjL1jOwUYWv0YDWkM2/A46+LDKVs5yZCUXjJG6bj4ndFoAg5iLIIue6dulg=="],
|
||||||
|
|
||||||
"shikiji": ["shikiji@0.6.13", "", { "dependencies": { "hast-util-to-html": "^9.0.0" } }, "sha512-4T7X39csvhT0p7GDnq9vysWddf2b6BeioiN3Ymhnt3xcy9tXmDcnsEFVxX18Z4YcQgEE/w48dLJ4pPPUcG9KkA=="],
|
"shikiji": ["shikiji@0.6.13", "", { "dependencies": { "hast-util-to-html": "^9.0.0" } }, "sha512-4T7X39csvhT0p7GDnq9vysWddf2b6BeioiN3Ymhnt3xcy9tXmDcnsEFVxX18Z4YcQgEE/w48dLJ4pPPUcG9KkA=="],
|
||||||
|
|
@ -3589,7 +3616,7 @@
|
||||||
|
|
||||||
"space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="],
|
"space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="],
|
||||||
|
|
||||||
"sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="],
|
"sprintf-js": ["sprintf-js@1.1.3", "", {}, "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="],
|
||||||
|
|
||||||
"sqlstring": ["sqlstring@2.3.3", "", {}, "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg=="],
|
"sqlstring": ["sqlstring@2.3.3", "", {}, "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg=="],
|
||||||
|
|
||||||
|
|
@ -3681,6 +3708,10 @@
|
||||||
|
|
||||||
"tar-stream": ["tar-stream@3.1.7", "", { "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ=="],
|
"tar-stream": ["tar-stream@3.1.7", "", { "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ=="],
|
||||||
|
|
||||||
|
"tarn": ["tarn@3.0.2", "", {}, "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ=="],
|
||||||
|
|
||||||
|
"tedious": ["tedious@18.6.2", "", { "dependencies": { "@azure/core-auth": "^1.7.2", "@azure/identity": "^4.2.1", "@azure/keyvault-keys": "^4.4.0", "@js-joda/core": "^5.6.1", "@types/node": ">=18", "bl": "^6.0.11", "iconv-lite": "^0.6.3", "js-md4": "^0.3.2", "native-duplexpair": "^1.0.0", "sprintf-js": "^1.1.3" } }, "sha512-g7jC56o3MzLkE3lHkaFe2ZdOVFBahq5bsB60/M4NYUbocw/MCrS89IOEQUFr+ba6pb8ZHczZ/VqCyYeYq0xBAg=="],
|
||||||
|
|
||||||
"terracotta": ["terracotta@1.1.0", "", { "dependencies": { "solid-use": "^0.9.1" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-kfQciWUBUBgYkXu7gh3CK3FAJng/iqZslAaY08C+k1Hdx17aVEpcFFb/WPaysxAfcupNH3y53s/pc53xxZauww=="],
|
"terracotta": ["terracotta@1.1.0", "", { "dependencies": { "solid-use": "^0.9.1" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-kfQciWUBUBgYkXu7gh3CK3FAJng/iqZslAaY08C+k1Hdx17aVEpcFFb/WPaysxAfcupNH3y53s/pc53xxZauww=="],
|
||||||
|
|
||||||
"terser": ["terser@5.46.0", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg=="],
|
"terser": ["terser@5.46.0", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg=="],
|
||||||
|
|
@ -3741,6 +3772,8 @@
|
||||||
|
|
||||||
"tsscmp": ["tsscmp@1.0.6", "", {}, "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA=="],
|
"tsscmp": ["tsscmp@1.0.6", "", {}, "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA=="],
|
||||||
|
|
||||||
|
"tsx": ["tsx@4.21.0", "", { "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw=="],
|
||||||
|
|
||||||
"tunnel": ["tunnel@0.0.6", "", {}, "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="],
|
"tunnel": ["tunnel@0.0.6", "", {}, "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="],
|
||||||
|
|
||||||
"turbo": ["turbo@2.5.6", "", { "optionalDependencies": { "turbo-darwin-64": "2.5.6", "turbo-darwin-arm64": "2.5.6", "turbo-linux-64": "2.5.6", "turbo-linux-arm64": "2.5.6", "turbo-windows-64": "2.5.6", "turbo-windows-arm64": "2.5.6" }, "bin": { "turbo": "bin/turbo" } }, "sha512-gxToHmi9oTBNB05UjUsrWf0OyN5ZXtD0apOarC1KIx232Vp3WimRNy3810QzeNSgyD5rsaIDXlxlbnOzlouo+w=="],
|
"turbo": ["turbo@2.5.6", "", { "optionalDependencies": { "turbo-darwin-64": "2.5.6", "turbo-darwin-arm64": "2.5.6", "turbo-linux-64": "2.5.6", "turbo-linux-arm64": "2.5.6", "turbo-windows-64": "2.5.6", "turbo-windows-arm64": "2.5.6" }, "bin": { "turbo": "bin/turbo" } }, "sha512-gxToHmi9oTBNB05UjUsrWf0OyN5ZXtD0apOarC1KIx232Vp3WimRNy3810QzeNSgyD5rsaIDXlxlbnOzlouo+w=="],
|
||||||
|
|
@ -3887,7 +3920,7 @@
|
||||||
|
|
||||||
"whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
|
"whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
|
||||||
|
|
||||||
"which": ["which@4.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="],
|
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||||
|
|
||||||
"which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="],
|
"which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="],
|
||||||
|
|
||||||
|
|
@ -4053,7 +4086,9 @@
|
||||||
|
|
||||||
"@azure/core-http/xml2js": ["xml2js@0.5.0", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA=="],
|
"@azure/core-http/xml2js": ["xml2js@0.5.0", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA=="],
|
||||||
|
|
||||||
"@azure/core-xml/fast-xml-parser": ["fast-xml-parser@5.3.3", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-2O3dkPAAC6JavuMm8+4+pgTk+5hoAs+CjZ+sWcQLkX9+/tHRuTkQh/Oaifr8qDmZ8iEHb771Ea6G8CdwkrgvYA=="],
|
"@azure/core-xml/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
|
||||||
|
|
||||||
|
"@azure/msal-node/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="],
|
||||||
|
|
||||||
"@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
"@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||||
|
|
||||||
|
|
@ -4069,8 +4104,6 @@
|
||||||
|
|
||||||
"@dot/log/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
"@dot/log/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="],
|
|
||||||
|
|
||||||
"@gitlab/gitlab-ai-provider/openai": ["openai@6.17.0", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-NHRpPEUPzAvFOAFs9+9pC6+HCw/iWsYsKCMPXH5Kw7BpMxqd8g/A07/1o7Gx2TWtCnzevVRyKMRFqyiHyAlqcA=="],
|
"@gitlab/gitlab-ai-provider/openai": ["openai@6.17.0", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-NHRpPEUPzAvFOAFs9+9pC6+HCw/iWsYsKCMPXH5Kw7BpMxqd8g/A07/1o7Gx2TWtCnzevVRyKMRFqyiHyAlqcA=="],
|
||||||
|
|
||||||
"@gitlab/gitlab-ai-provider/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
"@gitlab/gitlab-ai-provider/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||||
|
|
@ -4297,6 +4330,8 @@
|
||||||
|
|
||||||
"babel-plugin-module-resolver/glob": ["glob@9.3.5", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="],
|
"babel-plugin-module-resolver/glob": ["glob@9.3.5", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="],
|
||||||
|
|
||||||
|
"bl/buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
|
||||||
|
|
||||||
"body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
"body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||||
|
|
||||||
"body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
"body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
||||||
|
|
@ -4311,12 +4346,8 @@
|
||||||
|
|
||||||
"condense-newlines/kind-of": ["kind-of@3.2.2", "", { "dependencies": { "is-buffer": "^1.1.5" } }, "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ=="],
|
"condense-newlines/kind-of": ["kind-of@3.2.2", "", { "dependencies": { "is-buffer": "^1.1.5" } }, "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ=="],
|
||||||
|
|
||||||
"cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
|
||||||
|
|
||||||
"dot-prop/type-fest": ["type-fest@3.13.1", "", {}, "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g=="],
|
"dot-prop/type-fest": ["type-fest@3.13.1", "", {}, "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g=="],
|
||||||
|
|
||||||
"drizzle-kit/esbuild": ["esbuild@0.19.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.19.12", "@esbuild/android-arm": "0.19.12", "@esbuild/android-arm64": "0.19.12", "@esbuild/android-x64": "0.19.12", "@esbuild/darwin-arm64": "0.19.12", "@esbuild/darwin-x64": "0.19.12", "@esbuild/freebsd-arm64": "0.19.12", "@esbuild/freebsd-x64": "0.19.12", "@esbuild/linux-arm": "0.19.12", "@esbuild/linux-arm64": "0.19.12", "@esbuild/linux-ia32": "0.19.12", "@esbuild/linux-loong64": "0.19.12", "@esbuild/linux-mips64el": "0.19.12", "@esbuild/linux-ppc64": "0.19.12", "@esbuild/linux-riscv64": "0.19.12", "@esbuild/linux-s390x": "0.19.12", "@esbuild/linux-x64": "0.19.12", "@esbuild/netbsd-x64": "0.19.12", "@esbuild/openbsd-x64": "0.19.12", "@esbuild/sunos-x64": "0.19.12", "@esbuild/win32-arm64": "0.19.12", "@esbuild/win32-ia32": "0.19.12", "@esbuild/win32-x64": "0.19.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg=="],
|
|
||||||
|
|
||||||
"editorconfig/commander": ["commander@10.0.1", "", {}, "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="],
|
"editorconfig/commander": ["commander@10.0.1", "", {}, "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="],
|
||||||
|
|
||||||
"editorconfig/minimatch": ["minimatch@9.0.1", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w=="],
|
"editorconfig/minimatch": ["minimatch@9.0.1", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w=="],
|
||||||
|
|
@ -4375,6 +4406,8 @@
|
||||||
|
|
||||||
"miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="],
|
"miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="],
|
||||||
|
|
||||||
|
"mssql/commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="],
|
||||||
|
|
||||||
"nitro/h3": ["h3@2.0.1-rc.5", "", { "dependencies": { "rou3": "^0.7.9", "srvx": "^0.9.1" }, "peerDependencies": { "crossws": "^0.4.1" }, "optionalPeers": ["crossws"] }, "sha512-qkohAzCab0nLzXNm78tBjZDvtKMTmtygS8BJLT3VPczAQofdqlFXDPkXdLMJN4r05+xqneG8snZJ0HgkERCZTg=="],
|
"nitro/h3": ["h3@2.0.1-rc.5", "", { "dependencies": { "rou3": "^0.7.9", "srvx": "^0.9.1" }, "peerDependencies": { "crossws": "^0.4.1" }, "optionalPeers": ["crossws"] }, "sha512-qkohAzCab0nLzXNm78tBjZDvtKMTmtygS8BJLT3VPczAQofdqlFXDPkXdLMJN4r05+xqneG8snZJ0HgkERCZTg=="],
|
||||||
|
|
||||||
"npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
|
"npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
|
||||||
|
|
@ -4463,12 +4496,16 @@
|
||||||
|
|
||||||
"tar/yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
|
"tar/yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
|
||||||
|
|
||||||
|
"tedious/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
|
||||||
|
|
||||||
"terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
|
"terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
|
||||||
|
|
||||||
"token-types/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
"token-types/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||||
|
|
||||||
"tree-sitter-bash/node-addon-api": ["node-addon-api@8.5.0", "", {}, "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A=="],
|
"tree-sitter-bash/node-addon-api": ["node-addon-api@8.5.0", "", {}, "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A=="],
|
||||||
|
|
||||||
|
"tsx/esbuild": ["esbuild@0.27.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.2", "@esbuild/android-arm": "0.27.2", "@esbuild/android-arm64": "0.27.2", "@esbuild/android-x64": "0.27.2", "@esbuild/darwin-arm64": "0.27.2", "@esbuild/darwin-x64": "0.27.2", "@esbuild/freebsd-arm64": "0.27.2", "@esbuild/freebsd-x64": "0.27.2", "@esbuild/linux-arm": "0.27.2", "@esbuild/linux-arm64": "0.27.2", "@esbuild/linux-ia32": "0.27.2", "@esbuild/linux-loong64": "0.27.2", "@esbuild/linux-mips64el": "0.27.2", "@esbuild/linux-ppc64": "0.27.2", "@esbuild/linux-riscv64": "0.27.2", "@esbuild/linux-s390x": "0.27.2", "@esbuild/linux-x64": "0.27.2", "@esbuild/netbsd-arm64": "0.27.2", "@esbuild/netbsd-x64": "0.27.2", "@esbuild/openbsd-arm64": "0.27.2", "@esbuild/openbsd-x64": "0.27.2", "@esbuild/openharmony-arm64": "0.27.2", "@esbuild/sunos-x64": "0.27.2", "@esbuild/win32-arm64": "0.27.2", "@esbuild/win32-ia32": "0.27.2", "@esbuild/win32-x64": "0.27.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw=="],
|
||||||
|
|
||||||
"tw-to-css/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
|
"tw-to-css/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
|
||||||
|
|
||||||
"tw-to-css/tailwindcss": ["tailwindcss@3.3.2", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.5.3", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.2.12", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.18.2", "lilconfig": "^2.1.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.0.0", "postcss": "^8.4.23", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", "postcss-value-parser": "^4.2.0", "resolve": "^1.22.2", "sucrase": "^3.32.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w=="],
|
"tw-to-css/tailwindcss": ["tailwindcss@3.3.2", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.5.3", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.2.12", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.18.2", "lilconfig": "^2.1.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.0.0", "postcss": "^8.4.23", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", "postcss-value-parser": "^4.2.0", "resolve": "^1.22.2", "sucrase": "^3.32.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w=="],
|
||||||
|
|
@ -4543,50 +4580,6 @@
|
||||||
|
|
||||||
"@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
"@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="],
|
|
||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="],
|
|
||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.18.20", "", { "os": "android", "cpu": "x64" }, "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg=="],
|
|
||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.18.20", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA=="],
|
|
||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.18.20", "", { "os": "darwin", "cpu": "x64" }, "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ=="],
|
|
||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.18.20", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw=="],
|
|
||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.18.20", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ=="],
|
|
||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.18.20", "", { "os": "linux", "cpu": "arm" }, "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg=="],
|
|
||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.18.20", "", { "os": "linux", "cpu": "arm64" }, "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA=="],
|
|
||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.18.20", "", { "os": "linux", "cpu": "ia32" }, "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA=="],
|
|
||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg=="],
|
|
||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ=="],
|
|
||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.18.20", "", { "os": "linux", "cpu": "ppc64" }, "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA=="],
|
|
||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A=="],
|
|
||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.18.20", "", { "os": "linux", "cpu": "s390x" }, "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ=="],
|
|
||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.18.20", "", { "os": "linux", "cpu": "x64" }, "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w=="],
|
|
||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.18.20", "", { "os": "none", "cpu": "x64" }, "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A=="],
|
|
||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.18.20", "", { "os": "openbsd", "cpu": "x64" }, "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg=="],
|
|
||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.18.20", "", { "os": "sunos", "cpu": "x64" }, "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ=="],
|
|
||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.18.20", "", { "os": "win32", "cpu": "arm64" }, "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg=="],
|
|
||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.18.20", "", { "os": "win32", "cpu": "ia32" }, "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g=="],
|
|
||||||
|
|
||||||
"@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="],
|
|
||||||
|
|
||||||
"@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
"@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
||||||
|
|
||||||
"@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
"@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
||||||
|
|
@ -4829,58 +4822,12 @@
|
||||||
|
|
||||||
"babel-plugin-module-resolver/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
|
"babel-plugin-module-resolver/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
|
||||||
|
|
||||||
|
"bl/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||||
|
|
||||||
"body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
"body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||||
|
|
||||||
"c12/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
|
"c12/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
|
||||||
|
|
||||||
"cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
|
||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.19.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA=="],
|
|
||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.19.12", "", { "os": "android", "cpu": "arm" }, "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w=="],
|
|
||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.19.12", "", { "os": "android", "cpu": "arm64" }, "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA=="],
|
|
||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.19.12", "", { "os": "android", "cpu": "x64" }, "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew=="],
|
|
||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.19.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g=="],
|
|
||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.19.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A=="],
|
|
||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.19.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA=="],
|
|
||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.19.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg=="],
|
|
||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.19.12", "", { "os": "linux", "cpu": "arm" }, "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w=="],
|
|
||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.19.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA=="],
|
|
||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.19.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA=="],
|
|
||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA=="],
|
|
||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w=="],
|
|
||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.19.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg=="],
|
|
||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg=="],
|
|
||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.19.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg=="],
|
|
||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.19.12", "", { "os": "linux", "cpu": "x64" }, "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg=="],
|
|
||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.19.12", "", { "os": "none", "cpu": "x64" }, "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA=="],
|
|
||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.19.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw=="],
|
|
||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.19.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA=="],
|
|
||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.19.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A=="],
|
|
||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.19.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ=="],
|
|
||||||
|
|
||||||
"drizzle-kit/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.19.12", "", { "os": "win32", "cpu": "x64" }, "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA=="],
|
|
||||||
|
|
||||||
"esbuild-plugin-copy/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
|
"esbuild-plugin-copy/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
|
||||||
|
|
||||||
"express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
"express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||||
|
|
@ -4925,6 +4872,58 @@
|
||||||
|
|
||||||
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.2", "", { "os": "android", "cpu": "arm" }, "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.2", "", { "os": "android", "cpu": "arm64" }, "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.2", "", { "os": "android", "cpu": "x64" }, "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.2", "", { "os": "linux", "cpu": "arm" }, "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.2", "", { "os": "linux", "cpu": "x64" }, "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.2", "", { "os": "none", "cpu": "arm64" }, "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.2", "", { "os": "none", "cpu": "x64" }, "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.2", "", { "os": "none", "cpu": "arm64" }, "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ=="],
|
||||||
|
|
||||||
|
"tsx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.2", "", { "os": "win32", "cpu": "x64" }, "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ=="],
|
||||||
|
|
||||||
"tw-to-css/tailwindcss/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
|
"tw-to-css/tailwindcss/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
|
||||||
|
|
||||||
"tw-to-css/tailwindcss/glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
"tw-to-css/tailwindcss/glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
||||||
|
|
@ -5103,6 +5102,8 @@
|
||||||
|
|
||||||
"esbuild-plugin-copy/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
"esbuild-plugin-copy/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
|
|
||||||
|
"gray-matter/js-yaml/argparse/sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="],
|
||||||
|
|
||||||
"js-beautify/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
"js-beautify/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||||
|
|
||||||
"opencontrol/@modelcontextprotocol/sdk/express/accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
|
"opencontrol/@modelcontextprotocol/sdk/express/accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@
|
||||||
"@tailwindcss/vite": "4.1.11",
|
"@tailwindcss/vite": "4.1.11",
|
||||||
"diff": "8.0.2",
|
"diff": "8.0.2",
|
||||||
"dompurify": "3.3.1",
|
"dompurify": "3.3.1",
|
||||||
|
"drizzle-kit": "1.0.0-beta.12-a5629fb",
|
||||||
|
"drizzle-orm": "1.0.0-beta.12-a5629fb",
|
||||||
"ai": "5.0.124",
|
"ai": "5.0.124",
|
||||||
"hono": "4.10.7",
|
"hono": "4.10.7",
|
||||||
"hono-openapi": "1.1.2",
|
"hono-openapi": "1.1.2",
|
||||||
|
|
|
||||||
|
|
@ -841,6 +841,23 @@ function createGlobalSync() {
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case "message.part.delta": {
|
||||||
|
const parts = store.part[event.properties.messageID]
|
||||||
|
if (!parts) break
|
||||||
|
const result = Binary.search(parts, event.properties.partID, (p) => p.id)
|
||||||
|
if (!result.found) break
|
||||||
|
setStore(
|
||||||
|
"part",
|
||||||
|
event.properties.messageID,
|
||||||
|
produce((draft) => {
|
||||||
|
const part = draft[result.index]
|
||||||
|
const field = event.properties.field as keyof typeof part
|
||||||
|
const existing = part[field] as string | undefined
|
||||||
|
;(part[field] as string) = (existing ?? "") + event.properties.delta
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
case "message.part.removed": {
|
case "message.part.removed": {
|
||||||
const messageID = event.properties.messageID
|
const messageID = event.properties.messageID
|
||||||
const parts = store.part[messageID]
|
const parts = store.part[messageID]
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
"@opencode-ai/console-resource": "workspace:*",
|
"@opencode-ai/console-resource": "workspace:*",
|
||||||
"@planetscale/database": "1.19.0",
|
"@planetscale/database": "1.19.0",
|
||||||
"aws4fetch": "1.0.20",
|
"aws4fetch": "1.0.20",
|
||||||
"drizzle-orm": "0.41.0",
|
"drizzle-orm": "catalog:",
|
||||||
"postgres": "3.4.7",
|
"postgres": "3.4.7",
|
||||||
"stripe": "18.0.0",
|
"stripe": "18.0.0",
|
||||||
"ulid": "catalog:",
|
"ulid": "catalog:",
|
||||||
|
|
@ -43,7 +43,7 @@
|
||||||
"@tsconfig/node22": "22.0.2",
|
"@tsconfig/node22": "22.0.2",
|
||||||
"@types/bun": "1.3.0",
|
"@types/bun": "1.3.0",
|
||||||
"@types/node": "catalog:",
|
"@types/node": "catalog:",
|
||||||
"drizzle-kit": "0.30.5",
|
"drizzle-kit": "catalog:",
|
||||||
"mysql2": "3.14.4",
|
"mysql2": "3.14.4",
|
||||||
"typescript": "catalog:",
|
"typescript": "catalog:",
|
||||||
"@typescript/native-preview": "catalog:"
|
"@typescript/native-preview": "catalog:"
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ export * from "drizzle-orm"
|
||||||
import { Client } from "@planetscale/database"
|
import { Client } from "@planetscale/database"
|
||||||
|
|
||||||
import { MySqlTransaction, type MySqlTransactionConfig } from "drizzle-orm/mysql-core"
|
import { MySqlTransaction, type MySqlTransactionConfig } from "drizzle-orm/mysql-core"
|
||||||
import type { ExtractTablesWithRelations } from "drizzle-orm"
|
|
||||||
import type { PlanetScalePreparedQueryHKT, PlanetscaleQueryResultHKT } from "drizzle-orm/planetscale-serverless"
|
import type { PlanetScalePreparedQueryHKT, PlanetscaleQueryResultHKT } from "drizzle-orm/planetscale-serverless"
|
||||||
import { Context } from "../context"
|
import { Context } from "../context"
|
||||||
import { memo } from "../util/memo"
|
import { memo } from "../util/memo"
|
||||||
|
|
@ -14,7 +13,7 @@ export namespace Database {
|
||||||
PlanetscaleQueryResultHKT,
|
PlanetscaleQueryResultHKT,
|
||||||
PlanetScalePreparedQueryHKT,
|
PlanetScalePreparedQueryHKT,
|
||||||
Record<string, never>,
|
Record<string, never>,
|
||||||
ExtractTablesWithRelations<Record<string, never>>
|
any
|
||||||
>
|
>
|
||||||
|
|
||||||
const client = memo(() => {
|
const client = memo(() => {
|
||||||
|
|
@ -23,7 +22,7 @@ export namespace Database {
|
||||||
username: Resource.Database.username,
|
username: Resource.Database.username,
|
||||||
password: Resource.Database.password,
|
password: Resource.Database.password,
|
||||||
})
|
})
|
||||||
const db = drizzle(result, {})
|
const db = drizzle({ client: result })
|
||||||
return db
|
return db
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,10 @@
|
||||||
# opencode agent guidelines
|
# opencode database guide
|
||||||
|
|
||||||
## Build/Test Commands
|
## Database
|
||||||
|
|
||||||
- **Install**: `bun install`
|
- **Schema**: Drizzle schema lives in `src/**/*.sql.ts`.
|
||||||
- **Run**: `bun run --conditions=browser ./src/index.ts`
|
- **Naming**: tables and columns use snake*case; join columns are `<entity>_id`; indexes are `<table>*<column>\_idx`.
|
||||||
- **Typecheck**: `bun run typecheck` (npm run typecheck)
|
- **Migrations**: generated by Drizzle Kit using `drizzle.config.ts` (schema: `./src/**/*.sql.ts`, output: `./migration`).
|
||||||
- **Test**: `bun test` (runs all tests)
|
- **Command**: `bun run db generate --name <slug>`.
|
||||||
- **Single test**: `bun test test/tool/tool.test.ts` (specific test file)
|
- **Output**: creates `migration/<timestamp>_<slug>/migration.sql` and `snapshot.json`.
|
||||||
|
- **Tests**: migration tests should read the per-folder layout (no `_journal.json`).
|
||||||
## Code Style
|
|
||||||
|
|
||||||
- **Runtime**: Bun with TypeScript ESM modules
|
|
||||||
- **Imports**: Use relative imports for local modules, named imports preferred
|
|
||||||
- **Types**: Zod schemas for validation, TypeScript interfaces for structure
|
|
||||||
- **Naming**: camelCase for variables/functions, PascalCase for classes/namespaces
|
|
||||||
- **Error handling**: Use Result patterns, avoid throwing exceptions in tools
|
|
||||||
- **File structure**: Namespace-based organization (e.g., `Tool.define()`, `Session.create()`)
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
- **Tools**: Implement `Tool.Info` interface with `execute()` method
|
|
||||||
- **Context**: Pass `sessionID` in tool context, use `App.provide()` for DI
|
|
||||||
- **Validation**: All inputs validated with Zod schemas
|
|
||||||
- **Logging**: Use `Log.create({ service: "name" })` pattern
|
|
||||||
- **Storage**: Use `Storage` namespace for persistence
|
|
||||||
- **API Client**: The TypeScript TUI (built with SolidJS + OpenTUI) communicates with the OpenCode server using `@opencode-ai/sdk`. When adding/modifying server endpoints in `packages/opencode/src/server/server.ts`, run `./script/generate.ts` to regenerate the SDK and related files.
|
|
||||||
|
|
|
||||||
10
packages/opencode/drizzle.config.ts
Normal file
10
packages/opencode/drizzle.config.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { defineConfig } from "drizzle-kit"
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
dialect: "sqlite",
|
||||||
|
schema: "./src/**/*.sql.ts",
|
||||||
|
out: "./migration",
|
||||||
|
dbCredentials: {
|
||||||
|
url: "/home/thdxr/.local/share/opencode/opencode.db",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
CREATE TABLE `project` (
|
||||||
|
`id` text PRIMARY KEY,
|
||||||
|
`worktree` text NOT NULL,
|
||||||
|
`vcs` text,
|
||||||
|
`name` text,
|
||||||
|
`icon_url` text,
|
||||||
|
`icon_color` text,
|
||||||
|
`time_created` integer NOT NULL,
|
||||||
|
`time_updated` integer NOT NULL,
|
||||||
|
`time_initialized` integer,
|
||||||
|
`sandboxes` text NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `message` (
|
||||||
|
`id` text PRIMARY KEY,
|
||||||
|
`session_id` text NOT NULL,
|
||||||
|
`time_created` integer NOT NULL,
|
||||||
|
`time_updated` integer NOT NULL,
|
||||||
|
`data` text NOT NULL,
|
||||||
|
CONSTRAINT `fk_message_session_id_session_id_fk` FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `part` (
|
||||||
|
`id` text PRIMARY KEY,
|
||||||
|
`message_id` text NOT NULL,
|
||||||
|
`session_id` text NOT NULL,
|
||||||
|
`time_created` integer NOT NULL,
|
||||||
|
`time_updated` integer NOT NULL,
|
||||||
|
`data` text NOT NULL,
|
||||||
|
CONSTRAINT `fk_part_message_id_message_id_fk` FOREIGN KEY (`message_id`) REFERENCES `message`(`id`) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `permission` (
|
||||||
|
`project_id` text PRIMARY KEY,
|
||||||
|
`time_created` integer NOT NULL,
|
||||||
|
`time_updated` integer NOT NULL,
|
||||||
|
`data` text NOT NULL,
|
||||||
|
CONSTRAINT `fk_permission_project_id_project_id_fk` FOREIGN KEY (`project_id`) REFERENCES `project`(`id`) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `session` (
|
||||||
|
`id` text PRIMARY KEY,
|
||||||
|
`project_id` text NOT NULL,
|
||||||
|
`parent_id` text,
|
||||||
|
`slug` text NOT NULL,
|
||||||
|
`directory` text NOT NULL,
|
||||||
|
`title` text NOT NULL,
|
||||||
|
`version` text NOT NULL,
|
||||||
|
`share_url` text,
|
||||||
|
`summary_additions` integer,
|
||||||
|
`summary_deletions` integer,
|
||||||
|
`summary_files` integer,
|
||||||
|
`summary_diffs` text,
|
||||||
|
`revert` text,
|
||||||
|
`permission` text,
|
||||||
|
`time_created` integer NOT NULL,
|
||||||
|
`time_updated` integer NOT NULL,
|
||||||
|
`time_compacting` integer,
|
||||||
|
`time_archived` integer,
|
||||||
|
CONSTRAINT `fk_session_project_id_project_id_fk` FOREIGN KEY (`project_id`) REFERENCES `project`(`id`) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `todo` (
|
||||||
|
`session_id` text NOT NULL,
|
||||||
|
`content` text NOT NULL,
|
||||||
|
`status` text NOT NULL,
|
||||||
|
`priority` text NOT NULL,
|
||||||
|
`position` integer NOT NULL,
|
||||||
|
`time_created` integer NOT NULL,
|
||||||
|
`time_updated` integer NOT NULL,
|
||||||
|
CONSTRAINT `todo_pk` PRIMARY KEY(`session_id`, `position`),
|
||||||
|
CONSTRAINT `fk_todo_session_id_session_id_fk` FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE `session_share` (
|
||||||
|
`session_id` text PRIMARY KEY,
|
||||||
|
`id` text NOT NULL,
|
||||||
|
`secret` text NOT NULL,
|
||||||
|
`url` text NOT NULL,
|
||||||
|
`time_created` integer NOT NULL,
|
||||||
|
`time_updated` integer NOT NULL,
|
||||||
|
CONSTRAINT `fk_session_share_session_id_session_id_fk` FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE INDEX `message_session_idx` ON `message` (`session_id`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `part_message_idx` ON `part` (`message_id`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `part_session_idx` ON `part` (`session_id`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `session_project_idx` ON `session` (`project_id`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `session_parent_idx` ON `session` (`parent_id`);--> statement-breakpoint
|
||||||
|
CREATE INDEX `todo_session_idx` ON `todo` (`session_id`);
|
||||||
|
|
@ -0,0 +1,796 @@
|
||||||
|
{
|
||||||
|
"version": "7",
|
||||||
|
"dialect": "sqlite",
|
||||||
|
"id": "068758ed-a97a-46f6-8a59-6c639ae7c20c",
|
||||||
|
"prevIds": ["00000000-0000-0000-0000-000000000000"],
|
||||||
|
"ddl": [
|
||||||
|
{
|
||||||
|
"name": "project",
|
||||||
|
"entityType": "tables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "message",
|
||||||
|
"entityType": "tables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "part",
|
||||||
|
"entityType": "tables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "permission",
|
||||||
|
"entityType": "tables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "session",
|
||||||
|
"entityType": "tables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "todo",
|
||||||
|
"entityType": "tables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "session_share",
|
||||||
|
"entityType": "tables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "id",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "project"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "worktree",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "project"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "vcs",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "project"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "name",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "project"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "icon_url",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "project"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "icon_color",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "project"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "time_created",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "project"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "time_updated",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "project"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "time_initialized",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "project"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "sandboxes",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "project"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "id",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "session_id",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "time_created",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "time_updated",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "data",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "id",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "part"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "message_id",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "part"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "session_id",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "part"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "time_created",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "part"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "time_updated",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "part"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "data",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "part"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "project_id",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "permission"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "time_created",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "permission"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "time_updated",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "permission"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "data",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "permission"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "id",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "session"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "project_id",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "session"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "parent_id",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "session"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "slug",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "session"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "directory",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "session"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "title",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "session"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "version",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "session"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "share_url",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "session"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "summary_additions",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "session"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "summary_deletions",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "session"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "summary_files",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "session"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "summary_diffs",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "session"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "revert",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "session"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "permission",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "session"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "time_created",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "session"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "time_updated",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "session"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "time_compacting",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "session"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "time_archived",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "session"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "session_id",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "todo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "content",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "todo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "status",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "todo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "priority",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "todo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "position",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "todo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "time_created",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "todo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "time_updated",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "todo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "session_id",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "session_share"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "id",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "session_share"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "secret",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "session_share"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "url",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "session_share"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "time_created",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "session_share"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "time_updated",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "session_share"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": ["session_id"],
|
||||||
|
"tableTo": "session",
|
||||||
|
"columnsTo": ["id"],
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"nameExplicit": false,
|
||||||
|
"name": "fk_message_session_id_session_id_fk",
|
||||||
|
"entityType": "fks",
|
||||||
|
"table": "message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": ["message_id"],
|
||||||
|
"tableTo": "message",
|
||||||
|
"columnsTo": ["id"],
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"nameExplicit": false,
|
||||||
|
"name": "fk_part_message_id_message_id_fk",
|
||||||
|
"entityType": "fks",
|
||||||
|
"table": "part"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": ["project_id"],
|
||||||
|
"tableTo": "project",
|
||||||
|
"columnsTo": ["id"],
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"nameExplicit": false,
|
||||||
|
"name": "fk_permission_project_id_project_id_fk",
|
||||||
|
"entityType": "fks",
|
||||||
|
"table": "permission"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": ["project_id"],
|
||||||
|
"tableTo": "project",
|
||||||
|
"columnsTo": ["id"],
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"nameExplicit": false,
|
||||||
|
"name": "fk_session_project_id_project_id_fk",
|
||||||
|
"entityType": "fks",
|
||||||
|
"table": "session"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": ["session_id"],
|
||||||
|
"tableTo": "session",
|
||||||
|
"columnsTo": ["id"],
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"nameExplicit": false,
|
||||||
|
"name": "fk_todo_session_id_session_id_fk",
|
||||||
|
"entityType": "fks",
|
||||||
|
"table": "todo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": ["session_id"],
|
||||||
|
"tableTo": "session",
|
||||||
|
"columnsTo": ["id"],
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"nameExplicit": false,
|
||||||
|
"name": "fk_session_share_session_id_session_id_fk",
|
||||||
|
"entityType": "fks",
|
||||||
|
"table": "session_share"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": ["session_id", "position"],
|
||||||
|
"nameExplicit": false,
|
||||||
|
"name": "todo_pk",
|
||||||
|
"entityType": "pks",
|
||||||
|
"table": "todo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": ["id"],
|
||||||
|
"nameExplicit": false,
|
||||||
|
"name": "project_pk",
|
||||||
|
"table": "project",
|
||||||
|
"entityType": "pks"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": ["id"],
|
||||||
|
"nameExplicit": false,
|
||||||
|
"name": "message_pk",
|
||||||
|
"table": "message",
|
||||||
|
"entityType": "pks"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": ["id"],
|
||||||
|
"nameExplicit": false,
|
||||||
|
"name": "part_pk",
|
||||||
|
"table": "part",
|
||||||
|
"entityType": "pks"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": ["project_id"],
|
||||||
|
"nameExplicit": false,
|
||||||
|
"name": "permission_pk",
|
||||||
|
"table": "permission",
|
||||||
|
"entityType": "pks"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": ["id"],
|
||||||
|
"nameExplicit": false,
|
||||||
|
"name": "session_pk",
|
||||||
|
"table": "session",
|
||||||
|
"entityType": "pks"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": ["session_id"],
|
||||||
|
"nameExplicit": false,
|
||||||
|
"name": "session_share_pk",
|
||||||
|
"table": "session_share",
|
||||||
|
"entityType": "pks"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"value": "session_id",
|
||||||
|
"isExpression": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isUnique": false,
|
||||||
|
"where": null,
|
||||||
|
"origin": "manual",
|
||||||
|
"name": "message_session_idx",
|
||||||
|
"entityType": "indexes",
|
||||||
|
"table": "message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"value": "message_id",
|
||||||
|
"isExpression": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isUnique": false,
|
||||||
|
"where": null,
|
||||||
|
"origin": "manual",
|
||||||
|
"name": "part_message_idx",
|
||||||
|
"entityType": "indexes",
|
||||||
|
"table": "part"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"value": "session_id",
|
||||||
|
"isExpression": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isUnique": false,
|
||||||
|
"where": null,
|
||||||
|
"origin": "manual",
|
||||||
|
"name": "part_session_idx",
|
||||||
|
"entityType": "indexes",
|
||||||
|
"table": "part"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"value": "project_id",
|
||||||
|
"isExpression": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isUnique": false,
|
||||||
|
"where": null,
|
||||||
|
"origin": "manual",
|
||||||
|
"name": "session_project_idx",
|
||||||
|
"entityType": "indexes",
|
||||||
|
"table": "session"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"value": "parent_id",
|
||||||
|
"isExpression": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isUnique": false,
|
||||||
|
"where": null,
|
||||||
|
"origin": "manual",
|
||||||
|
"name": "session_parent_idx",
|
||||||
|
"entityType": "indexes",
|
||||||
|
"table": "session"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"value": "session_id",
|
||||||
|
"isExpression": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isUnique": false,
|
||||||
|
"where": null,
|
||||||
|
"origin": "manual",
|
||||||
|
"name": "todo_session_idx",
|
||||||
|
"entityType": "indexes",
|
||||||
|
"table": "todo"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"renames": []
|
||||||
|
}
|
||||||
|
|
@ -15,7 +15,8 @@
|
||||||
"lint": "echo 'Running lint checks...' && bun test --coverage",
|
"lint": "echo 'Running lint checks...' && bun test --coverage",
|
||||||
"format": "echo 'Formatting code...' && bun run --prettier --write src/**/*.ts",
|
"format": "echo 'Formatting code...' && bun run --prettier --write src/**/*.ts",
|
||||||
"docs": "echo 'Generating documentation...' && find src -name '*.ts' -exec echo 'Processing: {}' \\;",
|
"docs": "echo 'Generating documentation...' && find src -name '*.ts' -exec echo 'Processing: {}' \\;",
|
||||||
"deploy": "echo 'Deploying application...' && bun run build && echo 'Deployment completed successfully'"
|
"deploy": "echo 'Deploying application...' && bun run build && echo 'Deployment completed successfully'",
|
||||||
|
"db": "bun drizzle-kit"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"opencode": "./bin/opencode"
|
"opencode": "./bin/opencode"
|
||||||
|
|
@ -42,6 +43,8 @@
|
||||||
"@types/turndown": "5.0.5",
|
"@types/turndown": "5.0.5",
|
||||||
"@types/yargs": "17.0.33",
|
"@types/yargs": "17.0.33",
|
||||||
"@typescript/native-preview": "catalog:",
|
"@typescript/native-preview": "catalog:",
|
||||||
|
"drizzle-kit": "1.0.0-beta.12-a5629fb",
|
||||||
|
"drizzle-orm": "1.0.0-beta.12-a5629fb",
|
||||||
"typescript": "catalog:",
|
"typescript": "catalog:",
|
||||||
"vscode-languageserver-types": "3.17.5",
|
"vscode-languageserver-types": "3.17.5",
|
||||||
"why-is-node-running": "3.2.2",
|
"why-is-node-running": "3.2.2",
|
||||||
|
|
@ -98,6 +101,7 @@
|
||||||
"clipboardy": "4.0.0",
|
"clipboardy": "4.0.0",
|
||||||
"decimal.js": "10.5.0",
|
"decimal.js": "10.5.0",
|
||||||
"diff": "catalog:",
|
"diff": "catalog:",
|
||||||
|
"drizzle-orm": "1.0.0-beta.12-a5629fb",
|
||||||
"fuzzysort": "3.1.0",
|
"fuzzysort": "3.1.0",
|
||||||
"gray-matter": "4.0.3",
|
"gray-matter": "4.0.3",
|
||||||
"hono": "catalog:",
|
"hono": "catalog:",
|
||||||
|
|
@ -120,5 +124,8 @@
|
||||||
"yargs": "18.0.0",
|
"yargs": "18.0.0",
|
||||||
"zod": "catalog:",
|
"zod": "catalog:",
|
||||||
"zod-to-json-schema": "3.24.5"
|
"zod-to-json-schema": "3.24.5"
|
||||||
|
},
|
||||||
|
"overrides": {
|
||||||
|
"drizzle-orm": "1.0.0-beta.12-a5629fb"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,32 @@ await Bun.write(
|
||||||
)
|
)
|
||||||
console.log("Generated models-snapshot.ts")
|
console.log("Generated models-snapshot.ts")
|
||||||
|
|
||||||
|
// Load migrations from migration directories
|
||||||
|
const migrationDirs = (await fs.promises.readdir(path.join(dir, "migration"), { withFileTypes: true }))
|
||||||
|
.filter((entry) => entry.isDirectory() && /^\d{4}\d{2}\d{2}\d{2}\d{2}\d{2}/.test(entry.name))
|
||||||
|
.map((entry) => entry.name)
|
||||||
|
.sort()
|
||||||
|
|
||||||
|
const migrations = await Promise.all(
|
||||||
|
migrationDirs.map(async (name) => {
|
||||||
|
const file = path.join(dir, "migration", name, "migration.sql")
|
||||||
|
const sql = await Bun.file(file).text()
|
||||||
|
const match = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/.exec(name)
|
||||||
|
const timestamp = match
|
||||||
|
? Date.UTC(
|
||||||
|
Number(match[1]),
|
||||||
|
Number(match[2]) - 1,
|
||||||
|
Number(match[3]),
|
||||||
|
Number(match[4]),
|
||||||
|
Number(match[5]),
|
||||||
|
Number(match[6]),
|
||||||
|
)
|
||||||
|
: 0
|
||||||
|
return { sql, timestamp }
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
console.log(`Loaded ${migrations.length} migrations`)
|
||||||
|
|
||||||
const singleFlag = process.argv.includes("--single")
|
const singleFlag = process.argv.includes("--single")
|
||||||
const baselineFlag = process.argv.includes("--baseline")
|
const baselineFlag = process.argv.includes("--baseline")
|
||||||
const skipInstall = process.argv.includes("--skip-install")
|
const skipInstall = process.argv.includes("--skip-install")
|
||||||
|
|
@ -156,6 +182,7 @@ for (const item of targets) {
|
||||||
entrypoints: ["./src/index.ts", parserWorker, workerPath],
|
entrypoints: ["./src/index.ts", parserWorker, workerPath],
|
||||||
define: {
|
define: {
|
||||||
OPENCODE_VERSION: `'${Script.version}'`,
|
OPENCODE_VERSION: `'${Script.version}'`,
|
||||||
|
OPENCODE_MIGRATIONS: JSON.stringify(migrations),
|
||||||
OTUI_TREE_SITTER_WORKER_PATH: bunfsRoot + workerRelativePath,
|
OTUI_TREE_SITTER_WORKER_PATH: bunfsRoot + workerRelativePath,
|
||||||
OPENCODE_WORKER_PATH: workerPath,
|
OPENCODE_WORKER_PATH: workerPath,
|
||||||
OPENCODE_CHANNEL: `'${Script.channel}'`,
|
OPENCODE_CHANNEL: `'${Script.channel}'`,
|
||||||
|
|
|
||||||
16
packages/opencode/script/check-migrations.ts
Normal file
16
packages/opencode/script/check-migrations.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/usr/bin/env bun
|
||||||
|
|
||||||
|
import { $ } from "bun"
|
||||||
|
|
||||||
|
// drizzle-kit check compares schema to migrations, exits non-zero if drift
|
||||||
|
const result = await $`bun drizzle-kit check`.quiet().nothrow()
|
||||||
|
|
||||||
|
if (result.exitCode !== 0) {
|
||||||
|
console.error("Schema has changes not captured in migrations!")
|
||||||
|
console.error("Run: bun drizzle-kit generate")
|
||||||
|
console.error("")
|
||||||
|
console.error(result.stderr.toString())
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Migrations are up to date")
|
||||||
|
|
@ -365,46 +365,68 @@ export namespace ACP {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (part.type === "text") {
|
case "message.part.delta": {
|
||||||
const delta = props.delta
|
const props = event.properties
|
||||||
if (delta && part.ignored !== true) {
|
const session = this.sessionManager.tryGet(props.sessionID)
|
||||||
await this.connection
|
if (!session) return
|
||||||
.sessionUpdate({
|
const sessionId = session.id
|
||||||
sessionId,
|
|
||||||
update: {
|
const message = await this.sdk.session
|
||||||
sessionUpdate: "agent_message_chunk",
|
.message(
|
||||||
content: {
|
{
|
||||||
type: "text",
|
sessionID: props.sessionID,
|
||||||
text: delta,
|
messageID: props.messageID,
|
||||||
},
|
directory: session.cwd,
|
||||||
|
},
|
||||||
|
{ throwOnError: true },
|
||||||
|
)
|
||||||
|
.then((x) => x.data)
|
||||||
|
.catch((error) => {
|
||||||
|
log.error("unexpected error when fetching message", { error })
|
||||||
|
return undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!message || message.info.role !== "assistant") return
|
||||||
|
|
||||||
|
const part = message.parts.find((p) => p.id === props.partID)
|
||||||
|
if (!part) return
|
||||||
|
|
||||||
|
if (part.type === "text" && props.field === "text" && part.ignored !== true) {
|
||||||
|
await this.connection
|
||||||
|
.sessionUpdate({
|
||||||
|
sessionId,
|
||||||
|
update: {
|
||||||
|
sessionUpdate: "agent_message_chunk",
|
||||||
|
content: {
|
||||||
|
type: "text",
|
||||||
|
text: props.delta,
|
||||||
},
|
},
|
||||||
})
|
},
|
||||||
.catch((error) => {
|
})
|
||||||
log.error("failed to send text to ACP", { error })
|
.catch((error) => {
|
||||||
})
|
log.error("failed to send text delta to ACP", { error })
|
||||||
}
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (part.type === "reasoning") {
|
if (part.type === "reasoning" && props.field === "text") {
|
||||||
const delta = props.delta
|
await this.connection
|
||||||
if (delta) {
|
.sessionUpdate({
|
||||||
await this.connection
|
sessionId,
|
||||||
.sessionUpdate({
|
update: {
|
||||||
sessionId,
|
sessionUpdate: "agent_thought_chunk",
|
||||||
update: {
|
content: {
|
||||||
sessionUpdate: "agent_thought_chunk",
|
type: "text",
|
||||||
content: {
|
text: props.delta,
|
||||||
type: "text",
|
|
||||||
text: delta,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
},
|
||||||
.catch((error) => {
|
})
|
||||||
log.error("failed to send reasoning to ACP", { error })
|
.catch((error) => {
|
||||||
})
|
log.error("failed to send reasoning delta to ACP", { error })
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ import type { Argv } from "yargs"
|
||||||
import { Session } from "../../session"
|
import { Session } from "../../session"
|
||||||
import { cmd } from "./cmd"
|
import { cmd } from "./cmd"
|
||||||
import { bootstrap } from "../bootstrap"
|
import { bootstrap } from "../bootstrap"
|
||||||
import { Storage } from "../../storage/storage"
|
import { Database } from "../../storage/db"
|
||||||
|
import { SessionTable, MessageTable, PartTable } from "../../session/session.sql"
|
||||||
import { Instance } from "../../project/instance"
|
import { Instance } from "../../project/instance"
|
||||||
import { EOL } from "os"
|
import { EOL } from "os"
|
||||||
|
|
||||||
|
|
@ -81,13 +82,35 @@ export const ImportCommand = cmd({
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await Storage.write(["session", Instance.project.id, exportData.info.id], exportData.info)
|
Database.use((db) => db.insert(SessionTable).values(Session.toRow(exportData.info)).onConflictDoNothing().run())
|
||||||
|
|
||||||
for (const msg of exportData.messages) {
|
for (const msg of exportData.messages) {
|
||||||
await Storage.write(["message", exportData.info.id, msg.info.id], msg.info)
|
Database.use((db) =>
|
||||||
|
db
|
||||||
|
.insert(MessageTable)
|
||||||
|
.values({
|
||||||
|
id: msg.info.id,
|
||||||
|
session_id: exportData.info.id,
|
||||||
|
time_created: msg.info.time?.created ?? Date.now(),
|
||||||
|
data: msg.info,
|
||||||
|
})
|
||||||
|
.onConflictDoNothing()
|
||||||
|
.run(),
|
||||||
|
)
|
||||||
|
|
||||||
for (const part of msg.parts) {
|
for (const part of msg.parts) {
|
||||||
await Storage.write(["part", msg.info.id, part.id], part)
|
Database.use((db) =>
|
||||||
|
db
|
||||||
|
.insert(PartTable)
|
||||||
|
.values({
|
||||||
|
id: part.id,
|
||||||
|
message_id: msg.info.id,
|
||||||
|
session_id: exportData.info.id,
|
||||||
|
data: part,
|
||||||
|
})
|
||||||
|
.onConflictDoNothing()
|
||||||
|
.run(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ import type { Argv } from "yargs"
|
||||||
import { cmd } from "./cmd"
|
import { cmd } from "./cmd"
|
||||||
import { Session } from "../../session"
|
import { Session } from "../../session"
|
||||||
import { bootstrap } from "../bootstrap"
|
import { bootstrap } from "../bootstrap"
|
||||||
import { Storage } from "../../storage/storage"
|
import { Database } from "../../storage/db"
|
||||||
|
import { SessionTable } from "../../session/session.sql"
|
||||||
import { Project } from "../../project/project"
|
import { Project } from "../../project/project"
|
||||||
import { Instance } from "../../project/instance"
|
import { Instance } from "../../project/instance"
|
||||||
|
|
||||||
|
|
@ -87,25 +88,8 @@ async function getCurrentProject(): Promise<Project.Info> {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getAllSessions(): Promise<Session.Info[]> {
|
async function getAllSessions(): Promise<Session.Info[]> {
|
||||||
const sessions: Session.Info[] = []
|
const rows = Database.use((db) => db.select().from(SessionTable).all())
|
||||||
|
return rows.map((row) => Session.fromRow(row))
|
||||||
const projectKeys = await Storage.list(["project"])
|
|
||||||
const projects = await Promise.all(projectKeys.map((key) => Storage.read<Project.Info>(key)))
|
|
||||||
|
|
||||||
for (const project of projects) {
|
|
||||||
if (!project) continue
|
|
||||||
|
|
||||||
const sessionKeys = await Storage.list(["session", project.id])
|
|
||||||
const projectSessions = await Promise.all(sessionKeys.map((key) => Storage.read<Session.Info>(key)))
|
|
||||||
|
|
||||||
for (const session of projectSessions) {
|
|
||||||
if (session) {
|
|
||||||
sessions.push(session)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sessions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function aggregateSessionStats(days?: number, projectFilter?: string): Promise<SessionStats> {
|
export async function aggregateSessionStats(days?: number, projectFilter?: string): Promise<SessionStats> {
|
||||||
|
|
|
||||||
|
|
@ -299,6 +299,24 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case "message.part.delta": {
|
||||||
|
const parts = store.part[event.properties.messageID]
|
||||||
|
if (!parts) break
|
||||||
|
const result = Binary.search(parts, event.properties.partID, (p) => p.id)
|
||||||
|
if (!result.found) break
|
||||||
|
setStore(
|
||||||
|
"part",
|
||||||
|
event.properties.messageID,
|
||||||
|
produce((draft) => {
|
||||||
|
const part = draft[result.index]
|
||||||
|
const field = event.properties.field as keyof typeof part
|
||||||
|
const existing = part[field] as string | undefined
|
||||||
|
;(part[field] as string) = (existing ?? "") + event.properties.delta
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
case "message.part.removed": {
|
case "message.part.removed": {
|
||||||
const parts = store.part[event.properties.messageID]
|
const parts = store.part[event.properties.messageID]
|
||||||
const result = Binary.search(parts, event.properties.partID, (p) => p.id)
|
const result = Binary.search(parts, event.properties.partID, (p) => p.id)
|
||||||
|
|
|
||||||
|
|
@ -2023,8 +2023,8 @@ function ApplyPatch(props: ToolProps<typeof ApplyPatchTool>) {
|
||||||
</For>
|
</For>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={true}>
|
<Match when={true}>
|
||||||
<InlineTool icon="%" pending="Preparing apply_patch..." complete={false} part={props.part}>
|
<InlineTool icon="%" pending="Preparing patch..." complete={false} part={props.part}>
|
||||||
apply_patch
|
Patch
|
||||||
</InlineTool>
|
</InlineTool>
|
||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,10 @@ import { EOL } from "os"
|
||||||
import { WebCommand } from "./cli/cmd/web"
|
import { WebCommand } from "./cli/cmd/web"
|
||||||
import { PrCommand } from "./cli/cmd/pr"
|
import { PrCommand } from "./cli/cmd/pr"
|
||||||
import { SessionCommand } from "./cli/cmd/session"
|
import { SessionCommand } from "./cli/cmd/session"
|
||||||
|
import path from "path"
|
||||||
|
import { Global } from "./global"
|
||||||
|
import { JsonMigration } from "./storage/json-migration"
|
||||||
|
import { Database } from "./storage/db"
|
||||||
|
|
||||||
process.on("unhandledRejection", (e) => {
|
process.on("unhandledRejection", (e) => {
|
||||||
Log.Default.error("rejection", {
|
Log.Default.error("rejection", {
|
||||||
|
|
@ -74,6 +78,13 @@ const cli = yargs(hideBin(process.argv))
|
||||||
version: Installation.VERSION,
|
version: Installation.VERSION,
|
||||||
args: process.argv.slice(2),
|
args: process.argv.slice(2),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const marker = path.join(Global.Path.data, "opencode.db")
|
||||||
|
if (!(await Bun.file(marker).exists())) {
|
||||||
|
console.log("Performing one time database migration, may take a few minutes...")
|
||||||
|
await JsonMigration.run(Database.Client().$client)
|
||||||
|
console.log("Database migration complete.")
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.usage("\n" + UI.logo())
|
.usage("\n" + UI.logo())
|
||||||
.completion("completion", "generate shell completion script")
|
.completion("completion", "generate shell completion script")
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ import { BusEvent } from "@/bus/bus-event"
|
||||||
import { Config } from "@/config/config"
|
import { Config } from "@/config/config"
|
||||||
import { Identifier } from "@/id/id"
|
import { Identifier } from "@/id/id"
|
||||||
import { Instance } from "@/project/instance"
|
import { Instance } from "@/project/instance"
|
||||||
import { Storage } from "@/storage/storage"
|
import { Database, eq } from "@/storage/db"
|
||||||
|
import { PermissionTable } from "@/session/session.sql"
|
||||||
import { fn } from "@/util/fn"
|
import { fn } from "@/util/fn"
|
||||||
import { Log } from "@/util/log"
|
import { Log } from "@/util/log"
|
||||||
import { Wildcard } from "@/util/wildcard"
|
import { Wildcard } from "@/util/wildcard"
|
||||||
|
|
@ -105,9 +106,12 @@ export namespace PermissionNext {
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = Instance.state(async () => {
|
const state = Instance.state(() => {
|
||||||
const projectID = Instance.project.id
|
const projectID = Instance.project.id
|
||||||
const stored = await Storage.read<Ruleset>(["permission", projectID]).catch(() => [] as Ruleset)
|
const row = Database.use((db) =>
|
||||||
|
db.select().from(PermissionTable).where(eq(PermissionTable.project_id, projectID)).get(),
|
||||||
|
)
|
||||||
|
const stored = row?.data ?? ([] as Ruleset)
|
||||||
|
|
||||||
const pending: Record<
|
const pending: Record<
|
||||||
string,
|
string,
|
||||||
|
|
@ -222,7 +226,8 @@ export namespace PermissionNext {
|
||||||
|
|
||||||
// TODO: we don't save the permission ruleset to disk yet until there's
|
// TODO: we don't save the permission ruleset to disk yet until there's
|
||||||
// UI to manage it
|
// UI to manage it
|
||||||
// await Storage.write(["permission", Instance.project.id], s.approved)
|
// db().insert(PermissionTable).values({ projectID: Instance.project.id, data: s.approved })
|
||||||
|
// .onConflictDoUpdate({ target: PermissionTable.projectID, set: { data: s.approved } }).run()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -275,6 +280,7 @@ export namespace PermissionNext {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function list() {
|
export async function list() {
|
||||||
return state().then((x) => Object.values(x.pending).map((x) => x.info))
|
const s = await state()
|
||||||
|
return Object.values(s.pending).map((x) => x.info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { Plugin } from "../plugin"
|
import { Plugin } from "../plugin"
|
||||||
import { Share } from "../share/share"
|
|
||||||
import { Format } from "../format"
|
import { Format } from "../format"
|
||||||
import { LSP } from "../lsp"
|
import { LSP } from "../lsp"
|
||||||
import { FileWatcher } from "../file/watcher"
|
import { FileWatcher } from "../file/watcher"
|
||||||
|
|
@ -17,7 +16,6 @@ import { Truncate } from "../tool/truncation"
|
||||||
export async function InstanceBootstrap() {
|
export async function InstanceBootstrap() {
|
||||||
Log.Default.info("bootstrapping", { directory: Instance.directory })
|
Log.Default.info("bootstrapping", { directory: Instance.directory })
|
||||||
await Plugin.init()
|
await Plugin.init()
|
||||||
Share.init()
|
|
||||||
ShareNext.init()
|
ShareNext.init()
|
||||||
Format.init()
|
Format.init()
|
||||||
await LSP.init()
|
await LSP.init()
|
||||||
|
|
|
||||||
14
packages/opencode/src/project/project.sql.ts
Normal file
14
packages/opencode/src/project/project.sql.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core"
|
||||||
|
import { Timestamps } from "@/storage/schema.sql"
|
||||||
|
|
||||||
|
export const ProjectTable = sqliteTable("project", {
|
||||||
|
id: text().primaryKey(),
|
||||||
|
worktree: text().notNull(),
|
||||||
|
vcs: text(),
|
||||||
|
name: text(),
|
||||||
|
icon_url: text(),
|
||||||
|
icon_color: text(),
|
||||||
|
...Timestamps,
|
||||||
|
time_initialized: integer(),
|
||||||
|
sandboxes: text({ mode: "json" }).notNull().$type<string[]>(),
|
||||||
|
})
|
||||||
|
|
@ -1,18 +1,17 @@
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
import fs from "fs/promises"
|
|
||||||
import { Filesystem } from "../util/filesystem"
|
import { Filesystem } from "../util/filesystem"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { $ } from "bun"
|
import { $ } from "bun"
|
||||||
import { Storage } from "../storage/storage"
|
import { Database, eq } from "../storage/db"
|
||||||
|
import { ProjectTable } from "./project.sql"
|
||||||
|
import { SessionTable } from "../session/session.sql"
|
||||||
import { Log } from "../util/log"
|
import { Log } from "../util/log"
|
||||||
import { Flag } from "@/flag/flag"
|
import { Flag } from "@/flag/flag"
|
||||||
import { Session } from "../session"
|
|
||||||
import { work } from "../util/queue"
|
import { work } from "../util/queue"
|
||||||
import { fn } from "@opencode-ai/util/fn"
|
import { fn } from "@opencode-ai/util/fn"
|
||||||
import { BusEvent } from "@/bus/bus-event"
|
import { BusEvent } from "@/bus/bus-event"
|
||||||
import { iife } from "@/util/iife"
|
import { iife } from "@/util/iife"
|
||||||
import { GlobalBus } from "@/bus/global"
|
import { GlobalBus } from "@/bus/global"
|
||||||
import { existsSync } from "fs"
|
|
||||||
|
|
||||||
export namespace Project {
|
export namespace Project {
|
||||||
const log = Log.create({ service: "project" })
|
const log = Log.create({ service: "project" })
|
||||||
|
|
@ -50,66 +49,85 @@ export namespace Project {
|
||||||
Updated: BusEvent.define("project.updated", Info),
|
Updated: BusEvent.define("project.updated", Info),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Row = typeof ProjectTable.$inferSelect
|
||||||
|
|
||||||
|
export function fromRow(row: Row): Info {
|
||||||
|
const icon =
|
||||||
|
row.icon_url || row.icon_color
|
||||||
|
? { url: row.icon_url ?? undefined, color: row.icon_color ?? undefined }
|
||||||
|
: undefined
|
||||||
|
return {
|
||||||
|
id: row.id,
|
||||||
|
worktree: row.worktree,
|
||||||
|
vcs: row.vcs ? Info.shape.vcs.parse(row.vcs) : undefined,
|
||||||
|
name: row.name ?? undefined,
|
||||||
|
icon,
|
||||||
|
time: {
|
||||||
|
created: row.time_created,
|
||||||
|
updated: row.time_updated,
|
||||||
|
initialized: row.time_initialized ?? undefined,
|
||||||
|
},
|
||||||
|
sandboxes: row.sandboxes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function fromDirectory(directory: string) {
|
export async function fromDirectory(directory: string) {
|
||||||
log.info("fromDirectory", { directory })
|
log.info("fromDirectory", { directory })
|
||||||
|
|
||||||
const { id, sandbox, worktree, vcs } = await iife(async () => {
|
const data = await iife(async () => {
|
||||||
const matches = Filesystem.up({ targets: [".git"], start: directory })
|
const matches = Filesystem.up({ targets: [".git"], start: directory })
|
||||||
const git = await matches.next().then((x) => x.value)
|
const git = await matches.next().then((x) => x.value)
|
||||||
await matches.return()
|
await matches.return()
|
||||||
if (git) {
|
if (git) {
|
||||||
let sandbox = path.dirname(git)
|
const sandbox = path.dirname(git)
|
||||||
|
const bin = Bun.which("git")
|
||||||
|
|
||||||
const gitBinary = Bun.which("git")
|
const cached = await Bun.file(path.join(git, "opencode"))
|
||||||
|
|
||||||
// cached id calculation
|
|
||||||
let id = await Bun.file(path.join(git, "opencode"))
|
|
||||||
.text()
|
.text()
|
||||||
.then((x) => x.trim())
|
.then((x) => x.trim())
|
||||||
.catch(() => undefined)
|
.catch(() => undefined)
|
||||||
|
|
||||||
if (!gitBinary) {
|
if (!bin) {
|
||||||
return {
|
return {
|
||||||
id: id ?? "global",
|
id: cached ?? "global",
|
||||||
worktree: sandbox,
|
worktree: sandbox,
|
||||||
sandbox: sandbox,
|
sandbox: sandbox,
|
||||||
vcs: Info.shape.vcs.parse(Flag.OPENCODE_FAKE_VCS),
|
vcs: Info.shape.vcs.parse(Flag.OPENCODE_FAKE_VCS),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate id from root commit
|
const roots = cached
|
||||||
if (!id) {
|
? undefined
|
||||||
const roots = await $`git rev-list --max-parents=0 --all`
|
: await $`git rev-list --max-parents=0 --all`
|
||||||
.quiet()
|
.quiet()
|
||||||
.nothrow()
|
.nothrow()
|
||||||
.cwd(sandbox)
|
.cwd(sandbox)
|
||||||
.text()
|
.text()
|
||||||
.then((x) =>
|
.then((x) =>
|
||||||
x
|
x
|
||||||
.split("\n")
|
.split("\n")
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.map((x) => x.trim())
|
.map((x) => x.trim())
|
||||||
.toSorted(),
|
.toSorted(),
|
||||||
)
|
)
|
||||||
.catch(() => undefined)
|
|
||||||
|
|
||||||
if (!roots) {
|
|
||||||
return {
|
|
||||||
id: "global",
|
|
||||||
worktree: sandbox,
|
|
||||||
sandbox: sandbox,
|
|
||||||
vcs: Info.shape.vcs.parse(Flag.OPENCODE_FAKE_VCS),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
id = roots[0]
|
|
||||||
if (id) {
|
|
||||||
void Bun.file(path.join(git, "opencode"))
|
|
||||||
.write(id)
|
|
||||||
.catch(() => undefined)
|
.catch(() => undefined)
|
||||||
|
|
||||||
|
if (!cached && !roots) {
|
||||||
|
return {
|
||||||
|
id: "global",
|
||||||
|
worktree: sandbox,
|
||||||
|
sandbox: sandbox,
|
||||||
|
vcs: Info.shape.vcs.parse(Flag.OPENCODE_FAKE_VCS),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const id = cached ?? roots?.[0]
|
||||||
|
if (!cached && id) {
|
||||||
|
void Bun.file(path.join(git, "opencode"))
|
||||||
|
.write(id)
|
||||||
|
.catch(() => undefined)
|
||||||
|
}
|
||||||
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return {
|
return {
|
||||||
id: "global",
|
id: "global",
|
||||||
|
|
@ -136,33 +154,31 @@ export namespace Project {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sandbox = top
|
const tree = await $`git rev-parse --git-common-dir`
|
||||||
|
|
||||||
const worktree = await $`git rev-parse --git-common-dir`
|
|
||||||
.quiet()
|
.quiet()
|
||||||
.nothrow()
|
.nothrow()
|
||||||
.cwd(sandbox)
|
.cwd(top)
|
||||||
.text()
|
.text()
|
||||||
.then((x) => {
|
.then((x) => {
|
||||||
const dirname = path.dirname(x.trim())
|
const dirname = path.dirname(x.trim())
|
||||||
if (dirname === ".") return sandbox
|
if (dirname === ".") return top
|
||||||
return dirname
|
return dirname
|
||||||
})
|
})
|
||||||
.catch(() => undefined)
|
.catch(() => undefined)
|
||||||
|
|
||||||
if (!worktree) {
|
if (!tree) {
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
sandbox,
|
sandbox: top,
|
||||||
worktree: sandbox,
|
worktree: top,
|
||||||
vcs: Info.shape.vcs.parse(Flag.OPENCODE_FAKE_VCS),
|
vcs: Info.shape.vcs.parse(Flag.OPENCODE_FAKE_VCS),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
sandbox,
|
sandbox: top,
|
||||||
worktree,
|
worktree: tree,
|
||||||
vcs: "git",
|
vcs: "git",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -175,47 +191,78 @@ export namespace Project {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
let existing = await Storage.read<Info>(["project", id]).catch(() => undefined)
|
const row = Database.use((db) => db.select().from(ProjectTable).where(eq(ProjectTable.id, data.id)).get())
|
||||||
if (!existing) {
|
const existing = await iife(async () => {
|
||||||
existing = {
|
if (row) return fromRow(row)
|
||||||
id,
|
const fresh: Info = {
|
||||||
worktree,
|
id: data.id,
|
||||||
vcs: vcs as Info["vcs"],
|
worktree: data.worktree,
|
||||||
|
vcs: data.vcs as Info["vcs"],
|
||||||
sandboxes: [],
|
sandboxes: [],
|
||||||
time: {
|
time: {
|
||||||
created: Date.now(),
|
created: Date.now(),
|
||||||
updated: Date.now(),
|
updated: Date.now(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if (id !== "global") {
|
if (data.id !== "global") {
|
||||||
await migrateFromGlobal(id, worktree)
|
await migrateFromGlobal(data.id, data.worktree)
|
||||||
}
|
}
|
||||||
}
|
return fresh
|
||||||
|
})
|
||||||
// migrate old projects before sandboxes
|
|
||||||
if (!existing.sandboxes) existing.sandboxes = []
|
|
||||||
|
|
||||||
if (Flag.OPENCODE_EXPERIMENTAL_ICON_DISCOVERY) discover(existing)
|
if (Flag.OPENCODE_EXPERIMENTAL_ICON_DISCOVERY) discover(existing)
|
||||||
|
|
||||||
const result: Info = {
|
const result: Info = {
|
||||||
...existing,
|
...existing,
|
||||||
worktree,
|
worktree: data.worktree,
|
||||||
vcs: vcs as Info["vcs"],
|
vcs: data.vcs as Info["vcs"],
|
||||||
time: {
|
time: {
|
||||||
...existing.time,
|
...existing.time,
|
||||||
updated: Date.now(),
|
updated: Date.now(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if (sandbox !== result.worktree && !result.sandboxes.includes(sandbox)) result.sandboxes.push(sandbox)
|
if (data.sandbox !== result.worktree && !result.sandboxes.includes(data.sandbox))
|
||||||
result.sandboxes = result.sandboxes.filter((x) => existsSync(x))
|
result.sandboxes.push(data.sandbox)
|
||||||
await Storage.write<Info>(["project", id], result)
|
const sandboxes: string[] = []
|
||||||
|
for (const x of result.sandboxes) {
|
||||||
|
const stat = await Bun.file(x)
|
||||||
|
.stat()
|
||||||
|
.catch(() => undefined)
|
||||||
|
if (stat) sandboxes.push(x)
|
||||||
|
}
|
||||||
|
result.sandboxes = sandboxes
|
||||||
|
const insert = {
|
||||||
|
id: result.id,
|
||||||
|
worktree: result.worktree,
|
||||||
|
vcs: result.vcs ?? null,
|
||||||
|
name: result.name,
|
||||||
|
icon_url: result.icon?.url,
|
||||||
|
icon_color: result.icon?.color,
|
||||||
|
time_created: result.time.created,
|
||||||
|
time_updated: result.time.updated,
|
||||||
|
time_initialized: result.time.initialized,
|
||||||
|
sandboxes: result.sandboxes,
|
||||||
|
}
|
||||||
|
const updateSet = {
|
||||||
|
worktree: result.worktree,
|
||||||
|
vcs: result.vcs ?? null,
|
||||||
|
name: result.name,
|
||||||
|
icon_url: result.icon?.url,
|
||||||
|
icon_color: result.icon?.color,
|
||||||
|
time_updated: result.time.updated,
|
||||||
|
time_initialized: result.time.initialized,
|
||||||
|
sandboxes: result.sandboxes,
|
||||||
|
}
|
||||||
|
Database.use((db) =>
|
||||||
|
db.insert(ProjectTable).values(insert).onConflictDoUpdate({ target: ProjectTable.id, set: updateSet }).run(),
|
||||||
|
)
|
||||||
GlobalBus.emit("event", {
|
GlobalBus.emit("event", {
|
||||||
payload: {
|
payload: {
|
||||||
type: Event.Updated.type,
|
type: Event.Updated.type,
|
||||||
properties: result,
|
properties: result,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return { project: result, sandbox }
|
return { project: result, sandbox: data.sandbox }
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function discover(input: Info) {
|
export async function discover(input: Info) {
|
||||||
|
|
@ -248,43 +295,54 @@ export namespace Project {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
async function migrateFromGlobal(newProjectID: string, worktree: string) {
|
async function migrateFromGlobal(id: string, worktree: string) {
|
||||||
const globalProject = await Storage.read<Info>(["project", "global"]).catch(() => undefined)
|
const row = Database.use((db) => db.select().from(ProjectTable).where(eq(ProjectTable.id, "global")).get())
|
||||||
if (!globalProject) return
|
if (!row) return
|
||||||
|
|
||||||
const globalSessions = await Storage.list(["session", "global"]).catch(() => [])
|
const sessions = Database.use((db) =>
|
||||||
if (globalSessions.length === 0) return
|
db.select().from(SessionTable).where(eq(SessionTable.project_id, "global")).all(),
|
||||||
|
)
|
||||||
|
if (sessions.length === 0) return
|
||||||
|
|
||||||
log.info("migrating sessions from global", { newProjectID, worktree, count: globalSessions.length })
|
log.info("migrating sessions from global", { newProjectID: id, worktree, count: sessions.length })
|
||||||
|
|
||||||
await work(10, globalSessions, async (key) => {
|
await work(10, sessions, async (row) => {
|
||||||
const sessionID = key[key.length - 1]
|
// Skip sessions that belong to a different directory
|
||||||
const session = await Storage.read<Session.Info>(key).catch(() => undefined)
|
if (row.directory && row.directory !== worktree) return
|
||||||
if (!session) return
|
|
||||||
if (session.directory && session.directory !== worktree) return
|
|
||||||
|
|
||||||
session.projectID = newProjectID
|
log.info("migrating session", { sessionID: row.id, from: "global", to: id })
|
||||||
log.info("migrating session", { sessionID, from: "global", to: newProjectID })
|
Database.use((db) => db.update(SessionTable).set({ project_id: id }).where(eq(SessionTable.id, row.id)).run())
|
||||||
await Storage.write(["session", newProjectID, sessionID], session)
|
|
||||||
await Storage.remove(key)
|
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
log.error("failed to migrate sessions from global to project", { error, projectId: newProjectID })
|
log.error("failed to migrate sessions from global to project", { error, projectId: id })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setInitialized(projectID: string) {
|
export function setInitialized(id: string) {
|
||||||
await Storage.update<Info>(["project", projectID], (draft) => {
|
Database.use((db) =>
|
||||||
draft.time.initialized = Date.now()
|
db
|
||||||
})
|
.update(ProjectTable)
|
||||||
|
.set({
|
||||||
|
time_initialized: Date.now(),
|
||||||
|
})
|
||||||
|
.where(eq(ProjectTable.id, id))
|
||||||
|
.run(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function list() {
|
export function list() {
|
||||||
const keys = await Storage.list(["project"])
|
return Database.use((db) =>
|
||||||
const projects = await Promise.all(keys.map((x) => Storage.read<Info>(x)))
|
db
|
||||||
return projects.map((project) => ({
|
.select()
|
||||||
...project,
|
.from(ProjectTable)
|
||||||
sandboxes: project.sandboxes?.filter((x) => existsSync(x)),
|
.all()
|
||||||
}))
|
.map((row) => fromRow(row)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function get(id: string): Info | undefined {
|
||||||
|
const row = Database.use((db) => db.select().from(ProjectTable).where(eq(ProjectTable.id, id)).get())
|
||||||
|
if (!row) return undefined
|
||||||
|
return fromRow(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const update = fn(
|
export const update = fn(
|
||||||
|
|
@ -295,77 +353,89 @@ export namespace Project {
|
||||||
commands: Info.shape.commands.optional(),
|
commands: Info.shape.commands.optional(),
|
||||||
}),
|
}),
|
||||||
async (input) => {
|
async (input) => {
|
||||||
const result = await Storage.update<Info>(["project", input.projectID], (draft) => {
|
const result = Database.use((db) =>
|
||||||
if (input.name !== undefined) draft.name = input.name
|
db
|
||||||
if (input.icon !== undefined) {
|
.update(ProjectTable)
|
||||||
draft.icon = {
|
.set({
|
||||||
...draft.icon,
|
name: input.name,
|
||||||
}
|
icon_url: input.icon?.url,
|
||||||
if (input.icon.url !== undefined) draft.icon.url = input.icon.url
|
icon_color: input.icon?.color,
|
||||||
if (input.icon.override !== undefined) draft.icon.override = input.icon.override || undefined
|
time_updated: Date.now(),
|
||||||
if (input.icon.color !== undefined) draft.icon.color = input.icon.color
|
})
|
||||||
}
|
.where(eq(ProjectTable.id, input.projectID))
|
||||||
|
.returning()
|
||||||
if (input.commands?.start !== undefined) {
|
.get(),
|
||||||
const start = input.commands.start || undefined
|
)
|
||||||
draft.commands = {
|
if (!result) throw new Error(`Project not found: ${input.projectID}`)
|
||||||
...(draft.commands ?? {}),
|
const data = fromRow(result)
|
||||||
}
|
|
||||||
draft.commands.start = start
|
|
||||||
if (!draft.commands.start) draft.commands = undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
draft.time.updated = Date.now()
|
|
||||||
})
|
|
||||||
GlobalBus.emit("event", {
|
GlobalBus.emit("event", {
|
||||||
payload: {
|
payload: {
|
||||||
type: Event.Updated.type,
|
type: Event.Updated.type,
|
||||||
properties: result,
|
properties: data,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return result
|
return data
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
export async function sandboxes(projectID: string) {
|
export async function sandboxes(id: string) {
|
||||||
const project = await Storage.read<Info>(["project", projectID]).catch(() => undefined)
|
const row = Database.use((db) => db.select().from(ProjectTable).where(eq(ProjectTable.id, id)).get())
|
||||||
if (!project?.sandboxes) return []
|
if (!row) return []
|
||||||
|
const data = fromRow(row)
|
||||||
const valid: string[] = []
|
const valid: string[] = []
|
||||||
for (const dir of project.sandboxes) {
|
for (const dir of data.sandboxes) {
|
||||||
const stat = await fs.stat(dir).catch(() => undefined)
|
const stat = await Bun.file(dir)
|
||||||
|
.stat()
|
||||||
|
.catch(() => undefined)
|
||||||
if (stat?.isDirectory()) valid.push(dir)
|
if (stat?.isDirectory()) valid.push(dir)
|
||||||
}
|
}
|
||||||
return valid
|
return valid
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function addSandbox(projectID: string, directory: string) {
|
export async function addSandbox(id: string, directory: string) {
|
||||||
const result = await Storage.update<Info>(["project", projectID], (draft) => {
|
const row = Database.use((db) => db.select().from(ProjectTable).where(eq(ProjectTable.id, id)).get())
|
||||||
const sandboxes = draft.sandboxes ?? []
|
if (!row) throw new Error(`Project not found: ${id}`)
|
||||||
if (!sandboxes.includes(directory)) sandboxes.push(directory)
|
const sandboxes = [...row.sandboxes]
|
||||||
draft.sandboxes = sandboxes
|
if (!sandboxes.includes(directory)) sandboxes.push(directory)
|
||||||
draft.time.updated = Date.now()
|
const result = Database.use((db) =>
|
||||||
})
|
db
|
||||||
|
.update(ProjectTable)
|
||||||
|
.set({ sandboxes, time_updated: Date.now() })
|
||||||
|
.where(eq(ProjectTable.id, id))
|
||||||
|
.returning()
|
||||||
|
.get(),
|
||||||
|
)
|
||||||
|
if (!result) throw new Error(`Project not found: ${id}`)
|
||||||
|
const data = fromRow(result)
|
||||||
GlobalBus.emit("event", {
|
GlobalBus.emit("event", {
|
||||||
payload: {
|
payload: {
|
||||||
type: Event.Updated.type,
|
type: Event.Updated.type,
|
||||||
properties: result,
|
properties: data,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return result
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function removeSandbox(projectID: string, directory: string) {
|
export async function removeSandbox(id: string, directory: string) {
|
||||||
const result = await Storage.update<Info>(["project", projectID], (draft) => {
|
const row = Database.use((db) => db.select().from(ProjectTable).where(eq(ProjectTable.id, id)).get())
|
||||||
const sandboxes = draft.sandboxes ?? []
|
if (!row) throw new Error(`Project not found: ${id}`)
|
||||||
draft.sandboxes = sandboxes.filter((sandbox) => sandbox !== directory)
|
const sandboxes = row.sandboxes.filter((s) => s !== directory)
|
||||||
draft.time.updated = Date.now()
|
const result = Database.use((db) =>
|
||||||
})
|
db
|
||||||
|
.update(ProjectTable)
|
||||||
|
.set({ sandboxes, time_updated: Date.now() })
|
||||||
|
.where(eq(ProjectTable.id, id))
|
||||||
|
.returning()
|
||||||
|
.get(),
|
||||||
|
)
|
||||||
|
if (!result) throw new Error(`Project not found: ${id}`)
|
||||||
|
const data = fromRow(result)
|
||||||
GlobalBus.emit("event", {
|
GlobalBus.emit("event", {
|
||||||
payload: {
|
payload: {
|
||||||
type: Event.Updated.type,
|
type: Event.Updated.type,
|
||||||
properties: result,
|
properties: data,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return result
|
return data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { resolver } from "hono-openapi"
|
import { resolver } from "hono-openapi"
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
import { Storage } from "../storage/storage"
|
import { NotFoundError } from "../storage/db"
|
||||||
|
|
||||||
export const ERRORS = {
|
export const ERRORS = {
|
||||||
400: {
|
400: {
|
||||||
|
|
@ -25,7 +25,7 @@ export const ERRORS = {
|
||||||
description: "Not found",
|
description: "Not found",
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: resolver(Storage.NotFoundError.Schema),
|
schema: resolver(NotFoundError.Schema),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { describeRoute, validator, resolver } from "hono-openapi"
|
||||||
import { upgradeWebSocket } from "hono/bun"
|
import { upgradeWebSocket } from "hono/bun"
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
import { Pty } from "@/pty"
|
import { Pty } from "@/pty"
|
||||||
import { Storage } from "../../storage/storage"
|
import { NotFoundError } from "../../storage/db"
|
||||||
import { errors } from "../error"
|
import { errors } from "../error"
|
||||||
import { lazy } from "../../util/lazy"
|
import { lazy } from "../../util/lazy"
|
||||||
|
|
||||||
|
|
@ -76,7 +76,7 @@ export const PtyRoutes = lazy(() =>
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const info = Pty.get(c.req.valid("param").ptyID)
|
const info = Pty.get(c.req.valid("param").ptyID)
|
||||||
if (!info) {
|
if (!info) {
|
||||||
throw new Storage.NotFoundError({ message: "Session not found" })
|
throw new NotFoundError({ message: "Session not found" })
|
||||||
}
|
}
|
||||||
return c.json(info)
|
return c.json(info)
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -276,18 +276,15 @@ export const SessionRoutes = lazy(() =>
|
||||||
const sessionID = c.req.valid("param").sessionID
|
const sessionID = c.req.valid("param").sessionID
|
||||||
const updates = c.req.valid("json")
|
const updates = c.req.valid("json")
|
||||||
|
|
||||||
const updatedSession = await Session.update(
|
let session = await Session.get(sessionID)
|
||||||
sessionID,
|
if (updates.title !== undefined) {
|
||||||
(session) => {
|
session = await Session.setTitle({ sessionID, title: updates.title })
|
||||||
if (updates.title !== undefined) {
|
}
|
||||||
session.title = updates.title
|
if (updates.time?.archived !== undefined) {
|
||||||
}
|
session = await Session.setArchived({ sessionID, time: updates.time.archived })
|
||||||
if (updates.time?.archived !== undefined) session.time.archived = updates.time.archived
|
}
|
||||||
},
|
|
||||||
{ touch: false },
|
|
||||||
)
|
|
||||||
|
|
||||||
return c.json(updatedSession)
|
return c.json(session)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.post(
|
.post(
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ import { ExperimentalRoutes } from "./routes/experimental"
|
||||||
import { ProviderRoutes } from "./routes/provider"
|
import { ProviderRoutes } from "./routes/provider"
|
||||||
import { lazy } from "../util/lazy"
|
import { lazy } from "../util/lazy"
|
||||||
import { InstanceBootstrap } from "../project/bootstrap"
|
import { InstanceBootstrap } from "../project/bootstrap"
|
||||||
import { Storage } from "../storage/storage"
|
import { NotFoundError } from "../storage/db"
|
||||||
import type { ContentfulStatusCode } from "hono/utils/http-status"
|
import type { ContentfulStatusCode } from "hono/utils/http-status"
|
||||||
import { websocket } from "hono/bun"
|
import { websocket } from "hono/bun"
|
||||||
import { HTTPException } from "hono/http-exception"
|
import { HTTPException } from "hono/http-exception"
|
||||||
|
|
@ -65,7 +65,7 @@ export namespace Server {
|
||||||
})
|
})
|
||||||
if (err instanceof NamedError) {
|
if (err instanceof NamedError) {
|
||||||
let status: ContentfulStatusCode
|
let status: ContentfulStatusCode
|
||||||
if (err instanceof Storage.NotFoundError) status = 404
|
if (err instanceof NotFoundError) status = 404
|
||||||
else if (err instanceof Provider.ModelNotFoundError) status = 400
|
else if (err instanceof Provider.ModelNotFoundError) status = 400
|
||||||
else if (err.name.startsWith("Worktree")) status = 400
|
else if (err.name.startsWith("Worktree")) status = 400
|
||||||
else status = 500
|
else status = 500
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,9 @@ import { Flag } from "../flag/flag"
|
||||||
import { Identifier } from "../id/id"
|
import { Identifier } from "../id/id"
|
||||||
import { Installation } from "../installation"
|
import { Installation } from "../installation"
|
||||||
|
|
||||||
import { Storage } from "../storage/storage"
|
import { Database, NotFoundError, eq, and } from "../storage/db"
|
||||||
|
import { SessionTable, MessageTable, PartTable } from "./session.sql"
|
||||||
|
import { Storage } from "@/storage/storage"
|
||||||
import { Log } from "../util/log"
|
import { Log } from "../util/log"
|
||||||
import { MessageV2 } from "./message-v2"
|
import { MessageV2 } from "./message-v2"
|
||||||
import { Instance } from "../project/instance"
|
import { Instance } from "../project/instance"
|
||||||
|
|
@ -39,6 +41,64 @@ export namespace Session {
|
||||||
).test(title)
|
).test(title)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SessionRow = typeof SessionTable.$inferSelect
|
||||||
|
|
||||||
|
export function fromRow(row: SessionRow): Info {
|
||||||
|
const summary =
|
||||||
|
row.summary_additions !== null || row.summary_deletions !== null || row.summary_files !== null
|
||||||
|
? {
|
||||||
|
additions: row.summary_additions ?? 0,
|
||||||
|
deletions: row.summary_deletions ?? 0,
|
||||||
|
files: row.summary_files ?? 0,
|
||||||
|
diffs: row.summary_diffs ?? undefined,
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
const share = row.share_url ? { url: row.share_url } : undefined
|
||||||
|
const revert = row.revert ?? undefined
|
||||||
|
return {
|
||||||
|
id: row.id,
|
||||||
|
slug: row.slug,
|
||||||
|
projectID: row.project_id,
|
||||||
|
directory: row.directory,
|
||||||
|
parentID: row.parent_id ?? undefined,
|
||||||
|
title: row.title,
|
||||||
|
version: row.version,
|
||||||
|
summary,
|
||||||
|
share,
|
||||||
|
revert,
|
||||||
|
permission: row.permission ?? undefined,
|
||||||
|
time: {
|
||||||
|
created: row.time_created,
|
||||||
|
updated: row.time_updated,
|
||||||
|
compacting: row.time_compacting ?? undefined,
|
||||||
|
archived: row.time_archived ?? undefined,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toRow(info: Info) {
|
||||||
|
return {
|
||||||
|
id: info.id,
|
||||||
|
project_id: info.projectID,
|
||||||
|
parent_id: info.parentID,
|
||||||
|
slug: info.slug,
|
||||||
|
directory: info.directory,
|
||||||
|
title: info.title,
|
||||||
|
version: info.version,
|
||||||
|
share_url: info.share?.url,
|
||||||
|
summary_additions: info.summary?.additions,
|
||||||
|
summary_deletions: info.summary?.deletions,
|
||||||
|
summary_files: info.summary?.files,
|
||||||
|
summary_diffs: info.summary?.diffs,
|
||||||
|
revert: info.revert ?? null,
|
||||||
|
permission: info.permission,
|
||||||
|
time_created: info.time.created,
|
||||||
|
time_updated: info.time.updated,
|
||||||
|
time_compacting: info.time.compacting,
|
||||||
|
time_archived: info.time.archived,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getForkedTitle(title: string): string {
|
function getForkedTitle(title: string): string {
|
||||||
const match = title.match(/^(.+) \(fork #(\d+)\)$/)
|
const match = title.match(/^(.+) \(fork #(\d+)\)$/)
|
||||||
if (match) {
|
if (match) {
|
||||||
|
|
@ -92,16 +152,6 @@ export namespace Session {
|
||||||
})
|
})
|
||||||
export type Info = z.output<typeof Info>
|
export type Info = z.output<typeof Info>
|
||||||
|
|
||||||
export const ShareInfo = z
|
|
||||||
.object({
|
|
||||||
secret: z.string(),
|
|
||||||
url: z.string(),
|
|
||||||
})
|
|
||||||
.meta({
|
|
||||||
ref: "SessionShare",
|
|
||||||
})
|
|
||||||
export type ShareInfo = z.output<typeof ShareInfo>
|
|
||||||
|
|
||||||
export const Event = {
|
export const Event = {
|
||||||
Created: BusEvent.define(
|
Created: BusEvent.define(
|
||||||
"session.created",
|
"session.created",
|
||||||
|
|
@ -198,8 +248,17 @@ export namespace Session {
|
||||||
)
|
)
|
||||||
|
|
||||||
export const touch = fn(Identifier.schema("session"), async (sessionID) => {
|
export const touch = fn(Identifier.schema("session"), async (sessionID) => {
|
||||||
await update(sessionID, (draft) => {
|
const now = Date.now()
|
||||||
draft.time.updated = Date.now()
|
Database.use((db) => {
|
||||||
|
const row = db
|
||||||
|
.update(SessionTable)
|
||||||
|
.set({ time_updated: now })
|
||||||
|
.where(eq(SessionTable.id, sessionID))
|
||||||
|
.returning()
|
||||||
|
.get()
|
||||||
|
if (!row) throw new NotFoundError({ message: `Session not found: ${sessionID}` })
|
||||||
|
const info = fromRow(row)
|
||||||
|
Database.effect(() => Bus.publish(Event.Updated, { info }))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -225,21 +284,19 @@ export namespace Session {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
log.info("created", result)
|
log.info("created", result)
|
||||||
await Storage.write(["session", Instance.project.id, result.id], result)
|
Database.use((db) => {
|
||||||
Bus.publish(Event.Created, {
|
db.insert(SessionTable).values(toRow(result)).run()
|
||||||
info: result,
|
Database.effect(() =>
|
||||||
|
Bus.publish(Event.Created, {
|
||||||
|
info: result,
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
const cfg = await Config.get()
|
const cfg = await Config.get()
|
||||||
if (!result.parentID && (Flag.OPENCODE_AUTO_SHARE || cfg.share === "auto"))
|
if (!result.parentID && (Flag.OPENCODE_AUTO_SHARE || cfg.share === "auto"))
|
||||||
share(result.id)
|
share(result.id).catch(() => {
|
||||||
.then((share) => {
|
// Silently ignore sharing errors during session creation
|
||||||
update(result.id, (draft) => {
|
})
|
||||||
draft.share = share
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
// Silently ignore sharing errors during session creation
|
|
||||||
})
|
|
||||||
Bus.publish(Event.Updated, {
|
Bus.publish(Event.Updated, {
|
||||||
info: result,
|
info: result,
|
||||||
})
|
})
|
||||||
|
|
@ -254,12 +311,9 @@ export namespace Session {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const get = fn(Identifier.schema("session"), async (id) => {
|
export const get = fn(Identifier.schema("session"), async (id) => {
|
||||||
const read = await Storage.read<Info>(["session", Instance.project.id, id])
|
const row = Database.use((db) => db.select().from(SessionTable).where(eq(SessionTable.id, id)).get())
|
||||||
return read as Info
|
if (!row) throw new NotFoundError({ message: `Session not found: ${id}` })
|
||||||
})
|
return fromRow(row)
|
||||||
|
|
||||||
export const getShare = fn(Identifier.schema("session"), async (id) => {
|
|
||||||
return Storage.read<ShareInfo>(["share", id])
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export const share = fn(Identifier.schema("session"), async (id) => {
|
export const share = fn(Identifier.schema("session"), async (id) => {
|
||||||
|
|
@ -269,15 +323,12 @@ export namespace Session {
|
||||||
}
|
}
|
||||||
const { ShareNext } = await import("@/share/share-next")
|
const { ShareNext } = await import("@/share/share-next")
|
||||||
const share = await ShareNext.create(id)
|
const share = await ShareNext.create(id)
|
||||||
await update(
|
Database.use((db) => {
|
||||||
id,
|
const row = db.update(SessionTable).set({ share_url: share.url }).where(eq(SessionTable.id, id)).returning().get()
|
||||||
(draft) => {
|
if (!row) throw new NotFoundError({ message: `Session not found: ${id}` })
|
||||||
draft.share = {
|
const info = fromRow(row)
|
||||||
url: share.url,
|
Database.effect(() => Bus.publish(Event.Updated, { info }))
|
||||||
}
|
})
|
||||||
},
|
|
||||||
{ touch: false },
|
|
||||||
)
|
|
||||||
return share
|
return share
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -285,32 +336,155 @@ export namespace Session {
|
||||||
// Use ShareNext to remove the share (same as share function uses ShareNext to create)
|
// Use ShareNext to remove the share (same as share function uses ShareNext to create)
|
||||||
const { ShareNext } = await import("@/share/share-next")
|
const { ShareNext } = await import("@/share/share-next")
|
||||||
await ShareNext.remove(id)
|
await ShareNext.remove(id)
|
||||||
await update(
|
Database.use((db) => {
|
||||||
id,
|
const row = db.update(SessionTable).set({ share_url: null }).where(eq(SessionTable.id, id)).returning().get()
|
||||||
(draft) => {
|
if (!row) throw new NotFoundError({ message: `Session not found: ${id}` })
|
||||||
draft.share = undefined
|
const info = fromRow(row)
|
||||||
},
|
Database.effect(() => Bus.publish(Event.Updated, { info }))
|
||||||
{ touch: false },
|
})
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export async function update(id: string, editor: (session: Info) => void, options?: { touch?: boolean }) {
|
export const setTitle = fn(
|
||||||
const project = Instance.project
|
z.object({
|
||||||
const result = await Storage.update<Info>(["session", project.id, id], (draft) => {
|
sessionID: Identifier.schema("session"),
|
||||||
editor(draft)
|
title: z.string(),
|
||||||
if (options?.touch !== false) {
|
}),
|
||||||
draft.time.updated = Date.now()
|
async (input) => {
|
||||||
}
|
return Database.use((db) => {
|
||||||
|
const row = db
|
||||||
|
.update(SessionTable)
|
||||||
|
.set({ title: input.title })
|
||||||
|
.where(eq(SessionTable.id, input.sessionID))
|
||||||
|
.returning()
|
||||||
|
.get()
|
||||||
|
if (!row) throw new NotFoundError({ message: `Session not found: ${input.sessionID}` })
|
||||||
|
const info = fromRow(row)
|
||||||
|
Database.effect(() => Bus.publish(Event.Updated, { info }))
|
||||||
|
return info
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
export const setArchived = fn(
|
||||||
|
z.object({
|
||||||
|
sessionID: Identifier.schema("session"),
|
||||||
|
time: z.number().optional(),
|
||||||
|
}),
|
||||||
|
async (input) => {
|
||||||
|
return Database.use((db) => {
|
||||||
|
const row = db
|
||||||
|
.update(SessionTable)
|
||||||
|
.set({ time_archived: input.time })
|
||||||
|
.where(eq(SessionTable.id, input.sessionID))
|
||||||
|
.returning()
|
||||||
|
.get()
|
||||||
|
if (!row) throw new NotFoundError({ message: `Session not found: ${input.sessionID}` })
|
||||||
|
const info = fromRow(row)
|
||||||
|
Database.effect(() => Bus.publish(Event.Updated, { info }))
|
||||||
|
return info
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
export const setPermission = fn(
|
||||||
|
z.object({
|
||||||
|
sessionID: Identifier.schema("session"),
|
||||||
|
permission: PermissionNext.Ruleset,
|
||||||
|
}),
|
||||||
|
async (input) => {
|
||||||
|
return Database.use((db) => {
|
||||||
|
const row = db
|
||||||
|
.update(SessionTable)
|
||||||
|
.set({ permission: input.permission, time_updated: Date.now() })
|
||||||
|
.where(eq(SessionTable.id, input.sessionID))
|
||||||
|
.returning()
|
||||||
|
.get()
|
||||||
|
if (!row) throw new NotFoundError({ message: `Session not found: ${input.sessionID}` })
|
||||||
|
const info = fromRow(row)
|
||||||
|
Database.effect(() => Bus.publish(Event.Updated, { info }))
|
||||||
|
return info
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
export const setRevert = fn(
|
||||||
|
z.object({
|
||||||
|
sessionID: Identifier.schema("session"),
|
||||||
|
revert: Info.shape.revert,
|
||||||
|
summary: Info.shape.summary,
|
||||||
|
}),
|
||||||
|
async (input) => {
|
||||||
|
return Database.use((db) => {
|
||||||
|
const row = db
|
||||||
|
.update(SessionTable)
|
||||||
|
.set({
|
||||||
|
revert: input.revert ?? null,
|
||||||
|
summary_additions: input.summary?.additions,
|
||||||
|
summary_deletions: input.summary?.deletions,
|
||||||
|
summary_files: input.summary?.files,
|
||||||
|
time_updated: Date.now(),
|
||||||
|
})
|
||||||
|
.where(eq(SessionTable.id, input.sessionID))
|
||||||
|
.returning()
|
||||||
|
.get()
|
||||||
|
if (!row) throw new NotFoundError({ message: `Session not found: ${input.sessionID}` })
|
||||||
|
const info = fromRow(row)
|
||||||
|
Database.effect(() => Bus.publish(Event.Updated, { info }))
|
||||||
|
return info
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
export const clearRevert = fn(Identifier.schema("session"), async (sessionID) => {
|
||||||
|
return Database.use((db) => {
|
||||||
|
const row = db
|
||||||
|
.update(SessionTable)
|
||||||
|
.set({
|
||||||
|
revert: null,
|
||||||
|
time_updated: Date.now(),
|
||||||
|
})
|
||||||
|
.where(eq(SessionTable.id, sessionID))
|
||||||
|
.returning()
|
||||||
|
.get()
|
||||||
|
if (!row) throw new NotFoundError({ message: `Session not found: ${sessionID}` })
|
||||||
|
const info = fromRow(row)
|
||||||
|
Database.effect(() => Bus.publish(Event.Updated, { info }))
|
||||||
|
return info
|
||||||
})
|
})
|
||||||
Bus.publish(Event.Updated, {
|
})
|
||||||
info: result,
|
|
||||||
})
|
export const setSummary = fn(
|
||||||
return result
|
z.object({
|
||||||
}
|
sessionID: Identifier.schema("session"),
|
||||||
|
summary: Info.shape.summary,
|
||||||
|
}),
|
||||||
|
async (input) => {
|
||||||
|
return Database.use((db) => {
|
||||||
|
const row = db
|
||||||
|
.update(SessionTable)
|
||||||
|
.set({
|
||||||
|
summary_additions: input.summary?.additions,
|
||||||
|
summary_deletions: input.summary?.deletions,
|
||||||
|
summary_files: input.summary?.files,
|
||||||
|
time_updated: Date.now(),
|
||||||
|
})
|
||||||
|
.where(eq(SessionTable.id, input.sessionID))
|
||||||
|
.returning()
|
||||||
|
.get()
|
||||||
|
if (!row) throw new NotFoundError({ message: `Session not found: ${input.sessionID}` })
|
||||||
|
const info = fromRow(row)
|
||||||
|
Database.effect(() => Bus.publish(Event.Updated, { info }))
|
||||||
|
return info
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
export const diff = fn(Identifier.schema("session"), async (sessionID) => {
|
export const diff = fn(Identifier.schema("session"), async (sessionID) => {
|
||||||
const diffs = await Storage.read<Snapshot.FileDiff[]>(["session_diff", sessionID])
|
try {
|
||||||
return diffs ?? []
|
return await Storage.read<Snapshot.FileDiff[]>(["session_diff", sessionID])
|
||||||
|
} catch {
|
||||||
|
return []
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export const messages = fn(
|
export const messages = fn(
|
||||||
|
|
@ -329,25 +503,26 @@ export namespace Session {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
export async function* list() {
|
export function* list() {
|
||||||
const project = Instance.project
|
const project = Instance.project
|
||||||
for (const item of await Storage.list(["session", project.id])) {
|
const rows = Database.use((db) =>
|
||||||
const session = await Storage.read<Info>(item).catch(() => undefined)
|
db.select().from(SessionTable).where(eq(SessionTable.project_id, project.id)).all(),
|
||||||
if (!session) continue
|
)
|
||||||
yield session
|
for (const row of rows) {
|
||||||
|
yield fromRow(row)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const children = fn(Identifier.schema("session"), async (parentID) => {
|
export const children = fn(Identifier.schema("session"), async (parentID) => {
|
||||||
const project = Instance.project
|
const project = Instance.project
|
||||||
const result = [] as Session.Info[]
|
const rows = Database.use((db) =>
|
||||||
for (const item of await Storage.list(["session", project.id])) {
|
db
|
||||||
const session = await Storage.read<Info>(item).catch(() => undefined)
|
.select()
|
||||||
if (!session) continue
|
.from(SessionTable)
|
||||||
if (session.parentID !== parentID) continue
|
.where(and(eq(SessionTable.project_id, project.id), eq(SessionTable.parent_id, parentID)))
|
||||||
result.push(session)
|
.all(),
|
||||||
}
|
)
|
||||||
return result
|
return rows.map(fromRow)
|
||||||
})
|
})
|
||||||
|
|
||||||
export const remove = fn(Identifier.schema("session"), async (sessionID) => {
|
export const remove = fn(Identifier.schema("session"), async (sessionID) => {
|
||||||
|
|
@ -358,15 +533,14 @@ export namespace Session {
|
||||||
await remove(child.id)
|
await remove(child.id)
|
||||||
}
|
}
|
||||||
await unshare(sessionID).catch(() => {})
|
await unshare(sessionID).catch(() => {})
|
||||||
for (const msg of await Storage.list(["message", sessionID])) {
|
// CASCADE delete handles messages and parts automatically
|
||||||
for (const part of await Storage.list(["part", msg.at(-1)!])) {
|
Database.use((db) => {
|
||||||
await Storage.remove(part)
|
db.delete(SessionTable).where(eq(SessionTable.id, sessionID)).run()
|
||||||
}
|
Database.effect(() =>
|
||||||
await Storage.remove(msg)
|
Bus.publish(Event.Deleted, {
|
||||||
}
|
info: session,
|
||||||
await Storage.remove(["session", project.id, sessionID])
|
}),
|
||||||
Bus.publish(Event.Deleted, {
|
)
|
||||||
info: session,
|
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.error(e)
|
log.error(e)
|
||||||
|
|
@ -374,9 +548,23 @@ export namespace Session {
|
||||||
})
|
})
|
||||||
|
|
||||||
export const updateMessage = fn(MessageV2.Info, async (msg) => {
|
export const updateMessage = fn(MessageV2.Info, async (msg) => {
|
||||||
await Storage.write(["message", msg.sessionID, msg.id], msg)
|
const time_created = msg.role === "user" ? msg.time.created : msg.time.created
|
||||||
Bus.publish(MessageV2.Event.Updated, {
|
const { id, sessionID, ...data } = msg
|
||||||
info: msg,
|
Database.use((db) => {
|
||||||
|
db.insert(MessageTable)
|
||||||
|
.values({
|
||||||
|
id,
|
||||||
|
session_id: sessionID,
|
||||||
|
time_created,
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
.onConflictDoUpdate({ target: MessageTable.id, set: { data } })
|
||||||
|
.run()
|
||||||
|
Database.effect(() =>
|
||||||
|
Bus.publish(MessageV2.Event.Updated, {
|
||||||
|
info: msg,
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
return msg
|
return msg
|
||||||
})
|
})
|
||||||
|
|
@ -387,10 +575,15 @@ export namespace Session {
|
||||||
messageID: Identifier.schema("message"),
|
messageID: Identifier.schema("message"),
|
||||||
}),
|
}),
|
||||||
async (input) => {
|
async (input) => {
|
||||||
await Storage.remove(["message", input.sessionID, input.messageID])
|
// CASCADE delete handles parts automatically
|
||||||
Bus.publish(MessageV2.Event.Removed, {
|
Database.use((db) => {
|
||||||
sessionID: input.sessionID,
|
db.delete(MessageTable).where(eq(MessageTable.id, input.messageID)).run()
|
||||||
messageID: input.messageID,
|
Database.effect(() =>
|
||||||
|
Bus.publish(MessageV2.Event.Removed, {
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
messageID: input.messageID,
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
return input.messageID
|
return input.messageID
|
||||||
},
|
},
|
||||||
|
|
@ -403,39 +596,58 @@ export namespace Session {
|
||||||
partID: Identifier.schema("part"),
|
partID: Identifier.schema("part"),
|
||||||
}),
|
}),
|
||||||
async (input) => {
|
async (input) => {
|
||||||
await Storage.remove(["part", input.messageID, input.partID])
|
Database.use((db) => {
|
||||||
Bus.publish(MessageV2.Event.PartRemoved, {
|
db.delete(PartTable).where(eq(PartTable.id, input.partID)).run()
|
||||||
sessionID: input.sessionID,
|
Database.effect(() =>
|
||||||
messageID: input.messageID,
|
Bus.publish(MessageV2.Event.PartRemoved, {
|
||||||
partID: input.partID,
|
sessionID: input.sessionID,
|
||||||
|
messageID: input.messageID,
|
||||||
|
partID: input.partID,
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
return input.partID
|
return input.partID
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
const UpdatePartInput = z.union([
|
const UpdatePartInput = MessageV2.Part
|
||||||
MessageV2.Part,
|
|
||||||
z.object({
|
|
||||||
part: MessageV2.TextPart,
|
|
||||||
delta: z.string(),
|
|
||||||
}),
|
|
||||||
z.object({
|
|
||||||
part: MessageV2.ReasoningPart,
|
|
||||||
delta: z.string(),
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
|
|
||||||
export const updatePart = fn(UpdatePartInput, async (input) => {
|
export const updatePart = fn(UpdatePartInput, async (part) => {
|
||||||
const part = "delta" in input ? input.part : input
|
const { id, messageID, sessionID, ...data } = part
|
||||||
const delta = "delta" in input ? input.delta : undefined
|
const time = Date.now()
|
||||||
await Storage.write(["part", part.messageID, part.id], part)
|
Database.use((db) => {
|
||||||
Bus.publish(MessageV2.Event.PartUpdated, {
|
db.insert(PartTable)
|
||||||
part,
|
.values({
|
||||||
delta,
|
id,
|
||||||
|
message_id: messageID,
|
||||||
|
session_id: sessionID,
|
||||||
|
time_created: time,
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
.onConflictDoUpdate({ target: PartTable.id, set: { data } })
|
||||||
|
.run()
|
||||||
|
Database.effect(() =>
|
||||||
|
Bus.publish(MessageV2.Event.PartUpdated, {
|
||||||
|
part,
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
return part
|
return part
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const updatePartDelta = fn(
|
||||||
|
z.object({
|
||||||
|
sessionID: z.string(),
|
||||||
|
messageID: z.string(),
|
||||||
|
partID: z.string(),
|
||||||
|
field: z.string(),
|
||||||
|
delta: z.string(),
|
||||||
|
}),
|
||||||
|
async (input) => {
|
||||||
|
Bus.publish(MessageV2.Event.PartDelta, input)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
export const getUsage = fn(
|
export const getUsage = fn(
|
||||||
z.object({
|
z.object({
|
||||||
model: z.custom<Provider.Model>(),
|
model: z.custom<Provider.Model>(),
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@ import { Identifier } from "../id/id"
|
||||||
import { LSP } from "../lsp"
|
import { LSP } from "../lsp"
|
||||||
import { Snapshot } from "@/snapshot"
|
import { Snapshot } from "@/snapshot"
|
||||||
import { fn } from "@/util/fn"
|
import { fn } from "@/util/fn"
|
||||||
import { Storage } from "@/storage/storage"
|
import { Database, eq, desc, inArray } from "@/storage/db"
|
||||||
|
import { MessageTable, PartTable } from "./session.sql"
|
||||||
import { ProviderTransform } from "@/provider/transform"
|
import { ProviderTransform } from "@/provider/transform"
|
||||||
import { STATUS_CODES } from "http"
|
import { STATUS_CODES } from "http"
|
||||||
import { iife } from "@/util/iife"
|
import { iife } from "@/util/iife"
|
||||||
|
|
@ -416,7 +417,16 @@ export namespace MessageV2 {
|
||||||
"message.part.updated",
|
"message.part.updated",
|
||||||
z.object({
|
z.object({
|
||||||
part: Part,
|
part: Part,
|
||||||
delta: z.string().optional(),
|
}),
|
||||||
|
),
|
||||||
|
PartDelta: BusEvent.define(
|
||||||
|
"message.part.delta",
|
||||||
|
z.object({
|
||||||
|
sessionID: z.string(),
|
||||||
|
messageID: z.string(),
|
||||||
|
partID: z.string(),
|
||||||
|
field: z.string(),
|
||||||
|
delta: z.string(),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
PartRemoved: BusEvent.define(
|
PartRemoved: BusEvent.define(
|
||||||
|
|
@ -609,23 +619,65 @@ export namespace MessageV2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const stream = fn(Identifier.schema("session"), async function* (sessionID) {
|
export const stream = fn(Identifier.schema("session"), async function* (sessionID) {
|
||||||
const list = await Array.fromAsync(await Storage.list(["message", sessionID]))
|
const size = 50
|
||||||
for (let i = list.length - 1; i >= 0; i--) {
|
let offset = 0
|
||||||
yield await get({
|
while (true) {
|
||||||
sessionID,
|
const rows = Database.use((db) =>
|
||||||
messageID: list[i][2],
|
db
|
||||||
})
|
.select()
|
||||||
|
.from(MessageTable)
|
||||||
|
.where(eq(MessageTable.session_id, sessionID))
|
||||||
|
.orderBy(desc(MessageTable.time_created))
|
||||||
|
.limit(size)
|
||||||
|
.offset(offset)
|
||||||
|
.all(),
|
||||||
|
)
|
||||||
|
if (rows.length === 0) break
|
||||||
|
|
||||||
|
const ids = rows.map((row) => row.id)
|
||||||
|
const partsByMessage = new Map<string, MessageV2.Part[]>()
|
||||||
|
if (ids.length > 0) {
|
||||||
|
const partRows = Database.use((db) =>
|
||||||
|
db
|
||||||
|
.select()
|
||||||
|
.from(PartTable)
|
||||||
|
.where(inArray(PartTable.message_id, ids))
|
||||||
|
.orderBy(PartTable.message_id, PartTable.id)
|
||||||
|
.all(),
|
||||||
|
)
|
||||||
|
for (const row of partRows) {
|
||||||
|
const part = {
|
||||||
|
...row.data,
|
||||||
|
id: row.id,
|
||||||
|
sessionID: row.session_id,
|
||||||
|
messageID: row.message_id,
|
||||||
|
} as MessageV2.Part
|
||||||
|
const list = partsByMessage.get(row.message_id)
|
||||||
|
if (list) list.push(part)
|
||||||
|
else partsByMessage.set(row.message_id, [part])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const row of rows) {
|
||||||
|
const info = { ...row.data, id: row.id, sessionID: row.session_id } as MessageV2.Info
|
||||||
|
yield {
|
||||||
|
info,
|
||||||
|
parts: partsByMessage.get(row.id) ?? [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += rows.length
|
||||||
|
if (rows.length < size) break
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export const parts = fn(Identifier.schema("message"), async (messageID) => {
|
export const parts = fn(Identifier.schema("message"), async (message_id) => {
|
||||||
const result = [] as MessageV2.Part[]
|
const rows = Database.use((db) =>
|
||||||
for (const item of await Storage.list(["part", messageID])) {
|
db.select().from(PartTable).where(eq(PartTable.message_id, message_id)).orderBy(PartTable.id).all(),
|
||||||
const read = await Storage.read<MessageV2.Part>(item)
|
)
|
||||||
result.push(read)
|
return rows.map(
|
||||||
}
|
(row) => ({ ...row.data, id: row.id, sessionID: row.session_id, messageID: row.message_id }) as MessageV2.Part,
|
||||||
result.sort((a, b) => (a.id > b.id ? 1 : -1))
|
)
|
||||||
return result
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export const get = fn(
|
export const get = fn(
|
||||||
|
|
@ -634,8 +686,11 @@ export namespace MessageV2 {
|
||||||
messageID: Identifier.schema("message"),
|
messageID: Identifier.schema("message"),
|
||||||
}),
|
}),
|
||||||
async (input): Promise<WithParts> => {
|
async (input): Promise<WithParts> => {
|
||||||
|
const row = Database.use((db) => db.select().from(MessageTable).where(eq(MessageTable.id, input.messageID)).get())
|
||||||
|
if (!row) throw new Error(`Message not found: ${input.messageID}`)
|
||||||
|
const info = { ...row.data, id: row.id, sessionID: row.session_id } as MessageV2.Info
|
||||||
return {
|
return {
|
||||||
info: await Storage.read<MessageV2.Info>(["message", input.sessionID, input.messageID]),
|
info,
|
||||||
parts: await parts(input.messageID),
|
parts: await parts(input.messageID),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -63,17 +63,19 @@ export namespace SessionProcessor {
|
||||||
if (value.id in reasoningMap) {
|
if (value.id in reasoningMap) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
reasoningMap[value.id] = {
|
const reasoningPart = {
|
||||||
id: Identifier.ascending("part"),
|
id: Identifier.ascending("part"),
|
||||||
messageID: input.assistantMessage.id,
|
messageID: input.assistantMessage.id,
|
||||||
sessionID: input.assistantMessage.sessionID,
|
sessionID: input.assistantMessage.sessionID,
|
||||||
type: "reasoning",
|
type: "reasoning" as const,
|
||||||
text: "",
|
text: "",
|
||||||
time: {
|
time: {
|
||||||
start: Date.now(),
|
start: Date.now(),
|
||||||
},
|
},
|
||||||
metadata: value.providerMetadata,
|
metadata: value.providerMetadata,
|
||||||
}
|
}
|
||||||
|
reasoningMap[value.id] = reasoningPart
|
||||||
|
await Session.updatePart(reasoningPart)
|
||||||
break
|
break
|
||||||
|
|
||||||
case "reasoning-delta":
|
case "reasoning-delta":
|
||||||
|
|
@ -81,7 +83,13 @@ export namespace SessionProcessor {
|
||||||
const part = reasoningMap[value.id]
|
const part = reasoningMap[value.id]
|
||||||
part.text += value.text
|
part.text += value.text
|
||||||
if (value.providerMetadata) part.metadata = value.providerMetadata
|
if (value.providerMetadata) part.metadata = value.providerMetadata
|
||||||
if (part.text) await Session.updatePart({ part, delta: value.text })
|
await Session.updatePartDelta({
|
||||||
|
sessionID: part.sessionID,
|
||||||
|
messageID: part.messageID,
|
||||||
|
partID: part.id,
|
||||||
|
field: "text",
|
||||||
|
delta: value.text,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
@ -288,17 +296,20 @@ export namespace SessionProcessor {
|
||||||
},
|
},
|
||||||
metadata: value.providerMetadata,
|
metadata: value.providerMetadata,
|
||||||
}
|
}
|
||||||
|
await Session.updatePart(currentText)
|
||||||
break
|
break
|
||||||
|
|
||||||
case "text-delta":
|
case "text-delta":
|
||||||
if (currentText) {
|
if (currentText) {
|
||||||
currentText.text += value.text
|
currentText.text += value.text
|
||||||
if (value.providerMetadata) currentText.metadata = value.providerMetadata
|
if (value.providerMetadata) currentText.metadata = value.providerMetadata
|
||||||
if (currentText.text)
|
await Session.updatePartDelta({
|
||||||
await Session.updatePart({
|
sessionID: currentText.sessionID,
|
||||||
part: currentText,
|
messageID: currentText.messageID,
|
||||||
delta: value.text,
|
partID: currentText.id,
|
||||||
})
|
field: "text",
|
||||||
|
delta: value.text,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -168,9 +168,7 @@ export namespace SessionPrompt {
|
||||||
}
|
}
|
||||||
if (permissions.length > 0) {
|
if (permissions.length > 0) {
|
||||||
session.permission = permissions
|
session.permission = permissions
|
||||||
await Session.update(session.id, (draft) => {
|
await Session.setPermission({ sessionID: session.id, permission: permissions })
|
||||||
draft.permission = permissions
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.noReply === true) {
|
if (input.noReply === true) {
|
||||||
|
|
@ -1818,21 +1816,16 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
const text = await result.text.catch((err) => log.error("failed to generate title", { error: err }))
|
const text = await result.text.catch((err) => log.error("failed to generate title", { error: err }))
|
||||||
if (text)
|
if (text) {
|
||||||
return Session.update(
|
const cleaned = text
|
||||||
input.session.id,
|
.replace(/<think>[\s\S]*?<\/think>\s*/g, "")
|
||||||
(draft) => {
|
.split("\n")
|
||||||
const cleaned = text
|
.map((line) => line.trim())
|
||||||
.replace(/<think>[\s\S]*?<\/think>\s*/g, "")
|
.find((line) => line.length > 0)
|
||||||
.split("\n")
|
if (!cleaned) return
|
||||||
.map((line) => line.trim())
|
|
||||||
.find((line) => line.length > 0)
|
|
||||||
if (!cleaned) return
|
|
||||||
|
|
||||||
const title = cleaned.length > 100 ? cleaned.substring(0, 97) + "..." : cleaned
|
const title = cleaned.length > 100 ? cleaned.substring(0, 97) + "..." : cleaned
|
||||||
draft.title = title
|
return Session.setTitle({ sessionID: input.session.id, title })
|
||||||
},
|
}
|
||||||
{ touch: false },
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,9 @@ import { Snapshot } from "../snapshot"
|
||||||
import { MessageV2 } from "./message-v2"
|
import { MessageV2 } from "./message-v2"
|
||||||
import { Session } from "."
|
import { Session } from "."
|
||||||
import { Log } from "../util/log"
|
import { Log } from "../util/log"
|
||||||
import { splitWhen } from "remeda"
|
import { Database, eq } from "../storage/db"
|
||||||
import { Storage } from "../storage/storage"
|
import { MessageTable, PartTable } from "./session.sql"
|
||||||
|
import { Storage } from "@/storage/storage"
|
||||||
import { Bus } from "../bus"
|
import { Bus } from "../bus"
|
||||||
import { SessionPrompt } from "./prompt"
|
import { SessionPrompt } from "./prompt"
|
||||||
import { SessionSummary } from "./summary"
|
import { SessionSummary } from "./summary"
|
||||||
|
|
@ -65,13 +66,14 @@ export namespace SessionRevert {
|
||||||
sessionID: input.sessionID,
|
sessionID: input.sessionID,
|
||||||
diff: diffs,
|
diff: diffs,
|
||||||
})
|
})
|
||||||
return Session.update(input.sessionID, (draft) => {
|
return Session.setRevert({
|
||||||
draft.revert = revert
|
sessionID: input.sessionID,
|
||||||
draft.summary = {
|
revert,
|
||||||
|
summary: {
|
||||||
additions: diffs.reduce((sum, x) => sum + x.additions, 0),
|
additions: diffs.reduce((sum, x) => sum + x.additions, 0),
|
||||||
deletions: diffs.reduce((sum, x) => sum + x.deletions, 0),
|
deletions: diffs.reduce((sum, x) => sum + x.deletions, 0),
|
||||||
files: diffs.length,
|
files: diffs.length,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return session
|
return session
|
||||||
|
|
@ -83,39 +85,54 @@ export namespace SessionRevert {
|
||||||
const session = await Session.get(input.sessionID)
|
const session = await Session.get(input.sessionID)
|
||||||
if (!session.revert) return session
|
if (!session.revert) return session
|
||||||
if (session.revert.snapshot) await Snapshot.restore(session.revert.snapshot)
|
if (session.revert.snapshot) await Snapshot.restore(session.revert.snapshot)
|
||||||
const next = await Session.update(input.sessionID, (draft) => {
|
return Session.clearRevert(input.sessionID)
|
||||||
draft.revert = undefined
|
|
||||||
})
|
|
||||||
return next
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function cleanup(session: Session.Info) {
|
export async function cleanup(session: Session.Info) {
|
||||||
if (!session.revert) return
|
if (!session.revert) return
|
||||||
const sessionID = session.id
|
const sessionID = session.id
|
||||||
let msgs = await Session.messages({ sessionID })
|
const msgs = await Session.messages({ sessionID })
|
||||||
const messageID = session.revert.messageID
|
const messageID = session.revert.messageID
|
||||||
const [preserve, remove] = splitWhen(msgs, (x) => x.info.id === messageID)
|
const preserve = [] as MessageV2.WithParts[]
|
||||||
msgs = preserve
|
const remove = [] as MessageV2.WithParts[]
|
||||||
|
let target: MessageV2.WithParts | undefined
|
||||||
|
for (const msg of msgs) {
|
||||||
|
if (msg.info.id < messageID) {
|
||||||
|
preserve.push(msg)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (msg.info.id > messageID) {
|
||||||
|
remove.push(msg)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (session.revert.partID) {
|
||||||
|
preserve.push(msg)
|
||||||
|
target = msg
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
remove.push(msg)
|
||||||
|
}
|
||||||
for (const msg of remove) {
|
for (const msg of remove) {
|
||||||
await Storage.remove(["message", sessionID, msg.info.id])
|
Database.use((db) => db.delete(MessageTable).where(eq(MessageTable.id, msg.info.id)).run())
|
||||||
await Bus.publish(MessageV2.Event.Removed, { sessionID: sessionID, messageID: msg.info.id })
|
await Bus.publish(MessageV2.Event.Removed, { sessionID: sessionID, messageID: msg.info.id })
|
||||||
}
|
}
|
||||||
const last = preserve.at(-1)
|
if (session.revert.partID && target) {
|
||||||
if (session.revert.partID && last) {
|
|
||||||
const partID = session.revert.partID
|
const partID = session.revert.partID
|
||||||
const [preserveParts, removeParts] = splitWhen(last.parts, (x) => x.id === partID)
|
const removeStart = target.parts.findIndex((part) => part.id === partID)
|
||||||
last.parts = preserveParts
|
if (removeStart >= 0) {
|
||||||
for (const part of removeParts) {
|
const preserveParts = target.parts.slice(0, removeStart)
|
||||||
await Storage.remove(["part", last.info.id, part.id])
|
const removeParts = target.parts.slice(removeStart)
|
||||||
await Bus.publish(MessageV2.Event.PartRemoved, {
|
target.parts = preserveParts
|
||||||
sessionID: sessionID,
|
for (const part of removeParts) {
|
||||||
messageID: last.info.id,
|
Database.use((db) => db.delete(PartTable).where(eq(PartTable.id, part.id)).run())
|
||||||
partID: part.id,
|
await Bus.publish(MessageV2.Event.PartRemoved, {
|
||||||
})
|
sessionID: sessionID,
|
||||||
|
messageID: target.info.id,
|
||||||
|
partID: part.id,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await Session.update(sessionID, (draft) => {
|
await Session.clearRevert(sessionID)
|
||||||
draft.revert = undefined
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
88
packages/opencode/src/session/session.sql.ts
Normal file
88
packages/opencode/src/session/session.sql.ts
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
import { sqliteTable, text, integer, index, primaryKey } from "drizzle-orm/sqlite-core"
|
||||||
|
import { ProjectTable } from "../project/project.sql"
|
||||||
|
import type { MessageV2 } from "./message-v2"
|
||||||
|
import type { Snapshot } from "@/snapshot"
|
||||||
|
import type { PermissionNext } from "@/permission/next"
|
||||||
|
import { Timestamps } from "@/storage/schema.sql"
|
||||||
|
|
||||||
|
type PartData = Omit<MessageV2.Part, "id" | "sessionID" | "messageID">
|
||||||
|
type InfoData = Omit<MessageV2.Info, "id" | "sessionID">
|
||||||
|
|
||||||
|
export const SessionTable = sqliteTable(
|
||||||
|
"session",
|
||||||
|
{
|
||||||
|
id: text().primaryKey(),
|
||||||
|
project_id: text()
|
||||||
|
.notNull()
|
||||||
|
.references(() => ProjectTable.id, { onDelete: "cascade" }),
|
||||||
|
parent_id: text(),
|
||||||
|
slug: text().notNull(),
|
||||||
|
directory: text().notNull(),
|
||||||
|
title: text().notNull(),
|
||||||
|
version: text().notNull(),
|
||||||
|
share_url: text(),
|
||||||
|
summary_additions: integer(),
|
||||||
|
summary_deletions: integer(),
|
||||||
|
summary_files: integer(),
|
||||||
|
summary_diffs: text({ mode: "json" }).$type<Snapshot.FileDiff[]>(),
|
||||||
|
revert: text({ mode: "json" }).$type<{ messageID: string; partID?: string; snapshot?: string; diff?: string }>(),
|
||||||
|
permission: text({ mode: "json" }).$type<PermissionNext.Ruleset>(),
|
||||||
|
...Timestamps,
|
||||||
|
time_compacting: integer(),
|
||||||
|
time_archived: integer(),
|
||||||
|
},
|
||||||
|
(table) => [index("session_project_idx").on(table.project_id), index("session_parent_idx").on(table.parent_id)],
|
||||||
|
)
|
||||||
|
|
||||||
|
export const MessageTable = sqliteTable(
|
||||||
|
"message",
|
||||||
|
{
|
||||||
|
id: text().primaryKey(),
|
||||||
|
session_id: text()
|
||||||
|
.notNull()
|
||||||
|
.references(() => SessionTable.id, { onDelete: "cascade" }),
|
||||||
|
...Timestamps,
|
||||||
|
data: text({ mode: "json" }).notNull().$type<InfoData>(),
|
||||||
|
},
|
||||||
|
(table) => [index("message_session_idx").on(table.session_id)],
|
||||||
|
)
|
||||||
|
|
||||||
|
export const PartTable = sqliteTable(
|
||||||
|
"part",
|
||||||
|
{
|
||||||
|
id: text().primaryKey(),
|
||||||
|
message_id: text()
|
||||||
|
.notNull()
|
||||||
|
.references(() => MessageTable.id, { onDelete: "cascade" }),
|
||||||
|
session_id: text().notNull(),
|
||||||
|
...Timestamps,
|
||||||
|
data: text({ mode: "json" }).notNull().$type<PartData>(),
|
||||||
|
},
|
||||||
|
(table) => [index("part_message_idx").on(table.message_id), index("part_session_idx").on(table.session_id)],
|
||||||
|
)
|
||||||
|
|
||||||
|
export const TodoTable = sqliteTable(
|
||||||
|
"todo",
|
||||||
|
{
|
||||||
|
session_id: text()
|
||||||
|
.notNull()
|
||||||
|
.references(() => SessionTable.id, { onDelete: "cascade" }),
|
||||||
|
content: text().notNull(),
|
||||||
|
status: text().notNull(),
|
||||||
|
priority: text().notNull(),
|
||||||
|
position: integer().notNull(),
|
||||||
|
...Timestamps,
|
||||||
|
},
|
||||||
|
(table) => [
|
||||||
|
primaryKey({ columns: [table.session_id, table.position] }),
|
||||||
|
index("todo_session_idx").on(table.session_id),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
export const PermissionTable = sqliteTable("permission", {
|
||||||
|
project_id: text()
|
||||||
|
.primaryKey()
|
||||||
|
.references(() => ProjectTable.id, { onDelete: "cascade" }),
|
||||||
|
...Timestamps,
|
||||||
|
data: text({ mode: "json" }).notNull().$type<PermissionNext.Ruleset>(),
|
||||||
|
})
|
||||||
|
|
@ -103,12 +103,13 @@ export namespace SessionSummary {
|
||||||
return files.has(x.file)
|
return files.has(x.file)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
await Session.update(input.sessionID, (draft) => {
|
await Session.setSummary({
|
||||||
draft.summary = {
|
sessionID: input.sessionID,
|
||||||
|
summary: {
|
||||||
additions: diffs.reduce((sum, x) => sum + x.additions, 0),
|
additions: diffs.reduce((sum, x) => sum + x.additions, 0),
|
||||||
deletions: diffs.reduce((sum, x) => sum + x.deletions, 0),
|
deletions: diffs.reduce((sum, x) => sum + x.deletions, 0),
|
||||||
files: diffs.length,
|
files: diffs.length,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
await Storage.write(["session_diff", input.sessionID], diffs)
|
await Storage.write(["session_diff", input.sessionID], diffs)
|
||||||
Bus.publish(Session.Event.Diff, {
|
Bus.publish(Session.Event.Diff, {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import { BusEvent } from "@/bus/bus-event"
|
import { BusEvent } from "@/bus/bus-event"
|
||||||
import { Bus } from "@/bus"
|
import { Bus } from "@/bus"
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
import { Storage } from "../storage/storage"
|
import { Database, eq, asc } from "../storage/db"
|
||||||
|
import { TodoTable } from "./session.sql"
|
||||||
|
|
||||||
export namespace Todo {
|
export namespace Todo {
|
||||||
export const Info = z
|
export const Info = z
|
||||||
|
|
@ -9,7 +10,6 @@ export namespace Todo {
|
||||||
content: z.string().describe("Brief description of the task"),
|
content: z.string().describe("Brief description of the task"),
|
||||||
status: z.string().describe("Current status of the task: pending, in_progress, completed, cancelled"),
|
status: z.string().describe("Current status of the task: pending, in_progress, completed, cancelled"),
|
||||||
priority: z.string().describe("Priority level of the task: high, medium, low"),
|
priority: z.string().describe("Priority level of the task: high, medium, low"),
|
||||||
id: z.string().describe("Unique identifier for the todo item"),
|
|
||||||
})
|
})
|
||||||
.meta({ ref: "Todo" })
|
.meta({ ref: "Todo" })
|
||||||
export type Info = z.infer<typeof Info>
|
export type Info = z.infer<typeof Info>
|
||||||
|
|
@ -24,14 +24,33 @@ export namespace Todo {
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function update(input: { sessionID: string; todos: Info[] }) {
|
export function update(input: { sessionID: string; todos: Info[] }) {
|
||||||
await Storage.write(["todo", input.sessionID], input.todos)
|
Database.transaction((db) => {
|
||||||
|
db.delete(TodoTable).where(eq(TodoTable.session_id, input.sessionID)).run()
|
||||||
|
if (input.todos.length === 0) return
|
||||||
|
db.insert(TodoTable)
|
||||||
|
.values(
|
||||||
|
input.todos.map((todo, position) => ({
|
||||||
|
session_id: input.sessionID,
|
||||||
|
content: todo.content,
|
||||||
|
status: todo.status,
|
||||||
|
priority: todo.priority,
|
||||||
|
position,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
.run()
|
||||||
|
})
|
||||||
Bus.publish(Event.Updated, input)
|
Bus.publish(Event.Updated, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function get(sessionID: string) {
|
export function get(sessionID: string) {
|
||||||
return Storage.read<Info[]>(["todo", sessionID])
|
const rows = Database.use((db) =>
|
||||||
.then((x) => x || [])
|
db.select().from(TodoTable).where(eq(TodoTable.session_id, sessionID)).orderBy(asc(TodoTable.position)).all(),
|
||||||
.catch(() => [])
|
)
|
||||||
|
return rows.map((row) => ({
|
||||||
|
content: row.content,
|
||||||
|
status: row.status,
|
||||||
|
priority: row.priority,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@ import { ulid } from "ulid"
|
||||||
import { Provider } from "@/provider/provider"
|
import { Provider } from "@/provider/provider"
|
||||||
import { Session } from "@/session"
|
import { Session } from "@/session"
|
||||||
import { MessageV2 } from "@/session/message-v2"
|
import { MessageV2 } from "@/session/message-v2"
|
||||||
import { Storage } from "@/storage/storage"
|
import { Database, eq } from "@/storage/db"
|
||||||
|
import { SessionShareTable } from "./share.sql"
|
||||||
import { Log } from "@/util/log"
|
import { Log } from "@/util/log"
|
||||||
import type * as SDK from "@opencode-ai/sdk/v2"
|
import type * as SDK from "@opencode-ai/sdk/v2"
|
||||||
|
|
||||||
|
|
@ -77,17 +78,26 @@ export namespace ShareNext {
|
||||||
})
|
})
|
||||||
.then((x) => x.json())
|
.then((x) => x.json())
|
||||||
.then((x) => x as { id: string; url: string; secret: string })
|
.then((x) => x as { id: string; url: string; secret: string })
|
||||||
await Storage.write(["session_share", sessionID], result)
|
Database.use((db) =>
|
||||||
|
db
|
||||||
|
.insert(SessionShareTable)
|
||||||
|
.values({ session_id: sessionID, id: result.id, secret: result.secret, url: result.url })
|
||||||
|
.onConflictDoUpdate({
|
||||||
|
target: SessionShareTable.session_id,
|
||||||
|
set: { id: result.id, secret: result.secret, url: result.url },
|
||||||
|
})
|
||||||
|
.run(),
|
||||||
|
)
|
||||||
fullSync(sessionID)
|
fullSync(sessionID)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
function get(sessionID: string) {
|
function get(sessionID: string) {
|
||||||
return Storage.read<{
|
const row = Database.use((db) =>
|
||||||
id: string
|
db.select().from(SessionShareTable).where(eq(SessionShareTable.session_id, sessionID)).get(),
|
||||||
secret: string
|
)
|
||||||
url: string
|
if (!row) return
|
||||||
}>(["session_share", sessionID])
|
return { id: row.id, secret: row.secret, url: row.url }
|
||||||
}
|
}
|
||||||
|
|
||||||
type Data =
|
type Data =
|
||||||
|
|
@ -132,7 +142,7 @@ export namespace ShareNext {
|
||||||
const queued = queue.get(sessionID)
|
const queued = queue.get(sessionID)
|
||||||
if (!queued) return
|
if (!queued) return
|
||||||
queue.delete(sessionID)
|
queue.delete(sessionID)
|
||||||
const share = await get(sessionID).catch(() => undefined)
|
const share = get(sessionID)
|
||||||
if (!share) return
|
if (!share) return
|
||||||
|
|
||||||
await fetch(`${await url()}/api/share/${share.id}/sync`, {
|
await fetch(`${await url()}/api/share/${share.id}/sync`, {
|
||||||
|
|
@ -152,7 +162,7 @@ export namespace ShareNext {
|
||||||
export async function remove(sessionID: string) {
|
export async function remove(sessionID: string) {
|
||||||
if (disabled) return
|
if (disabled) return
|
||||||
log.info("removing share", { sessionID })
|
log.info("removing share", { sessionID })
|
||||||
const share = await get(sessionID)
|
const share = get(sessionID)
|
||||||
if (!share) return
|
if (!share) return
|
||||||
await fetch(`${await url()}/api/share/${share.id}`, {
|
await fetch(`${await url()}/api/share/${share.id}`, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
|
|
@ -163,7 +173,7 @@ export namespace ShareNext {
|
||||||
secret: share.secret,
|
secret: share.secret,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
await Storage.remove(["session_share", sessionID])
|
Database.use((db) => db.delete(SessionShareTable).where(eq(SessionShareTable.session_id, sessionID)).run())
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fullSync(sessionID: string) {
|
async function fullSync(sessionID: string) {
|
||||||
|
|
|
||||||
13
packages/opencode/src/share/share.sql.ts
Normal file
13
packages/opencode/src/share/share.sql.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { sqliteTable, text } from "drizzle-orm/sqlite-core"
|
||||||
|
import { SessionTable } from "../session/session.sql"
|
||||||
|
import { Timestamps } from "@/storage/schema.sql"
|
||||||
|
|
||||||
|
export const SessionShareTable = sqliteTable("session_share", {
|
||||||
|
session_id: text()
|
||||||
|
.primaryKey()
|
||||||
|
.references(() => SessionTable.id, { onDelete: "cascade" }),
|
||||||
|
id: text().notNull(),
|
||||||
|
secret: text().notNull(),
|
||||||
|
url: text().notNull(),
|
||||||
|
...Timestamps,
|
||||||
|
})
|
||||||
|
|
@ -1,92 +0,0 @@
|
||||||
import { Bus } from "../bus"
|
|
||||||
import { Installation } from "../installation"
|
|
||||||
import { Session } from "../session"
|
|
||||||
import { MessageV2 } from "../session/message-v2"
|
|
||||||
import { Log } from "../util/log"
|
|
||||||
|
|
||||||
export namespace Share {
|
|
||||||
const log = Log.create({ service: "share" })
|
|
||||||
|
|
||||||
let queue: Promise<void> = Promise.resolve()
|
|
||||||
const pending = new Map<string, any>()
|
|
||||||
|
|
||||||
export async function sync(key: string, content: any) {
|
|
||||||
if (disabled) return
|
|
||||||
const [root, ...splits] = key.split("/")
|
|
||||||
if (root !== "session") return
|
|
||||||
const [sub, sessionID] = splits
|
|
||||||
if (sub === "share") return
|
|
||||||
const share = await Session.getShare(sessionID).catch(() => {})
|
|
||||||
if (!share) return
|
|
||||||
const { secret } = share
|
|
||||||
pending.set(key, content)
|
|
||||||
queue = queue
|
|
||||||
.then(async () => {
|
|
||||||
const content = pending.get(key)
|
|
||||||
if (content === undefined) return
|
|
||||||
pending.delete(key)
|
|
||||||
|
|
||||||
return fetch(`${URL}/share_sync`, {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify({
|
|
||||||
sessionID: sessionID,
|
|
||||||
secret,
|
|
||||||
key: key,
|
|
||||||
content,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.then((x) => {
|
|
||||||
if (x) {
|
|
||||||
log.info("synced", {
|
|
||||||
key: key,
|
|
||||||
status: x.status,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function init() {
|
|
||||||
Bus.subscribe(Session.Event.Updated, async (evt) => {
|
|
||||||
await sync("session/info/" + evt.properties.info.id, evt.properties.info)
|
|
||||||
})
|
|
||||||
Bus.subscribe(MessageV2.Event.Updated, async (evt) => {
|
|
||||||
await sync("session/message/" + evt.properties.info.sessionID + "/" + evt.properties.info.id, evt.properties.info)
|
|
||||||
})
|
|
||||||
Bus.subscribe(MessageV2.Event.PartUpdated, async (evt) => {
|
|
||||||
await sync(
|
|
||||||
"session/part/" +
|
|
||||||
evt.properties.part.sessionID +
|
|
||||||
"/" +
|
|
||||||
evt.properties.part.messageID +
|
|
||||||
"/" +
|
|
||||||
evt.properties.part.id,
|
|
||||||
evt.properties.part,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const URL =
|
|
||||||
process.env["OPENCODE_API"] ??
|
|
||||||
(Installation.isPreview() || Installation.isLocal() ? "https://api.dev.opencode.ai" : "https://api.opencode.ai")
|
|
||||||
|
|
||||||
const disabled = process.env["OPENCODE_DISABLE_SHARE"] === "true" || process.env["OPENCODE_DISABLE_SHARE"] === "1"
|
|
||||||
|
|
||||||
export async function create(sessionID: string) {
|
|
||||||
if (disabled) return { url: "", secret: "" }
|
|
||||||
return fetch(`${URL}/share_create`, {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify({ sessionID: sessionID }),
|
|
||||||
})
|
|
||||||
.then((x) => x.json())
|
|
||||||
.then((x) => x as { url: string; secret: string })
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function remove(sessionID: string, secret: string) {
|
|
||||||
if (disabled) return {}
|
|
||||||
return fetch(`${URL}/share_delete`, {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify({ sessionID, secret }),
|
|
||||||
}).then((x) => x.json())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
4
packages/opencode/src/sql.d.ts
vendored
Normal file
4
packages/opencode/src/sql.d.ts
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
declare module "*.sql" {
|
||||||
|
const content: string
|
||||||
|
export default content
|
||||||
|
}
|
||||||
140
packages/opencode/src/storage/db.ts
Normal file
140
packages/opencode/src/storage/db.ts
Normal file
|
|
@ -0,0 +1,140 @@
|
||||||
|
import { Database as BunDatabase } from "bun:sqlite"
|
||||||
|
import { drizzle, type SQLiteBunDatabase } from "drizzle-orm/bun-sqlite"
|
||||||
|
import { migrate } from "drizzle-orm/bun-sqlite/migrator"
|
||||||
|
import { type SQLiteTransaction } from "drizzle-orm/sqlite-core"
|
||||||
|
export * from "drizzle-orm"
|
||||||
|
import { Context } from "../util/context"
|
||||||
|
import { lazy } from "../util/lazy"
|
||||||
|
import { Global } from "../global"
|
||||||
|
import { Log } from "../util/log"
|
||||||
|
import { NamedError } from "@opencode-ai/util/error"
|
||||||
|
import z from "zod"
|
||||||
|
import path from "path"
|
||||||
|
import { readFileSync, readdirSync } from "fs"
|
||||||
|
import fs from "fs/promises"
|
||||||
|
import { Instance } from "@/project/instance"
|
||||||
|
|
||||||
|
declare const OPENCODE_MIGRATIONS: { sql: string; timestamp: number }[] | undefined
|
||||||
|
|
||||||
|
export const NotFoundError = NamedError.create(
|
||||||
|
"NotFoundError",
|
||||||
|
z.object({
|
||||||
|
message: z.string(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
const log = Log.create({ service: "db" })
|
||||||
|
|
||||||
|
export namespace Database {
|
||||||
|
export type Transaction = SQLiteTransaction<"sync", void, Record<string, never>, Record<string, never>>
|
||||||
|
|
||||||
|
type Client = SQLiteBunDatabase
|
||||||
|
|
||||||
|
type Journal = { sql: string; timestamp: number }[]
|
||||||
|
|
||||||
|
function time(tag: string) {
|
||||||
|
const match = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/.exec(tag)
|
||||||
|
if (!match) return 0
|
||||||
|
return Date.UTC(
|
||||||
|
Number(match[1]),
|
||||||
|
Number(match[2]) - 1,
|
||||||
|
Number(match[3]),
|
||||||
|
Number(match[4]),
|
||||||
|
Number(match[5]),
|
||||||
|
Number(match[6]),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function migrations(dir: string): Journal {
|
||||||
|
const dirs = readdirSync(dir, { withFileTypes: true })
|
||||||
|
.filter((entry) => entry.isDirectory())
|
||||||
|
.map((entry) => entry.name)
|
||||||
|
|
||||||
|
const sql = dirs
|
||||||
|
.map((name) => {
|
||||||
|
const file = path.join(dir, name, "migration.sql")
|
||||||
|
if (!Bun.file(file).size) return
|
||||||
|
return {
|
||||||
|
sql: readFileSync(file, "utf-8"),
|
||||||
|
timestamp: time(name),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(Boolean) as Journal
|
||||||
|
|
||||||
|
return sql.sort((a, b) => a.timestamp - b.timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Client = lazy(() => {
|
||||||
|
log.info("opening database", { path: path.join(Global.Path.data, "opencode.db") })
|
||||||
|
|
||||||
|
const sqlite = new BunDatabase(path.join(Global.Path.data, "opencode.db"), { create: true })
|
||||||
|
|
||||||
|
sqlite.run("PRAGMA journal_mode = WAL")
|
||||||
|
sqlite.run("PRAGMA synchronous = NORMAL")
|
||||||
|
sqlite.run("PRAGMA busy_timeout = 5000")
|
||||||
|
sqlite.run("PRAGMA cache_size = -64000")
|
||||||
|
sqlite.run("PRAGMA foreign_keys = ON")
|
||||||
|
|
||||||
|
const db = drizzle({ client: sqlite })
|
||||||
|
|
||||||
|
// Apply schema migrations
|
||||||
|
const entries =
|
||||||
|
typeof OPENCODE_MIGRATIONS !== "undefined"
|
||||||
|
? OPENCODE_MIGRATIONS
|
||||||
|
: migrations(path.join(import.meta.dirname, "../../migration"))
|
||||||
|
if (entries.length > 0) {
|
||||||
|
log.info("applying migrations", {
|
||||||
|
count: entries.length,
|
||||||
|
mode: typeof OPENCODE_MIGRATIONS !== "undefined" ? "bundled" : "dev",
|
||||||
|
})
|
||||||
|
migrate(db, entries)
|
||||||
|
}
|
||||||
|
|
||||||
|
return db
|
||||||
|
})
|
||||||
|
|
||||||
|
export type TxOrDb = Transaction | Client
|
||||||
|
|
||||||
|
const ctx = Context.create<{
|
||||||
|
tx: TxOrDb
|
||||||
|
effects: (() => void | Promise<void>)[]
|
||||||
|
}>("database")
|
||||||
|
|
||||||
|
export function use<T>(callback: (trx: TxOrDb) => T): T {
|
||||||
|
try {
|
||||||
|
return callback(ctx.use().tx)
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof Context.NotFound) {
|
||||||
|
const effects: (() => void | Promise<void>)[] = []
|
||||||
|
const result = ctx.provide({ effects, tx: Client() }, () => callback(Client()))
|
||||||
|
for (const effect of effects) effect()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function effect(fn: () => any | Promise<any>) {
|
||||||
|
try {
|
||||||
|
ctx.use().effects.push(fn)
|
||||||
|
} catch {
|
||||||
|
fn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function transaction<T>(callback: (tx: TxOrDb) => T): T {
|
||||||
|
try {
|
||||||
|
return callback(ctx.use().tx)
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof Context.NotFound) {
|
||||||
|
const effects: (() => void | Promise<void>)[] = []
|
||||||
|
const result = Client().transaction((tx) => {
|
||||||
|
return ctx.provide({ tx, effects }, () => callback(tx))
|
||||||
|
})
|
||||||
|
for (const effect of effects) effect()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
390
packages/opencode/src/storage/json-migration.ts
Normal file
390
packages/opencode/src/storage/json-migration.ts
Normal file
|
|
@ -0,0 +1,390 @@
|
||||||
|
import { Database } from "bun:sqlite"
|
||||||
|
import { drizzle } from "drizzle-orm/bun-sqlite"
|
||||||
|
import { Global } from "../global"
|
||||||
|
import { Log } from "../util/log"
|
||||||
|
import { ProjectTable } from "../project/project.sql"
|
||||||
|
import { SessionTable, MessageTable, PartTable, TodoTable, PermissionTable } from "../session/session.sql"
|
||||||
|
import { SessionShareTable } from "../share/share.sql"
|
||||||
|
import path from "path"
|
||||||
|
import { existsSync } from "fs"
|
||||||
|
|
||||||
|
export namespace JsonMigration {
|
||||||
|
const log = Log.create({ service: "json-migration" })
|
||||||
|
|
||||||
|
export async function run(sqlite: Database) {
|
||||||
|
const storageDir = path.join(Global.Path.data, "storage")
|
||||||
|
|
||||||
|
if (!existsSync(storageDir)) {
|
||||||
|
log.info("storage directory does not exist, skipping migration")
|
||||||
|
return {
|
||||||
|
projects: 0,
|
||||||
|
sessions: 0,
|
||||||
|
messages: 0,
|
||||||
|
parts: 0,
|
||||||
|
todos: 0,
|
||||||
|
permissions: 0,
|
||||||
|
shares: 0,
|
||||||
|
errors: [] as string[],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("starting json to sqlite migration", { storageDir })
|
||||||
|
const start = performance.now()
|
||||||
|
|
||||||
|
const db = drizzle({ client: sqlite })
|
||||||
|
|
||||||
|
// Optimize SQLite for bulk inserts
|
||||||
|
sqlite.exec("PRAGMA journal_mode = WAL")
|
||||||
|
sqlite.exec("PRAGMA synchronous = OFF")
|
||||||
|
sqlite.exec("PRAGMA cache_size = 10000")
|
||||||
|
sqlite.exec("PRAGMA temp_store = MEMORY")
|
||||||
|
const stats = {
|
||||||
|
projects: 0,
|
||||||
|
sessions: 0,
|
||||||
|
messages: 0,
|
||||||
|
parts: 0,
|
||||||
|
todos: 0,
|
||||||
|
permissions: 0,
|
||||||
|
shares: 0,
|
||||||
|
errors: [] as string[],
|
||||||
|
}
|
||||||
|
const orphans = {
|
||||||
|
sessions: 0,
|
||||||
|
todos: 0,
|
||||||
|
permissions: 0,
|
||||||
|
shares: 0,
|
||||||
|
}
|
||||||
|
const errs = stats.errors
|
||||||
|
|
||||||
|
const batchSize = 1000
|
||||||
|
const now = Date.now()
|
||||||
|
|
||||||
|
async function list(pattern: string) {
|
||||||
|
const items: string[] = []
|
||||||
|
const scan = new Bun.Glob(pattern)
|
||||||
|
for await (const file of scan.scan({ cwd: storageDir, absolute: true })) {
|
||||||
|
items.push(file)
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
|
async function read(files: string[], start: number, end: number) {
|
||||||
|
const count = end - start
|
||||||
|
const tasks = new Array(count)
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
tasks[i] = Bun.file(files[start + i]).json()
|
||||||
|
}
|
||||||
|
const results = await Promise.allSettled(tasks)
|
||||||
|
const items = new Array(count)
|
||||||
|
for (let i = 0; i < results.length; i++) {
|
||||||
|
const result = results[i]
|
||||||
|
if (result.status === "fulfilled") {
|
||||||
|
items[i] = result.value
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
errs.push(`failed to read ${files[start + i]}: ${result.reason}`)
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
|
function insert(values: any[], table: any, label: string) {
|
||||||
|
if (values.length === 0) return 0
|
||||||
|
try {
|
||||||
|
db.insert(table).values(values).onConflictDoNothing().run()
|
||||||
|
return values.length
|
||||||
|
} catch (e) {
|
||||||
|
errs.push(`failed to migrate ${label} batch: ${e}`)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-scan all files upfront to avoid repeated glob operations
|
||||||
|
log.info("scanning files...")
|
||||||
|
const [projectFiles, sessionFiles, messageFiles, partFiles, todoFiles, permFiles, shareFiles] = await Promise.all([
|
||||||
|
list("project/*.json"),
|
||||||
|
list("session/*/*.json"),
|
||||||
|
list("message/*/*.json"),
|
||||||
|
list("part/*/*.json"),
|
||||||
|
list("todo/*.json"),
|
||||||
|
list("permission/*.json"),
|
||||||
|
list("session_share/*.json"),
|
||||||
|
])
|
||||||
|
|
||||||
|
log.info("file scan complete", {
|
||||||
|
projects: projectFiles.length,
|
||||||
|
sessions: sessionFiles.length,
|
||||||
|
messages: messageFiles.length,
|
||||||
|
parts: partFiles.length,
|
||||||
|
todos: todoFiles.length,
|
||||||
|
permissions: permFiles.length,
|
||||||
|
shares: shareFiles.length,
|
||||||
|
})
|
||||||
|
|
||||||
|
sqlite.exec("BEGIN TRANSACTION")
|
||||||
|
|
||||||
|
// Migrate projects first (no FK deps)
|
||||||
|
const projectIds = new Set<string>()
|
||||||
|
const projectValues = [] as any[]
|
||||||
|
for (let i = 0; i < projectFiles.length; i += batchSize) {
|
||||||
|
const end = Math.min(i + batchSize, projectFiles.length)
|
||||||
|
const batch = await read(projectFiles, i, end)
|
||||||
|
projectValues.length = 0
|
||||||
|
for (let j = 0; j < batch.length; j++) {
|
||||||
|
const data = batch[j]
|
||||||
|
if (!data) continue
|
||||||
|
if (!data?.id) {
|
||||||
|
errs.push(`project missing id: ${projectFiles[i + j]}`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
projectIds.add(data.id)
|
||||||
|
projectValues.push({
|
||||||
|
id: data.id,
|
||||||
|
worktree: data.worktree ?? "/",
|
||||||
|
vcs: data.vcs,
|
||||||
|
name: data.name ?? undefined,
|
||||||
|
icon_url: data.icon?.url,
|
||||||
|
icon_color: data.icon?.color,
|
||||||
|
time_created: data.time?.created ?? now,
|
||||||
|
time_updated: data.time?.updated ?? now,
|
||||||
|
time_initialized: data.time?.initialized,
|
||||||
|
sandboxes: data.sandboxes ?? [],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
stats.projects += insert(projectValues, ProjectTable, "project")
|
||||||
|
}
|
||||||
|
log.info("migrated projects", { count: stats.projects, duration: Math.round(performance.now() - start) })
|
||||||
|
|
||||||
|
// Migrate sessions (depends on projects)
|
||||||
|
const sessionIds = new Set<string>()
|
||||||
|
const sessionValues = [] as any[]
|
||||||
|
for (let i = 0; i < sessionFiles.length; i += batchSize) {
|
||||||
|
const end = Math.min(i + batchSize, sessionFiles.length)
|
||||||
|
const batch = await read(sessionFiles, i, end)
|
||||||
|
sessionValues.length = 0
|
||||||
|
for (let j = 0; j < batch.length; j++) {
|
||||||
|
const data = batch[j]
|
||||||
|
if (!data) continue
|
||||||
|
if (!data?.id || !data?.projectID) {
|
||||||
|
errs.push(`session missing id or projectID: ${sessionFiles[i + j]}`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (!projectIds.has(data.projectID)) {
|
||||||
|
orphans.sessions++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sessionIds.add(data.id)
|
||||||
|
sessionValues.push({
|
||||||
|
id: data.id,
|
||||||
|
project_id: data.projectID,
|
||||||
|
parent_id: data.parentID ?? null,
|
||||||
|
slug: data.slug ?? "",
|
||||||
|
directory: data.directory ?? "",
|
||||||
|
title: data.title ?? "",
|
||||||
|
version: data.version ?? "",
|
||||||
|
share_url: data.share?.url ?? null,
|
||||||
|
summary_additions: data.summary?.additions ?? null,
|
||||||
|
summary_deletions: data.summary?.deletions ?? null,
|
||||||
|
summary_files: data.summary?.files ?? null,
|
||||||
|
summary_diffs: data.summary?.diffs ?? null,
|
||||||
|
revert: data.revert ?? null,
|
||||||
|
permission: data.permission ?? null,
|
||||||
|
time_created: data.time?.created ?? now,
|
||||||
|
time_updated: data.time?.updated ?? now,
|
||||||
|
time_compacting: data.time?.compacting ?? null,
|
||||||
|
time_archived: data.time?.archived ?? null,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
stats.sessions += insert(sessionValues, SessionTable, "session")
|
||||||
|
}
|
||||||
|
log.info("migrated sessions", { count: stats.sessions })
|
||||||
|
if (orphans.sessions > 0) {
|
||||||
|
log.warn("skipped orphaned sessions", { count: orphans.sessions })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate messages using pre-scanned file map
|
||||||
|
const allMessageFiles = [] as string[]
|
||||||
|
const allMessageSessions = [] as string[]
|
||||||
|
for (const file of messageFiles) {
|
||||||
|
const sessionID = path.basename(path.dirname(file))
|
||||||
|
if (!sessionIds.has(sessionID)) continue
|
||||||
|
allMessageFiles.push(file)
|
||||||
|
allMessageSessions.push(sessionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < allMessageFiles.length; i += batchSize) {
|
||||||
|
const end = Math.min(i + batchSize, allMessageFiles.length)
|
||||||
|
const batch = await read(allMessageFiles, i, end)
|
||||||
|
const values = new Array(batch.length)
|
||||||
|
let count = 0
|
||||||
|
for (let j = 0; j < batch.length; j++) {
|
||||||
|
const data = batch[j]
|
||||||
|
if (!data) continue
|
||||||
|
if (!data?.id) {
|
||||||
|
errs.push(`message missing id: ${allMessageFiles[i + j]}`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const sessionID = allMessageSessions[i + j]
|
||||||
|
const id = data.id
|
||||||
|
const rest = data
|
||||||
|
delete rest.id
|
||||||
|
delete rest.sessionID
|
||||||
|
values[count++] = {
|
||||||
|
id,
|
||||||
|
session_id: sessionID,
|
||||||
|
time_created: data.time?.created ?? now,
|
||||||
|
time_updated: data.time?.updated ?? now,
|
||||||
|
data: rest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
values.length = count
|
||||||
|
stats.messages += insert(values, MessageTable, "message")
|
||||||
|
}
|
||||||
|
log.info("migrated messages", { count: stats.messages })
|
||||||
|
|
||||||
|
// Migrate parts using pre-scanned file map
|
||||||
|
for (let i = 0; i < partFiles.length; i += batchSize) {
|
||||||
|
const end = Math.min(i + batchSize, partFiles.length)
|
||||||
|
const batch = await read(partFiles, i, end)
|
||||||
|
const values = new Array(batch.length)
|
||||||
|
let count = 0
|
||||||
|
for (let j = 0; j < batch.length; j++) {
|
||||||
|
const data = batch[j]
|
||||||
|
if (!data) continue
|
||||||
|
if (!data?.id || !data?.messageID || !data?.sessionID) {
|
||||||
|
errs.push(`part missing id/messageID/sessionID: ${partFiles[i + j]}`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (!sessionIds.has(data.sessionID)) continue
|
||||||
|
const id = data.id
|
||||||
|
const messageID = data.messageID
|
||||||
|
const sessionID = data.sessionID
|
||||||
|
const rest = data
|
||||||
|
delete rest.id
|
||||||
|
delete rest.messageID
|
||||||
|
delete rest.sessionID
|
||||||
|
values[count++] = {
|
||||||
|
id,
|
||||||
|
message_id: messageID,
|
||||||
|
session_id: sessionID,
|
||||||
|
time_created: data.time?.created ?? now,
|
||||||
|
time_updated: data.time?.updated ?? now,
|
||||||
|
data: rest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
values.length = count
|
||||||
|
stats.parts += insert(values, PartTable, "part")
|
||||||
|
}
|
||||||
|
log.info("migrated parts", { count: stats.parts })
|
||||||
|
|
||||||
|
// Migrate todos
|
||||||
|
const todoSessions = todoFiles.map((file) => path.basename(file, ".json"))
|
||||||
|
for (let i = 0; i < todoFiles.length; i += batchSize) {
|
||||||
|
const end = Math.min(i + batchSize, todoFiles.length)
|
||||||
|
const batch = await read(todoFiles, i, end)
|
||||||
|
const values = [] as any[]
|
||||||
|
for (let j = 0; j < batch.length; j++) {
|
||||||
|
const data = batch[j]
|
||||||
|
if (!data) continue
|
||||||
|
const sessionID = todoSessions[i + j]
|
||||||
|
if (!sessionIds.has(sessionID)) {
|
||||||
|
orphans.todos++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (!Array.isArray(data)) {
|
||||||
|
errs.push(`todo not an array: ${todoFiles[i + j]}`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for (let position = 0; position < data.length; position++) {
|
||||||
|
const todo = data[position]
|
||||||
|
if (!todo?.content || !todo?.status || !todo?.priority) continue
|
||||||
|
values.push({
|
||||||
|
session_id: sessionID,
|
||||||
|
content: todo.content,
|
||||||
|
status: todo.status,
|
||||||
|
priority: todo.priority,
|
||||||
|
position,
|
||||||
|
time_created: now,
|
||||||
|
time_updated: now,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stats.todos += insert(values, TodoTable, "todo")
|
||||||
|
}
|
||||||
|
log.info("migrated todos", { count: stats.todos })
|
||||||
|
if (orphans.todos > 0) {
|
||||||
|
log.warn("skipped orphaned todos", { count: orphans.todos })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate permissions
|
||||||
|
const permProjects = permFiles.map((file) => path.basename(file, ".json"))
|
||||||
|
const permValues = [] as any[]
|
||||||
|
for (let i = 0; i < permFiles.length; i += batchSize) {
|
||||||
|
const end = Math.min(i + batchSize, permFiles.length)
|
||||||
|
const batch = await read(permFiles, i, end)
|
||||||
|
permValues.length = 0
|
||||||
|
for (let j = 0; j < batch.length; j++) {
|
||||||
|
const data = batch[j]
|
||||||
|
if (!data) continue
|
||||||
|
const projectID = permProjects[i + j]
|
||||||
|
if (!projectIds.has(projectID)) {
|
||||||
|
orphans.permissions++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
permValues.push({ project_id: projectID, data })
|
||||||
|
}
|
||||||
|
stats.permissions += insert(permValues, PermissionTable, "permission")
|
||||||
|
}
|
||||||
|
log.info("migrated permissions", { count: stats.permissions })
|
||||||
|
if (orphans.permissions > 0) {
|
||||||
|
log.warn("skipped orphaned permissions", { count: orphans.permissions })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate session shares
|
||||||
|
const shareSessions = shareFiles.map((file) => path.basename(file, ".json"))
|
||||||
|
const shareValues = [] as any[]
|
||||||
|
for (let i = 0; i < shareFiles.length; i += batchSize) {
|
||||||
|
const end = Math.min(i + batchSize, shareFiles.length)
|
||||||
|
const batch = await read(shareFiles, i, end)
|
||||||
|
shareValues.length = 0
|
||||||
|
for (let j = 0; j < batch.length; j++) {
|
||||||
|
const data = batch[j]
|
||||||
|
if (!data) continue
|
||||||
|
const sessionID = shareSessions[i + j]
|
||||||
|
if (!sessionIds.has(sessionID)) {
|
||||||
|
orphans.shares++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (!data?.id || !data?.secret || !data?.url) {
|
||||||
|
errs.push(`session_share missing id/secret/url: ${shareFiles[i + j]}`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
shareValues.push({ session_id: sessionID, id: data.id, secret: data.secret, url: data.url })
|
||||||
|
}
|
||||||
|
stats.shares += insert(shareValues, SessionShareTable, "session_share")
|
||||||
|
}
|
||||||
|
log.info("migrated session shares", { count: stats.shares })
|
||||||
|
if (orphans.shares > 0) {
|
||||||
|
log.warn("skipped orphaned session shares", { count: orphans.shares })
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite.exec("COMMIT")
|
||||||
|
|
||||||
|
log.info("json migration complete", {
|
||||||
|
projects: stats.projects,
|
||||||
|
sessions: stats.sessions,
|
||||||
|
messages: stats.messages,
|
||||||
|
parts: stats.parts,
|
||||||
|
todos: stats.todos,
|
||||||
|
permissions: stats.permissions,
|
||||||
|
shares: stats.shares,
|
||||||
|
errorCount: stats.errors.length,
|
||||||
|
duration: Math.round(performance.now() - start),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (stats.errors.length > 0) {
|
||||||
|
log.warn("migration errors", { errors: stats.errors.slice(0, 20) })
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats
|
||||||
|
}
|
||||||
|
}
|
||||||
10
packages/opencode/src/storage/schema.sql.ts
Normal file
10
packages/opencode/src/storage/schema.sql.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { integer } from "drizzle-orm/sqlite-core"
|
||||||
|
|
||||||
|
export const Timestamps = {
|
||||||
|
time_created: integer()
|
||||||
|
.notNull()
|
||||||
|
.$default(() => Date.now()),
|
||||||
|
time_updated: integer()
|
||||||
|
.notNull()
|
||||||
|
.$onUpdate(() => Date.now()),
|
||||||
|
}
|
||||||
|
|
@ -4,9 +4,14 @@ export function lazy<T>(fn: () => T) {
|
||||||
|
|
||||||
const result = (): T => {
|
const result = (): T => {
|
||||||
if (loaded) return value as T
|
if (loaded) return value as T
|
||||||
loaded = true
|
try {
|
||||||
value = fn()
|
value = fn()
|
||||||
return value as T
|
loaded = true
|
||||||
|
return value as T
|
||||||
|
} catch (e) {
|
||||||
|
// Don't mark as loaded if initialization failed
|
||||||
|
throw e
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result.reset = () => {
|
result.reset = () => {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@ import { Global } from "../global"
|
||||||
import { Instance } from "../project/instance"
|
import { Instance } from "../project/instance"
|
||||||
import { InstanceBootstrap } from "../project/bootstrap"
|
import { InstanceBootstrap } from "../project/bootstrap"
|
||||||
import { Project } from "../project/project"
|
import { Project } from "../project/project"
|
||||||
import { Storage } from "../storage/storage"
|
import { Database, eq } from "../storage/db"
|
||||||
|
import { ProjectTable } from "../project/project.sql"
|
||||||
import { fn } from "../util/fn"
|
import { fn } from "../util/fn"
|
||||||
import { Log } from "../util/log"
|
import { Log } from "../util/log"
|
||||||
import { BusEvent } from "@/bus/bus-event"
|
import { BusEvent } from "@/bus/bus-event"
|
||||||
|
|
@ -269,7 +270,8 @@ export namespace Worktree {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runStartScripts(directory: string, input: { projectID: string; extra?: string }) {
|
async function runStartScripts(directory: string, input: { projectID: string; extra?: string }) {
|
||||||
const project = await Storage.read<Project.Info>(["project", input.projectID]).catch(() => undefined)
|
const row = Database.use((db) => db.select().from(ProjectTable).where(eq(ProjectTable.id, input.projectID)).get())
|
||||||
|
const project = row ? Project.fromRow(row) : undefined
|
||||||
const startup = project?.commands?.start?.trim() ?? ""
|
const startup = project?.commands?.start?.trim() ?? ""
|
||||||
const ok = await runStartScript(directory, startup, "project")
|
const ok = await runStartScript(directory, startup, "project")
|
||||||
if (!ok) return false
|
if (!ok) return false
|
||||||
|
|
|
||||||
|
|
@ -122,12 +122,20 @@ function createFakeAgent() {
|
||||||
messages: async () => {
|
messages: async () => {
|
||||||
return { data: [] }
|
return { data: [] }
|
||||||
},
|
},
|
||||||
message: async () => {
|
message: async (params?: any) => {
|
||||||
|
// Return a message with parts that can be looked up by partID
|
||||||
return {
|
return {
|
||||||
data: {
|
data: {
|
||||||
info: {
|
info: {
|
||||||
role: "assistant",
|
role: "assistant",
|
||||||
},
|
},
|
||||||
|
parts: [
|
||||||
|
{
|
||||||
|
id: params?.messageID ? `${params.messageID}_part` : "part_1",
|
||||||
|
type: "text",
|
||||||
|
text: "",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -193,7 +201,7 @@ function createFakeAgent() {
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("acp.agent event subscription", () => {
|
describe("acp.agent event subscription", () => {
|
||||||
test("routes message.part.updated by the event sessionID (no cross-session pollution)", async () => {
|
test("routes message.part.delta by the event sessionID (no cross-session pollution)", async () => {
|
||||||
await using tmp = await tmpdir()
|
await using tmp = await tmpdir()
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
|
|
@ -207,14 +215,12 @@ describe("acp.agent event subscription", () => {
|
||||||
controller.push({
|
controller.push({
|
||||||
directory: cwd,
|
directory: cwd,
|
||||||
payload: {
|
payload: {
|
||||||
type: "message.part.updated",
|
type: "message.part.delta",
|
||||||
properties: {
|
properties: {
|
||||||
part: {
|
sessionID: sessionB,
|
||||||
sessionID: sessionB,
|
messageID: "msg_1",
|
||||||
messageID: "msg_1",
|
partID: "msg_1_part",
|
||||||
type: "text",
|
field: "text",
|
||||||
synthetic: false,
|
|
||||||
},
|
|
||||||
delta: "hello",
|
delta: "hello",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -230,7 +236,7 @@ describe("acp.agent event subscription", () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test("keeps concurrent sessions isolated when message.part.updated events are interleaved", async () => {
|
test("keeps concurrent sessions isolated when message.part.delta events are interleaved", async () => {
|
||||||
await using tmp = await tmpdir()
|
await using tmp = await tmpdir()
|
||||||
await Instance.provide({
|
await Instance.provide({
|
||||||
directory: tmp.path,
|
directory: tmp.path,
|
||||||
|
|
@ -248,14 +254,12 @@ describe("acp.agent event subscription", () => {
|
||||||
controller.push({
|
controller.push({
|
||||||
directory: cwd,
|
directory: cwd,
|
||||||
payload: {
|
payload: {
|
||||||
type: "message.part.updated",
|
type: "message.part.delta",
|
||||||
properties: {
|
properties: {
|
||||||
part: {
|
sessionID: sessionId,
|
||||||
sessionID: sessionId,
|
messageID,
|
||||||
messageID,
|
partID: `${messageID}_part`,
|
||||||
type: "text",
|
field: "text",
|
||||||
synthetic: false,
|
|
||||||
},
|
|
||||||
delta,
|
delta,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -402,14 +406,12 @@ describe("acp.agent event subscription", () => {
|
||||||
controller.push({
|
controller.push({
|
||||||
directory: cwd,
|
directory: cwd,
|
||||||
payload: {
|
payload: {
|
||||||
type: "message.part.updated",
|
type: "message.part.delta",
|
||||||
properties: {
|
properties: {
|
||||||
part: {
|
sessionID: sessionB,
|
||||||
sessionID: sessionB,
|
messageID: "msg_b",
|
||||||
messageID: "msg_b",
|
partID: "msg_b_part",
|
||||||
type: "text",
|
field: "text",
|
||||||
synthetic: false,
|
|
||||||
},
|
|
||||||
delta: "session_b_message",
|
delta: "session_b_message",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import { test, expect } from "bun:test"
|
||||||
import os from "os"
|
import os from "os"
|
||||||
import { PermissionNext } from "../../src/permission/next"
|
import { PermissionNext } from "../../src/permission/next"
|
||||||
import { Instance } from "../../src/project/instance"
|
import { Instance } from "../../src/project/instance"
|
||||||
import { Storage } from "../../src/storage/storage"
|
|
||||||
import { tmpdir } from "../fixture/fixture"
|
import { tmpdir } from "../fixture/fixture"
|
||||||
|
|
||||||
// fromConfig tests
|
// fromConfig tests
|
||||||
|
|
|
||||||
|
|
@ -1,63 +1,70 @@
|
||||||
// IMPORTANT: Set env vars BEFORE any imports from src/ directory
|
// IMPORTANT: Set env vars BEFORE any imports from src/ directory
|
||||||
// xdg-basedir reads env vars at import time, so we must set these first
|
// xdg-basedir reads env vars at import time, so we must set these first
|
||||||
import os from "os"
|
import os from "os";
|
||||||
import path from "path"
|
import path from "path";
|
||||||
import fs from "fs/promises"
|
import fs from "fs/promises";
|
||||||
import fsSync from "fs"
|
import fsSync from "fs";
|
||||||
import { afterAll } from "bun:test"
|
import { afterAll } from "bun:test";
|
||||||
|
|
||||||
const dir = path.join(os.tmpdir(), "opencode-test-data-" + process.pid)
|
// Set XDG env vars FIRST, before any src/ imports
|
||||||
await fs.mkdir(dir, { recursive: true })
|
const dir = path.join(os.tmpdir(), "opencode-test-data-" + process.pid);
|
||||||
|
await fs.mkdir(dir, { recursive: true });
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
fsSync.rmSync(dir, { recursive: true, force: true })
|
fsSync.rmSync(dir, { recursive: true, force: true });
|
||||||
})
|
});
|
||||||
|
|
||||||
|
process.env["XDG_DATA_HOME"] = path.join(dir, "share");
|
||||||
|
process.env["XDG_CACHE_HOME"] = path.join(dir, "cache");
|
||||||
|
process.env["XDG_CONFIG_HOME"] = path.join(dir, "config");
|
||||||
|
process.env["XDG_STATE_HOME"] = path.join(dir, "state");
|
||||||
|
process.env["OPENCODE_MODELS_PATH"] = path.join(
|
||||||
|
import.meta.dir,
|
||||||
|
"tool",
|
||||||
|
"fixtures",
|
||||||
|
"models-api.json",
|
||||||
|
);
|
||||||
|
|
||||||
// Set test home directory to isolate tests from user's actual home directory
|
// Set test home directory to isolate tests from user's actual home directory
|
||||||
// This prevents tests from picking up real user configs/skills from ~/.claude/skills
|
// This prevents tests from picking up real user configs/skills from ~/.claude/skills
|
||||||
const testHome = path.join(dir, "home")
|
const testHome = path.join(dir, "home");
|
||||||
await fs.mkdir(testHome, { recursive: true })
|
await fs.mkdir(testHome, { recursive: true });
|
||||||
process.env["OPENCODE_TEST_HOME"] = testHome
|
process.env["OPENCODE_TEST_HOME"] = testHome;
|
||||||
|
|
||||||
// Set test managed config directory to isolate tests from system managed settings
|
// Set test managed config directory to isolate tests from system managed settings
|
||||||
const testManagedConfigDir = path.join(dir, "managed")
|
const testManagedConfigDir = path.join(dir, "managed");
|
||||||
process.env["OPENCODE_TEST_MANAGED_CONFIG_DIR"] = testManagedConfigDir
|
process.env["OPENCODE_TEST_MANAGED_CONFIG_DIR"] = testManagedConfigDir;
|
||||||
|
|
||||||
process.env["XDG_DATA_HOME"] = path.join(dir, "share")
|
|
||||||
process.env["XDG_CACHE_HOME"] = path.join(dir, "cache")
|
|
||||||
process.env["XDG_CONFIG_HOME"] = path.join(dir, "config")
|
|
||||||
process.env["XDG_STATE_HOME"] = path.join(dir, "state")
|
|
||||||
process.env["OPENCODE_MODELS_PATH"] = path.join(import.meta.dir, "tool", "fixtures", "models-api.json")
|
|
||||||
|
|
||||||
// Write the cache version file to prevent global/index.ts from clearing the cache
|
// Write the cache version file to prevent global/index.ts from clearing the cache
|
||||||
const cacheDir = path.join(dir, "cache", "opencode")
|
const cacheDir = path.join(dir, "cache", "opencode");
|
||||||
await fs.mkdir(cacheDir, { recursive: true })
|
await fs.mkdir(cacheDir, { recursive: true });
|
||||||
await fs.writeFile(path.join(cacheDir, "version"), "14")
|
await fs.writeFile(path.join(cacheDir, "version"), "14");
|
||||||
|
|
||||||
// Clear provider env vars to ensure clean test state
|
// Clear provider env vars to ensure clean test state
|
||||||
delete process.env["ANTHROPIC_API_KEY"]
|
delete process.env["ANTHROPIC_API_KEY"];
|
||||||
delete process.env["OPENAI_API_KEY"]
|
delete process.env["OPENAI_API_KEY"];
|
||||||
delete process.env["GOOGLE_API_KEY"]
|
delete process.env["GOOGLE_API_KEY"];
|
||||||
delete process.env["GOOGLE_GENERATIVE_AI_API_KEY"]
|
delete process.env["GOOGLE_GENERATIVE_AI_API_KEY"];
|
||||||
delete process.env["AZURE_OPENAI_API_KEY"]
|
delete process.env["AZURE_OPENAI_API_KEY"];
|
||||||
delete process.env["AWS_ACCESS_KEY_ID"]
|
delete process.env["AWS_ACCESS_KEY_ID"];
|
||||||
delete process.env["AWS_PROFILE"]
|
delete process.env["AWS_PROFILE"];
|
||||||
delete process.env["AWS_REGION"]
|
delete process.env["AWS_REGION"];
|
||||||
delete process.env["AWS_BEARER_TOKEN_BEDROCK"]
|
delete process.env["AWS_BEARER_TOKEN_BEDROCK"];
|
||||||
delete process.env["OPENROUTER_API_KEY"]
|
delete process.env["OPENROUTER_API_KEY"];
|
||||||
delete process.env["GROQ_API_KEY"]
|
delete process.env["GROQ_API_KEY"];
|
||||||
delete process.env["MISTRAL_API_KEY"]
|
delete process.env["MISTRAL_API_KEY"];
|
||||||
delete process.env["PERPLEXITY_API_KEY"]
|
delete process.env["PERPLEXITY_API_KEY"];
|
||||||
delete process.env["TOGETHER_API_KEY"]
|
delete process.env["TOGETHER_API_KEY"];
|
||||||
delete process.env["XAI_API_KEY"]
|
delete process.env["XAI_API_KEY"];
|
||||||
delete process.env["DEEPSEEK_API_KEY"]
|
delete process.env["DEEPSEEK_API_KEY"];
|
||||||
delete process.env["FIREWORKS_API_KEY"]
|
delete process.env["FIREWORKS_API_KEY"];
|
||||||
delete process.env["CEREBRAS_API_KEY"]
|
delete process.env["CEREBRAS_API_KEY"];
|
||||||
delete process.env["SAMBANOVA_API_KEY"]
|
delete process.env["SAMBANOVA_API_KEY"];
|
||||||
|
|
||||||
// Now safe to import from src/
|
// Now safe to import from src/
|
||||||
const { Log } = await import("../src/util/log")
|
const { Log } = await import("../src/util/log");
|
||||||
|
|
||||||
Log.init({
|
Log.init({
|
||||||
print: false,
|
print: false,
|
||||||
dev: true,
|
dev: true,
|
||||||
level: "DEBUG",
|
level: "DEBUG",
|
||||||
})
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { describe, expect, test } from "bun:test"
|
import { describe, expect, test } from "bun:test"
|
||||||
import { Project } from "../../src/project/project"
|
import { Project } from "../../src/project/project"
|
||||||
import { Log } from "../../src/util/log"
|
import { Log } from "../../src/util/log"
|
||||||
import { Storage } from "../../src/storage/storage"
|
|
||||||
import { $ } from "bun"
|
import { $ } from "bun"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { tmpdir } from "../fixture/fixture"
|
import { tmpdir } from "../fixture/fixture"
|
||||||
|
|
@ -55,37 +54,50 @@ describe("Project.fromDirectory with worktrees", () => {
|
||||||
test("should set worktree to root when called from a worktree", async () => {
|
test("should set worktree to root when called from a worktree", async () => {
|
||||||
await using tmp = await tmpdir({ git: true })
|
await using tmp = await tmpdir({ git: true })
|
||||||
|
|
||||||
const worktreePath = path.join(tmp.path, "..", "worktree-test")
|
const worktreePath = path.join(tmp.path, "..", path.basename(tmp.path) + "-worktree")
|
||||||
await $`git worktree add ${worktreePath} -b test-branch`.cwd(tmp.path).quiet()
|
try {
|
||||||
|
await $`git worktree add ${worktreePath} -b test-branch-${Date.now()}`.cwd(tmp.path).quiet()
|
||||||
|
|
||||||
const { project, sandbox } = await Project.fromDirectory(worktreePath)
|
const { project, sandbox } = await Project.fromDirectory(worktreePath)
|
||||||
|
|
||||||
expect(project.worktree).toBe(tmp.path)
|
expect(project.worktree).toBe(tmp.path)
|
||||||
expect(sandbox).toBe(worktreePath)
|
expect(sandbox).toBe(worktreePath)
|
||||||
expect(project.sandboxes).toContain(worktreePath)
|
expect(project.sandboxes).toContain(worktreePath)
|
||||||
expect(project.sandboxes).not.toContain(tmp.path)
|
expect(project.sandboxes).not.toContain(tmp.path)
|
||||||
|
} finally {
|
||||||
await $`git worktree remove ${worktreePath}`.cwd(tmp.path).quiet()
|
await $`git worktree remove ${worktreePath}`
|
||||||
|
.cwd(tmp.path)
|
||||||
|
.quiet()
|
||||||
|
.catch(() => {})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
test("should accumulate multiple worktrees in sandboxes", async () => {
|
test("should accumulate multiple worktrees in sandboxes", async () => {
|
||||||
await using tmp = await tmpdir({ git: true })
|
await using tmp = await tmpdir({ git: true })
|
||||||
|
|
||||||
const worktree1 = path.join(tmp.path, "..", "worktree-1")
|
const worktree1 = path.join(tmp.path, "..", path.basename(tmp.path) + "-wt1")
|
||||||
const worktree2 = path.join(tmp.path, "..", "worktree-2")
|
const worktree2 = path.join(tmp.path, "..", path.basename(tmp.path) + "-wt2")
|
||||||
await $`git worktree add ${worktree1} -b branch-1`.cwd(tmp.path).quiet()
|
try {
|
||||||
await $`git worktree add ${worktree2} -b branch-2`.cwd(tmp.path).quiet()
|
await $`git worktree add ${worktree1} -b branch-${Date.now()}`.cwd(tmp.path).quiet()
|
||||||
|
await $`git worktree add ${worktree2} -b branch-${Date.now() + 1}`.cwd(tmp.path).quiet()
|
||||||
|
|
||||||
await Project.fromDirectory(worktree1)
|
await Project.fromDirectory(worktree1)
|
||||||
const { project } = await Project.fromDirectory(worktree2)
|
const { project } = await Project.fromDirectory(worktree2)
|
||||||
|
|
||||||
expect(project.worktree).toBe(tmp.path)
|
expect(project.worktree).toBe(tmp.path)
|
||||||
expect(project.sandboxes).toContain(worktree1)
|
expect(project.sandboxes).toContain(worktree1)
|
||||||
expect(project.sandboxes).toContain(worktree2)
|
expect(project.sandboxes).toContain(worktree2)
|
||||||
expect(project.sandboxes).not.toContain(tmp.path)
|
expect(project.sandboxes).not.toContain(tmp.path)
|
||||||
|
} finally {
|
||||||
await $`git worktree remove ${worktree1}`.cwd(tmp.path).quiet()
|
await $`git worktree remove ${worktree1}`
|
||||||
await $`git worktree remove ${worktree2}`.cwd(tmp.path).quiet()
|
.cwd(tmp.path)
|
||||||
|
.quiet()
|
||||||
|
.catch(() => {})
|
||||||
|
await $`git worktree remove ${worktree2}`
|
||||||
|
.cwd(tmp.path)
|
||||||
|
.quiet()
|
||||||
|
.catch(() => {})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -99,11 +111,12 @@ describe("Project.discover", () => {
|
||||||
|
|
||||||
await Project.discover(project)
|
await Project.discover(project)
|
||||||
|
|
||||||
const updated = await Storage.read<Project.Info>(["project", project.id])
|
const updated = Project.get(project.id)
|
||||||
expect(updated.icon).toBeDefined()
|
expect(updated).toBeDefined()
|
||||||
expect(updated.icon?.url).toStartWith("data:")
|
expect(updated!.icon).toBeDefined()
|
||||||
expect(updated.icon?.url).toContain("base64")
|
expect(updated!.icon?.url).toStartWith("data:")
|
||||||
expect(updated.icon?.color).toBeUndefined()
|
expect(updated!.icon?.url).toContain("base64")
|
||||||
|
expect(updated!.icon?.color).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
test("should not discover non-image files", async () => {
|
test("should not discover non-image files", async () => {
|
||||||
|
|
@ -114,7 +127,8 @@ describe("Project.discover", () => {
|
||||||
|
|
||||||
await Project.discover(project)
|
await Project.discover(project)
|
||||||
|
|
||||||
const updated = await Storage.read<Project.Info>(["project", project.id])
|
const updated = Project.get(project.id)
|
||||||
expect(updated.icon).toBeUndefined()
|
expect(updated).toBeDefined()
|
||||||
|
expect(updated!.icon).toBeUndefined()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
402
packages/opencode/test/storage/json-migration.test.ts
Normal file
402
packages/opencode/test/storage/json-migration.test.ts
Normal file
|
|
@ -0,0 +1,402 @@
|
||||||
|
import { describe, test, expect, beforeEach, afterEach } from "bun:test"
|
||||||
|
import { Database } from "bun:sqlite"
|
||||||
|
import { drizzle } from "drizzle-orm/bun-sqlite"
|
||||||
|
import { migrate } from "drizzle-orm/bun-sqlite/migrator"
|
||||||
|
import { eq } from "drizzle-orm"
|
||||||
|
import path from "path"
|
||||||
|
import fs from "fs/promises"
|
||||||
|
import { readFileSync, readdirSync } from "fs"
|
||||||
|
import { JsonMigration } from "../../src/storage/json-migration"
|
||||||
|
import { Global } from "../../src/global"
|
||||||
|
import { ProjectTable } from "../../src/project/project.sql"
|
||||||
|
import { Project } from "../../src/project/project"
|
||||||
|
import { SessionTable, MessageTable, PartTable, TodoTable, PermissionTable } from "../../src/session/session.sql"
|
||||||
|
import { SessionShareTable } from "../../src/share/share.sql"
|
||||||
|
|
||||||
|
// Test fixtures
|
||||||
|
const fixtures = {
|
||||||
|
project: {
|
||||||
|
id: "proj_test123abc",
|
||||||
|
name: "Test Project",
|
||||||
|
worktree: "/test/path",
|
||||||
|
vcs: "git" as const,
|
||||||
|
sandboxes: [],
|
||||||
|
},
|
||||||
|
session: {
|
||||||
|
id: "ses_test456def",
|
||||||
|
projectID: "proj_test123abc",
|
||||||
|
slug: "test-session",
|
||||||
|
directory: "/test/path",
|
||||||
|
title: "Test Session",
|
||||||
|
version: "1.0.0",
|
||||||
|
time: { created: 1700000000000, updated: 1700000001000 },
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
id: "msg_test789ghi",
|
||||||
|
sessionID: "ses_test456def",
|
||||||
|
role: "user" as const,
|
||||||
|
agent: "default",
|
||||||
|
model: { providerID: "openai", modelID: "gpt-4" },
|
||||||
|
time: { created: 1700000000000 },
|
||||||
|
},
|
||||||
|
part: {
|
||||||
|
id: "prt_testabc123",
|
||||||
|
messageID: "msg_test789ghi",
|
||||||
|
sessionID: "ses_test456def",
|
||||||
|
type: "text" as const,
|
||||||
|
text: "Hello, world!",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to create test storage directory structure
|
||||||
|
async function setupStorageDir() {
|
||||||
|
const storageDir = path.join(Global.Path.data, "storage")
|
||||||
|
await fs.rm(storageDir, { recursive: true, force: true })
|
||||||
|
await fs.mkdir(path.join(storageDir, "project"), { recursive: true })
|
||||||
|
await fs.mkdir(path.join(storageDir, "session", "proj_test123abc"), { recursive: true })
|
||||||
|
await fs.mkdir(path.join(storageDir, "message", "ses_test456def"), { recursive: true })
|
||||||
|
await fs.mkdir(path.join(storageDir, "part", "msg_test789ghi"), { recursive: true })
|
||||||
|
await fs.mkdir(path.join(storageDir, "session_diff"), { recursive: true })
|
||||||
|
await fs.mkdir(path.join(storageDir, "todo"), { recursive: true })
|
||||||
|
await fs.mkdir(path.join(storageDir, "permission"), { recursive: true })
|
||||||
|
await fs.mkdir(path.join(storageDir, "session_share"), { recursive: true })
|
||||||
|
// Create legacy marker to indicate JSON storage exists
|
||||||
|
await Bun.write(path.join(storageDir, "migration"), "1")
|
||||||
|
return storageDir
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to create in-memory test database with schema
|
||||||
|
function createTestDb() {
|
||||||
|
const sqlite = new Database(":memory:")
|
||||||
|
sqlite.exec("PRAGMA foreign_keys = ON")
|
||||||
|
|
||||||
|
// Apply schema migrations using drizzle migrate
|
||||||
|
const dir = path.join(import.meta.dirname, "../../migration")
|
||||||
|
const entries = readdirSync(dir, { withFileTypes: true })
|
||||||
|
const migrations = entries
|
||||||
|
.filter((entry) => entry.isDirectory())
|
||||||
|
.map((entry) => ({
|
||||||
|
sql: readFileSync(path.join(dir, entry.name, "migration.sql"), "utf-8"),
|
||||||
|
timestamp: Number(entry.name.split("_")[0]),
|
||||||
|
}))
|
||||||
|
.sort((a, b) => a.timestamp - b.timestamp)
|
||||||
|
migrate(drizzle({ client: sqlite }), migrations)
|
||||||
|
|
||||||
|
return sqlite
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("JSON to SQLite migration", () => {
|
||||||
|
let storageDir: string
|
||||||
|
let sqlite: Database
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
storageDir = await setupStorageDir()
|
||||||
|
sqlite = createTestDb()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
sqlite.close()
|
||||||
|
await fs.rm(storageDir, { recursive: true, force: true })
|
||||||
|
})
|
||||||
|
|
||||||
|
test("migrates project", async () => {
|
||||||
|
await Bun.write(
|
||||||
|
path.join(storageDir, "project", "proj_test123abc.json"),
|
||||||
|
JSON.stringify({
|
||||||
|
id: "proj_test123abc",
|
||||||
|
worktree: "/test/path",
|
||||||
|
vcs: "git",
|
||||||
|
name: "Test Project",
|
||||||
|
time: { created: 1700000000000, updated: 1700000001000 },
|
||||||
|
sandboxes: ["/test/sandbox"],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
const stats = await JsonMigration.run(sqlite)
|
||||||
|
|
||||||
|
expect(stats?.projects).toBe(1)
|
||||||
|
|
||||||
|
const db = drizzle({ client: sqlite })
|
||||||
|
const projects = db.select().from(ProjectTable).all()
|
||||||
|
expect(projects.length).toBe(1)
|
||||||
|
expect(projects[0].id).toBe("proj_test123abc")
|
||||||
|
expect(projects[0].worktree).toBe("/test/path")
|
||||||
|
expect(projects[0].name).toBe("Test Project")
|
||||||
|
expect(projects[0].sandboxes).toEqual(["/test/sandbox"])
|
||||||
|
})
|
||||||
|
|
||||||
|
test("migrates session with individual columns", async () => {
|
||||||
|
// First create the project
|
||||||
|
await Bun.write(
|
||||||
|
path.join(storageDir, "project", "proj_test123abc.json"),
|
||||||
|
JSON.stringify({
|
||||||
|
id: "proj_test123abc",
|
||||||
|
worktree: "/test/path",
|
||||||
|
time: { created: Date.now(), updated: Date.now() },
|
||||||
|
sandboxes: [],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
await Bun.write(
|
||||||
|
path.join(storageDir, "session", "proj_test123abc", "ses_test456def.json"),
|
||||||
|
JSON.stringify({
|
||||||
|
id: "ses_test456def",
|
||||||
|
projectID: "proj_test123abc",
|
||||||
|
slug: "test-session",
|
||||||
|
directory: "/test/dir",
|
||||||
|
title: "Test Session Title",
|
||||||
|
version: "1.0.0",
|
||||||
|
time: { created: 1700000000000, updated: 1700000001000 },
|
||||||
|
summary: { additions: 10, deletions: 5, files: 3 },
|
||||||
|
share: { url: "https://example.com/share" },
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
await JsonMigration.run(sqlite)
|
||||||
|
|
||||||
|
const db = drizzle({ client: sqlite })
|
||||||
|
const sessions = db.select().from(SessionTable).all()
|
||||||
|
expect(sessions.length).toBe(1)
|
||||||
|
expect(sessions[0].id).toBe("ses_test456def")
|
||||||
|
expect(sessions[0].project_id).toBe("proj_test123abc")
|
||||||
|
expect(sessions[0].slug).toBe("test-session")
|
||||||
|
expect(sessions[0].title).toBe("Test Session Title")
|
||||||
|
expect(sessions[0].summary_additions).toBe(10)
|
||||||
|
expect(sessions[0].summary_deletions).toBe(5)
|
||||||
|
expect(sessions[0].share_url).toBe("https://example.com/share")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("migrates messages and parts", async () => {
|
||||||
|
await Bun.write(
|
||||||
|
path.join(storageDir, "project", "proj_test123abc.json"),
|
||||||
|
JSON.stringify({
|
||||||
|
id: "proj_test123abc",
|
||||||
|
worktree: "/",
|
||||||
|
time: { created: Date.now(), updated: Date.now() },
|
||||||
|
sandboxes: [],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
await Bun.write(
|
||||||
|
path.join(storageDir, "session", "proj_test123abc", "ses_test456def.json"),
|
||||||
|
JSON.stringify({ ...fixtures.session }),
|
||||||
|
)
|
||||||
|
await Bun.write(
|
||||||
|
path.join(storageDir, "message", "ses_test456def", "msg_test789ghi.json"),
|
||||||
|
JSON.stringify({ ...fixtures.message }),
|
||||||
|
)
|
||||||
|
await Bun.write(
|
||||||
|
path.join(storageDir, "part", "msg_test789ghi", "prt_testabc123.json"),
|
||||||
|
JSON.stringify({ ...fixtures.part }),
|
||||||
|
)
|
||||||
|
|
||||||
|
const stats = await JsonMigration.run(sqlite)
|
||||||
|
|
||||||
|
expect(stats?.messages).toBe(1)
|
||||||
|
expect(stats?.parts).toBe(1)
|
||||||
|
|
||||||
|
const db = drizzle({ client: sqlite })
|
||||||
|
const messages = db.select().from(MessageTable).all()
|
||||||
|
expect(messages.length).toBe(1)
|
||||||
|
expect(messages[0].id).toBe("msg_test789ghi")
|
||||||
|
|
||||||
|
const parts = db.select().from(PartTable).all()
|
||||||
|
expect(parts.length).toBe(1)
|
||||||
|
expect(parts[0].id).toBe("prt_testabc123")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("skips orphaned sessions (no parent project)", async () => {
|
||||||
|
await Bun.write(
|
||||||
|
path.join(storageDir, "session", "proj_test123abc", "ses_orphan.json"),
|
||||||
|
JSON.stringify({
|
||||||
|
id: "ses_orphan",
|
||||||
|
projectID: "proj_nonexistent",
|
||||||
|
slug: "orphan",
|
||||||
|
directory: "/",
|
||||||
|
title: "Orphan",
|
||||||
|
version: "1.0.0",
|
||||||
|
time: { created: Date.now(), updated: Date.now() },
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
const stats = await JsonMigration.run(sqlite)
|
||||||
|
|
||||||
|
expect(stats?.sessions).toBe(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("is idempotent (running twice doesn't duplicate)", async () => {
|
||||||
|
await Bun.write(
|
||||||
|
path.join(storageDir, "project", "proj_test123abc.json"),
|
||||||
|
JSON.stringify({
|
||||||
|
id: "proj_test123abc",
|
||||||
|
worktree: "/",
|
||||||
|
time: { created: Date.now(), updated: Date.now() },
|
||||||
|
sandboxes: [],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
await JsonMigration.run(sqlite)
|
||||||
|
await JsonMigration.run(sqlite)
|
||||||
|
|
||||||
|
const db = drizzle({ client: sqlite })
|
||||||
|
const projects = db.select().from(ProjectTable).all()
|
||||||
|
expect(projects.length).toBe(1) // Still only 1 due to onConflictDoNothing
|
||||||
|
})
|
||||||
|
|
||||||
|
test("migrates todos", async () => {
|
||||||
|
// First create the project and session
|
||||||
|
await Bun.write(
|
||||||
|
path.join(storageDir, "project", "proj_test123abc.json"),
|
||||||
|
JSON.stringify({
|
||||||
|
id: "proj_test123abc",
|
||||||
|
worktree: "/",
|
||||||
|
time: { created: Date.now(), updated: Date.now() },
|
||||||
|
sandboxes: [],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
await Bun.write(
|
||||||
|
path.join(storageDir, "session", "proj_test123abc", "ses_test456def.json"),
|
||||||
|
JSON.stringify({ ...fixtures.session }),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create todo file (named by sessionID, contains array of todos)
|
||||||
|
await Bun.write(
|
||||||
|
path.join(storageDir, "todo", "ses_test456def.json"),
|
||||||
|
JSON.stringify([
|
||||||
|
{
|
||||||
|
id: "todo_1",
|
||||||
|
content: "First todo",
|
||||||
|
status: "pending",
|
||||||
|
priority: "high",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "todo_2",
|
||||||
|
content: "Second todo",
|
||||||
|
status: "completed",
|
||||||
|
priority: "medium",
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
|
||||||
|
const stats = await JsonMigration.run(sqlite)
|
||||||
|
|
||||||
|
expect(stats?.todos).toBe(2)
|
||||||
|
|
||||||
|
const db = drizzle({ client: sqlite })
|
||||||
|
const todos = db.select().from(TodoTable).orderBy(TodoTable.position).all()
|
||||||
|
expect(todos.length).toBe(2)
|
||||||
|
expect(todos[0].content).toBe("First todo")
|
||||||
|
expect(todos[0].status).toBe("pending")
|
||||||
|
expect(todos[0].priority).toBe("high")
|
||||||
|
expect(todos[0].position).toBe(0)
|
||||||
|
expect(todos[1].content).toBe("Second todo")
|
||||||
|
expect(todos[1].position).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("todos are ordered by position", async () => {
|
||||||
|
await Bun.write(
|
||||||
|
path.join(storageDir, "project", "proj_test123abc.json"),
|
||||||
|
JSON.stringify({
|
||||||
|
id: "proj_test123abc",
|
||||||
|
worktree: "/",
|
||||||
|
time: { created: Date.now(), updated: Date.now() },
|
||||||
|
sandboxes: [],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
await Bun.write(
|
||||||
|
path.join(storageDir, "session", "proj_test123abc", "ses_test456def.json"),
|
||||||
|
JSON.stringify({ ...fixtures.session }),
|
||||||
|
)
|
||||||
|
|
||||||
|
await Bun.write(
|
||||||
|
path.join(storageDir, "todo", "ses_test456def.json"),
|
||||||
|
JSON.stringify([
|
||||||
|
{ content: "Third", status: "pending", priority: "low" },
|
||||||
|
{ content: "First", status: "pending", priority: "high" },
|
||||||
|
{ content: "Second", status: "in_progress", priority: "medium" },
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
|
||||||
|
await JsonMigration.run(sqlite)
|
||||||
|
|
||||||
|
const db = drizzle({ client: sqlite })
|
||||||
|
const todos = db.select().from(TodoTable).orderBy(TodoTable.position).all()
|
||||||
|
|
||||||
|
expect(todos.length).toBe(3)
|
||||||
|
expect(todos[0].content).toBe("Third")
|
||||||
|
expect(todos[0].position).toBe(0)
|
||||||
|
expect(todos[1].content).toBe("First")
|
||||||
|
expect(todos[1].position).toBe(1)
|
||||||
|
expect(todos[2].content).toBe("Second")
|
||||||
|
expect(todos[2].position).toBe(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("migrates permissions", async () => {
|
||||||
|
// First create the project
|
||||||
|
await Bun.write(
|
||||||
|
path.join(storageDir, "project", "proj_test123abc.json"),
|
||||||
|
JSON.stringify({
|
||||||
|
id: "proj_test123abc",
|
||||||
|
worktree: "/",
|
||||||
|
time: { created: Date.now(), updated: Date.now() },
|
||||||
|
sandboxes: [],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create permission file (named by projectID, contains array of rules)
|
||||||
|
const permissionData = [
|
||||||
|
{ permission: "file.read", pattern: "/test/file1.ts", action: "allow" as const },
|
||||||
|
{ permission: "file.write", pattern: "/test/file2.ts", action: "ask" as const },
|
||||||
|
{ permission: "command.run", pattern: "npm install", action: "deny" as const },
|
||||||
|
]
|
||||||
|
await Bun.write(path.join(storageDir, "permission", "proj_test123abc.json"), JSON.stringify(permissionData))
|
||||||
|
|
||||||
|
const stats = await JsonMigration.run(sqlite)
|
||||||
|
|
||||||
|
expect(stats?.permissions).toBe(1)
|
||||||
|
|
||||||
|
const db = drizzle({ client: sqlite })
|
||||||
|
const permissions = db.select().from(PermissionTable).all()
|
||||||
|
expect(permissions.length).toBe(1)
|
||||||
|
expect(permissions[0].project_id).toBe("proj_test123abc")
|
||||||
|
expect(permissions[0].data).toEqual(permissionData)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("migrates session shares", async () => {
|
||||||
|
// First create the project and session
|
||||||
|
await Bun.write(
|
||||||
|
path.join(storageDir, "project", "proj_test123abc.json"),
|
||||||
|
JSON.stringify({
|
||||||
|
id: "proj_test123abc",
|
||||||
|
worktree: "/",
|
||||||
|
time: { created: Date.now(), updated: Date.now() },
|
||||||
|
sandboxes: [],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
await Bun.write(
|
||||||
|
path.join(storageDir, "session", "proj_test123abc", "ses_test456def.json"),
|
||||||
|
JSON.stringify({ ...fixtures.session }),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create session share file (named by sessionID)
|
||||||
|
await Bun.write(
|
||||||
|
path.join(storageDir, "session_share", "ses_test456def.json"),
|
||||||
|
JSON.stringify({
|
||||||
|
id: "share_123",
|
||||||
|
secret: "supersecretkey",
|
||||||
|
url: "https://share.example.com/ses_test456def",
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
const stats = await JsonMigration.run(sqlite)
|
||||||
|
|
||||||
|
expect(stats?.shares).toBe(1)
|
||||||
|
|
||||||
|
const db = drizzle({ client: sqlite })
|
||||||
|
const shares = db.select().from(SessionShareTable).all()
|
||||||
|
expect(shares.length).toBe(1)
|
||||||
|
expect(shares[0].session_id).toBe("ses_test456def")
|
||||||
|
expect(shares[0].id).toBe("share_123")
|
||||||
|
expect(shares[0].secret).toBe("supersecretkey")
|
||||||
|
expect(shares[0].url).toBe("https://share.example.com/ses_test456def")
|
||||||
|
})
|
||||||
|
})
|
||||||
0
packages/sdk/js/openapi.json
Normal file
0
packages/sdk/js/openapi.json
Normal file
|
|
@ -481,7 +481,17 @@ export type EventMessagePartUpdated = {
|
||||||
type: "message.part.updated"
|
type: "message.part.updated"
|
||||||
properties: {
|
properties: {
|
||||||
part: Part
|
part: Part
|
||||||
delta?: string
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EventMessagePartDelta = {
|
||||||
|
type: "message.part.delta"
|
||||||
|
properties: {
|
||||||
|
sessionID: string
|
||||||
|
messageID: string
|
||||||
|
partID: string
|
||||||
|
field: string
|
||||||
|
delta: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -651,10 +661,6 @@ export type Todo = {
|
||||||
* Priority level of the task: high, medium, low
|
* Priority level of the task: high, medium, low
|
||||||
*/
|
*/
|
||||||
priority: string
|
priority: string
|
||||||
/**
|
|
||||||
* Unique identifier for the todo item
|
|
||||||
*/
|
|
||||||
id: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EventTodoUpdated = {
|
export type EventTodoUpdated = {
|
||||||
|
|
@ -897,6 +903,7 @@ export type Event =
|
||||||
| EventMessageUpdated
|
| EventMessageUpdated
|
||||||
| EventMessageRemoved
|
| EventMessageRemoved
|
||||||
| EventMessagePartUpdated
|
| EventMessagePartUpdated
|
||||||
|
| EventMessagePartDelta
|
||||||
| EventMessagePartRemoved
|
| EventMessagePartRemoved
|
||||||
| EventPermissionAsked
|
| EventPermissionAsked
|
||||||
| EventPermissionReplied
|
| EventPermissionReplied
|
||||||
|
|
|
||||||
|
|
@ -7221,12 +7221,40 @@
|
||||||
"properties": {
|
"properties": {
|
||||||
"part": {
|
"part": {
|
||||||
"$ref": "#/components/schemas/Part"
|
"$ref": "#/components/schemas/Part"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["part"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["type", "properties"]
|
||||||
|
},
|
||||||
|
"Event.message.part.delta": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"const": "message.part.delta"
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"sessionID": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"messageID": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"partID": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"field": {
|
||||||
|
"type": "string"
|
||||||
},
|
},
|
||||||
"delta": {
|
"delta": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["part"]
|
"required": ["sessionID", "messageID", "partID", "field", "delta"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["type", "properties"]
|
"required": ["type", "properties"]
|
||||||
|
|
@ -7640,13 +7668,9 @@
|
||||||
"priority": {
|
"priority": {
|
||||||
"description": "Priority level of the task: high, medium, low",
|
"description": "Priority level of the task: high, medium, low",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"description": "Unique identifier for the todo item",
|
|
||||||
"type": "string"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["content", "status", "priority", "id"]
|
"required": ["content", "status", "priority"]
|
||||||
},
|
},
|
||||||
"Event.todo.updated": {
|
"Event.todo.updated": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|
@ -8311,6 +8335,9 @@
|
||||||
{
|
{
|
||||||
"$ref": "#/components/schemas/Event.message.part.updated"
|
"$ref": "#/components/schemas/Event.message.part.updated"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Event.message.part.delta"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/components/schemas/Event.message.part.removed"
|
"$ref": "#/components/schemas/Event.message.part.removed"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue