Compare commits
1 commit
dev
...
jlongster/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba499fb405 |
28 changed files with 2830 additions and 406 deletions
|
|
@ -11,6 +11,7 @@
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"mcp": {},
|
"mcp": {},
|
||||||
|
"plugin": ["../daytonaWorkspacePlugin.ts", "../debugWorkspacePlugin.ts"],
|
||||||
"tools": {
|
"tools": {
|
||||||
"github-triage": false,
|
"github-triage": false,
|
||||||
"github-pr-search": false,
|
"github-pr-search": false,
|
||||||
|
|
|
||||||
121
WORKSPACE_SESSION_DELETE_NOTES.md
Normal file
121
WORKSPACE_SESSION_DELETE_NOTES.md
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
# Workspace Session Delete Notes
|
||||||
|
|
||||||
|
## Current state
|
||||||
|
|
||||||
|
- Deleting a workspace-backed session now removes the session record correctly.
|
||||||
|
- The UI still goes stale in some cases because the delete is handled locally and the expected event propagation is incomplete.
|
||||||
|
|
||||||
|
## Important behavior discovered
|
||||||
|
|
||||||
|
### 1. `DELETE /session/:id` is currently being handled locally
|
||||||
|
|
||||||
|
This is because workspace routing was changed so path handling happens earlier, and we now always let `DELETE /session/:id` through the local route path.
|
||||||
|
|
||||||
|
That is a little weird semantically, because for workspace-backed sessions the delete action conceptually belongs to the remote workspace session too.
|
||||||
|
|
||||||
|
Relevant code:
|
||||||
|
|
||||||
|
- `packages/opencode/src/server/router.ts`
|
||||||
|
- special-case for `DELETE /session/:id`
|
||||||
|
|
||||||
|
### 2. Local delete works, but UI does not fully update from events
|
||||||
|
|
||||||
|
The delete operation works server-side, but because we do not have an active instance in this local-delete path, we are not publishing the same events the TUI expects for immediate sync/UI updates.
|
||||||
|
|
||||||
|
That means:
|
||||||
|
|
||||||
|
- delete succeeds
|
||||||
|
- persistence updates
|
||||||
|
- but event-driven UI refresh may not happen
|
||||||
|
|
||||||
|
## Root issue
|
||||||
|
|
||||||
|
We need event publication for local handling of workspace session deletes, but in this code path we may not have an instance context.
|
||||||
|
|
||||||
|
So the current system is in an awkward middle state:
|
||||||
|
|
||||||
|
- delete is handled locally
|
||||||
|
- remote session/workspace semantics still matter
|
||||||
|
- but local event publishing is instance-dependent in places
|
||||||
|
|
||||||
|
## Key design question
|
||||||
|
|
||||||
|
Where should the "delete last session in workspace -> delete workspace" logic live?
|
||||||
|
|
||||||
|
### Option A: handle it remotely
|
||||||
|
|
||||||
|
If the remote workspace handles session deletion and also decides whether the workspace should be deleted, then the result can sync back naturally.
|
||||||
|
|
||||||
|
Pros:
|
||||||
|
|
||||||
|
- cleaner ownership model
|
||||||
|
- remote workspace remains source of truth for workspace-backed session lifecycle
|
||||||
|
- sync/event flow stays more consistent
|
||||||
|
|
||||||
|
Cons:
|
||||||
|
|
||||||
|
- requires remote delete path to be used reliably
|
||||||
|
- local special-casing in router becomes more suspect
|
||||||
|
|
||||||
|
### Option B: handle it locally
|
||||||
|
|
||||||
|
If local server deletes the session and then checks whether any sessions remain for that `workspaceID`, local can also delete the workspace.
|
||||||
|
|
||||||
|
Pros:
|
||||||
|
|
||||||
|
- straightforward to implement
|
||||||
|
- does not depend on remote behavior
|
||||||
|
|
||||||
|
Cons:
|
||||||
|
|
||||||
|
- local path now owns workspace lifecycle decisions for remote workspaces
|
||||||
|
- still has event propagation problems unless we explicitly publish/update correctly
|
||||||
|
|
||||||
|
## Current leaning
|
||||||
|
|
||||||
|
The workspace cleanup logic probably belongs on the remote side if workspace-backed sessions are supposed to behave as remote-owned state.
|
||||||
|
|
||||||
|
Reason:
|
||||||
|
|
||||||
|
- if remote handles it, the result can sync back
|
||||||
|
- avoids local special-case ownership drift
|
||||||
|
|
||||||
|
But this depends on whether `DELETE /session/:id` should actually be routed remotely for workspace sessions instead of always being forced local.
|
||||||
|
|
||||||
|
## Things to inspect next
|
||||||
|
|
||||||
|
1. `packages/opencode/src/server/router.ts`
|
||||||
|
|
||||||
|
- Revisit why `DELETE /session/:id` is forced local.
|
||||||
|
- Decide whether workspace-backed session deletes should proxy to remote instead.
|
||||||
|
|
||||||
|
2. `packages/opencode/src/session/index.ts`
|
||||||
|
|
||||||
|
- Current local cleanup logic removes workspace if no sessions remain for its `workspaceID`.
|
||||||
|
- Re-evaluate whether this should stay here or move to remote handling.
|
||||||
|
|
||||||
|
3. Event publication path
|
||||||
|
|
||||||
|
- Figure out what event(s) the TUI actually needs to update correctly after delete.
|
||||||
|
- Check whether local delete without instance can still publish enough global/sync events.
|
||||||
|
|
||||||
|
4. TUI refresh path
|
||||||
|
|
||||||
|
- `packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx`
|
||||||
|
- `packages/opencode/src/cli/cmd/tui/context/sync.tsx`
|
||||||
|
|
||||||
|
We added an explicit session-list refresh to work around stale UI, but that is treating the symptom.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
The real unresolved issue is ownership:
|
||||||
|
|
||||||
|
- local currently handles `DELETE /session/:id`
|
||||||
|
- remote workspace semantics still matter
|
||||||
|
- event propagation is incomplete when delete is handled without an instance
|
||||||
|
|
||||||
|
Next session should start by deciding:
|
||||||
|
|
||||||
|
1. Should workspace session delete be handled locally or remotely?
|
||||||
|
2. Where should "delete workspace if no sessions remain" live?
|
||||||
|
3. What event must be published so the UI updates without manual refresh?
|
||||||
250
bun.lock
250
bun.lock
|
|
@ -6,6 +6,7 @@
|
||||||
"name": "opencode",
|
"name": "opencode",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "3.933.0",
|
"@aws-sdk/client-s3": "3.933.0",
|
||||||
|
"@daytona/sdk": "0.164.0",
|
||||||
"@opencode-ai/plugin": "workspace:*",
|
"@opencode-ai/plugin": "workspace:*",
|
||||||
"@opencode-ai/script": "workspace:*",
|
"@opencode-ai/script": "workspace:*",
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
|
|
@ -628,6 +629,7 @@
|
||||||
"trustedDependencies": [
|
"trustedDependencies": [
|
||||||
"esbuild",
|
"esbuild",
|
||||||
"tree-sitter-powershell",
|
"tree-sitter-powershell",
|
||||||
|
"protobufjs",
|
||||||
"electron",
|
"electron",
|
||||||
"web-tree-sitter",
|
"web-tree-sitter",
|
||||||
"tree-sitter-bash",
|
"tree-sitter-bash",
|
||||||
|
|
@ -836,6 +838,8 @@
|
||||||
|
|
||||||
"@aws-sdk/credential-providers": ["@aws-sdk/credential-providers@3.993.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.993.0", "@aws-sdk/core": "^3.973.11", "@aws-sdk/credential-provider-cognito-identity": "^3.972.3", "@aws-sdk/credential-provider-env": "^3.972.9", "@aws-sdk/credential-provider-http": "^3.972.11", "@aws-sdk/credential-provider-ini": "^3.972.9", "@aws-sdk/credential-provider-login": "^3.972.9", "@aws-sdk/credential-provider-node": "^3.972.10", "@aws-sdk/credential-provider-process": "^3.972.9", "@aws-sdk/credential-provider-sso": "^3.972.9", "@aws-sdk/credential-provider-web-identity": "^3.972.9", "@aws-sdk/nested-clients": "3.993.0", "@aws-sdk/types": "^3.973.1", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.23.2", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-1M/nukgPSLqe9krzOKHnE8OylUaKAiokAV3xRLdeExVHcRE7WG5uzCTKWTj1imKvPjDqXq/FWhlbbdWIn7xIwA=="],
|
"@aws-sdk/credential-providers": ["@aws-sdk/credential-providers@3.993.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.993.0", "@aws-sdk/core": "^3.973.11", "@aws-sdk/credential-provider-cognito-identity": "^3.972.3", "@aws-sdk/credential-provider-env": "^3.972.9", "@aws-sdk/credential-provider-http": "^3.972.11", "@aws-sdk/credential-provider-ini": "^3.972.9", "@aws-sdk/credential-provider-login": "^3.972.9", "@aws-sdk/credential-provider-node": "^3.972.10", "@aws-sdk/credential-provider-process": "^3.972.9", "@aws-sdk/credential-provider-sso": "^3.972.9", "@aws-sdk/credential-provider-web-identity": "^3.972.9", "@aws-sdk/nested-clients": "3.993.0", "@aws-sdk/types": "^3.973.1", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.23.2", "@smithy/credential-provider-imds": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/property-provider": "^4.2.8", "@smithy/types": "^4.12.0", "tslib": "^2.6.2" } }, "sha512-1M/nukgPSLqe9krzOKHnE8OylUaKAiokAV3xRLdeExVHcRE7WG5uzCTKWTj1imKvPjDqXq/FWhlbbdWIn7xIwA=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage": ["@aws-sdk/lib-storage@3.1028.0", "", { "dependencies": { "@smithy/middleware-endpoint": "^4.4.29", "@smithy/protocol-http": "^5.3.13", "@smithy/smithy-client": "^4.12.9", "@smithy/types": "^4.14.0", "buffer": "5.6.0", "events": "3.3.0", "stream-browserify": "3.0.0", "tslib": "^2.6.2" }, "peerDependencies": { "@aws-sdk/client-s3": "^3.1028.0" } }, "sha512-AT937nfpMDW/8oDiWPBP/BdGJ6943ALMWTBpUi0fD0qelA3lyZgErSnX7yp9j3t/enzyHdlyBOPq9kGFBt0Xcg=="],
|
||||||
|
|
||||||
"@aws-sdk/middleware-bucket-endpoint": ["@aws-sdk/middleware-bucket-endpoint@3.930.0", "", { "dependencies": { "@aws-sdk/types": "3.930.0", "@aws-sdk/util-arn-parser": "3.893.0", "@smithy/node-config-provider": "^4.3.5", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "@smithy/util-config-provider": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-cnCLWeKPYgvV4yRYPFH6pWMdUByvu2cy2BAlfsPpvnm4RaVioztyvxmQj5PmVN5fvWs5w/2d6U7le8X9iye2sA=="],
|
"@aws-sdk/middleware-bucket-endpoint": ["@aws-sdk/middleware-bucket-endpoint@3.930.0", "", { "dependencies": { "@aws-sdk/types": "3.930.0", "@aws-sdk/util-arn-parser": "3.893.0", "@smithy/node-config-provider": "^4.3.5", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "@smithy/util-config-provider": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-cnCLWeKPYgvV4yRYPFH6pWMdUByvu2cy2BAlfsPpvnm4RaVioztyvxmQj5PmVN5fvWs5w/2d6U7le8X9iye2sA=="],
|
||||||
|
|
||||||
"@aws-sdk/middleware-expect-continue": ["@aws-sdk/middleware-expect-continue@3.930.0", "", { "dependencies": { "@aws-sdk/types": "3.930.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-5HEQ+JU4DrLNWeY27wKg/jeVa8Suy62ivJHOSUf6e6hZdVIMx0h/kXS1fHEQNNiLu2IzSEP/bFXsKBaW7x7s0g=="],
|
"@aws-sdk/middleware-expect-continue": ["@aws-sdk/middleware-expect-continue@3.930.0", "", { "dependencies": { "@aws-sdk/types": "3.930.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-5HEQ+JU4DrLNWeY27wKg/jeVa8Suy62ivJHOSUf6e6hZdVIMx0h/kXS1fHEQNNiLu2IzSEP/bFXsKBaW7x7s0g=="],
|
||||||
|
|
@ -1020,6 +1024,12 @@
|
||||||
|
|
||||||
"@ctrl/tinycolor": ["@ctrl/tinycolor@4.2.0", "", {}, "sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A=="],
|
"@ctrl/tinycolor": ["@ctrl/tinycolor@4.2.0", "", {}, "sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A=="],
|
||||||
|
|
||||||
|
"@daytona/api-client": ["@daytona/api-client@0.164.0", "", { "dependencies": { "axios": "^1.6.1" } }, "sha512-G1cV6gC0mTOMSkGy4C5FFkxEytNItOzNCWXtnYVGepLqcLsnXuv1+vpIJFiIsfKEjrZKax8bu2sdSA8lQHTyOw=="],
|
||||||
|
|
||||||
|
"@daytona/sdk": ["@daytona/sdk@0.164.0", "", { "dependencies": { "@aws-sdk/client-s3": "^3.787.0", "@aws-sdk/lib-storage": "^3.798.0", "@daytona/api-client": "0.164.0", "@daytona/toolbox-api-client": "0.164.0", "@iarna/toml": "^2.2.5", "@opentelemetry/api": "^1.9.0", "@opentelemetry/exporter-trace-otlp-http": "^0.207.0", "@opentelemetry/instrumentation-http": "^0.207.0", "@opentelemetry/otlp-exporter-base": "0.207.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-node": "^0.207.0", "@opentelemetry/sdk-trace-base": "^2.2.0", "@opentelemetry/semantic-conventions": "^1.37.0", "axios": "^1.13.5", "busboy": "^1.0.0", "dotenv": "^17.0.1", "expand-tilde": "^2.0.2", "fast-glob": "^3.3.0", "form-data": "^4.0.4", "isomorphic-ws": "^5.0.0", "pathe": "^2.0.3", "shell-quote": "^1.8.2", "tar": "^7.5.11" } }, "sha512-EBxKyWjvbMMR7ZpcHp+QvtJHPeCQ7SVO08okEWMLiKeJ0bqSyXgzYW5gbXUyhhogOkcDptD+j89yK1EnTTWjSg=="],
|
||||||
|
|
||||||
|
"@daytona/toolbox-api-client": ["@daytona/toolbox-api-client@0.164.0", "", { "dependencies": { "axios": "^1.6.1" } }, "sha512-7LU6mxPuobkgDIg24MHP7kf36Wm1kSSvorM/8ODGTkbLfUdXk7DSTGBs9Lqoy0cM4/62LJ9kPmjw607pPSTXbQ=="],
|
||||||
|
|
||||||
"@develar/schema-utils": ["@develar/schema-utils@2.6.5", "", { "dependencies": { "ajv": "^6.12.0", "ajv-keywords": "^3.4.1" } }, "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig=="],
|
"@develar/schema-utils": ["@develar/schema-utils@2.6.5", "", { "dependencies": { "ajv": "^6.12.0", "ajv-keywords": "^3.4.1" } }, "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig=="],
|
||||||
|
|
||||||
"@dimforge/rapier2d-simd-compat": ["@dimforge/rapier2d-simd-compat@0.17.3", "", {}, "sha512-bijvwWz6NHsNj5e5i1vtd3dU2pDhthSaTUZSh14DUGGKJfw8eMnlWZsxwHBxB/a3AXVNDjL9abuHw1k9FGR+jg=="],
|
"@dimforge/rapier2d-simd-compat": ["@dimforge/rapier2d-simd-compat@0.17.3", "", {}, "sha512-bijvwWz6NHsNj5e5i1vtd3dU2pDhthSaTUZSh14DUGGKJfw8eMnlWZsxwHBxB/a3AXVNDjL9abuHw1k9FGR+jg=="],
|
||||||
|
|
@ -1168,6 +1178,10 @@
|
||||||
|
|
||||||
"@graphql-typed-document-node/core": ["@graphql-typed-document-node/core@3.2.0", "", { "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ=="],
|
"@graphql-typed-document-node/core": ["@graphql-typed-document-node/core@3.2.0", "", { "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ=="],
|
||||||
|
|
||||||
|
"@grpc/grpc-js": ["@grpc/grpc-js@1.14.3", "", { "dependencies": { "@grpc/proto-loader": "^0.8.0", "@js-sdsl/ordered-map": "^4.4.2" } }, "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA=="],
|
||||||
|
|
||||||
|
"@grpc/proto-loader": ["@grpc/proto-loader@0.8.0", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.5.3", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ=="],
|
||||||
|
|
||||||
"@happy-dom/global-registrator": ["@happy-dom/global-registrator@20.0.11", "", { "dependencies": { "@types/node": "^20.0.0", "happy-dom": "^20.0.11" } }, "sha512-GqNqiShBT/lzkHTMC/slKBrvN0DsD4Di8ssBk4aDaVgEn+2WMzE6DXxq701ndSXj7/0cJ8mNT71pM7Bnrr6JRw=="],
|
"@happy-dom/global-registrator": ["@happy-dom/global-registrator@20.0.11", "", { "dependencies": { "@types/node": "^20.0.0", "happy-dom": "^20.0.11" } }, "sha512-GqNqiShBT/lzkHTMC/slKBrvN0DsD4Di8ssBk4aDaVgEn+2WMzE6DXxq701ndSXj7/0cJ8mNT71pM7Bnrr6JRw=="],
|
||||||
|
|
||||||
"@hey-api/codegen-core": ["@hey-api/codegen-core@0.5.5", "", { "dependencies": { "@hey-api/types": "0.1.2", "ansi-colors": "4.1.3", "c12": "3.3.3", "color-support": "1.1.3" }, "peerDependencies": { "typescript": ">=5.5.3" } }, "sha512-f2ZHucnA2wBGAY8ipB4wn/mrEYW+WUxU2huJmUvfDO6AE2vfILSHeF3wCO39Pz4wUYPoAWZByaauftLrOfC12Q=="],
|
"@hey-api/codegen-core": ["@hey-api/codegen-core@0.5.5", "", { "dependencies": { "@hey-api/types": "0.1.2", "ansi-colors": "4.1.3", "c12": "3.3.3", "color-support": "1.1.3" }, "peerDependencies": { "typescript": ">=5.5.3" } }, "sha512-f2ZHucnA2wBGAY8ipB4wn/mrEYW+WUxU2huJmUvfDO6AE2vfILSHeF3wCO39Pz4wUYPoAWZByaauftLrOfC12Q=="],
|
||||||
|
|
@ -1186,6 +1200,8 @@
|
||||||
|
|
||||||
"@hono/zod-validator": ["@hono/zod-validator@0.4.2", "", { "peerDependencies": { "hono": ">=3.9.0", "zod": "^3.19.1" } }, "sha512-1rrlBg+EpDPhzOV4hT9pxr5+xDVmKuz6YJl+la7VCwK6ass5ldyKm5fD+umJdV2zhHD6jROoCCv8NbTwyfhT0g=="],
|
"@hono/zod-validator": ["@hono/zod-validator@0.4.2", "", { "peerDependencies": { "hono": ">=3.9.0", "zod": "^3.19.1" } }, "sha512-1rrlBg+EpDPhzOV4hT9pxr5+xDVmKuz6YJl+la7VCwK6ass5ldyKm5fD+umJdV2zhHD6jROoCCv8NbTwyfhT0g=="],
|
||||||
|
|
||||||
|
"@iarna/toml": ["@iarna/toml@2.2.5", "", {}, "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg=="],
|
||||||
|
|
||||||
"@ibm/plex": ["@ibm/plex@6.4.1", "", { "dependencies": { "@ibm/telemetry-js": "^1.5.1" } }, "sha512-fnsipQywHt3zWvsnlyYKMikcVI7E2fEwpiPnIHFqlbByXVfQfANAAeJk1IV4mNnxhppUIDlhU0TzwYwL++Rn2g=="],
|
"@ibm/plex": ["@ibm/plex@6.4.1", "", { "dependencies": { "@ibm/telemetry-js": "^1.5.1" } }, "sha512-fnsipQywHt3zWvsnlyYKMikcVI7E2fEwpiPnIHFqlbByXVfQfANAAeJk1IV4mNnxhppUIDlhU0TzwYwL++Rn2g=="],
|
||||||
|
|
||||||
"@ibm/telemetry-js": ["@ibm/telemetry-js@1.11.0", "", { "bin": { "ibmtelemetry": "dist/collect.js" } }, "sha512-RO/9j+URJnSfseWg9ZkEX9p+a3Ousd33DBU7rOafoZB08RqdzxFVYJ2/iM50dkBuD0o7WX7GYt1sLbNgCoE+pA=="],
|
"@ibm/telemetry-js": ["@ibm/telemetry-js@1.11.0", "", { "bin": { "ibmtelemetry": "dist/collect.js" } }, "sha512-RO/9j+URJnSfseWg9ZkEX9p+a3Ousd33DBU7rOafoZB08RqdzxFVYJ2/iM50dkBuD0o7WX7GYt1sLbNgCoE+pA=="],
|
||||||
|
|
@ -1316,6 +1332,8 @@
|
||||||
|
|
||||||
"@js-joda/core": ["@js-joda/core@5.7.0", "", {}, "sha512-WBu4ULVVxySLLzK1Ppq+OdfP+adRS4ntmDQT915rzDJ++i95gc2jZkM5B6LWEAwN3lGXpfie3yPABozdD3K3Vg=="],
|
"@js-joda/core": ["@js-joda/core@5.7.0", "", {}, "sha512-WBu4ULVVxySLLzK1Ppq+OdfP+adRS4ntmDQT915rzDJ++i95gc2jZkM5B6LWEAwN3lGXpfie3yPABozdD3K3Vg=="],
|
||||||
|
|
||||||
|
"@js-sdsl/ordered-map": ["@js-sdsl/ordered-map@4.4.2", "", {}, "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw=="],
|
||||||
|
|
||||||
"@js-temporal/polyfill": ["@js-temporal/polyfill@0.5.1", "", { "dependencies": { "jsbi": "^4.3.0" } }, "sha512-hloP58zRVCRSpgDxmqCWJNlizAlUgJFqG2ypq79DCvyv9tHjRYMDOcPFjzfl/A1/YxDvRCZz8wvZvmapQnKwFQ=="],
|
"@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=="],
|
||||||
|
|
@ -1544,6 +1562,62 @@
|
||||||
|
|
||||||
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
|
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
|
||||||
|
|
||||||
|
"@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.207.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-lAb0jQRVyleQQGiuuvCOTDVspc14nx6XJjP4FspJ1sNARo3Regq4ZZbrc3rN4b1TYSuUCvgH+UXUPug4SLOqEQ=="],
|
||||||
|
|
||||||
|
"@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@2.2.0", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-qRkLWiUEZNAmYapZ7KGS5C4OmBLcP/H2foXeOEaowYCR0wi89fHejrfYfbuLVCMLp/dWZXKvQusdbUEZjERfwQ=="],
|
||||||
|
|
||||||
|
"@opentelemetry/core": ["@opentelemetry/core@2.2.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw=="],
|
||||||
|
|
||||||
|
"@opentelemetry/exporter-logs-otlp-grpc": ["@opentelemetry/exporter-logs-otlp-grpc@0.207.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-exporter-base": "0.207.0", "@opentelemetry/otlp-grpc-exporter-base": "0.207.0", "@opentelemetry/otlp-transformer": "0.207.0", "@opentelemetry/sdk-logs": "0.207.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-K92RN+kQGTMzFDsCzsYNGqOsXRUnko/Ckk+t/yPJao72MewOLgBUTWVHhebgkNfRCYqDz1v3K0aPT9OJkemvgg=="],
|
||||||
|
|
||||||
|
"@opentelemetry/exporter-logs-otlp-http": ["@opentelemetry/exporter-logs-otlp-http@0.207.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.207.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-exporter-base": "0.207.0", "@opentelemetry/otlp-transformer": "0.207.0", "@opentelemetry/sdk-logs": "0.207.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-JpOh7MguEUls8eRfkVVW3yRhClo5b9LqwWTOg8+i4gjr/+8eiCtquJnC7whvpTIGyff06cLZ2NsEj+CVP3Mjeg=="],
|
||||||
|
|
||||||
|
"@opentelemetry/exporter-logs-otlp-proto": ["@opentelemetry/exporter-logs-otlp-proto@0.207.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.207.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-exporter-base": "0.207.0", "@opentelemetry/otlp-transformer": "0.207.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.207.0", "@opentelemetry/sdk-trace-base": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-RQJEV/K6KPbQrIUbsrRkEe0ufks1o5OGLHy6jbDD8tRjeCsbFHWfg99lYBRqBV33PYZJXsigqMaAbjWGTFYzLw=="],
|
||||||
|
|
||||||
|
"@opentelemetry/exporter-metrics-otlp-grpc": ["@opentelemetry/exporter-metrics-otlp-grpc@0.207.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.2.0", "@opentelemetry/exporter-metrics-otlp-http": "0.207.0", "@opentelemetry/otlp-exporter-base": "0.207.0", "@opentelemetry/otlp-grpc-exporter-base": "0.207.0", "@opentelemetry/otlp-transformer": "0.207.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-metrics": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-6flX89W54gkwmqYShdcTBR1AEF5C1Ob0O8pDgmLPikTKyEv27lByr9yBmO5WrP0+5qJuNPHrLfgFQFYi6npDGA=="],
|
||||||
|
|
||||||
|
"@opentelemetry/exporter-metrics-otlp-http": ["@opentelemetry/exporter-metrics-otlp-http@0.207.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-exporter-base": "0.207.0", "@opentelemetry/otlp-transformer": "0.207.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-metrics": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-fG8FAJmvXOrKXGIRN8+y41U41IfVXxPRVwyB05LoMqYSjugx/FSBkMZUZXUT/wclTdmBKtS5MKoi0bEKkmRhSw=="],
|
||||||
|
|
||||||
|
"@opentelemetry/exporter-metrics-otlp-proto": ["@opentelemetry/exporter-metrics-otlp-proto@0.207.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/exporter-metrics-otlp-http": "0.207.0", "@opentelemetry/otlp-exporter-base": "0.207.0", "@opentelemetry/otlp-transformer": "0.207.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-metrics": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-kDBxiTeQjaRlUQzS1COT9ic+et174toZH6jxaVuVAvGqmxOkgjpLOjrI5ff8SMMQE69r03L3Ll3nPKekLopLwg=="],
|
||||||
|
|
||||||
|
"@opentelemetry/exporter-prometheus": ["@opentelemetry/exporter-prometheus@0.207.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-metrics": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Y5p1s39FvIRmU+F1++j7ly8/KSqhMmn6cMfpQqiDCqDjdDHwUtSq0XI0WwL3HYGnZeaR/VV4BNmsYQJ7GAPrhw=="],
|
||||||
|
|
||||||
|
"@opentelemetry/exporter-trace-otlp-grpc": ["@opentelemetry/exporter-trace-otlp-grpc@0.207.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-exporter-base": "0.207.0", "@opentelemetry/otlp-grpc-exporter-base": "0.207.0", "@opentelemetry/otlp-transformer": "0.207.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-7u2ZmcIx6D4KG/+5np4X2qA0o+O0K8cnUDhR4WI/vr5ZZ0la9J9RG+tkSjC7Yz+2XgL6760gSIM7/nyd3yaBLA=="],
|
||||||
|
|
||||||
|
"@opentelemetry/exporter-trace-otlp-http": ["@opentelemetry/exporter-trace-otlp-http@0.207.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-exporter-base": "0.207.0", "@opentelemetry/otlp-transformer": "0.207.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-HSRBzXHIC7C8UfPQdu15zEEoBGv0yWkhEwxqgPCHVUKUQ9NLHVGXkVrf65Uaj7UwmAkC1gQfkuVYvLlD//AnUQ=="],
|
||||||
|
|
||||||
|
"@opentelemetry/exporter-trace-otlp-proto": ["@opentelemetry/exporter-trace-otlp-proto@0.207.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-exporter-base": "0.207.0", "@opentelemetry/otlp-transformer": "0.207.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-ruUQB4FkWtxHjNmSXjrhmJZFvyMm+tBzHyMm7YPQshApy4wvZUTcrpPyP/A/rCl/8M4BwoVIZdiwijMdbZaq4w=="],
|
||||||
|
|
||||||
|
"@opentelemetry/exporter-zipkin": ["@opentelemetry/exporter-zipkin@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-VV4QzhGCT7cWrGasBWxelBjqbNBbyHicWWS/66KoZoe9BzYwFB72SH2/kkc4uAviQlO8iwv2okIJy+/jqqEHTg=="],
|
||||||
|
|
||||||
|
"@opentelemetry/instrumentation": ["@opentelemetry/instrumentation@0.207.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.207.0", "import-in-the-middle": "^2.0.0", "require-in-the-middle": "^8.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-y6eeli9+TLKnznrR8AZlQMSJT7wILpXH+6EYq5Vf/4Ao+huI7EedxQHwRgVUOMLFbe7VFDvHJrX9/f4lcwnJsA=="],
|
||||||
|
|
||||||
|
"@opentelemetry/instrumentation-http": ["@opentelemetry/instrumentation-http@0.207.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/instrumentation": "0.207.0", "@opentelemetry/semantic-conventions": "^1.29.0", "forwarded-parse": "2.1.2" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-FC4i5hVixTzuhg4SV2ycTEAYx+0E2hm+GwbdoVPSA6kna0pPVI4etzaA9UkpJ9ussumQheFXP6rkGIaFJjMxsw=="],
|
||||||
|
|
||||||
|
"@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.207.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-transformer": "0.207.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-4RQluMVVGMrHok/3SVeSJ6EnRNkA2MINcX88sh+d/7DjGUrewW/WT88IsMEci0wUM+5ykTpPPNbEOoW+jwHnbw=="],
|
||||||
|
|
||||||
|
"@opentelemetry/otlp-grpc-exporter-base": ["@opentelemetry/otlp-grpc-exporter-base@0.207.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-exporter-base": "0.207.0", "@opentelemetry/otlp-transformer": "0.207.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-eKFjKNdsPed4q9yYqeI5gBTLjXxDM/8jwhiC0icw3zKxHVGBySoDsed5J5q/PGY/3quzenTr3FiTxA3NiNT+nw=="],
|
||||||
|
|
||||||
|
"@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.207.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.207.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.207.0", "@opentelemetry/sdk-metrics": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+6DRZLqM02uTIY5GASMZWUwr52sLfNiEe20+OEaZKhztCs3+2LxoTjb6JxFRd9q1qNqckXKYlUKjbH/AhG8/ZA=="],
|
||||||
|
|
||||||
|
"@opentelemetry/propagator-b3": ["@opentelemetry/propagator-b3@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-9CrbTLFi5Ee4uepxg2qlpQIozoJuoAZU5sKMx0Mn7Oh+p7UrgCiEV6C02FOxxdYVRRFQVCinYR8Kf6eMSQsIsw=="],
|
||||||
|
|
||||||
|
"@opentelemetry/propagator-jaeger": ["@opentelemetry/propagator-jaeger@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-FfeOHOrdhiNzecoB1jZKp2fybqmqMPJUXe2ZOydP7QzmTPYcfPeuaclTLYVhK3HyJf71kt8sTl92nV4YIaLaKA=="],
|
||||||
|
|
||||||
|
"@opentelemetry/resources": ["@opentelemetry/resources@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A=="],
|
||||||
|
|
||||||
|
"@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.207.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.207.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-4MEQmn04y+WFe6cyzdrXf58hZxilvY59lzZj2AccuHW/+BxLn/rGVN/Irsi/F0qfBOpMOrrCLKTExoSL2zoQmg=="],
|
||||||
|
|
||||||
|
"@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-G5KYP6+VJMZzpGipQw7Giif48h6SGQ2PFKEYCybeXJsOCB4fp8azqMAAzE5lnnHK3ZVwYQrgmFbsUJO/zOnwGw=="],
|
||||||
|
|
||||||
|
"@opentelemetry/sdk-node": ["@opentelemetry/sdk-node@0.207.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.207.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/exporter-logs-otlp-grpc": "0.207.0", "@opentelemetry/exporter-logs-otlp-http": "0.207.0", "@opentelemetry/exporter-logs-otlp-proto": "0.207.0", "@opentelemetry/exporter-metrics-otlp-grpc": "0.207.0", "@opentelemetry/exporter-metrics-otlp-http": "0.207.0", "@opentelemetry/exporter-metrics-otlp-proto": "0.207.0", "@opentelemetry/exporter-prometheus": "0.207.0", "@opentelemetry/exporter-trace-otlp-grpc": "0.207.0", "@opentelemetry/exporter-trace-otlp-http": "0.207.0", "@opentelemetry/exporter-trace-otlp-proto": "0.207.0", "@opentelemetry/exporter-zipkin": "2.2.0", "@opentelemetry/instrumentation": "0.207.0", "@opentelemetry/propagator-b3": "2.2.0", "@opentelemetry/propagator-jaeger": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.207.0", "@opentelemetry/sdk-metrics": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0", "@opentelemetry/sdk-trace-node": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-hnRsX/M8uj0WaXOBvFenQ8XsE8FLVh2uSnn1rkWu4mx+qu7EKGUZvZng6y/95cyzsqOfiaDDr08Ek4jppkIDNg=="],
|
||||||
|
|
||||||
|
"@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.6.1", "", { "dependencies": { "@opentelemetry/core": "2.6.1", "@opentelemetry/resources": "2.6.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-r86ut4T1e8vNwB35CqCcKd45yzqH6/6Wzvpk2/cZB8PsPLlZFTvrh8yfOS3CYZYcUmAx4hHTZJ8AO8Dj8nrdhw=="],
|
||||||
|
|
||||||
|
"@opentelemetry/sdk-trace-node": ["@opentelemetry/sdk-trace-node@2.2.0", "", { "dependencies": { "@opentelemetry/context-async-hooks": "2.2.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-+OaRja3f0IqGG2kptVeYsrZQK9nKRSpfFrKtRBq4uh6nIB8bTBgaGvYQrQoRrQWQMA5dK5yLhDMDc0dvYvCOIQ=="],
|
||||||
|
|
||||||
|
"@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.40.0", "", {}, "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw=="],
|
||||||
|
|
||||||
"@opentui/core": ["@opentui/core@0.1.97", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "marked": "17.0.1", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.97", "@opentui/core-darwin-x64": "0.1.97", "@opentui/core-linux-arm64": "0.1.97", "@opentui/core-linux-x64": "0.1.97", "@opentui/core-win32-arm64": "0.1.97", "@opentui/core-win32-x64": "0.1.97", "bun-webgpu": "0.1.5", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-2ENH0Dc4NUAeHeeQCQhF1lg68RuyntOUP68UvortvDqTz/hqLG0tIwF+DboCKtWi8Nmao4SAQEJ7lfmyQNEDOQ=="],
|
"@opentui/core": ["@opentui/core@0.1.97", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "marked": "17.0.1", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.97", "@opentui/core-darwin-x64": "0.1.97", "@opentui/core-linux-arm64": "0.1.97", "@opentui/core-linux-x64": "0.1.97", "@opentui/core-win32-arm64": "0.1.97", "@opentui/core-win32-x64": "0.1.97", "bun-webgpu": "0.1.5", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-2ENH0Dc4NUAeHeeQCQhF1lg68RuyntOUP68UvortvDqTz/hqLG0tIwF+DboCKtWi8Nmao4SAQEJ7lfmyQNEDOQ=="],
|
||||||
|
|
||||||
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.97", "", { "os": "darwin", "cpu": "arm64" }, "sha512-t7oMGEfMPQsqLEx7/rPqv/UGJ+vqhe4RWHRRQRYcuHuLKssZ2S8P9mSS7MBPtDqGcxg4PosCrh5nHYeZ94EXUw=="],
|
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.97", "", { "os": "darwin", "cpu": "arm64" }, "sha512-t7oMGEfMPQsqLEx7/rPqv/UGJ+vqhe4RWHRRQRYcuHuLKssZ2S8P9mSS7MBPtDqGcxg4PosCrh5nHYeZ94EXUw=="],
|
||||||
|
|
@ -1698,6 +1772,26 @@
|
||||||
|
|
||||||
"@protobuf-ts/runtime-rpc": ["@protobuf-ts/runtime-rpc@2.11.1", "", { "dependencies": { "@protobuf-ts/runtime": "^2.11.1" } }, "sha512-4CqqUmNA+/uMz00+d3CYKgElXO9VrEbucjnBFEjqI4GuDrEQ32MaI3q+9qPBvIGOlL4PmHXrzM32vBPWRhQKWQ=="],
|
"@protobuf-ts/runtime-rpc": ["@protobuf-ts/runtime-rpc@2.11.1", "", { "dependencies": { "@protobuf-ts/runtime": "^2.11.1" } }, "sha512-4CqqUmNA+/uMz00+d3CYKgElXO9VrEbucjnBFEjqI4GuDrEQ32MaI3q+9qPBvIGOlL4PmHXrzM32vBPWRhQKWQ=="],
|
||||||
|
|
||||||
|
"@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="],
|
||||||
|
|
||||||
|
"@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="],
|
||||||
|
|
||||||
|
"@protobufjs/codegen": ["@protobufjs/codegen@2.0.4", "", {}, "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="],
|
||||||
|
|
||||||
|
"@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="],
|
||||||
|
|
||||||
|
"@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ=="],
|
||||||
|
|
||||||
|
"@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="],
|
||||||
|
|
||||||
|
"@protobufjs/inquire": ["@protobufjs/inquire@1.1.0", "", {}, "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="],
|
||||||
|
|
||||||
|
"@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="],
|
||||||
|
|
||||||
|
"@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="],
|
||||||
|
|
||||||
|
"@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="],
|
||||||
|
|
||||||
"@radix-ui/colors": ["@radix-ui/colors@1.0.1", "", {}, "sha512-xySw8f0ZVsAEP+e7iLl3EvcBXX7gsIlC1Zso/sPBW9gIWerBTgz6axrjU+MZ39wD+WFi5h5zdWpsg3+hwt2Qsg=="],
|
"@radix-ui/colors": ["@radix-ui/colors@1.0.1", "", {}, "sha512-xySw8f0ZVsAEP+e7iLl3EvcBXX7gsIlC1Zso/sPBW9gIWerBTgz6axrjU+MZ39wD+WFi5h5zdWpsg3+hwt2Qsg=="],
|
||||||
|
|
||||||
"@radix-ui/primitive": ["@radix-ui/primitive@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" } }, "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw=="],
|
"@radix-ui/primitive": ["@radix-ui/primitive@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" } }, "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw=="],
|
||||||
|
|
@ -2378,6 +2472,8 @@
|
||||||
|
|
||||||
"acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="],
|
"acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="],
|
||||||
|
|
||||||
|
"acorn-import-attributes": ["acorn-import-attributes@1.9.5", "", { "peerDependencies": { "acorn": "^8" } }, "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ=="],
|
||||||
|
|
||||||
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
||||||
|
|
||||||
"acorn-walk": ["acorn-walk@8.3.2", "", {}, "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A=="],
|
"acorn-walk": ["acorn-walk@8.3.2", "", {}, "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A=="],
|
||||||
|
|
@ -2600,6 +2696,8 @@
|
||||||
|
|
||||||
"bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="],
|
"bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="],
|
||||||
|
|
||||||
|
"busboy": ["busboy@1.6.0", "", { "dependencies": { "streamsearch": "^1.1.0" } }, "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA=="],
|
||||||
|
|
||||||
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
||||||
|
|
||||||
"c12": ["c12@3.3.3", "", { "dependencies": { "chokidar": "^5.0.0", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^17.2.3", "exsolve": "^1.0.8", "giget": "^2.0.0", "jiti": "^2.6.1", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^2.0.0", "pkg-types": "^2.3.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "*" }, "optionalPeers": ["magicast"] }, "sha512-750hTRvgBy5kcMNPdh95Qo+XUBeGo8C7nsKSmedDmaQI+E0r82DwHeM6vBewDe4rGFbnxoa4V9pw+sPh5+Iz8Q=="],
|
"c12": ["c12@3.3.3", "", { "dependencies": { "chokidar": "^5.0.0", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^17.2.3", "exsolve": "^1.0.8", "giget": "^2.0.0", "jiti": "^2.6.1", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^2.0.0", "pkg-types": "^2.3.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "*" }, "optionalPeers": ["magicast"] }, "sha512-750hTRvgBy5kcMNPdh95Qo+XUBeGo8C7nsKSmedDmaQI+E0r82DwHeM6vBewDe4rGFbnxoa4V9pw+sPh5+Iz8Q=="],
|
||||||
|
|
@ -2660,6 +2758,8 @@
|
||||||
|
|
||||||
"citty": ["citty@0.1.6", "", { "dependencies": { "consola": "^3.2.3" } }, "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ=="],
|
"citty": ["citty@0.1.6", "", { "dependencies": { "consola": "^3.2.3" } }, "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ=="],
|
||||||
|
|
||||||
|
"cjs-module-lexer": ["cjs-module-lexer@2.2.0", "", {}, "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ=="],
|
||||||
|
|
||||||
"classnames": ["classnames@2.3.2", "", {}, "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="],
|
"classnames": ["classnames@2.3.2", "", {}, "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="],
|
||||||
|
|
||||||
"clean-css": ["clean-css@5.3.3", "", { "dependencies": { "source-map": "~0.6.0" } }, "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg=="],
|
"clean-css": ["clean-css@5.3.3", "", { "dependencies": { "source-map": "~0.6.0" } }, "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg=="],
|
||||||
|
|
@ -2870,7 +2970,7 @@
|
||||||
|
|
||||||
"dot-prop": ["dot-prop@8.0.2", "", { "dependencies": { "type-fest": "^3.8.0" } }, "sha512-xaBe6ZT4DHPkg0k4Ytbvn5xoxgpG0jOS1dYxSOwAHPuNLjP3/OzN0gH55SrLqpx8cBfSaVt91lXYkApjb+nYdQ=="],
|
"dot-prop": ["dot-prop@8.0.2", "", { "dependencies": { "type-fest": "^3.8.0" } }, "sha512-xaBe6ZT4DHPkg0k4Ytbvn5xoxgpG0jOS1dYxSOwAHPuNLjP3/OzN0gH55SrLqpx8cBfSaVt91lXYkApjb+nYdQ=="],
|
||||||
|
|
||||||
"dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
|
"dotenv": ["dotenv@17.3.1", "", {}, "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA=="],
|
||||||
|
|
||||||
"dotenv-expand": ["dotenv-expand@11.0.7", "", { "dependencies": { "dotenv": "^16.4.5" } }, "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA=="],
|
"dotenv-expand": ["dotenv-expand@11.0.7", "", { "dependencies": { "dotenv": "^16.4.5" } }, "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA=="],
|
||||||
|
|
||||||
|
|
@ -3022,6 +3122,8 @@
|
||||||
|
|
||||||
"exit-hook": ["exit-hook@2.2.1", "", {}, "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw=="],
|
"exit-hook": ["exit-hook@2.2.1", "", {}, "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw=="],
|
||||||
|
|
||||||
|
"expand-tilde": ["expand-tilde@2.0.2", "", { "dependencies": { "homedir-polyfill": "^1.0.1" } }, "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw=="],
|
||||||
|
|
||||||
"expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="],
|
"expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="],
|
||||||
|
|
||||||
"exponential-backoff": ["exponential-backoff@3.1.3", "", {}, "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA=="],
|
"exponential-backoff": ["exponential-backoff@3.1.3", "", {}, "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA=="],
|
||||||
|
|
@ -3122,6 +3224,8 @@
|
||||||
|
|
||||||
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
|
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
|
||||||
|
|
||||||
|
"forwarded-parse": ["forwarded-parse@2.1.2", "", {}, "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw=="],
|
||||||
|
|
||||||
"fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="],
|
"fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="],
|
||||||
|
|
||||||
"framer-motion": ["framer-motion@8.5.5", "", { "dependencies": { "@motionone/dom": "^10.15.3", "hey-listen": "^1.0.8", "tslib": "^2.4.0" }, "optionalDependencies": { "@emotion/is-prop-valid": "^0.8.2" }, "peerDependencies": { "react": "^18.0.0", "react-dom": "^18.0.0" } }, "sha512-5IDx5bxkjWHWUF3CVJoSyUVOtrbAxtzYBBowRE2uYI/6VYhkEBD+rbTHEGuUmbGHRj6YqqSfoG7Aa1cLyWCrBA=="],
|
"framer-motion": ["framer-motion@8.5.5", "", { "dependencies": { "@motionone/dom": "^10.15.3", "hey-listen": "^1.0.8", "tslib": "^2.4.0" }, "optionalDependencies": { "@emotion/is-prop-valid": "^0.8.2" }, "peerDependencies": { "react": "^18.0.0", "react-dom": "^18.0.0" } }, "sha512-5IDx5bxkjWHWUF3CVJoSyUVOtrbAxtzYBBowRE2uYI/6VYhkEBD+rbTHEGuUmbGHRj6YqqSfoG7Aa1cLyWCrBA=="],
|
||||||
|
|
@ -3278,6 +3382,8 @@
|
||||||
|
|
||||||
"hey-listen": ["hey-listen@1.0.8", "", {}, "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q=="],
|
"hey-listen": ["hey-listen@1.0.8", "", {}, "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q=="],
|
||||||
|
|
||||||
|
"homedir-polyfill": ["homedir-polyfill@1.0.3", "", { "dependencies": { "parse-passwd": "^1.0.0" } }, "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA=="],
|
||||||
|
|
||||||
"hono": ["hono@4.10.7", "", {}, "sha512-icXIITfw/07Q88nLSkB9aiUrd8rYzSweK681Kjo/TSggaGbOX4RRyxxm71v+3PC8C/j+4rlxGeoTRxQDkaJkUw=="],
|
"hono": ["hono@4.10.7", "", {}, "sha512-icXIITfw/07Q88nLSkB9aiUrd8rYzSweK681Kjo/TSggaGbOX4RRyxxm71v+3PC8C/j+4rlxGeoTRxQDkaJkUw=="],
|
||||||
|
|
||||||
"hono-openapi": ["hono-openapi@1.1.2", "", { "peerDependencies": { "@hono/standard-validator": "^0.2.0", "@standard-community/standard-json": "^0.3.5", "@standard-community/standard-openapi": "^0.2.9", "@types/json-schema": "^7.0.15", "hono": "^4.8.3", "openapi-types": "^12.1.3" }, "optionalPeers": ["@hono/standard-validator", "hono"] }, "sha512-toUcO60MftRBxqcVyxsHNYs2m4vf4xkQaiARAucQx3TiBPDtMNNkoh+C4I1vAretQZiGyaLOZNWn1YxfSyUA5g=="],
|
"hono-openapi": ["hono-openapi@1.1.2", "", { "peerDependencies": { "@hono/standard-validator": "^0.2.0", "@standard-community/standard-json": "^0.3.5", "@standard-community/standard-openapi": "^0.2.9", "@types/json-schema": "^7.0.15", "hono": "^4.8.3", "openapi-types": "^12.1.3" }, "optionalPeers": ["@hono/standard-validator", "hono"] }, "sha512-toUcO60MftRBxqcVyxsHNYs2m4vf4xkQaiARAucQx3TiBPDtMNNkoh+C4I1vAretQZiGyaLOZNWn1YxfSyUA5g=="],
|
||||||
|
|
@ -3330,6 +3436,8 @@
|
||||||
|
|
||||||
"image-q": ["image-q@4.0.0", "", { "dependencies": { "@types/node": "16.9.1" } }, "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw=="],
|
"image-q": ["image-q@4.0.0", "", { "dependencies": { "@types/node": "16.9.1" } }, "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw=="],
|
||||||
|
|
||||||
|
"import-in-the-middle": ["import-in-the-middle@2.0.6", "", { "dependencies": { "acorn": "^8.15.0", "acorn-import-attributes": "^1.9.5", "cjs-module-lexer": "^2.2.0", "module-details-from-path": "^1.0.4" } }, "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw=="],
|
||||||
|
|
||||||
"import-local": ["import-local@3.2.0", "", { "dependencies": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" }, "bin": { "import-local-fixture": "fixtures/cli.js" } }, "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA=="],
|
"import-local": ["import-local@3.2.0", "", { "dependencies": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" }, "bin": { "import-local-fixture": "fixtures/cli.js" } }, "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA=="],
|
||||||
|
|
||||||
"import-meta-resolve": ["import-meta-resolve@4.2.0", "", {}, "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg=="],
|
"import-meta-resolve": ["import-meta-resolve@4.2.0", "", {}, "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg=="],
|
||||||
|
|
@ -3592,6 +3700,8 @@
|
||||||
|
|
||||||
"lodash": ["lodash@4.17.23", "", {}, "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="],
|
"lodash": ["lodash@4.17.23", "", {}, "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="],
|
||||||
|
|
||||||
|
"lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="],
|
||||||
|
|
||||||
"lodash.defaults": ["lodash.defaults@4.2.0", "", {}, "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="],
|
"lodash.defaults": ["lodash.defaults@4.2.0", "", {}, "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="],
|
||||||
|
|
||||||
"lodash.escaperegexp": ["lodash.escaperegexp@4.1.2", "", {}, "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw=="],
|
"lodash.escaperegexp": ["lodash.escaperegexp@4.1.2", "", {}, "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw=="],
|
||||||
|
|
@ -3822,6 +3932,8 @@
|
||||||
|
|
||||||
"mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="],
|
"mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="],
|
||||||
|
|
||||||
|
"module-details-from-path": ["module-details-from-path@1.0.4", "", {}, "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w=="],
|
||||||
|
|
||||||
"morphdom": ["morphdom@2.7.8", "", {}, "sha512-D/fR4xgGUyVRbdMGU6Nejea1RFzYxYtyurG4Fbv2Fi/daKlWKuXGLOdXtl+3eIwL110cI2hz1ZojGICjjFLgTg=="],
|
"morphdom": ["morphdom@2.7.8", "", {}, "sha512-D/fR4xgGUyVRbdMGU6Nejea1RFzYxYtyurG4Fbv2Fi/daKlWKuXGLOdXtl+3eIwL110cI2hz1ZojGICjjFLgTg=="],
|
||||||
|
|
||||||
"motion": ["motion@12.34.5", "", { "dependencies": { "framer-motion": "^12.34.5", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-N06NLJ9IeBHeielRqIvYvjPfXuRdyTxa+9++BgpGa+hY2D7TcMkI6QzV3jaRuv0aZRXgMa7cPy9YcBUBisPzAQ=="],
|
"motion": ["motion@12.34.5", "", { "dependencies": { "framer-motion": "^12.34.5", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-N06NLJ9IeBHeielRqIvYvjPfXuRdyTxa+9++BgpGa+hY2D7TcMkI6QzV3jaRuv0aZRXgMa7cPy9YcBUBisPzAQ=="],
|
||||||
|
|
@ -4024,6 +4136,8 @@
|
||||||
|
|
||||||
"parse-latin": ["parse-latin@7.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "@types/unist": "^3.0.0", "nlcst-to-string": "^4.0.0", "unist-util-modify-children": "^4.0.0", "unist-util-visit-children": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ=="],
|
"parse-latin": ["parse-latin@7.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "@types/unist": "^3.0.0", "nlcst-to-string": "^4.0.0", "unist-util-modify-children": "^4.0.0", "unist-util-visit-children": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ=="],
|
||||||
|
|
||||||
|
"parse-passwd": ["parse-passwd@1.0.0", "", {}, "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q=="],
|
||||||
|
|
||||||
"parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
|
"parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
|
||||||
|
|
||||||
"parse5-htmlparser2-tree-adapter": ["parse5-htmlparser2-tree-adapter@7.1.0", "", { "dependencies": { "domhandler": "^5.0.3", "parse5": "^7.0.0" } }, "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g=="],
|
"parse5-htmlparser2-tree-adapter": ["parse5-htmlparser2-tree-adapter@7.1.0", "", { "dependencies": { "domhandler": "^5.0.3", "parse5": "^7.0.0" } }, "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g=="],
|
||||||
|
|
@ -4166,6 +4280,8 @@
|
||||||
|
|
||||||
"proto-list": ["proto-list@1.2.4", "", {}, "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA=="],
|
"proto-list": ["proto-list@1.2.4", "", {}, "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA=="],
|
||||||
|
|
||||||
|
"protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="],
|
||||||
|
|
||||||
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
|
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
|
||||||
|
|
||||||
"proxy-from-env": ["proxy-from-env@2.1.0", "", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="],
|
"proxy-from-env": ["proxy-from-env@2.1.0", "", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="],
|
||||||
|
|
@ -4302,6 +4418,8 @@
|
||||||
|
|
||||||
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
|
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
|
||||||
|
|
||||||
|
"require-in-the-middle": ["require-in-the-middle@8.0.1", "", { "dependencies": { "debug": "^4.3.5", "module-details-from-path": "^1.0.3" } }, "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ=="],
|
||||||
|
|
||||||
"resedit": ["resedit@1.7.2", "", { "dependencies": { "pe-library": "^0.4.1" } }, "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA=="],
|
"resedit": ["resedit@1.7.2", "", { "dependencies": { "pe-library": "^0.4.1" } }, "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA=="],
|
||||||
|
|
||||||
"reselect": ["reselect@4.1.8", "", {}, "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ=="],
|
"reselect": ["reselect@4.1.8", "", {}, "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ=="],
|
||||||
|
|
@ -4412,6 +4530,8 @@
|
||||||
|
|
||||||
"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=="],
|
||||||
|
|
@ -4540,8 +4660,12 @@
|
||||||
|
|
||||||
"storybook-solidjs-vite": ["storybook-solidjs-vite@10.0.11", "", { "dependencies": { "@joshwooding/vite-plugin-react-docgen-typescript": "^0.6.4", "@storybook/builder-vite": "^10.3.1", "@storybook/global": "^5.0.0", "vite-plugin-solid": "^2.11.11" }, "peerDependencies": { "solid-js": "^1.9.0", "storybook": "^0.0.0-0 || ^10.0.0", "typescript": "^4.0.0 || ^5.0.0 || ^6.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["typescript"] }, "sha512-aDe2ipD0HAH54zsQbbCUI9AcBPiLwljtdx+GRmyHsHaPyij1KmGVKo5N004qXZfQaoK0uru9DsdnWon5rRkjfg=="],
|
"storybook-solidjs-vite": ["storybook-solidjs-vite@10.0.11", "", { "dependencies": { "@joshwooding/vite-plugin-react-docgen-typescript": "^0.6.4", "@storybook/builder-vite": "^10.3.1", "@storybook/global": "^5.0.0", "vite-plugin-solid": "^2.11.11" }, "peerDependencies": { "solid-js": "^1.9.0", "storybook": "^0.0.0-0 || ^10.0.0", "typescript": "^4.0.0 || ^5.0.0 || ^6.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["typescript"] }, "sha512-aDe2ipD0HAH54zsQbbCUI9AcBPiLwljtdx+GRmyHsHaPyij1KmGVKo5N004qXZfQaoK0uru9DsdnWon5rRkjfg=="],
|
||||||
|
|
||||||
|
"stream-browserify": ["stream-browserify@3.0.0", "", { "dependencies": { "inherits": "~2.0.4", "readable-stream": "^3.5.0" } }, "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA=="],
|
||||||
|
|
||||||
"stream-replace-string": ["stream-replace-string@2.0.0", "", {}, "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w=="],
|
"stream-replace-string": ["stream-replace-string@2.0.0", "", {}, "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w=="],
|
||||||
|
|
||||||
|
"streamsearch": ["streamsearch@1.1.0", "", {}, "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="],
|
||||||
|
|
||||||
"streamx": ["streamx@2.25.0", "", { "dependencies": { "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" } }, "sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg=="],
|
"streamx": ["streamx@2.25.0", "", { "dependencies": { "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" } }, "sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg=="],
|
||||||
|
|
||||||
"string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
"string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
||||||
|
|
@ -4956,7 +5080,7 @@
|
||||||
|
|
||||||
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
|
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
|
||||||
|
|
||||||
"yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
|
"yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
|
||||||
|
|
||||||
"yaml": ["yaml@2.8.3", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg=="],
|
"yaml": ["yaml@2.8.3", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg=="],
|
||||||
|
|
||||||
|
|
@ -5192,6 +5316,16 @@
|
||||||
|
|
||||||
"@aws-sdk/credential-providers/@aws-sdk/types": ["@aws-sdk/types@3.973.6", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw=="],
|
"@aws-sdk/credential-providers/@aws-sdk/types": ["@aws-sdk/types@3.973.6", "", { "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.29", "", { "dependencies": { "@smithy/core": "^3.23.14", "@smithy/middleware-serde": "^4.2.17", "@smithy/node-config-provider": "^4.3.13", "@smithy/shared-ini-file-loader": "^4.4.8", "@smithy/types": "^4.14.0", "@smithy/url-parser": "^4.2.13", "@smithy/util-middleware": "^4.2.13", "tslib": "^2.6.2" } }, "sha512-R9Q/58U+qBiSARGWbAbFLczECg/RmysRksX6Q8BaQEpt75I7LI6WGDZnjuC9GXSGKljEbA7N118LhGaMbfrTXw=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/protocol-http": ["@smithy/protocol-http@5.3.13", "", { "dependencies": { "@smithy/types": "^4.14.0", "tslib": "^2.6.2" } }, "sha512-+HsmuJUF4u8POo6s8/a2Yb/AQ5t/YgLovCuHF9oxbocqv+SZ6gd8lC2duBFiCA/vFHoHQhoq7QjqJqZC6xOxxg=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/smithy-client": ["@smithy/smithy-client@4.12.9", "", { "dependencies": { "@smithy/core": "^3.23.14", "@smithy/middleware-endpoint": "^4.4.29", "@smithy/middleware-stack": "^4.2.13", "@smithy/protocol-http": "^5.3.13", "@smithy/types": "^4.14.0", "@smithy/util-stream": "^4.5.22", "tslib": "^2.6.2" } }, "sha512-ovaLEcTU5olSeHcRXcxV6viaKtpkHZumn6Ps0yn7dRf2rRSfy794vpjOtrWDO0d1auDSvAqxO+lyhERSXQ03EQ=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/types": ["@smithy/types@4.14.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-OWgntFLW88kx2qvf/c/67Vno1yuXm/f9M7QFAtVkkO29IJXGBIg0ycEaBTH0kvCtwmvZxRujrgP5a86RvsXJAQ=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/buffer": ["buffer@5.6.0", "", { "dependencies": { "base64-js": "^1.0.2", "ieee754": "^1.1.4" } }, "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw=="],
|
||||||
|
|
||||||
"@aws-sdk/nested-clients/@aws-sdk/core": ["@aws-sdk/core@3.973.26", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@aws-sdk/xml-builder": "^3.972.16", "@smithy/core": "^3.23.13", "@smithy/node-config-provider": "^4.3.12", "@smithy/property-provider": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/signature-v4": "^5.3.12", "@smithy/smithy-client": "^4.12.8", "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-middleware": "^4.2.12", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-A/E6n2W42ruU+sfWk+mMUOyVXbsSgGrY3MJ9/0Az5qUdG67y8I6HYzzoAa+e/lzxxl1uCYmEL6BTMi9ZiZnplQ=="],
|
"@aws-sdk/nested-clients/@aws-sdk/core": ["@aws-sdk/core@3.973.26", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@aws-sdk/xml-builder": "^3.972.16", "@smithy/core": "^3.23.13", "@smithy/node-config-provider": "^4.3.12", "@smithy/property-provider": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/signature-v4": "^5.3.12", "@smithy/smithy-client": "^4.12.8", "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-middleware": "^4.2.12", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-A/E6n2W42ruU+sfWk+mMUOyVXbsSgGrY3MJ9/0Az5qUdG67y8I6HYzzoAa+e/lzxxl1uCYmEL6BTMi9ZiZnplQ=="],
|
||||||
|
|
||||||
"@aws-sdk/nested-clients/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.972.8", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ=="],
|
"@aws-sdk/nested-clients/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.972.8", "", { "dependencies": { "@aws-sdk/types": "^3.973.6", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" } }, "sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ=="],
|
||||||
|
|
@ -5290,6 +5424,8 @@
|
||||||
|
|
||||||
"@gitlab/opencode-gitlab-auth/open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="],
|
"@gitlab/opencode-gitlab-auth/open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="],
|
||||||
|
|
||||||
|
"@grpc/proto-loader/yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
|
||||||
|
|
||||||
"@hey-api/openapi-ts/open": ["open@11.0.0", "", { "dependencies": { "default-browser": "^5.4.0", "define-lazy-prop": "^3.0.0", "is-in-ssh": "^1.0.0", "is-inside-container": "^1.0.0", "powershell-utils": "^0.1.0", "wsl-utils": "^0.3.0" } }, "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw=="],
|
"@hey-api/openapi-ts/open": ["open@11.0.0", "", { "dependencies": { "default-browser": "^5.4.0", "define-lazy-prop": "^3.0.0", "is-in-ssh": "^1.0.0", "is-inside-container": "^1.0.0", "powershell-utils": "^0.1.0", "wsl-utils": "^0.3.0" } }, "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw=="],
|
||||||
|
|
||||||
"@hey-api/openapi-ts/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
"@hey-api/openapi-ts/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
||||||
|
|
@ -5442,6 +5578,26 @@
|
||||||
|
|
||||||
"@opencode-ai/web/@shikijs/transformers": ["@shikijs/transformers@3.20.0", "", { "dependencies": { "@shikijs/core": "3.20.0", "@shikijs/types": "3.20.0" } }, "sha512-PrHHMRr3Q5W1qB/42kJW6laqFyWdhrPF2hNR9qjOm1xcSiAO3hAHo7HaVyHE6pMyevmy3i51O8kuGGXC78uK3g=="],
|
"@opencode-ai/web/@shikijs/transformers": ["@shikijs/transformers@3.20.0", "", { "dependencies": { "@shikijs/core": "3.20.0", "@shikijs/types": "3.20.0" } }, "sha512-PrHHMRr3Q5W1qB/42kJW6laqFyWdhrPF2hNR9qjOm1xcSiAO3hAHo7HaVyHE6pMyevmy3i51O8kuGGXC78uK3g=="],
|
||||||
|
|
||||||
|
"@opentelemetry/exporter-logs-otlp-proto/@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw=="],
|
||||||
|
|
||||||
|
"@opentelemetry/exporter-trace-otlp-grpc/@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw=="],
|
||||||
|
|
||||||
|
"@opentelemetry/exporter-trace-otlp-http/@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw=="],
|
||||||
|
|
||||||
|
"@opentelemetry/exporter-trace-otlp-proto/@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw=="],
|
||||||
|
|
||||||
|
"@opentelemetry/exporter-zipkin/@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw=="],
|
||||||
|
|
||||||
|
"@opentelemetry/otlp-transformer/@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw=="],
|
||||||
|
|
||||||
|
"@opentelemetry/sdk-node/@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw=="],
|
||||||
|
|
||||||
|
"@opentelemetry/sdk-trace-base/@opentelemetry/core": ["@opentelemetry/core@2.6.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-8xHSGWpJP9wBxgBpnqGL0R3PbdWQndL1Qp50qrg71+B28zK5OQmUgcDKLJgzyAAV38t4tOyLMGDD60LneR5W8g=="],
|
||||||
|
|
||||||
|
"@opentelemetry/sdk-trace-base/@opentelemetry/resources": ["@opentelemetry/resources@2.6.1", "", { "dependencies": { "@opentelemetry/core": "2.6.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA=="],
|
||||||
|
|
||||||
|
"@opentelemetry/sdk-trace-node/@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw=="],
|
||||||
|
|
||||||
"@opentui/solid/@babel/core": ["@babel/core@7.28.0", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.6", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ=="],
|
"@opentui/solid/@babel/core": ["@babel/core@7.28.0", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.6", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ=="],
|
||||||
|
|
||||||
"@opentui/solid/babel-preset-solid": ["babel-preset-solid@1.9.10", "", { "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.3" }, "peerDependencies": { "@babel/core": "^7.0.0", "solid-js": "^1.9.10" }, "optionalPeers": ["solid-js"] }, "sha512-HCelrgua/Y+kqO8RyL04JBWS/cVdrtUv/h45GntgQY+cJl4eBcKkCDV3TdMjtKx1nXwRaR9QXslM/Npm1dxdZQ=="],
|
"@opentui/solid/babel-preset-solid": ["babel-preset-solid@1.9.10", "", { "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.3" }, "peerDependencies": { "@babel/core": "^7.0.0", "solid-js": "^1.9.10" }, "optionalPeers": ["solid-js"] }, "sha512-HCelrgua/Y+kqO8RyL04JBWS/cVdrtUv/h45GntgQY+cJl4eBcKkCDV3TdMjtKx1nXwRaR9QXslM/Npm1dxdZQ=="],
|
||||||
|
|
@ -5572,6 +5728,8 @@
|
||||||
|
|
||||||
"app-builder-lib/ci-info": ["ci-info@4.3.1", "", {}, "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA=="],
|
"app-builder-lib/ci-info": ["ci-info@4.3.1", "", {}, "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA=="],
|
||||||
|
|
||||||
|
"app-builder-lib/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
|
||||||
|
|
||||||
"app-builder-lib/hosted-git-info": ["hosted-git-info@4.1.0", "", { "dependencies": { "lru-cache": "^6.0.0" } }, "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA=="],
|
"app-builder-lib/hosted-git-info": ["hosted-git-info@4.1.0", "", { "dependencies": { "lru-cache": "^6.0.0" } }, "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA=="],
|
||||||
|
|
||||||
"app-builder-lib/minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
|
"app-builder-lib/minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
|
||||||
|
|
@ -5616,8 +5774,6 @@
|
||||||
|
|
||||||
"c12/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
|
"c12/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
|
||||||
|
|
||||||
"c12/dotenv": ["dotenv@17.3.1", "", {}, "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA=="],
|
|
||||||
|
|
||||||
"clone-response/mimic-response": ["mimic-response@1.0.1", "", {}, "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="],
|
"clone-response/mimic-response": ["mimic-response@1.0.1", "", {}, "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="],
|
||||||
|
|
||||||
"compress-commons/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
|
"compress-commons/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
|
||||||
|
|
@ -5648,6 +5804,8 @@
|
||||||
|
|
||||||
"dot-prop/type-fest": ["type-fest@3.13.1", "", {}, "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g=="],
|
"dot-prop/type-fest": ["type-fest@3.13.1", "", {}, "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g=="],
|
||||||
|
|
||||||
|
"dotenv-expand/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
|
||||||
|
|
||||||
"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.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="],
|
"editorconfig/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="],
|
||||||
|
|
@ -5878,6 +6036,8 @@
|
||||||
|
|
||||||
"storybook-solidjs-vite/vite-plugin-solid": ["vite-plugin-solid@2.11.11", "", { "dependencies": { "@babel/core": "^7.23.3", "@types/babel__core": "^7.20.4", "babel-preset-solid": "^1.8.4", "merge-anything": "^5.1.7", "solid-refresh": "^0.6.3", "vitefu": "^1.0.4" }, "peerDependencies": { "@testing-library/jest-dom": "^5.16.6 || ^5.17.0 || ^6.*", "solid-js": "^1.7.2", "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["@testing-library/jest-dom"] }, "sha512-YMZCXsLw9kyuvQFEdwLP27fuTQJLmjNoHy90AOJnbRuJ6DwShUxKFo38gdFrWn9v11hnGicKCZEaeI/TFs6JKw=="],
|
"storybook-solidjs-vite/vite-plugin-solid": ["vite-plugin-solid@2.11.11", "", { "dependencies": { "@babel/core": "^7.23.3", "@types/babel__core": "^7.20.4", "babel-preset-solid": "^1.8.4", "merge-anything": "^5.1.7", "solid-refresh": "^0.6.3", "vitefu": "^1.0.4" }, "peerDependencies": { "@testing-library/jest-dom": "^5.16.6 || ^5.17.0 || ^6.*", "solid-js": "^1.7.2", "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["@testing-library/jest-dom"] }, "sha512-YMZCXsLw9kyuvQFEdwLP27fuTQJLmjNoHy90AOJnbRuJ6DwShUxKFo38gdFrWn9v11hnGicKCZEaeI/TFs6JKw=="],
|
||||||
|
|
||||||
|
"stream-browserify/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
|
||||||
|
|
||||||
"string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
"string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||||
|
|
||||||
"string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
"string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||||
|
|
@ -5886,8 +6046,6 @@
|
||||||
|
|
||||||
"sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
|
"sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
|
||||||
|
|
||||||
"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=="],
|
"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=="],
|
||||||
|
|
@ -6162,6 +6320,26 @@
|
||||||
|
|
||||||
"@aws-sdk/credential-providers/@aws-sdk/core/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
|
"@aws-sdk/credential-providers/@aws-sdk/core/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/core": ["@smithy/core@3.23.14", "", { "dependencies": { "@smithy/protocol-http": "^5.3.13", "@smithy/types": "^4.14.0", "@smithy/url-parser": "^4.2.13", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-middleware": "^4.2.13", "@smithy/util-stream": "^4.5.22", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-vJ0IhpZxZAkFYOegMKSrxw7ujhhT2pass/1UEcZ4kfl5srTAqtPU5I7MdYQoreVas3204ykCiNhY1o7Xlz6Yyg=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.17", "", { "dependencies": { "@smithy/core": "^3.23.14", "@smithy/protocol-http": "^5.3.13", "@smithy/types": "^4.14.0", "tslib": "^2.6.2" } }, "sha512-0T2mcaM6v9W1xku86Dk0bEW7aEseG6KenFkPK98XNw0ZhOqOiD1MrMsdnQw9QsL3/Oa85T53iSMlm0SZdSuIEQ=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.13", "", { "dependencies": { "@smithy/property-provider": "^4.2.13", "@smithy/shared-ini-file-loader": "^4.4.8", "@smithy/types": "^4.14.0", "tslib": "^2.6.2" } }, "sha512-iGxQ04DsKXLckbgnX4ipElrOTk+IHgTyu0q0WssZfYhDm9CQWHmu6cOeI5wmWRxpXbBDhIIfXMWz5tPEtcVqbw=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.8", "", { "dependencies": { "@smithy/types": "^4.14.0", "tslib": "^2.6.2" } }, "sha512-VZCZx2bZasxdqxVgEAhREvDSlkatTPnkdWy1+Kiy8w7kYPBosW0V5IeDwzDUMvWBt56zpK658rx1cOBFOYaPaw=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/url-parser": ["@smithy/url-parser@4.2.13", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.13", "@smithy/types": "^4.14.0", "tslib": "^2.6.2" } }, "sha512-2G03yoboIRZlZze2+PT4GZEjgwQsJjUgn6iTsvxA02bVceHR6vp4Cuk7TUnPFWKF+ffNUk3kj4COwkENS2K3vw=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/util-middleware": ["@smithy/util-middleware@4.2.13", "", { "dependencies": { "@smithy/types": "^4.14.0", "tslib": "^2.6.2" } }, "sha512-GTooyrlmRTqvUen4eK7/K1p6kryF7bnDfq6XsAbIsf2mo51B/utaH+XThY6dKgNCWzMAaH/+OLmqaBuLhLWRow=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/smithy-client/@smithy/core": ["@smithy/core@3.23.14", "", { "dependencies": { "@smithy/protocol-http": "^5.3.13", "@smithy/types": "^4.14.0", "@smithy/url-parser": "^4.2.13", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-middleware": "^4.2.13", "@smithy/util-stream": "^4.5.22", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-vJ0IhpZxZAkFYOegMKSrxw7ujhhT2pass/1UEcZ4kfl5srTAqtPU5I7MdYQoreVas3204ykCiNhY1o7Xlz6Yyg=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/smithy-client/@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.13", "", { "dependencies": { "@smithy/types": "^4.14.0", "tslib": "^2.6.2" } }, "sha512-g72jN/sGDLyTanrCLH9fhg3oysO3f7tQa6eWWsMyn2BiYNCgjF24n4/I9wff/5XidFvjj9ilipAoQrurTUrLvw=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/smithy-client/@smithy/util-stream": ["@smithy/util-stream@4.5.22", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.16", "@smithy/node-http-handler": "^4.5.2", "@smithy/types": "^4.14.0", "@smithy/util-base64": "^4.3.2", "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-3H8iq/0BfQjUs2/4fbHZ9aG9yNzcuZs24LPkcX1Q7Z+qpqaGM8+qbGmE8zo9m2nCRgamyvS98cHdcWvR6YUsew=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||||
|
|
||||||
"@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.16", "", { "dependencies": { "@smithy/types": "^4.13.1", "fast-xml-parser": "5.5.8", "tslib": "^2.6.2" } }, "sha512-iu2pyvaqmeatIJLURLqx9D+4jKAdTH20ntzB6BFwjyN7V960r4jK32mx0Zf7YbtOYAbmbtQfDNuL60ONinyw7A=="],
|
"@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.16", "", { "dependencies": { "@smithy/types": "^4.13.1", "fast-xml-parser": "5.5.8", "tslib": "^2.6.2" } }, "sha512-iu2pyvaqmeatIJLURLqx9D+4jKAdTH20ntzB6BFwjyN7V960r4jK32mx0Zf7YbtOYAbmbtQfDNuL60ONinyw7A=="],
|
||||||
|
|
||||||
"@aws-sdk/nested-clients/@aws-sdk/core/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
|
"@aws-sdk/nested-clients/@aws-sdk/core/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
|
||||||
|
|
@ -6242,6 +6420,10 @@
|
||||||
|
|
||||||
"@gitlab/opencode-gitlab-auth/open/wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="],
|
"@gitlab/opencode-gitlab-auth/open/wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="],
|
||||||
|
|
||||||
|
"@grpc/proto-loader/yargs/cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
|
||||||
|
|
||||||
|
"@grpc/proto-loader/yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||||
|
|
||||||
"@jsx-email/cli/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.19.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA=="],
|
"@jsx-email/cli/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.19.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA=="],
|
||||||
|
|
||||||
"@jsx-email/cli/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.19.12", "", { "os": "android", "cpu": "arm" }, "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w=="],
|
"@jsx-email/cli/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.19.12", "", { "os": "android", "cpu": "arm" }, "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w=="],
|
||||||
|
|
@ -6540,6 +6722,10 @@
|
||||||
|
|
||||||
"lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
|
"lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
|
||||||
|
|
||||||
|
"minipass-flush/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
|
||||||
|
|
||||||
|
"minipass-pipeline/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
|
||||||
|
|
||||||
"motion/framer-motion/motion-dom": ["motion-dom@12.38.0", "", { "dependencies": { "motion-utils": "^12.36.0" } }, "sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA=="],
|
"motion/framer-motion/motion-dom": ["motion-dom@12.38.0", "", { "dependencies": { "motion-utils": "^12.36.0" } }, "sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA=="],
|
||||||
|
|
||||||
"motion/framer-motion/motion-utils": ["motion-utils@12.36.0", "", {}, "sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg=="],
|
"motion/framer-motion/motion-utils": ["motion-utils@12.36.0", "", {}, "sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg=="],
|
||||||
|
|
@ -6556,6 +6742,8 @@
|
||||||
|
|
||||||
"opencontrol/@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="],
|
"opencontrol/@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="],
|
||||||
|
|
||||||
|
"openid-client/lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
|
||||||
|
|
||||||
"ora/bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
|
"ora/bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
|
||||||
|
|
||||||
"ora/bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
|
"ora/bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
|
||||||
|
|
@ -6720,6 +6908,26 @@
|
||||||
|
|
||||||
"@aws-sdk/credential-providers/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.5.8", "", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.2.0", "strnum": "^2.2.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ=="],
|
"@aws-sdk/credential-providers/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.5.8", "", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.2.0", "strnum": "^2.2.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.22", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.16", "@smithy/node-http-handler": "^4.5.2", "@smithy/types": "^4.14.0", "@smithy/util-base64": "^4.3.2", "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-3H8iq/0BfQjUs2/4fbHZ9aG9yNzcuZs24LPkcX1Q7Z+qpqaGM8+qbGmE8zo9m2nCRgamyvS98cHdcWvR6YUsew=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/core/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/node-config-provider/@smithy/property-provider": ["@smithy/property-provider@4.2.13", "", { "dependencies": { "@smithy/types": "^4.14.0", "tslib": "^2.6.2" } }, "sha512-bGzUCthxRmezuxkbu9wD33wWg9KX3hJpCXpQ93vVkPrHn9ZW6KNNdY5xAUWNuRCwQ+VyboFuWirG1lZhhkcyRQ=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/url-parser/@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.13", "", { "dependencies": { "@smithy/types": "^4.14.0", "tslib": "^2.6.2" } }, "sha512-hqW3Q4P+CDzUyQ87GrboGMeD7XYNMOF+CuTwu936UQRB/zeYn3jys8C3w+wMkDfY7CyyyVwZQ5cNFoG0x1pYmA=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/smithy-client/@smithy/core/@smithy/url-parser": ["@smithy/url-parser@4.2.13", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.13", "@smithy/types": "^4.14.0", "tslib": "^2.6.2" } }, "sha512-2G03yoboIRZlZze2+PT4GZEjgwQsJjUgn6iTsvxA02bVceHR6vp4Cuk7TUnPFWKF+ffNUk3kj4COwkENS2K3vw=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/smithy-client/@smithy/core/@smithy/util-middleware": ["@smithy/util-middleware@4.2.13", "", { "dependencies": { "@smithy/types": "^4.14.0", "tslib": "^2.6.2" } }, "sha512-GTooyrlmRTqvUen4eK7/K1p6kryF7bnDfq6XsAbIsf2mo51B/utaH+XThY6dKgNCWzMAaH/+OLmqaBuLhLWRow=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/smithy-client/@smithy/core/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.16", "", { "dependencies": { "@smithy/protocol-http": "^5.3.13", "@smithy/querystring-builder": "^4.2.13", "@smithy/types": "^4.14.0", "@smithy/util-base64": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-nYDRUIvNd4mFmuXraRWt6w5UsZTNqtj4hXJA/iiOD4tuseIdLP9Lq38teH/SZTcIFCa2f+27o7hYpIsWktJKEQ=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/smithy-client/@smithy/util-stream/@smithy/node-http-handler": ["@smithy/node-http-handler@4.5.2", "", { "dependencies": { "@smithy/protocol-http": "^5.3.13", "@smithy/querystring-builder": "^4.2.13", "@smithy/types": "^4.14.0", "tslib": "^2.6.2" } }, "sha512-/oD7u8M0oj2ZTFw7GkuuHWpIxtWdLlnyNkbrWcyVYhd5RJNDuczdkb0wfnQICyNFrVPlr8YHOhamjNy3zidhmA=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/smithy-client/@smithy/util-stream/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
|
||||||
|
|
||||||
"@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.5.8", "", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.2.0", "strnum": "^2.2.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ=="],
|
"@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.5.8", "", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.2.0", "strnum": "^2.2.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ=="],
|
||||||
|
|
||||||
"@aws-sdk/token-providers/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.5.8", "", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.2.0", "strnum": "^2.2.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ=="],
|
"@aws-sdk/token-providers/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.5.8", "", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.2.0", "strnum": "^2.2.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ=="],
|
||||||
|
|
@ -6750,6 +6958,14 @@
|
||||||
|
|
||||||
"@electron/universal/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
"@electron/universal/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||||
|
|
||||||
|
"@grpc/proto-loader/yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||||
|
|
||||||
|
"@grpc/proto-loader/yargs/cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||||
|
|
||||||
|
"@grpc/proto-loader/yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||||
|
|
||||||
|
"@grpc/proto-loader/yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||||
|
|
||||||
"@jsx-email/cli/tailwindcss/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
"@jsx-email/cli/tailwindcss/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||||
|
|
||||||
"@jsx-email/cli/tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
|
"@jsx-email/cli/tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
|
||||||
|
|
@ -6834,6 +7050,8 @@
|
||||||
|
|
||||||
"app-builder-lib/@electron/get/fs-extra/universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="],
|
"app-builder-lib/@electron/get/fs-extra/universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="],
|
||||||
|
|
||||||
|
"app-builder-lib/hosted-git-info/lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
|
||||||
|
|
||||||
"archiver-utils/glob/jackspeak/@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
|
"archiver-utils/glob/jackspeak/@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
|
||||||
|
|
||||||
"archiver-utils/glob/minimatch/brace-expansion": ["brace-expansion@2.0.3", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA=="],
|
"archiver-utils/glob/minimatch/brace-expansion": ["brace-expansion@2.0.3", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA=="],
|
||||||
|
|
@ -6950,6 +7168,16 @@
|
||||||
|
|
||||||
"@aws-sdk/credential-providers/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.2", "", {}, "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA=="],
|
"@aws-sdk/credential-providers/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.2", "", {}, "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.16", "", { "dependencies": { "@smithy/protocol-http": "^5.3.13", "@smithy/querystring-builder": "^4.2.13", "@smithy/types": "^4.14.0", "@smithy/util-base64": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-nYDRUIvNd4mFmuXraRWt6w5UsZTNqtj4hXJA/iiOD4tuseIdLP9Lq38teH/SZTcIFCa2f+27o7hYpIsWktJKEQ=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/node-http-handler": ["@smithy/node-http-handler@4.5.2", "", { "dependencies": { "@smithy/protocol-http": "^5.3.13", "@smithy/querystring-builder": "^4.2.13", "@smithy/types": "^4.14.0", "tslib": "^2.6.2" } }, "sha512-/oD7u8M0oj2ZTFw7GkuuHWpIxtWdLlnyNkbrWcyVYhd5RJNDuczdkb0wfnQICyNFrVPlr8YHOhamjNy3zidhmA=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/smithy-client/@smithy/core/@smithy/url-parser/@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.13", "", { "dependencies": { "@smithy/types": "^4.14.0", "tslib": "^2.6.2" } }, "sha512-hqW3Q4P+CDzUyQ87GrboGMeD7XYNMOF+CuTwu936UQRB/zeYn3jys8C3w+wMkDfY7CyyyVwZQ5cNFoG0x1pYmA=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.13", "", { "dependencies": { "@smithy/types": "^4.14.0", "@smithy/util-uri-escape": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-tG4aOYFCZdPMjbgfhnIQ322H//ojujldp1SrHPHpBSb3NqgUp3dwiUGRJzie87hS1DYwWGqDuPaowoDF+rYCbQ=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/smithy-client/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.13", "", { "dependencies": { "@smithy/types": "^4.14.0", "@smithy/util-uri-escape": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-tG4aOYFCZdPMjbgfhnIQ322H//ojujldp1SrHPHpBSb3NqgUp3dwiUGRJzie87hS1DYwWGqDuPaowoDF+rYCbQ=="],
|
||||||
|
|
||||||
"@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.2", "", {}, "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA=="],
|
"@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.2", "", {}, "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA=="],
|
||||||
|
|
||||||
"@aws-sdk/token-providers/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.2", "", {}, "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA=="],
|
"@aws-sdk/token-providers/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.2", "", {}, "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA=="],
|
||||||
|
|
@ -6968,6 +7196,10 @@
|
||||||
|
|
||||||
"@electron/rebuild/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
"@electron/rebuild/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||||
|
|
||||||
|
"@grpc/proto-loader/yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||||
|
|
||||||
|
"@grpc/proto-loader/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||||
|
|
||||||
"@jsx-email/cli/tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
|
"@jsx-email/cli/tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
|
||||||
|
|
||||||
"@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es/regex": ["regex@5.1.1", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw=="],
|
"@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es/regex": ["regex@5.1.1", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw=="],
|
||||||
|
|
@ -7008,6 +7240,10 @@
|
||||||
|
|
||||||
"@aws-sdk/credential-provider-cognito-identity/@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.2", "", {}, "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA=="],
|
"@aws-sdk/credential-provider-cognito-identity/@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.2", "", {}, "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.13", "", { "dependencies": { "@smithy/types": "^4.14.0", "@smithy/util-uri-escape": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-tG4aOYFCZdPMjbgfhnIQ322H//ojujldp1SrHPHpBSb3NqgUp3dwiUGRJzie87hS1DYwWGqDuPaowoDF+rYCbQ=="],
|
||||||
|
|
||||||
|
"@aws-sdk/lib-storage/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.13", "", { "dependencies": { "@smithy/types": "^4.14.0", "@smithy/util-uri-escape": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-tG4aOYFCZdPMjbgfhnIQ322H//ojujldp1SrHPHpBSb3NqgUp3dwiUGRJzie87hS1DYwWGqDuPaowoDF+rYCbQ=="],
|
||||||
|
|
||||||
"@electron/rebuild/node-gyp/make-fetch-happen/cacache/glob/jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
|
"@electron/rebuild/node-gyp/make-fetch-happen/cacache/glob/jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
|
||||||
|
|
||||||
"@electron/rebuild/node-gyp/make-fetch-happen/cacache/glob/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="],
|
"@electron/rebuild/node-gyp/make-fetch-happen/cacache/glob/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="],
|
||||||
|
|
@ -7028,6 +7264,8 @@
|
||||||
|
|
||||||
"@electron/rebuild/node-gyp/make-fetch-happen/cacache/glob/minimatch/brace-expansion": ["brace-expansion@2.0.3", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA=="],
|
"@electron/rebuild/node-gyp/make-fetch-happen/cacache/glob/minimatch/brace-expansion": ["brace-expansion@2.0.3", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA=="],
|
||||||
|
|
||||||
|
"@electron/rebuild/node-gyp/make-fetch-happen/minipass-fetch/minipass-sized/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
|
||||||
|
|
||||||
"@electron/rebuild/node-gyp/make-fetch-happen/cacache/glob/jackspeak/@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
|
"@electron/rebuild/node-gyp/make-fetch-happen/cacache/glob/jackspeak/@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
|
||||||
|
|
||||||
"@electron/rebuild/node-gyp/make-fetch-happen/cacache/glob/jackspeak/@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
|
"@electron/rebuild/node-gyp/make-fetch-happen/cacache/glob/jackspeak/@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
|
||||||
|
|
|
||||||
43
daytonaWorkspaceBootstrap.sh
Normal file
43
daytonaWorkspaceBootstrap.sh
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euox pipefail
|
||||||
|
|
||||||
|
project="$1"
|
||||||
|
|
||||||
|
root="/home/daytona/workspace"
|
||||||
|
repo="$root/repo"
|
||||||
|
localbin="/home/daytona/opencode"
|
||||||
|
installbin="/home/daytona/.opencode/bin/opencode"
|
||||||
|
|
||||||
|
printf "%s\n" "ipv4" > "$HOME/.curlrc"
|
||||||
|
rm -rf "$repo"
|
||||||
|
mkdir -p "$root"
|
||||||
|
tar -xzf "$HOME/repo.tgz" -C "$HOME/workspace"
|
||||||
|
|
||||||
|
ls -last "$HOME"
|
||||||
|
|
||||||
|
if [ -f "$HOME/opencode" ]; then
|
||||||
|
chmod +x "$HOME/opencode"
|
||||||
|
exe="$localbin"
|
||||||
|
else
|
||||||
|
mkdir -p "$HOME/.opencode/bin"
|
||||||
|
OPENCODE_INSTALL_DIR="$HOME/.opencode/bin" curl -4 -fsSL https://opencode.ai/install | bash
|
||||||
|
exe="$installbin"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "opencode: $exe"
|
||||||
|
printf "%s\n" "$project" > "$repo/.git/opencode"
|
||||||
|
|
||||||
|
cd "$repo"
|
||||||
|
OPENCODE_WORKSPACE=true OPENCODE_EXPERIMENTAL_WORKSPACES=true nohup "$exe" serve --hostname 0.0.0.0 --port 3096 --print-logs > /tmp/opencode-server.log 2>&1 &
|
||||||
|
|
||||||
|
for i in $(seq 1 60); do
|
||||||
|
if curl -4 -fsS http://127.0.0.1:3096/global/health >/dev/null; then
|
||||||
|
echo "ready"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "waiting for server ($i/60)"
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "daytona workspace server did not become ready in time" >&2
|
||||||
|
exit 1
|
||||||
206
daytonaWorkspacePlugin.ts
Normal file
206
daytonaWorkspacePlugin.ts
Normal file
|
|
@ -0,0 +1,206 @@
|
||||||
|
import type { Daytona, Sandbox } from "@daytonaio/sdk"
|
||||||
|
import type { Plugin } from "@opencode-ai/plugin"
|
||||||
|
import { join } from "node:path"
|
||||||
|
import { fileURLToPath } from "node:url"
|
||||||
|
import { tmpdir } from "node:os"
|
||||||
|
import { access, mkdir } from "node:fs/promises"
|
||||||
|
import { randomUUID } from "node:crypto"
|
||||||
|
|
||||||
|
let client: Promise<Daytona> | undefined
|
||||||
|
|
||||||
|
let daytona = function daytona(): Promise<Daytona> {
|
||||||
|
if (client == null) {
|
||||||
|
client = import("@daytonaio/sdk").then(
|
||||||
|
({ Daytona }) =>
|
||||||
|
new Daytona({
|
||||||
|
apiKey: "dtn_2ffe19d27837953f1a46cc297d8a5331d4c46b00856eb5f4a4afded3f3426038",
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
const preview = new Map<string, { url: string; token: string }>()
|
||||||
|
const repo = "/home/daytona/workspace/repo"
|
||||||
|
|
||||||
|
const local = fileURLToPath(
|
||||||
|
new URL("./packages/opencode/dist/opencode-linux-x64-baseline/bin/opencode", import.meta.url),
|
||||||
|
)
|
||||||
|
const bootstrap = fileURLToPath(new URL("./daytonaWorkspaceBootstrap.sh", import.meta.url))
|
||||||
|
|
||||||
|
async function exists(file: string) {
|
||||||
|
return access(file)
|
||||||
|
.then(() => true)
|
||||||
|
.catch(() => false)
|
||||||
|
}
|
||||||
|
|
||||||
|
function sh(value: string) {
|
||||||
|
return `'${value.replace(/'/g, `'"'"'`)}'`
|
||||||
|
}
|
||||||
|
|
||||||
|
async function boot() {
|
||||||
|
return Bun.file(bootstrap).text()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internally Daytona uses axios, which tries to overwrite stack
|
||||||
|
// traces when a failure happens. That path fails in Bun, however, so
|
||||||
|
// when something goes wrong you only see a very obscure error.
|
||||||
|
async function withSandbox<T>(name: string, fn: (sandbox: Sandbox) => Promise<T>) {
|
||||||
|
const stack = Error.captureStackTrace
|
||||||
|
// @ts-expect-error temporary compatibility hack for Daytona's axios stack handling in Bun
|
||||||
|
Error.captureStackTrace = undefined
|
||||||
|
try {
|
||||||
|
return await fn(await (await daytona()).get(name))
|
||||||
|
} finally {
|
||||||
|
Error.captureStackTrace = stack
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DaytonaWorkspacePlugin: Plugin = async ({ experimental_workspace, worktree, project }) => {
|
||||||
|
experimental_workspace.register("daytona", {
|
||||||
|
name: "Daytona",
|
||||||
|
description: "Create a remote Daytona workspace",
|
||||||
|
configure(config) {
|
||||||
|
return config
|
||||||
|
},
|
||||||
|
async create(config) {
|
||||||
|
const temp = join(tmpdir(), `opencode-daytona-${randomUUID()}`)
|
||||||
|
|
||||||
|
console.log("creating sandbox...")
|
||||||
|
|
||||||
|
const sandbox = await (
|
||||||
|
await daytona()
|
||||||
|
).create({
|
||||||
|
name: config.name,
|
||||||
|
envVars: {
|
||||||
|
foo: "bar",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const sid = `setup-${randomUUID()}`
|
||||||
|
await sandbox.process.createSession(sid)
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log("creating ssh...")
|
||||||
|
|
||||||
|
const ssh = await withSandbox(config.name, (sandbox) => sandbox.createSshAccess())
|
||||||
|
console.log("daytona:", ssh.sshCommand)
|
||||||
|
|
||||||
|
const run = async (command: string, opts?: { stream?: boolean }) => {
|
||||||
|
if (!opts?.stream) {
|
||||||
|
const result = await sandbox.process.executeCommand(command)
|
||||||
|
if (result.exitCode === 0) return result
|
||||||
|
throw new Error(result.result || `sandbox command failed: ${command}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await sandbox.process.executeSessionCommand(sid, { command, runAsync: true })
|
||||||
|
if (!res.cmdId) throw new Error(`sandbox command failed to start: ${command}`)
|
||||||
|
|
||||||
|
let out = ""
|
||||||
|
let err = ""
|
||||||
|
await sandbox.process.getSessionCommandLogs(
|
||||||
|
sid,
|
||||||
|
res.cmdId,
|
||||||
|
(chunk) => {
|
||||||
|
out += chunk
|
||||||
|
process.stdout.write(chunk)
|
||||||
|
},
|
||||||
|
(chunk) => {
|
||||||
|
err += chunk
|
||||||
|
process.stderr.write(chunk)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
for (let i = 0; i < 120; i++) {
|
||||||
|
const cmd = await sandbox.process.getSessionCommand(sid, res.cmdId)
|
||||||
|
if (typeof cmd.exitCode !== "number") {
|
||||||
|
await Bun.sleep(500)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (cmd.exitCode === 0) return cmd
|
||||||
|
throw new Error(err || out || `sandbox command failed: ${command}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`sandbox command timed out waiting for exit code: ${command}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const dir = join(temp, "repo")
|
||||||
|
const tar = join(temp, "repo.tgz")
|
||||||
|
const scr = join(temp, "bootstrap.sh")
|
||||||
|
const source = `file://${worktree}`
|
||||||
|
await mkdir(temp, { recursive: true })
|
||||||
|
const args = ["clone", "--depth", "1", "--no-local"]
|
||||||
|
if (config.branch) args.push("--branch", config.branch)
|
||||||
|
args.push(source, dir)
|
||||||
|
|
||||||
|
console.log("git cloning...")
|
||||||
|
|
||||||
|
const clone = Bun.spawn(["git", ...args], {
|
||||||
|
cwd: tmpdir(),
|
||||||
|
stdout: "pipe",
|
||||||
|
stderr: "pipe",
|
||||||
|
})
|
||||||
|
const code = await clone.exited
|
||||||
|
if (code !== 0) throw new Error(await new Response(clone.stderr).text())
|
||||||
|
|
||||||
|
console.log("tarring...")
|
||||||
|
|
||||||
|
const packed = Bun.spawn(["tar", "-czf", tar, "-C", temp, "repo"], {
|
||||||
|
stdout: "ignore",
|
||||||
|
stderr: "pipe",
|
||||||
|
})
|
||||||
|
if ((await packed.exited) !== 0) throw new Error(await new Response(packed.stderr).text())
|
||||||
|
|
||||||
|
console.log("writing bootstrap script...")
|
||||||
|
|
||||||
|
await Bun.write(scr, await boot())
|
||||||
|
|
||||||
|
console.log("uploading files...")
|
||||||
|
|
||||||
|
await sandbox.fs.uploadFile(tar, "repo.tgz")
|
||||||
|
await sandbox.fs.uploadFile(scr, "bootstrap.sh")
|
||||||
|
|
||||||
|
console.log("local", local)
|
||||||
|
if (await exists(local)) {
|
||||||
|
console.log("uploading local binary...")
|
||||||
|
await sandbox.fs.uploadFile(local, "opencode")
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("bootstrapping workspace...")
|
||||||
|
|
||||||
|
await run(`bash bootstrap.sh ${sh(project.id)}`, {
|
||||||
|
stream: true,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
} finally {
|
||||||
|
await sandbox.process.deleteSession(sid).catch(() => undefined)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async remove(config) {
|
||||||
|
const sandbox = await (await daytona()).get(config.name).catch(() => undefined)
|
||||||
|
if (!sandbox) return
|
||||||
|
await (await daytona()).delete(sandbox)
|
||||||
|
preview.delete(config.name)
|
||||||
|
},
|
||||||
|
async target(config) {
|
||||||
|
let link = preview.get(config.name)
|
||||||
|
if (!link) {
|
||||||
|
link = await withSandbox(config.name, (sandbox) => sandbox.getPreviewLink(3096))
|
||||||
|
preview.set(config.name, link)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: "remote",
|
||||||
|
url: link.url,
|
||||||
|
headers: {
|
||||||
|
"x-daytona-preview-token": link.token,
|
||||||
|
"x-daytona-skip-preview-warning": "true",
|
||||||
|
"x-opencode-directory": repo,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DaytonaWorkspacePlugin
|
||||||
23
debugWorkspacePlugin.ts
Normal file
23
debugWorkspacePlugin.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import type { Plugin } from "@opencode-ai/plugin"
|
||||||
|
|
||||||
|
export const DebugWorkspacePlugin: Plugin = async ({ experimental_workspace }) => {
|
||||||
|
experimental_workspace.register("debug", {
|
||||||
|
name: "Debug",
|
||||||
|
description: "Create a debugging server",
|
||||||
|
configure(config) {
|
||||||
|
return config
|
||||||
|
},
|
||||||
|
async create(_config) {},
|
||||||
|
async remove(_config) {},
|
||||||
|
target(_config) {
|
||||||
|
return {
|
||||||
|
type: "remote",
|
||||||
|
url: "http://localhost:5096/",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DebugWorkspacePlugin
|
||||||
|
|
@ -89,6 +89,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "3.933.0",
|
"@aws-sdk/client-s3": "3.933.0",
|
||||||
|
"@daytona/sdk": "0.164.0",
|
||||||
"@opencode-ai/plugin": "workspace:*",
|
"@opencode-ai/plugin": "workspace:*",
|
||||||
"@opencode-ai/script": "workspace:*",
|
"@opencode-ai/script": "workspace:*",
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
import { TextAttributes } from "@opentui/core"
|
||||||
|
import { useTheme } from "../context/theme"
|
||||||
|
import { useDialog } from "../ui/dialog"
|
||||||
|
import { createStore } from "solid-js/store"
|
||||||
|
import { For } from "solid-js"
|
||||||
|
import { useKeyboard } from "@opentui/solid"
|
||||||
|
|
||||||
|
export function DialogSessionDeleteFailed(props: {
|
||||||
|
session: string
|
||||||
|
workspace: string
|
||||||
|
onDelete?: () => boolean | void | Promise<boolean | void>
|
||||||
|
onRestore?: () => boolean | void | Promise<boolean | void>
|
||||||
|
onDone?: () => void
|
||||||
|
}) {
|
||||||
|
const dialog = useDialog()
|
||||||
|
const { theme } = useTheme()
|
||||||
|
const [store, setStore] = createStore({
|
||||||
|
active: "delete" as "delete" | "restore",
|
||||||
|
})
|
||||||
|
|
||||||
|
const options = [
|
||||||
|
{
|
||||||
|
id: "delete" as const,
|
||||||
|
title: "Delete workspace",
|
||||||
|
description: "Delete the workspace and all sessions attached to it.",
|
||||||
|
run: props.onDelete,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "restore" as const,
|
||||||
|
title: "Restore to new workspace",
|
||||||
|
description: "Try to restore this session into a new workspace.",
|
||||||
|
run: props.onRestore,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
async function confirm() {
|
||||||
|
const result = await options.find((item) => item.id === store.active)?.run?.()
|
||||||
|
if (result === false) return
|
||||||
|
props.onDone?.()
|
||||||
|
if (!props.onDone) dialog.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
useKeyboard((evt) => {
|
||||||
|
if (evt.name === "return") {
|
||||||
|
void confirm()
|
||||||
|
}
|
||||||
|
if (evt.name === "left" || evt.name === "up") {
|
||||||
|
setStore("active", "delete")
|
||||||
|
}
|
||||||
|
if (evt.name === "right" || evt.name === "down") {
|
||||||
|
setStore("active", "restore")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<box paddingLeft={2} paddingRight={2} gap={1}>
|
||||||
|
<box flexDirection="row" justifyContent="space-between">
|
||||||
|
<text attributes={TextAttributes.BOLD} fg={theme.text}>
|
||||||
|
Failed to Delete Session
|
||||||
|
</text>
|
||||||
|
<text fg={theme.textMuted} onMouseUp={() => dialog.clear()}>
|
||||||
|
esc
|
||||||
|
</text>
|
||||||
|
</box>
|
||||||
|
<text fg={theme.textMuted} wrapMode="word">
|
||||||
|
{`The session "${props.session}" could not be deleted because the workspace "${props.workspace}" is not available.`}
|
||||||
|
</text>
|
||||||
|
<text fg={theme.textMuted} wrapMode="word">
|
||||||
|
Choose how you want to recover this broken workspace session.
|
||||||
|
</text>
|
||||||
|
<box flexDirection="column" paddingBottom={1} gap={1}>
|
||||||
|
<For each={options}>
|
||||||
|
{(item) => (
|
||||||
|
<box
|
||||||
|
flexDirection="column"
|
||||||
|
paddingLeft={1}
|
||||||
|
paddingRight={1}
|
||||||
|
paddingTop={1}
|
||||||
|
paddingBottom={1}
|
||||||
|
backgroundColor={item.id === store.active ? theme.primary : undefined}
|
||||||
|
onMouseUp={() => {
|
||||||
|
setStore("active", item.id)
|
||||||
|
void confirm()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<text
|
||||||
|
attributes={TextAttributes.BOLD}
|
||||||
|
fg={item.id === store.active ? theme.selectedListItemText : theme.text}
|
||||||
|
>
|
||||||
|
{item.title}
|
||||||
|
</text>
|
||||||
|
<text fg={item.id === store.active ? theme.selectedListItemText : theme.textMuted} wrapMode="word">
|
||||||
|
{item.description}
|
||||||
|
</text>
|
||||||
|
</box>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</box>
|
||||||
|
</box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -13,8 +13,10 @@ import { DialogSessionRename } from "./dialog-session-rename"
|
||||||
import { Keybind } from "@/util/keybind"
|
import { Keybind } from "@/util/keybind"
|
||||||
import { createDebouncedSignal } from "../util/signal"
|
import { createDebouncedSignal } from "../util/signal"
|
||||||
import { useToast } from "../ui/toast"
|
import { useToast } from "../ui/toast"
|
||||||
import { DialogWorkspaceCreate, openWorkspaceSession } from "./dialog-workspace-create"
|
import { DialogWorkspaceCreate, openWorkspaceSession, restoreWorkspaceSession } from "./dialog-workspace-create"
|
||||||
import { Spinner } from "./spinner"
|
import { Spinner } from "./spinner"
|
||||||
|
import { errorMessage } from "@/util/error"
|
||||||
|
import { DialogSessionDeleteFailed } from "./dialog-session-delete-failed"
|
||||||
|
|
||||||
type WorkspaceStatus = "connected" | "connecting" | "disconnected" | "error"
|
type WorkspaceStatus = "connected" | "connecting" | "disconnected" | "error"
|
||||||
|
|
||||||
|
|
@ -30,7 +32,7 @@ export function DialogSessionList() {
|
||||||
const [toDelete, setToDelete] = createSignal<string>()
|
const [toDelete, setToDelete] = createSignal<string>()
|
||||||
const [search, setSearch] = createDebouncedSignal("", 150)
|
const [search, setSearch] = createDebouncedSignal("", 150)
|
||||||
|
|
||||||
const [searchResults] = createResource(search, async (query) => {
|
const [searchResults, { refetch }] = createResource(search, async (query) => {
|
||||||
if (!query) return undefined
|
if (!query) return undefined
|
||||||
const result = await sdk.client.session.list({ search: query, limit: 30 })
|
const result = await sdk.client.session.list({ search: query, limit: 30 })
|
||||||
return result.data ?? []
|
return result.data ?? []
|
||||||
|
|
@ -56,6 +58,57 @@ export function DialogSessionList() {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function recover(session: NonNullable<ReturnType<typeof sessions>[number]>) {
|
||||||
|
const workspace = project.workspace.get(session.workspaceID!)
|
||||||
|
const list = () => dialog.replace(() => <DialogSessionList />)
|
||||||
|
dialog.replace(() => (
|
||||||
|
<DialogSessionDeleteFailed
|
||||||
|
session={session.title}
|
||||||
|
workspace={workspace?.name ?? session.workspaceID!}
|
||||||
|
onDone={list}
|
||||||
|
onDelete={async () => {
|
||||||
|
const current = currentSessionID()
|
||||||
|
const info = current ? sync.data.session.find((item) => item.id === current) : undefined
|
||||||
|
const result = await sdk.client.experimental.workspace.remove({ id: session.workspaceID! })
|
||||||
|
if (result.error) {
|
||||||
|
toast.show({
|
||||||
|
variant: "error",
|
||||||
|
title: "Failed to delete workspace",
|
||||||
|
message: errorMessage(result.error),
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
await project.workspace.sync()
|
||||||
|
await sync.session.refresh()
|
||||||
|
if (search()) await refetch()
|
||||||
|
if (info?.workspaceID === session.workspaceID) {
|
||||||
|
route.navigate({ type: "home" })
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}}
|
||||||
|
onRestore={() => {
|
||||||
|
dialog.replace(() => (
|
||||||
|
<DialogWorkspaceCreate
|
||||||
|
onSelect={(workspaceID) =>
|
||||||
|
restoreWorkspaceSession({
|
||||||
|
dialog,
|
||||||
|
sdk,
|
||||||
|
sync,
|
||||||
|
project,
|
||||||
|
toast,
|
||||||
|
workspaceID,
|
||||||
|
sessionID: session.id,
|
||||||
|
done: list,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
return false
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
const options = createMemo(() => {
|
const options = createMemo(() => {
|
||||||
const today = new Date().toDateString()
|
const today = new Date().toDateString()
|
||||||
return sessions()
|
return sessions()
|
||||||
|
|
@ -145,9 +198,42 @@ export function DialogSessionList() {
|
||||||
title: "delete",
|
title: "delete",
|
||||||
onTrigger: async (option) => {
|
onTrigger: async (option) => {
|
||||||
if (toDelete() === option.value) {
|
if (toDelete() === option.value) {
|
||||||
sdk.client.session.delete({
|
const session = sessions().find((item) => item.id === option.value)
|
||||||
sessionID: option.value,
|
const status = session?.workspaceID ? project.workspace.status(session.workspaceID) : undefined
|
||||||
})
|
try {
|
||||||
|
const result = await sdk.client.session.delete({
|
||||||
|
sessionID: option.value,
|
||||||
|
})
|
||||||
|
if (result.error) {
|
||||||
|
if (session?.workspaceID) {
|
||||||
|
recover(session)
|
||||||
|
} else {
|
||||||
|
toast.show({
|
||||||
|
variant: "error",
|
||||||
|
title: "Failed to delete session",
|
||||||
|
message: errorMessage(result.error),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
setToDelete(undefined)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (session?.workspaceID) {
|
||||||
|
recover(session)
|
||||||
|
} else {
|
||||||
|
toast.show({
|
||||||
|
variant: "error",
|
||||||
|
title: "Failed to delete session",
|
||||||
|
message: errorMessage(err),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
setToDelete(undefined)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (status && status !== "connected") {
|
||||||
|
await sync.session.refresh()
|
||||||
|
}
|
||||||
|
if (search()) await refetch()
|
||||||
setToDelete(undefined)
|
setToDelete(undefined)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import { useSync } from "@tui/context/sync"
|
||||||
import { useProject } from "@tui/context/project"
|
import { useProject } from "@tui/context/project"
|
||||||
import { createMemo, createSignal, onMount } from "solid-js"
|
import { createMemo, createSignal, onMount } from "solid-js"
|
||||||
import { setTimeout as sleep } from "node:timers/promises"
|
import { setTimeout as sleep } from "node:timers/promises"
|
||||||
|
import { errorData, errorMessage } from "@/util/error"
|
||||||
|
import { Log } from "@/util/log"
|
||||||
import { useSDK } from "../context/sdk"
|
import { useSDK } from "../context/sdk"
|
||||||
import { useToast } from "../ui/toast"
|
import { useToast } from "../ui/toast"
|
||||||
|
|
||||||
|
|
@ -15,6 +17,8 @@ type Adaptor = {
|
||||||
description: string
|
description: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const log = Log.Default.clone().tag("service", "tui-workspace")
|
||||||
|
|
||||||
function scoped(sdk: ReturnType<typeof useSDK>, sync: ReturnType<typeof useSync>, workspaceID: string) {
|
function scoped(sdk: ReturnType<typeof useSDK>, sync: ReturnType<typeof useSync>, workspaceID: string) {
|
||||||
return createOpencodeClient({
|
return createOpencodeClient({
|
||||||
baseUrl: sdk.url,
|
baseUrl: sdk.url,
|
||||||
|
|
@ -33,8 +37,19 @@ export async function openWorkspaceSession(input: {
|
||||||
workspaceID: string
|
workspaceID: string
|
||||||
}) {
|
}) {
|
||||||
const client = scoped(input.sdk, input.sync, input.workspaceID)
|
const client = scoped(input.sdk, input.sync, input.workspaceID)
|
||||||
|
log.info("workspace session create requested", {
|
||||||
|
workspaceID: input.workspaceID,
|
||||||
|
})
|
||||||
while (true) {
|
while (true) {
|
||||||
const result = await client.session.create({ workspaceID: input.workspaceID }).catch(() => undefined)
|
const result = await client.session
|
||||||
|
.create({ workspaceID: input.workspaceID, workspace: input.workspaceID })
|
||||||
|
.catch((err) => {
|
||||||
|
log.error("workspace session create request failed", {
|
||||||
|
workspaceID: input.workspaceID,
|
||||||
|
error: errorData(err),
|
||||||
|
})
|
||||||
|
return undefined
|
||||||
|
})
|
||||||
if (!result) {
|
if (!result) {
|
||||||
input.toast.show({
|
input.toast.show({
|
||||||
message: "Failed to create workspace session",
|
message: "Failed to create workspace session",
|
||||||
|
|
@ -42,26 +57,115 @@ export async function openWorkspaceSession(input: {
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (result.response.status >= 500 && result.response.status < 600) {
|
log.info("workspace session create response", {
|
||||||
|
workspaceID: input.workspaceID,
|
||||||
|
status: result.response?.status,
|
||||||
|
sessionID: result.data?.id,
|
||||||
|
})
|
||||||
|
if (result.response?.status && result.response.status >= 500 && result.response.status < 600) {
|
||||||
|
log.warn("workspace session create retrying after server error", {
|
||||||
|
workspaceID: input.workspaceID,
|
||||||
|
status: result.response.status,
|
||||||
|
})
|
||||||
await sleep(1000)
|
await sleep(1000)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (!result.data) {
|
if (!result.data) {
|
||||||
|
log.error("workspace session create returned no data", {
|
||||||
|
workspaceID: input.workspaceID,
|
||||||
|
status: result.response?.status,
|
||||||
|
})
|
||||||
input.toast.show({
|
input.toast.show({
|
||||||
message: "Failed to create workspace session",
|
message: "Failed to create workspace session",
|
||||||
variant: "error",
|
variant: "error",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await sleep(5000)
|
||||||
|
|
||||||
input.route.navigate({
|
input.route.navigate({
|
||||||
type: "session",
|
type: "session",
|
||||||
sessionID: result.data.id,
|
sessionID: result.data.id,
|
||||||
})
|
})
|
||||||
|
log.info("workspace session create complete", {
|
||||||
|
workspaceID: input.workspaceID,
|
||||||
|
sessionID: result.data.id,
|
||||||
|
})
|
||||||
input.dialog.clear()
|
input.dialog.clear()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function restoreWorkspaceSession(input: {
|
||||||
|
dialog: ReturnType<typeof useDialog>
|
||||||
|
sdk: ReturnType<typeof useSDK>
|
||||||
|
sync: ReturnType<typeof useSync>
|
||||||
|
project: ReturnType<typeof useProject>
|
||||||
|
toast: ReturnType<typeof useToast>
|
||||||
|
workspaceID: string
|
||||||
|
sessionID: string
|
||||||
|
done?: () => void
|
||||||
|
}) {
|
||||||
|
log.info("session restore requested", {
|
||||||
|
workspaceID: input.workspaceID,
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
})
|
||||||
|
const result = await input.sdk.client.experimental.workspace
|
||||||
|
.sessionRestore({ id: input.workspaceID, sessionID: input.sessionID })
|
||||||
|
.catch((err) => {
|
||||||
|
log.error("session restore request failed", {
|
||||||
|
workspaceID: input.workspaceID,
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
error: errorData(err),
|
||||||
|
})
|
||||||
|
return undefined
|
||||||
|
})
|
||||||
|
if (!result?.data) {
|
||||||
|
log.error("session restore failed", {
|
||||||
|
workspaceID: input.workspaceID,
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
status: result?.response?.status,
|
||||||
|
error: result?.error ? errorData(result.error) : undefined,
|
||||||
|
})
|
||||||
|
input.toast.show({
|
||||||
|
message: `Failed to restore session: ${errorMessage(result?.error ?? "no response")}`,
|
||||||
|
variant: "error",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("session restore response", {
|
||||||
|
workspaceID: input.workspaceID,
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
status: result.response?.status,
|
||||||
|
total: result.data.total,
|
||||||
|
})
|
||||||
|
|
||||||
|
await Promise.all([input.project.workspace.sync(), input.sync.session.refresh()]).catch((err) => {
|
||||||
|
log.error("session restore refresh failed", {
|
||||||
|
workspaceID: input.workspaceID,
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
error: errorData(err),
|
||||||
|
})
|
||||||
|
throw err
|
||||||
|
})
|
||||||
|
|
||||||
|
log.info("session restore complete", {
|
||||||
|
workspaceID: input.workspaceID,
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
total: result.data.total,
|
||||||
|
})
|
||||||
|
|
||||||
|
input.toast.show({
|
||||||
|
message: "Session restored into the new workspace",
|
||||||
|
variant: "success",
|
||||||
|
})
|
||||||
|
input.done?.()
|
||||||
|
if (input.done) return
|
||||||
|
input.dialog.clear()
|
||||||
|
}
|
||||||
|
|
||||||
export function DialogWorkspaceCreate(props: { onSelect: (workspaceID: string) => Promise<void> | void }) {
|
export function DialogWorkspaceCreate(props: { onSelect: (workspaceID: string) => Promise<void> | void }) {
|
||||||
const dialog = useDialog()
|
const dialog = useDialog()
|
||||||
const sync = useSync()
|
const sync = useSync()
|
||||||
|
|
@ -123,18 +227,41 @@ export function DialogWorkspaceCreate(props: { onSelect: (workspaceID: string) =
|
||||||
const create = async (type: string) => {
|
const create = async (type: string) => {
|
||||||
if (creating()) return
|
if (creating()) return
|
||||||
setCreating(type)
|
setCreating(type)
|
||||||
|
log.info("workspace create requested", {
|
||||||
|
type,
|
||||||
|
})
|
||||||
|
|
||||||
const result = await sdk.client.experimental.workspace.create({ type, branch: null }).catch(() => undefined)
|
const result = await sdk.client.experimental.workspace.create({ type, branch: null }).catch((err) => {
|
||||||
|
log.error("workspace create request failed", {
|
||||||
|
type,
|
||||||
|
error: errorData(err),
|
||||||
|
})
|
||||||
|
return undefined
|
||||||
|
})
|
||||||
const workspace = result?.data
|
const workspace = result?.data
|
||||||
if (!workspace) {
|
if (!workspace) {
|
||||||
setCreating(undefined)
|
setCreating(undefined)
|
||||||
|
log.error("workspace create failed", {
|
||||||
|
type,
|
||||||
|
status: result?.response.status,
|
||||||
|
error: result?.error ? errorData(result.error) : undefined,
|
||||||
|
})
|
||||||
toast.show({
|
toast.show({
|
||||||
message: "Failed to create workspace",
|
message: `Failed to create workspace: ${errorMessage(result?.error ?? "no response")}`,
|
||||||
variant: "error",
|
variant: "error",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
log.info("workspace create response", {
|
||||||
|
type,
|
||||||
|
workspaceID: workspace.id,
|
||||||
|
status: result.response?.status,
|
||||||
|
})
|
||||||
await project.workspace.sync()
|
await project.workspace.sync()
|
||||||
|
log.info("workspace create synced", {
|
||||||
|
type,
|
||||||
|
workspaceID: workspace.id,
|
||||||
|
})
|
||||||
await props.onSelect(workspace.id)
|
await props.onSelect(workspace.id)
|
||||||
setCreating(undefined)
|
setCreating(undefined)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -611,8 +611,11 @@ export function Prompt(props: PromptProps) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let sessionID = props.sessionID
|
let sessionID = props.sessionID
|
||||||
|
console.log("jwl", sessionID)
|
||||||
if (sessionID == null) {
|
if (sessionID == null) {
|
||||||
|
console.log("creating")
|
||||||
const res = await sdk.client.session.create({
|
const res = await sdk.client.session.create({
|
||||||
|
workspace: props.workspaceID,
|
||||||
workspaceID: props.workspaceID,
|
workspaceID: props.workspaceID,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,12 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
||||||
const sdk = useSDK()
|
const sdk = useSDK()
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
|
||||||
|
function providers() {
|
||||||
|
return sync.data?.provider ?? []
|
||||||
|
}
|
||||||
|
|
||||||
function isModelValid(model: { providerID: string; modelID: string }) {
|
function isModelValid(model: { providerID: string; modelID: string }) {
|
||||||
const provider = sync.data.provider.find((x) => x.id === model.providerID)
|
const provider = providers().find((x) => x.id === model.providerID)
|
||||||
return !!provider?.models[model.modelID]
|
return !!provider?.models[model.modelID]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -178,7 +182,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = sync.data.provider[0]
|
const provider = providers()[0]
|
||||||
if (!provider) return undefined
|
if (!provider) return undefined
|
||||||
const defaultModel = sync.data.provider_default[provider.id]
|
const defaultModel = sync.data.provider_default[provider.id]
|
||||||
const firstModel = Object.values(provider.models)[0]
|
const firstModel = Object.values(provider.models)[0]
|
||||||
|
|
@ -221,7 +225,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
||||||
reasoning: false,
|
reasoning: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const provider = sync.data.provider.find((x) => x.id === value.providerID)
|
const provider = providers().find((x) => x.id === value.providerID)
|
||||||
const info = provider?.models[value.modelID]
|
const info = provider?.models[value.modelID]
|
||||||
return {
|
return {
|
||||||
provider: provider?.name ?? value.providerID,
|
provider: provider?.name ?? value.providerID,
|
||||||
|
|
@ -336,7 +340,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
||||||
list() {
|
list() {
|
||||||
const m = currentModel()
|
const m = currentModel()
|
||||||
if (!m) return []
|
if (!m) return []
|
||||||
const provider = sync.data.provider.find((x) => x.id === m.providerID)
|
const provider = providers().find((x) => x.id === m.providerID)
|
||||||
const info = provider?.models[m.modelID]
|
const info = provider?.models[m.modelID]
|
||||||
if (!info?.variants) return []
|
if (!info?.variants) return []
|
||||||
return Object.keys(info.variants)
|
return Object.keys(info.variants)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ import type { GlobalEvent, Event } from "@opencode-ai/sdk/v2"
|
||||||
import { createSimpleContext } from "./helper"
|
import { createSimpleContext } from "./helper"
|
||||||
import { createGlobalEmitter } from "@solid-primitives/event-bus"
|
import { createGlobalEmitter } from "@solid-primitives/event-bus"
|
||||||
import { batch, onCleanup, onMount } from "solid-js"
|
import { batch, onCleanup, onMount } from "solid-js"
|
||||||
|
import { Log } from "@/util/log"
|
||||||
|
import { errorData } from "@/util/error"
|
||||||
|
|
||||||
export type EventSource = {
|
export type EventSource = {
|
||||||
subscribe: (handler: (event: GlobalEvent) => void) => Promise<() => void>
|
subscribe: (handler: (event: GlobalEvent) => void) => Promise<() => void>
|
||||||
|
|
@ -19,13 +21,49 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
|
||||||
}) => {
|
}) => {
|
||||||
const abort = new AbortController()
|
const abort = new AbortController()
|
||||||
let sse: AbortController | undefined
|
let sse: AbortController | undefined
|
||||||
|
const log = Log.Default.clone().tag("service", "tui-sdk")
|
||||||
|
|
||||||
|
const raw = props.fetch ?? fetch
|
||||||
|
|
||||||
|
const traced: typeof fetch = async (input, init) => {
|
||||||
|
const req = input instanceof Request ? input : new Request(input, init)
|
||||||
|
const start = Date.now()
|
||||||
|
try {
|
||||||
|
const res = await raw(req)
|
||||||
|
const url = new URL(res.url || req.url)
|
||||||
|
if (!res.ok || url.searchParams.get("workspace")) {
|
||||||
|
const body = await res
|
||||||
|
.clone()
|
||||||
|
.text()
|
||||||
|
.catch(() => "")
|
||||||
|
log.info("sdk fetch", {
|
||||||
|
method: req.method,
|
||||||
|
request: req.url,
|
||||||
|
response: res.url || req.url,
|
||||||
|
status: res.status,
|
||||||
|
duration: Date.now() - start,
|
||||||
|
workspace: url.searchParams.get("workspace"),
|
||||||
|
body: body.slice(0, 1000),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
} catch (error) {
|
||||||
|
log.error("sdk fetch failed", {
|
||||||
|
method: req.method,
|
||||||
|
request: req.url,
|
||||||
|
duration: Date.now() - start,
|
||||||
|
error: errorData(error),
|
||||||
|
})
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function createSDK() {
|
function createSDK() {
|
||||||
return createOpencodeClient({
|
return createOpencodeClient({
|
||||||
baseUrl: props.url,
|
baseUrl: props.url,
|
||||||
signal: abort.signal,
|
signal: abort.signal,
|
||||||
directory: props.directory,
|
directory: props.directory,
|
||||||
fetch: props.fetch,
|
fetch: traced,
|
||||||
headers: props.headers,
|
headers: props.headers,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -109,7 +147,7 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
|
||||||
},
|
},
|
||||||
directory: props.directory,
|
directory: props.directory,
|
||||||
event: emitter,
|
event: emitter,
|
||||||
fetch: props.fetch ?? fetch,
|
fetch: traced,
|
||||||
url: props.url,
|
url: props.url,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import { useExit } from "./exit"
|
||||||
import { useArgs } from "./args"
|
import { useArgs } from "./args"
|
||||||
import { batch, createEffect, on } from "solid-js"
|
import { batch, createEffect, on } from "solid-js"
|
||||||
import { Log } from "@/util/log"
|
import { Log } from "@/util/log"
|
||||||
|
import { errorData } from "@/util/error"
|
||||||
import { ConsoleState, emptyConsoleState, type ConsoleState as ConsoleStateType } from "@/config/console-state"
|
import { ConsoleState, emptyConsoleState, type ConsoleState as ConsoleStateType } from "@/config/console-state"
|
||||||
|
|
||||||
export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||||
|
|
@ -107,11 +108,20 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||||
const event = useEvent()
|
const event = useEvent()
|
||||||
const project = useProject()
|
const project = useProject()
|
||||||
const sdk = useSDK()
|
const sdk = useSDK()
|
||||||
|
const log = Log.Default.clone().tag("service", "tui-sync")
|
||||||
|
|
||||||
event.subscribe((event) => {
|
event.subscribe((event) => {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case "server.instance.disposed":
|
case "server.instance.disposed":
|
||||||
bootstrap()
|
log.info("bootstrap triggered by instance disposal", {
|
||||||
|
workspace: project.workspace.current(),
|
||||||
|
})
|
||||||
|
void bootstrap().catch((error) => {
|
||||||
|
log.error("bootstrap after disposal failed", {
|
||||||
|
workspace: project.workspace.current(),
|
||||||
|
error: errorData(error),
|
||||||
|
})
|
||||||
|
})
|
||||||
break
|
break
|
||||||
case "permission.replied": {
|
case "permission.replied": {
|
||||||
const requests = store.permission[event.properties.sessionID]
|
const requests = store.permission[event.properties.sessionID]
|
||||||
|
|
@ -351,30 +361,63 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||||
const args = useArgs()
|
const args = useArgs()
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
console.log("bootstrapping")
|
|
||||||
const workspace = project.workspace.current()
|
const workspace = project.workspace.current()
|
||||||
const start = Date.now() - 30 * 24 * 60 * 60 * 1000
|
const start = Date.now() - 30 * 24 * 60 * 60 * 1000
|
||||||
|
log.info("bootstrap started", { workspace, path: project.instance.path() })
|
||||||
|
|
||||||
|
function track<T>(name: string, promise: Promise<T>) {
|
||||||
|
return promise
|
||||||
|
.then((value) => {
|
||||||
|
log.debug("bootstrap request ok", { name, workspace })
|
||||||
|
return value
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
log.error("bootstrap request failed", {
|
||||||
|
name,
|
||||||
|
workspace,
|
||||||
|
error: errorData(error),
|
||||||
|
})
|
||||||
|
throw error
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const sessionListPromise = sdk.client.session
|
const sessionListPromise = sdk.client.session
|
||||||
.list({ start: start })
|
.list({ start: start })
|
||||||
.then((x) => (x.data ?? []).toSorted((a, b) => a.id.localeCompare(b.id)))
|
.then((x) => (x.data ?? []).toSorted((a, b) => a.id.localeCompare(b.id)))
|
||||||
|
|
||||||
// blocking - include session.list when continuing a session
|
// blocking - include session.list when continuing a session
|
||||||
const providersPromise = sdk.client.config.providers({ workspace }, { throwOnError: true })
|
const providersPromise = track(
|
||||||
const providerListPromise = sdk.client.provider.list({ workspace }, { throwOnError: true })
|
"config.providers",
|
||||||
const consoleStatePromise = sdk.client.experimental.console
|
sdk.client.config.providers({ workspace }, { throwOnError: true }),
|
||||||
.get({ workspace }, { throwOnError: true })
|
)
|
||||||
.then((x) => ConsoleState.parse(x.data))
|
const providerListPromise = track(
|
||||||
.catch(() => emptyConsoleState)
|
"provider.list",
|
||||||
const agentsPromise = sdk.client.app.agents({ workspace }, { throwOnError: true })
|
sdk.client.provider.list({ workspace }, { throwOnError: true }),
|
||||||
const configPromise = sdk.client.config.get({ workspace }, { throwOnError: true })
|
)
|
||||||
const projectPromise = project.sync()
|
const consoleStatePromise = track(
|
||||||
|
"experimental.console.get",
|
||||||
|
sdk.client.experimental.console
|
||||||
|
.get({ workspace }, { throwOnError: true })
|
||||||
|
.then((x) => ConsoleState.parse(x.data))
|
||||||
|
.catch((error) => {
|
||||||
|
log.warn("console state unavailable", {
|
||||||
|
workspace,
|
||||||
|
error: errorData(error),
|
||||||
|
})
|
||||||
|
return emptyConsoleState
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
const agentsPromise = track("app.agents", sdk.client.app.agents({ workspace }, { throwOnError: true }))
|
||||||
|
const configPromise = track("config.get", sdk.client.config.get({ workspace }, { throwOnError: true }))
|
||||||
|
const projectPromise = track("project.sync", project.sync())
|
||||||
|
const sessionSyncPromise = track("session.list", sessionListPromise)
|
||||||
const blockingRequests: Promise<unknown>[] = [
|
const blockingRequests: Promise<unknown>[] = [
|
||||||
providersPromise,
|
providersPromise,
|
||||||
providerListPromise,
|
providerListPromise,
|
||||||
agentsPromise,
|
agentsPromise,
|
||||||
configPromise,
|
configPromise,
|
||||||
projectPromise,
|
projectPromise,
|
||||||
...(args.continue ? [sessionListPromise] : []),
|
...(args.continue ? [sessionSyncPromise] : []),
|
||||||
]
|
]
|
||||||
|
|
||||||
await Promise.all(blockingRequests)
|
await Promise.all(blockingRequests)
|
||||||
|
|
@ -384,7 +427,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||||
const consoleStateResponse = consoleStatePromise
|
const consoleStateResponse = consoleStatePromise
|
||||||
const agentsResponse = agentsPromise.then((x) => x.data ?? [])
|
const agentsResponse = agentsPromise.then((x) => x.data ?? [])
|
||||||
const configResponse = configPromise.then((x) => x.data!)
|
const configResponse = configPromise.then((x) => x.data!)
|
||||||
const sessionListResponse = args.continue ? sessionListPromise : undefined
|
const sessionListResponse = args.continue ? sessionSyncPromise : undefined
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
providersResponse,
|
providersResponse,
|
||||||
|
|
@ -414,34 +457,57 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (store.status !== "complete") setStore("status", "partial")
|
if (store.status !== "complete") setStore("status", "partial")
|
||||||
|
log.info("bootstrap partial", { workspace })
|
||||||
// non-blocking
|
// non-blocking
|
||||||
Promise.all([
|
Promise.all([
|
||||||
...(args.continue ? [] : [sessionListPromise.then((sessions) => setStore("session", reconcile(sessions)))]),
|
...(args.continue
|
||||||
consoleStatePromise.then((consoleState) => setStore("console_state", reconcile(consoleState))),
|
? []
|
||||||
sdk.client.command.list({ workspace }).then((x) => setStore("command", reconcile(x.data ?? []))),
|
: [
|
||||||
sdk.client.lsp.status({ workspace }).then((x) => setStore("lsp", reconcile(x.data!))),
|
track("session.list.background", sessionListPromise).then((sessions) =>
|
||||||
sdk.client.mcp.status({ workspace }).then((x) => setStore("mcp", reconcile(x.data!))),
|
setStore("session", reconcile(sessions)),
|
||||||
sdk.client.experimental.resource
|
),
|
||||||
.list({ workspace })
|
]),
|
||||||
.then((x) => setStore("mcp_resource", reconcile(x.data ?? {}))),
|
track("experimental.console.get.background", consoleStatePromise).then((consoleState) =>
|
||||||
sdk.client.formatter.status({ workspace }).then((x) => setStore("formatter", reconcile(x.data!))),
|
setStore("console_state", reconcile(consoleState)),
|
||||||
sdk.client.session.status({ workspace }).then((x) => {
|
),
|
||||||
|
track("command.list", sdk.client.command.list({ workspace })).then((x) =>
|
||||||
|
setStore("command", reconcile(x.data ?? [])),
|
||||||
|
),
|
||||||
|
track("lsp.status", sdk.client.lsp.status({ workspace })).then((x) => setStore("lsp", reconcile(x.data!))),
|
||||||
|
track("mcp.status", sdk.client.mcp.status({ workspace })).then((x) => setStore("mcp", reconcile(x.data!))),
|
||||||
|
track("experimental.resource.list", sdk.client.experimental.resource.list({ workspace })).then((x) =>
|
||||||
|
setStore("mcp_resource", reconcile(x.data ?? {})),
|
||||||
|
),
|
||||||
|
track("formatter.status", sdk.client.formatter.status({ workspace })).then((x) =>
|
||||||
|
setStore("formatter", reconcile(x.data!)),
|
||||||
|
),
|
||||||
|
track("session.status", sdk.client.session.status({ workspace })).then((x) => {
|
||||||
setStore("session_status", reconcile(x.data!))
|
setStore("session_status", reconcile(x.data!))
|
||||||
}),
|
}),
|
||||||
sdk.client.provider.auth({ workspace }).then((x) => setStore("provider_auth", reconcile(x.data ?? {}))),
|
track("provider.auth", sdk.client.provider.auth({ workspace })).then((x) =>
|
||||||
sdk.client.vcs.get({ workspace }).then((x) => setStore("vcs", reconcile(x.data))),
|
setStore("provider_auth", reconcile(x.data ?? {})),
|
||||||
project.workspace.sync(),
|
),
|
||||||
]).then(() => {
|
track("vcs.get", sdk.client.vcs.get({ workspace })).then((x) => setStore("vcs", reconcile(x.data))),
|
||||||
setStore("status", "complete")
|
track("project.workspace.sync", project.workspace.sync()),
|
||||||
})
|
])
|
||||||
|
.then(() => {
|
||||||
|
log.info("bootstrap complete", { workspace })
|
||||||
|
setStore("status", "complete")
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
log.error("bootstrap background failed", {
|
||||||
|
workspace,
|
||||||
|
error: errorData(error),
|
||||||
|
})
|
||||||
|
throw error
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.catch(async (e) => {
|
.catch((error) => {
|
||||||
Log.Default.error("tui bootstrap failed", {
|
log.error("bootstrap failed", {
|
||||||
error: e instanceof Error ? e.message : String(e),
|
workspace,
|
||||||
name: e instanceof Error ? e.name : undefined,
|
error: errorData(error),
|
||||||
stack: e instanceof Error ? e.stack : undefined,
|
|
||||||
})
|
})
|
||||||
await exit(e)
|
throw error
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -451,7 +517,15 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||||
() => project.workspace.current(),
|
() => project.workspace.current(),
|
||||||
() => {
|
() => {
|
||||||
fullSyncedSessions.clear()
|
fullSyncedSessions.clear()
|
||||||
void bootstrap()
|
log.info("bootstrap triggered by workspace change", {
|
||||||
|
workspace: project.workspace.current(),
|
||||||
|
})
|
||||||
|
void bootstrap().catch((error) => {
|
||||||
|
log.error("bootstrap effect failed", {
|
||||||
|
workspace: project.workspace.current(),
|
||||||
|
error: errorData(error),
|
||||||
|
})
|
||||||
|
})
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -474,6 +548,13 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||||
if (match.found) return store.session[match.index]
|
if (match.found) return store.session[match.index]
|
||||||
return undefined
|
return undefined
|
||||||
},
|
},
|
||||||
|
async refresh() {
|
||||||
|
const start = Date.now() - 30 * 24 * 60 * 60 * 1000
|
||||||
|
const sessions = await sdk.client.session
|
||||||
|
.list({ start })
|
||||||
|
.then((x) => (x.data ?? []).toSorted((a, b) => a.id.localeCompare(b.id)))
|
||||||
|
setStore("session", reconcile(sessions))
|
||||||
|
},
|
||||||
status(sessionID: string) {
|
status(sessionID: string) {
|
||||||
const session = result.session.get(sessionID)
|
const session = result.session.get(sessionID)
|
||||||
if (!session) return "idle"
|
if (!session) return "idle"
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
import { setTimeout as sleep } from "node:timers/promises"
|
import { setTimeout as sleep } from "node:timers/promises"
|
||||||
import { fn } from "@/util/fn"
|
import { fn } from "@/util/fn"
|
||||||
import { Database, eq } from "@/storage/db"
|
import { Database, asc, eq } from "@/storage/db"
|
||||||
import { Project } from "@/project/project"
|
import { Project } from "@/project/project"
|
||||||
import { BusEvent } from "@/bus/bus-event"
|
import { BusEvent } from "@/bus/bus-event"
|
||||||
import { GlobalBus } from "@/bus/global"
|
import { GlobalBus } from "@/bus/global"
|
||||||
import { SyncEvent } from "@/sync"
|
import { SyncEvent } from "@/sync"
|
||||||
|
import { EventTable } from "@/sync/event.sql"
|
||||||
|
import { EventID } from "@/sync/schema"
|
||||||
import { Log } from "@/util/log"
|
import { Log } from "@/util/log"
|
||||||
import { Filesystem } from "@/util/filesystem"
|
import { Filesystem } from "@/util/filesystem"
|
||||||
import { ProjectID } from "@/project/schema"
|
import { ProjectID } from "@/project/schema"
|
||||||
|
|
@ -15,6 +17,10 @@ import { getAdaptor } from "./adaptors"
|
||||||
import { WorkspaceInfo } from "./types"
|
import { WorkspaceInfo } from "./types"
|
||||||
import { WorkspaceID } from "./schema"
|
import { WorkspaceID } from "./schema"
|
||||||
import { parseSSE } from "./sse"
|
import { parseSSE } from "./sse"
|
||||||
|
import { Session } from "@/session"
|
||||||
|
import { SessionTable } from "@/session/session.sql"
|
||||||
|
import { SessionID } from "@/session/schema"
|
||||||
|
import { errorData } from "@/util/error"
|
||||||
|
|
||||||
export namespace Workspace {
|
export namespace Workspace {
|
||||||
export const Info = WorkspaceInfo.meta({
|
export const Info = WorkspaceInfo.meta({
|
||||||
|
|
@ -29,6 +35,13 @@ export namespace Workspace {
|
||||||
})
|
})
|
||||||
export type ConnectionStatus = z.infer<typeof ConnectionStatus>
|
export type ConnectionStatus = z.infer<typeof ConnectionStatus>
|
||||||
|
|
||||||
|
const Restore = z.object({
|
||||||
|
workspaceID: WorkspaceID.zod,
|
||||||
|
sessionID: SessionID.zod,
|
||||||
|
total: z.number().int().min(0),
|
||||||
|
step: z.number().int().min(0),
|
||||||
|
})
|
||||||
|
|
||||||
export const Event = {
|
export const Event = {
|
||||||
Ready: BusEvent.define(
|
Ready: BusEvent.define(
|
||||||
"workspace.ready",
|
"workspace.ready",
|
||||||
|
|
@ -42,6 +55,7 @@ export namespace Workspace {
|
||||||
message: z.string(),
|
message: z.string(),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
Restore: BusEvent.define("workspace.restore", Restore),
|
||||||
Status: BusEvent.define("workspace.status", ConnectionStatus),
|
Status: BusEvent.define("workspace.status", ConnectionStatus),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,16 +111,186 @@ export namespace Workspace {
|
||||||
|
|
||||||
await adaptor.create(config)
|
await adaptor.create(config)
|
||||||
|
|
||||||
|
console.log("starting sync")
|
||||||
startSync(info)
|
startSync(info)
|
||||||
|
|
||||||
return info
|
return info
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const SessionRestoreInput = z.object({
|
||||||
|
workspaceID: WorkspaceID.zod,
|
||||||
|
sessionID: SessionID.zod,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const sessionRestore = fn(SessionRestoreInput, async (input) => {
|
||||||
|
log.info("session restore requested", {
|
||||||
|
workspaceID: input.workspaceID,
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
const space = await get(input.workspaceID)
|
||||||
|
if (!space) throw new Error(`Workspace not found: ${input.workspaceID}`)
|
||||||
|
|
||||||
|
const adaptor = await getAdaptor(space.projectID, space.type)
|
||||||
|
const target = await adaptor.target(space)
|
||||||
|
|
||||||
|
const rows = Database.use((db) =>
|
||||||
|
db
|
||||||
|
.select({
|
||||||
|
id: EventTable.id,
|
||||||
|
aggregateID: EventTable.aggregate_id,
|
||||||
|
seq: EventTable.seq,
|
||||||
|
type: EventTable.type,
|
||||||
|
data: EventTable.data,
|
||||||
|
})
|
||||||
|
.from(EventTable)
|
||||||
|
.where(eq(EventTable.aggregate_id, input.sessionID))
|
||||||
|
.orderBy(asc(EventTable.seq))
|
||||||
|
.all(),
|
||||||
|
)
|
||||||
|
if (rows.length === 0) throw new Error(`No events found for session: ${input.sessionID}`)
|
||||||
|
|
||||||
|
const next = rows.at(-1)!.seq + 1
|
||||||
|
const all = [
|
||||||
|
...rows,
|
||||||
|
{
|
||||||
|
id: EventID.ascending(),
|
||||||
|
aggregateID: input.sessionID,
|
||||||
|
seq: next,
|
||||||
|
type: SyncEvent.versionedType(Session.Event.Updated.type, Session.Event.Updated.version),
|
||||||
|
data: {
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
info: {
|
||||||
|
workspaceID: input.workspaceID,
|
||||||
|
time: {
|
||||||
|
updated: Date.now(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const size = 10
|
||||||
|
const sets = Array.from({ length: Math.ceil(all.length / size) }, (_, i) => all.slice(i * size, (i + 1) * size))
|
||||||
|
const total = sets.length
|
||||||
|
log.info("session restore prepared", {
|
||||||
|
workspaceID: input.workspaceID,
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
workspaceType: space.type,
|
||||||
|
directory: space.directory,
|
||||||
|
target: target.type === "remote" ? String(route(target.url, "/sync/replay")) : target.directory,
|
||||||
|
events: all.length,
|
||||||
|
batches: total,
|
||||||
|
first: all[0]?.seq,
|
||||||
|
last: all.at(-1)?.seq,
|
||||||
|
})
|
||||||
|
GlobalBus.emit("event", {
|
||||||
|
directory: "global",
|
||||||
|
workspace: input.workspaceID,
|
||||||
|
payload: {
|
||||||
|
type: Event.Restore.type,
|
||||||
|
properties: {
|
||||||
|
workspaceID: input.workspaceID,
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
total,
|
||||||
|
step: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
for (const [i, events] of sets.entries()) {
|
||||||
|
log.info("session restore batch starting", {
|
||||||
|
workspaceID: input.workspaceID,
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
step: i + 1,
|
||||||
|
total,
|
||||||
|
events: events.length,
|
||||||
|
first: events[0]?.seq,
|
||||||
|
last: events.at(-1)?.seq,
|
||||||
|
target: target.type === "remote" ? String(route(target.url, "/sync/replay")) : target.directory,
|
||||||
|
})
|
||||||
|
if (target.type === "local") {
|
||||||
|
SyncEvent.replayAll(events)
|
||||||
|
log.info("session restore batch replayed locally", {
|
||||||
|
workspaceID: input.workspaceID,
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
step: i + 1,
|
||||||
|
total,
|
||||||
|
events: events.length,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const url = route(target.url, "/sync/replay")
|
||||||
|
const headers = new Headers(target.headers)
|
||||||
|
headers.set("content-type", "application/json")
|
||||||
|
const res = await fetch(url, {
|
||||||
|
method: "POST",
|
||||||
|
headers,
|
||||||
|
body: JSON.stringify({
|
||||||
|
directory: space.directory ?? "",
|
||||||
|
events,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
if (!res.ok) {
|
||||||
|
const body = await res.text()
|
||||||
|
log.error("session restore batch failed", {
|
||||||
|
workspaceID: input.workspaceID,
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
step: i + 1,
|
||||||
|
total,
|
||||||
|
status: res.status,
|
||||||
|
body,
|
||||||
|
})
|
||||||
|
throw new Error(
|
||||||
|
`Failed to replay session ${input.sessionID} into workspace ${input.workspaceID}: HTTP ${res.status} ${body}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
log.info("session restore batch posted", {
|
||||||
|
workspaceID: input.workspaceID,
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
step: i + 1,
|
||||||
|
total,
|
||||||
|
status: res.status,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
GlobalBus.emit("event", {
|
||||||
|
directory: "global",
|
||||||
|
workspace: input.workspaceID,
|
||||||
|
payload: {
|
||||||
|
type: Event.Restore.type,
|
||||||
|
properties: {
|
||||||
|
workspaceID: input.workspaceID,
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
total,
|
||||||
|
step: i + 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("session restore complete", {
|
||||||
|
workspaceID: input.workspaceID,
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
batches: total,
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
total,
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
log.error("session restore failed", {
|
||||||
|
workspaceID: input.workspaceID,
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
error: errorData(err),
|
||||||
|
})
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
export function list(project: Project.Info) {
|
export function list(project: Project.Info) {
|
||||||
const rows = Database.use((db) =>
|
const rows = Database.use((db) =>
|
||||||
db.select().from(WorkspaceTable).where(eq(WorkspaceTable.project_id, project.id)).all(),
|
db.select().from(WorkspaceTable).where(eq(WorkspaceTable.project_id, project.id)).all(),
|
||||||
)
|
)
|
||||||
const spaces = rows.map(fromRow).sort((a, b) => a.id.localeCompare(b.id))
|
const spaces = rows.map(fromRow).sort((a, b) => a.id.localeCompare(b.id))
|
||||||
|
|
||||||
for (const space of spaces) startSync(space)
|
for (const space of spaces) startSync(space)
|
||||||
return spaces
|
return spaces
|
||||||
}
|
}
|
||||||
|
|
@ -120,13 +304,25 @@ export namespace Workspace {
|
||||||
})
|
})
|
||||||
|
|
||||||
export const remove = fn(WorkspaceID.zod, async (id) => {
|
export const remove = fn(WorkspaceID.zod, async (id) => {
|
||||||
|
const sessions = Database.use((db) =>
|
||||||
|
db.select({ id: SessionTable.id }).from(SessionTable).where(eq(SessionTable.workspace_id, id)).all(),
|
||||||
|
)
|
||||||
|
for (const session of sessions) {
|
||||||
|
await Session.remove(session.id)
|
||||||
|
}
|
||||||
|
|
||||||
const row = Database.use((db) => db.select().from(WorkspaceTable).where(eq(WorkspaceTable.id, id)).get())
|
const row = Database.use((db) => db.select().from(WorkspaceTable).where(eq(WorkspaceTable.id, id)).get())
|
||||||
|
|
||||||
if (row) {
|
if (row) {
|
||||||
stopSync(id)
|
stopSync(id)
|
||||||
|
|
||||||
const info = fromRow(row)
|
const info = fromRow(row)
|
||||||
const adaptor = await getAdaptor(info.projectID, row.type)
|
try {
|
||||||
adaptor.remove(info)
|
const adaptor = await getAdaptor(info.projectID, row.type)
|
||||||
|
await adaptor.remove(info)
|
||||||
|
} catch (err) {
|
||||||
|
log.error("adaptor not available when removing workspace", { type: row.type })
|
||||||
|
}
|
||||||
Database.use((db) => db.delete(WorkspaceTable).where(eq(WorkspaceTable.id, id)).run())
|
Database.use((db) => db.delete(WorkspaceTable).where(eq(WorkspaceTable.id, id)).run())
|
||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
@ -156,47 +352,75 @@ export namespace Workspace {
|
||||||
|
|
||||||
const log = Log.create({ service: "workspace-sync" })
|
const log = Log.create({ service: "workspace-sync" })
|
||||||
|
|
||||||
async function workspaceEventLoop(space: Info, signal: AbortSignal) {
|
function route(url: string | URL, path: string) {
|
||||||
log.info("starting sync: " + space.id)
|
const next = new URL(url)
|
||||||
|
next.pathname = `${next.pathname.replace(/\/$/, "")}${path}`
|
||||||
|
next.search = ""
|
||||||
|
next.hash = ""
|
||||||
|
return next
|
||||||
|
}
|
||||||
|
|
||||||
|
async function globalEventLoop(space: Info, signal: AbortSignal) {
|
||||||
while (!signal.aborted) {
|
while (!signal.aborted) {
|
||||||
log.info("connecting to sync: " + space.id)
|
console.log("connecting to global sync", { workspace: space.name })
|
||||||
|
|
||||||
setStatus(space.id, "connecting")
|
|
||||||
const adaptor = await getAdaptor(space.projectID, space.type)
|
const adaptor = await getAdaptor(space.projectID, space.type)
|
||||||
const target = await adaptor.target(space)
|
const target = await adaptor.target(space)
|
||||||
|
|
||||||
if (target.type === "local") return
|
if (target.type === "local") return
|
||||||
|
|
||||||
const res = await fetch(target.url + "/sync/event", { method: "GET", signal }).catch((err: unknown) => {
|
const res = await fetch(route(target.url, "/global/event"), {
|
||||||
setStatus(space.id, "error", String(err))
|
method: "GET",
|
||||||
|
headers: target.headers,
|
||||||
|
signal,
|
||||||
|
}).catch((err: unknown) => {
|
||||||
|
setStatus(space.id, "error")
|
||||||
|
|
||||||
|
console.log("failed to connect to global sync", {
|
||||||
|
workspace: space.name,
|
||||||
|
error: err,
|
||||||
|
})
|
||||||
return undefined
|
return undefined
|
||||||
})
|
})
|
||||||
if (!res || !res.ok || !res.body) {
|
|
||||||
log.info("failed to connect to sync: " + res?.status)
|
|
||||||
|
|
||||||
setStatus(space.id, "error", res ? `HTTP ${res.status}` : "no response")
|
if (!res || !res.ok || !res.body) {
|
||||||
await sleep(1000)
|
console.log("failed to connect to global sync", { workspace: space.name })
|
||||||
|
await sleep(50000)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
setStatus(space.id, "connected")
|
|
||||||
await parseSSE(res.body, signal, (evt) => {
|
|
||||||
const event = evt as SyncEvent.SerializedEvent
|
|
||||||
|
|
||||||
|
console.log("global sync connected", { workspace: space.name })
|
||||||
|
setStatus(space.id, "connected")
|
||||||
|
|
||||||
|
await parseSSE(res.body, signal, (evt: any) => {
|
||||||
try {
|
try {
|
||||||
if (!event.type.startsWith("server.")) {
|
if (!("payload" in evt)) return
|
||||||
SyncEvent.replay(event)
|
|
||||||
|
if (!evt.payload.type.startsWith("server.")) {
|
||||||
|
console.log("received workspace sse event", evt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (evt.payload.type === "sync") {
|
||||||
|
// This name -> type is temporary
|
||||||
|
SyncEvent.replay({ ...evt.payload, type: evt.payload.name } as SyncEvent.SerializedEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalBus.emit("event", {
|
||||||
|
directory: evt.directory,
|
||||||
|
project: evt.project,
|
||||||
|
workspace: space.id,
|
||||||
|
payload: evt.payload,
|
||||||
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log.warn("failed to replay sync event", {
|
console.log("failed to replay global event", {
|
||||||
workspaceID: space.id,
|
workspaceID: space.id,
|
||||||
error: err,
|
error: err,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
setStatus(space.id, "disconnected")
|
|
||||||
log.info("disconnected to sync: " + space.id)
|
log.info("disconnected from global sync: " + space.id)
|
||||||
await sleep(250)
|
await sleep(50000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -213,9 +437,9 @@ export namespace Workspace {
|
||||||
aborts.set(space.id, abort)
|
aborts.set(space.id, abort)
|
||||||
setStatus(space.id, "disconnected")
|
setStatus(space.id, "disconnected")
|
||||||
|
|
||||||
void workspaceEventLoop(space, abort.signal).catch((error) => {
|
void globalEventLoop(space, abort.signal).catch((error) => {
|
||||||
setStatus(space.id, "error", String(error))
|
setStatus(space.id, "error", String(error))
|
||||||
log.warn("workspace sync listener failed", {
|
log.warn("workspace listener failed", {
|
||||||
workspaceID: space.id,
|
workspaceID: space.id,
|
||||||
error,
|
error,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,7 @@ export namespace PluginLoader {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return { ok: false, error }
|
return { ok: false, error }
|
||||||
}
|
}
|
||||||
|
console.log('mod', mod)
|
||||||
if (!mod) return { ok: false, error: new Error(`Plugin ${row.spec} module is empty`) }
|
if (!mod) return { ok: false, error: new Error(`Plugin ${row.spec} module is empty`) }
|
||||||
return { ok: true, value: { ...row, mod } }
|
return { ok: true, value: { ...row, mod } }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,26 @@ import { Session } from "@/session"
|
||||||
import { SessionID } from "@/session/schema"
|
import { SessionID } from "@/session/schema"
|
||||||
import { WorkspaceContext } from "@/control-plane/workspace-context"
|
import { WorkspaceContext } from "@/control-plane/workspace-context"
|
||||||
import { AppRuntime } from "@/effect/app-runtime"
|
import { AppRuntime } from "@/effect/app-runtime"
|
||||||
|
import { Log } from "@/util/log"
|
||||||
|
|
||||||
type Rule = { method?: string; path: string; exact?: boolean; action: "local" | "forward" }
|
type Rule = { method?: string; path: string; exact?: boolean; action: "local" | "forward" }
|
||||||
|
|
||||||
|
const hop = new Set([
|
||||||
|
"connection",
|
||||||
|
"keep-alive",
|
||||||
|
"proxy-authenticate",
|
||||||
|
"proxy-authorization",
|
||||||
|
"proxy-connection",
|
||||||
|
"te",
|
||||||
|
"trailer",
|
||||||
|
"transfer-encoding",
|
||||||
|
"upgrade",
|
||||||
|
"host",
|
||||||
|
"content-length",
|
||||||
|
])
|
||||||
|
|
||||||
|
const IS_WORKSPACE = process.env.OPENCODE_WORKSPACE === "true"
|
||||||
|
|
||||||
const RULES: Array<Rule> = [
|
const RULES: Array<Rule> = [
|
||||||
{ path: "/session/status", action: "forward" },
|
{ path: "/session/status", action: "forward" },
|
||||||
{ method: "GET", path: "/session", action: "local" },
|
{ method: "GET", path: "/session", action: "local" },
|
||||||
|
|
@ -37,6 +54,40 @@ function getSessionID(url: URL) {
|
||||||
return SessionID.make(id)
|
return SessionID.make(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sh(value: string) {
|
||||||
|
return `'${value.replace(/'/g, `'"'"'`)}'`
|
||||||
|
}
|
||||||
|
|
||||||
|
async function curl(url: URL, extra: HeadersInit | undefined, req: Request) {
|
||||||
|
const headers = new Headers(req.headers)
|
||||||
|
for (const key of hop) headers.delete(key)
|
||||||
|
headers.delete("accept-encoding")
|
||||||
|
headers.delete("x-opencode-directory")
|
||||||
|
headers.delete("x-opencode-workspace")
|
||||||
|
|
||||||
|
if (extra) {
|
||||||
|
for (const [key, value] of new Headers(extra).entries()) {
|
||||||
|
headers.set(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const parts = ["curl", "-X", req.method]
|
||||||
|
for (const [key, value] of headers.entries()) {
|
||||||
|
parts.push("-H", sh(`${key}: ${value}`))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.method !== "GET" && req.method !== "HEAD") {
|
||||||
|
const body = await req
|
||||||
|
.clone()
|
||||||
|
.text()
|
||||||
|
.catch(() => "")
|
||||||
|
if (body) parts.push("--data-binary", sh(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
parts.push(sh(url.toString()))
|
||||||
|
return parts.join(" ")
|
||||||
|
}
|
||||||
|
|
||||||
async function getSessionWorkspace(url: URL) {
|
async function getSessionWorkspace(url: URL) {
|
||||||
const id = getSessionID(url)
|
const id = getSessionID(url)
|
||||||
if (!id) return null
|
if (!id) return null
|
||||||
|
|
@ -46,6 +97,8 @@ async function getSessionWorkspace(url: URL) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function WorkspaceRouterMiddleware(upgrade: UpgradeWebSocket): MiddlewareHandler {
|
export function WorkspaceRouterMiddleware(upgrade: UpgradeWebSocket): MiddlewareHandler {
|
||||||
|
const log = Log.Default.clone().tag("service", "workspace-router")
|
||||||
|
|
||||||
return async (c, next) => {
|
return async (c, next) => {
|
||||||
const raw = c.req.query("directory") || c.req.header("x-opencode-directory") || process.cwd()
|
const raw = c.req.query("directory") || c.req.header("x-opencode-directory") || process.cwd()
|
||||||
const directory = Filesystem.resolve(
|
const directory = Filesystem.resolve(
|
||||||
|
|
@ -64,7 +117,7 @@ export function WorkspaceRouterMiddleware(upgrade: UpgradeWebSocket): Middleware
|
||||||
const workspaceID = sessionWorkspaceID || url.searchParams.get("workspace")
|
const workspaceID = sessionWorkspaceID || url.searchParams.get("workspace")
|
||||||
|
|
||||||
// If no workspace is provided we use the project
|
// If no workspace is provided we use the project
|
||||||
if (!workspaceID) {
|
if (!workspaceID || url.pathname.startsWith("/console") || IS_WORKSPACE) {
|
||||||
return Instance.provide({
|
return Instance.provide({
|
||||||
directory,
|
directory,
|
||||||
init: () => AppRuntime.runPromise(InstanceBootstrap),
|
init: () => AppRuntime.runPromise(InstanceBootstrap),
|
||||||
|
|
@ -77,16 +130,6 @@ export function WorkspaceRouterMiddleware(upgrade: UpgradeWebSocket): Middleware
|
||||||
const workspace = await Workspace.get(WorkspaceID.make(workspaceID))
|
const workspace = await Workspace.get(WorkspaceID.make(workspaceID))
|
||||||
|
|
||||||
if (!workspace) {
|
if (!workspace) {
|
||||||
// Special-case deleting a session in case user's data in a
|
|
||||||
// weird state. Allow them to forcefully delete a synced session
|
|
||||||
// even if the remote workspace is not in their data.
|
|
||||||
//
|
|
||||||
// The lets the `DELETE /session/:id` endpoint through and we've
|
|
||||||
// made sure that it will run without an instance
|
|
||||||
if (url.pathname.match(/\/session\/[^/]+$/) && c.req.method === "DELETE") {
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Response(`Workspace not found: ${workspaceID}`, {
|
return new Response(`Workspace not found: ${workspaceID}`, {
|
||||||
status: 500,
|
status: 500,
|
||||||
headers: {
|
headers: {
|
||||||
|
|
@ -98,6 +141,13 @@ export function WorkspaceRouterMiddleware(upgrade: UpgradeWebSocket): Middleware
|
||||||
const adaptor = await getAdaptor(workspace.projectID, workspace.type)
|
const adaptor = await getAdaptor(workspace.projectID, workspace.type)
|
||||||
const target = await adaptor.target(workspace)
|
const target = await adaptor.target(workspace)
|
||||||
|
|
||||||
|
log.info("workspace route resolved", {
|
||||||
|
workspaceID,
|
||||||
|
workspace_type: workspace.type,
|
||||||
|
target_type: target.type,
|
||||||
|
request: url.toString(),
|
||||||
|
})
|
||||||
|
|
||||||
if (target.type === "local") {
|
if (target.type === "local") {
|
||||||
return WorkspaceContext.provide({
|
return WorkspaceContext.provide({
|
||||||
workspaceID: WorkspaceID.make(workspaceID),
|
workspaceID: WorkspaceID.make(workspaceID),
|
||||||
|
|
@ -118,18 +168,29 @@ export function WorkspaceRouterMiddleware(upgrade: UpgradeWebSocket): Middleware
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const proxyURL = new URL(target.url)
|
||||||
|
proxyURL.pathname = `${proxyURL.pathname.replace(/\/$/, "")}${url.pathname}`
|
||||||
|
proxyURL.search = url.search
|
||||||
|
proxyURL.hash = url.hash
|
||||||
|
proxyURL.searchParams.delete("workspace")
|
||||||
|
|
||||||
|
log.info("workspace proxy forwarding", {
|
||||||
|
workspaceID,
|
||||||
|
request: url.toString(),
|
||||||
|
target: String(target.url),
|
||||||
|
proxy: proxyURL.toString(),
|
||||||
|
})
|
||||||
|
|
||||||
if (c.req.header("upgrade")?.toLowerCase() === "websocket") {
|
if (c.req.header("upgrade")?.toLowerCase() === "websocket") {
|
||||||
return ServerProxy.websocket(upgrade, target, c.req.raw, c.env)
|
return ServerProxy.websocket(upgrade, proxyURL, target.headers, c.req.raw, c.env)
|
||||||
}
|
}
|
||||||
|
|
||||||
const headers = new Headers(c.req.raw.headers)
|
const headers = new Headers(c.req.raw.headers)
|
||||||
headers.delete("x-opencode-workspace")
|
headers.delete("x-opencode-workspace")
|
||||||
|
|
||||||
return ServerProxy.http(
|
const req = new Request(c.req.raw, { headers })
|
||||||
target,
|
console.log("workspace proxy curl", await curl(proxyURL, target.headers, req))
|
||||||
new Request(c.req.raw, {
|
|
||||||
headers,
|
return ServerProxy.http(proxyURL, target.headers, req)
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
118
packages/opencode/src/server/instance/sync.ts
Normal file
118
packages/opencode/src/server/instance/sync.ts
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
import z from "zod"
|
||||||
|
import { Hono } from "hono"
|
||||||
|
import { describeRoute, validator, resolver } from "hono-openapi"
|
||||||
|
import { SyncEvent } from "@/sync"
|
||||||
|
import { Database, asc, and, not, or, lte, eq } from "@/storage/db"
|
||||||
|
import { EventTable } from "@/sync/event.sql"
|
||||||
|
import { lazy } from "@/util/lazy"
|
||||||
|
import { Log } from "@/util/log"
|
||||||
|
import { errors } from "../error"
|
||||||
|
|
||||||
|
const ReplayEvent = z.object({
|
||||||
|
id: z.string(),
|
||||||
|
aggregateID: z.string(),
|
||||||
|
seq: z.number().int().min(0),
|
||||||
|
type: z.string(),
|
||||||
|
data: z.record(z.string(), z.unknown()),
|
||||||
|
})
|
||||||
|
|
||||||
|
const log = Log.create({ service: "server.sync" })
|
||||||
|
|
||||||
|
export const SyncRoutes = lazy(() =>
|
||||||
|
new Hono()
|
||||||
|
.post(
|
||||||
|
"/replay",
|
||||||
|
describeRoute({
|
||||||
|
summary: "Replay sync events",
|
||||||
|
description: "Validate and replay a complete sync event history.",
|
||||||
|
operationId: "global.sync-replay",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Replayed sync events",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(
|
||||||
|
z.object({
|
||||||
|
sessionID: z.string(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...errors(400),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
validator(
|
||||||
|
"json",
|
||||||
|
z.object({
|
||||||
|
directory: z.string(),
|
||||||
|
events: z.array(ReplayEvent).min(1),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
async (c) => {
|
||||||
|
const body = c.req.valid("json")
|
||||||
|
const events = body.events
|
||||||
|
const source = events[0].aggregateID
|
||||||
|
log.info("sync replay requested", {
|
||||||
|
sessionID: source,
|
||||||
|
events: events.length,
|
||||||
|
first: events[0]?.seq,
|
||||||
|
last: events.at(-1)?.seq,
|
||||||
|
directory: body.directory,
|
||||||
|
})
|
||||||
|
SyncEvent.replayAll(events)
|
||||||
|
|
||||||
|
log.info("sync replay complete", {
|
||||||
|
sessionID: source,
|
||||||
|
events: events.length,
|
||||||
|
first: events[0]?.seq,
|
||||||
|
last: events.at(-1)?.seq,
|
||||||
|
})
|
||||||
|
|
||||||
|
return c.json({
|
||||||
|
sessionID: source,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.get(
|
||||||
|
"/history",
|
||||||
|
describeRoute({
|
||||||
|
summary: "List sync events",
|
||||||
|
description:
|
||||||
|
"List sync events for all aggregates. Keys are aggregate IDs the client already knows about, values are the last known sequence ID. Events with seq > value are returned for those aggregates. Aggregates not listed in the input get their full history.",
|
||||||
|
operationId: "global.sync-history.list",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Sync events",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(
|
||||||
|
z.array(
|
||||||
|
z.object({
|
||||||
|
id: z.string(),
|
||||||
|
aggregate_id: z.string(),
|
||||||
|
seq: z.number(),
|
||||||
|
type: z.string(),
|
||||||
|
data: z.record(z.string(), z.unknown()),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...errors(400),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
validator("json", z.record(z.string(), z.number().int().min(0))),
|
||||||
|
async (c) => {
|
||||||
|
const body = c.req.valid("json")
|
||||||
|
const exclude = Object.entries(body)
|
||||||
|
const where =
|
||||||
|
exclude.length > 0
|
||||||
|
? not(or(...exclude.map(([id, seq]) => and(eq(EventTable.aggregate_id, id), lte(EventTable.seq, seq))))!)
|
||||||
|
: undefined
|
||||||
|
const rows = Database.use((db) => db.select().from(EventTable).where(where).orderBy(asc(EventTable.seq)).all())
|
||||||
|
return c.json(rows)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
@ -6,6 +6,16 @@ import { Workspace } from "../../control-plane/workspace"
|
||||||
import { Instance } from "../../project/instance"
|
import { Instance } from "../../project/instance"
|
||||||
import { errors } from "../error"
|
import { errors } from "../error"
|
||||||
import { lazy } from "../../util/lazy"
|
import { lazy } from "../../util/lazy"
|
||||||
|
import { Log } from "@/util/log"
|
||||||
|
import { errorData } from "@/util/error"
|
||||||
|
|
||||||
|
const SessionRestoreBody = Workspace.sessionRestore.schema.omit({
|
||||||
|
workspaceID: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const SessionRestoreResult = z.object({
|
||||||
|
total: z.number().int().min(0),
|
||||||
|
})
|
||||||
|
|
||||||
const WorkspaceAdaptor = z.object({
|
const WorkspaceAdaptor = z.object({
|
||||||
type: z.string(),
|
type: z.string(),
|
||||||
|
|
@ -13,6 +23,8 @@ const WorkspaceAdaptor = z.object({
|
||||||
description: z.string(),
|
description: z.string(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const log = Log.create({ service: "server.workspace" })
|
||||||
|
|
||||||
export const WorkspaceRoutes = lazy(() =>
|
export const WorkspaceRoutes = lazy(() =>
|
||||||
new Hono()
|
new Hono()
|
||||||
.get(
|
.get(
|
||||||
|
|
@ -140,5 +152,59 @@ export const WorkspaceRoutes = lazy(() =>
|
||||||
const { id } = c.req.valid("param")
|
const { id } = c.req.valid("param")
|
||||||
return c.json(await Workspace.remove(id))
|
return c.json(await Workspace.remove(id))
|
||||||
},
|
},
|
||||||
|
)
|
||||||
|
.post(
|
||||||
|
"/:id/session-restore",
|
||||||
|
describeRoute({
|
||||||
|
summary: "Restore session into workspace",
|
||||||
|
description: "Replay a session's sync events into the target workspace in batches.",
|
||||||
|
operationId: "experimental.workspace.sessionRestore",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Session replay started",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(SessionRestoreResult),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...errors(400),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
validator(
|
||||||
|
"param",
|
||||||
|
z.object({
|
||||||
|
id: Workspace.Info.shape.id,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
validator("json", SessionRestoreBody),
|
||||||
|
async (c) => {
|
||||||
|
const { id } = c.req.valid("param")
|
||||||
|
const body = c.req.valid("json")
|
||||||
|
log.info("session restore route requested", {
|
||||||
|
workspaceID: id,
|
||||||
|
sessionID: body.sessionID,
|
||||||
|
directory: Instance.directory,
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
const result = await Workspace.sessionRestore({
|
||||||
|
workspaceID: id,
|
||||||
|
...body,
|
||||||
|
})
|
||||||
|
log.info("session restore route complete", {
|
||||||
|
workspaceID: id,
|
||||||
|
sessionID: body.sessionID,
|
||||||
|
total: result.total,
|
||||||
|
})
|
||||||
|
return c.json(result)
|
||||||
|
} catch (err) {
|
||||||
|
log.error("session restore route failed", {
|
||||||
|
workspaceID: id,
|
||||||
|
sessionID: body.sessionID,
|
||||||
|
error: errorData(err),
|
||||||
|
})
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ const zipped = compress()
|
||||||
export const CompressionMiddleware: MiddlewareHandler = (c, next) => {
|
export const CompressionMiddleware: MiddlewareHandler = (c, next) => {
|
||||||
const path = c.req.path
|
const path = c.req.path
|
||||||
const method = c.req.method
|
const method = c.req.method
|
||||||
if (path === "/event" || path === "/global/event" || path === "/global/sync-event") return next()
|
if (path === "/event" || path === "/global/event") return next()
|
||||||
if (method === "POST" && /\/session\/[^/]+\/(message|prompt_async)$/.test(path)) return next()
|
if (method === "POST" && /\/session\/[^/]+\/(message|prompt_async)$/.test(path)) return next()
|
||||||
return zipped(c, next)
|
return zipped(c, next)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import type { Target } from "@/control-plane/types"
|
|
||||||
import { Hono } from "hono"
|
import { Hono } from "hono"
|
||||||
import type { UpgradeWebSocket } from "hono/ws"
|
import type { UpgradeWebSocket } from "hono/ws"
|
||||||
|
import { Log } from "@/util/log"
|
||||||
|
|
||||||
const hop = new Set([
|
const hop = new Set([
|
||||||
"connection",
|
"connection",
|
||||||
|
|
@ -20,6 +20,7 @@ type Msg = string | ArrayBuffer | Uint8Array
|
||||||
function headers(req: Request, extra?: HeadersInit) {
|
function headers(req: Request, extra?: HeadersInit) {
|
||||||
const out = new Headers(req.headers)
|
const out = new Headers(req.headers)
|
||||||
for (const key of hop) out.delete(key)
|
for (const key of hop) out.delete(key)
|
||||||
|
out.delete("accept-encoding")
|
||||||
out.delete("x-opencode-directory")
|
out.delete("x-opencode-directory")
|
||||||
out.delete("x-opencode-workspace")
|
out.delete("x-opencode-workspace")
|
||||||
if (!extra) return out
|
if (!extra) return out
|
||||||
|
|
@ -98,31 +99,63 @@ const app = (upgrade: UpgradeWebSocket) =>
|
||||||
)
|
)
|
||||||
|
|
||||||
export namespace ServerProxy {
|
export namespace ServerProxy {
|
||||||
export function http(target: Extract<Target, { type: "remote" }>, req: Request) {
|
const log = Log.Default.clone().tag("service", "server-proxy")
|
||||||
|
|
||||||
|
export function http(url: string | URL, extra: HeadersInit | undefined, req: Request) {
|
||||||
|
console.log("proxy http request", {
|
||||||
|
method: req.method,
|
||||||
|
request: req.url,
|
||||||
|
url: String(url),
|
||||||
|
})
|
||||||
return fetch(
|
return fetch(
|
||||||
new Request(target.url, {
|
new Request(url, {
|
||||||
method: req.method,
|
method: req.method,
|
||||||
headers: headers(req, target.headers),
|
headers: headers(req, extra),
|
||||||
body: req.method === "GET" || req.method === "HEAD" ? undefined : req.body,
|
body: req.method === "GET" || req.method === "HEAD" ? undefined : req.body,
|
||||||
redirect: "manual",
|
redirect: "manual",
|
||||||
signal: req.signal,
|
signal: req.signal,
|
||||||
}),
|
}),
|
||||||
)
|
).then((res) => {
|
||||||
|
const next = new Headers(res.headers)
|
||||||
|
next.delete("content-encoding")
|
||||||
|
next.delete("content-length")
|
||||||
|
|
||||||
|
console.log("proxy http response", {
|
||||||
|
method: req.method,
|
||||||
|
request: req.url,
|
||||||
|
url: String(url),
|
||||||
|
status: res.status,
|
||||||
|
statusText: res.statusText,
|
||||||
|
})
|
||||||
|
return new Response(res.body, {
|
||||||
|
status: res.status,
|
||||||
|
statusText: res.statusText,
|
||||||
|
headers: next,
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function websocket(
|
export function websocket(
|
||||||
upgrade: UpgradeWebSocket,
|
upgrade: UpgradeWebSocket,
|
||||||
target: Extract<Target, { type: "remote" }>,
|
target: string | URL,
|
||||||
|
extra: HeadersInit | undefined,
|
||||||
req: Request,
|
req: Request,
|
||||||
env: unknown,
|
env: unknown,
|
||||||
) {
|
) {
|
||||||
const url = new URL(req.url)
|
const proxy = new URL(req.url)
|
||||||
url.pathname = "/__workspace_ws"
|
proxy.pathname = "/__workspace_ws"
|
||||||
url.search = ""
|
proxy.search = ""
|
||||||
const next = new Headers(req.headers)
|
const next = new Headers(req.headers)
|
||||||
next.set("x-opencode-proxy-url", socket(target.url))
|
next.set("x-opencode-proxy-url", socket(target))
|
||||||
|
for (const [key, value] of new Headers(extra).entries()) {
|
||||||
|
next.set(key, value)
|
||||||
|
}
|
||||||
|
log.info("proxy websocket", {
|
||||||
|
request: req.url,
|
||||||
|
target: String(target),
|
||||||
|
})
|
||||||
return app(upgrade).fetch(
|
return app(upgrade).fetch(
|
||||||
new Request(url, {
|
new Request(proxy, {
|
||||||
method: req.method,
|
method: req.method,
|
||||||
headers: next,
|
headers: next,
|
||||||
signal: req.signal,
|
signal: req.signal,
|
||||||
|
|
|
||||||
|
|
@ -199,6 +199,25 @@ export namespace SyncEvent {
|
||||||
process(def, event, { publish: !!options?.publish })
|
process(def, event, { publish: !!options?.publish })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function replayAll(events: SerializedEvent[], options?: { publish: boolean }) {
|
||||||
|
const source = events[0]?.aggregateID
|
||||||
|
if (!source) return
|
||||||
|
if (events.some((item) => item.aggregateID !== source)) {
|
||||||
|
throw new Error("Replay events must belong to the same session")
|
||||||
|
}
|
||||||
|
const start = events[0].seq
|
||||||
|
for (const [i, item] of events.entries()) {
|
||||||
|
const seq = start + i
|
||||||
|
if (item.seq !== seq) {
|
||||||
|
throw new Error(`Replay sequence mismatch at index ${i}: expected ${seq}, got ${item.seq}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const item of events) {
|
||||||
|
replay(item, options)
|
||||||
|
}
|
||||||
|
return source
|
||||||
|
}
|
||||||
|
|
||||||
export function run<Def extends Definition>(def: Def, data: Event<Def>["data"], options?: { publish?: boolean }) {
|
export function run<Def extends Definition>(def: Def, data: Event<Def>["data"], options?: { publish?: boolean }) {
|
||||||
const agg = (data as Record<string, string>)[def.aggregate]
|
const agg = (data as Record<string, string>)[def.aggregate]
|
||||||
// This should never happen: we've enforced it via typescript in
|
// This should never happen: we've enforced it via typescript in
|
||||||
|
|
|
||||||
89
packages/opencode/test/cli/tui/workspace-restore.test.ts
Normal file
89
packages/opencode/test/cli/tui/workspace-restore.test.ts
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
import { describe, expect, mock, test } from "bun:test"
|
||||||
|
import { restoreWorkspaceSession } from "../../../src/cli/cmd/tui/component/dialog-workspace-create"
|
||||||
|
|
||||||
|
describe("restoreWorkspaceSession", () => {
|
||||||
|
test("refreshes workspace and session data after a successful restore", async () => {
|
||||||
|
const sessionRestore = mock(async () => ({ data: { total: 2 } }))
|
||||||
|
const syncWorkspace = mock(async () => {})
|
||||||
|
const refresh = mock(async () => {})
|
||||||
|
const clear = mock(() => {})
|
||||||
|
const show = mock(() => {})
|
||||||
|
|
||||||
|
await restoreWorkspaceSession({
|
||||||
|
dialog: { clear } as any,
|
||||||
|
sdk: {
|
||||||
|
client: {
|
||||||
|
experimental: {
|
||||||
|
workspace: {
|
||||||
|
sessionRestore,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as any,
|
||||||
|
sync: {
|
||||||
|
session: {
|
||||||
|
refresh,
|
||||||
|
},
|
||||||
|
} as any,
|
||||||
|
project: {
|
||||||
|
workspace: {
|
||||||
|
sync: syncWorkspace,
|
||||||
|
},
|
||||||
|
} as any,
|
||||||
|
toast: { show } as any,
|
||||||
|
workspaceID: "wrk_1",
|
||||||
|
sessionID: "ses_1",
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(sessionRestore).toHaveBeenCalledWith({ id: "wrk_1", sessionID: "ses_1" })
|
||||||
|
expect(syncWorkspace).toHaveBeenCalledTimes(1)
|
||||||
|
expect(refresh).toHaveBeenCalledTimes(1)
|
||||||
|
expect(show).toHaveBeenCalledWith({
|
||||||
|
message: "Session restored into the new workspace",
|
||||||
|
variant: "success",
|
||||||
|
})
|
||||||
|
expect(clear).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("shows an error and keeps the dialog open when restore fails", async () => {
|
||||||
|
const sessionRestore = mock(async () => undefined)
|
||||||
|
const syncWorkspace = mock(async () => {})
|
||||||
|
const refresh = mock(async () => {})
|
||||||
|
const clear = mock(() => {})
|
||||||
|
const show = mock(() => {})
|
||||||
|
|
||||||
|
await restoreWorkspaceSession({
|
||||||
|
dialog: { clear } as any,
|
||||||
|
sdk: {
|
||||||
|
client: {
|
||||||
|
experimental: {
|
||||||
|
workspace: {
|
||||||
|
sessionRestore,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as any,
|
||||||
|
sync: {
|
||||||
|
session: {
|
||||||
|
refresh,
|
||||||
|
},
|
||||||
|
} as any,
|
||||||
|
project: {
|
||||||
|
workspace: {
|
||||||
|
sync: syncWorkspace,
|
||||||
|
},
|
||||||
|
} as any,
|
||||||
|
toast: { show } as any,
|
||||||
|
workspaceID: "wrk_1",
|
||||||
|
sessionID: "ses_1",
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(syncWorkspace).not.toHaveBeenCalled()
|
||||||
|
expect(refresh).not.toHaveBeenCalled()
|
||||||
|
expect(clear).not.toHaveBeenCalled()
|
||||||
|
expect(show).toHaveBeenCalledWith({
|
||||||
|
message: "Failed to restore session: no response",
|
||||||
|
variant: "error",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
268
packages/opencode/test/server/workspace-restore.test.ts
Normal file
268
packages/opencode/test/server/workspace-restore.test.ts
Normal file
|
|
@ -0,0 +1,268 @@
|
||||||
|
import { afterEach, beforeEach, describe, expect, mock, spyOn, test } from "bun:test"
|
||||||
|
import fs from "node:fs/promises"
|
||||||
|
import path from "node:path"
|
||||||
|
import { GlobalBus } from "../../src/bus/global"
|
||||||
|
import { registerAdaptor } from "../../src/control-plane/adaptors"
|
||||||
|
import type { WorkspaceAdaptor } from "../../src/control-plane/types"
|
||||||
|
import { Workspace } from "../../src/control-plane/workspace"
|
||||||
|
import { Flag } from "../../src/flag/flag"
|
||||||
|
import { ModelID, ProviderID } from "../../src/provider/schema"
|
||||||
|
import { Instance } from "../../src/project/instance"
|
||||||
|
import { Server } from "../../src/server/server"
|
||||||
|
import { Session } from "../../src/session"
|
||||||
|
import { MessageID, PartID, type SessionID } from "../../src/session/schema"
|
||||||
|
import { Database, asc, eq } from "../../src/storage/db"
|
||||||
|
import { SyncEvent } from "../../src/sync"
|
||||||
|
import { EventTable } from "../../src/sync/event.sql"
|
||||||
|
import { Log } from "../../src/util/log"
|
||||||
|
import { resetDatabase } from "../fixture/db"
|
||||||
|
import { tmpdir } from "../fixture/fixture"
|
||||||
|
|
||||||
|
Log.init({ print: false })
|
||||||
|
|
||||||
|
const original = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
Database.close()
|
||||||
|
// @ts-expect-error test override
|
||||||
|
Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = true
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
mock.restore()
|
||||||
|
// @ts-expect-error test override
|
||||||
|
Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = original
|
||||||
|
await resetDatabase()
|
||||||
|
})
|
||||||
|
|
||||||
|
async function user(sessionID: SessionID, text: string) {
|
||||||
|
const msg = await Session.updateMessage({
|
||||||
|
id: MessageID.ascending(),
|
||||||
|
role: "user",
|
||||||
|
sessionID,
|
||||||
|
agent: "build",
|
||||||
|
model: { providerID: ProviderID.make("test"), modelID: ModelID.make("test") },
|
||||||
|
time: { created: Date.now() },
|
||||||
|
})
|
||||||
|
await Session.updatePart({
|
||||||
|
id: PartID.ascending(),
|
||||||
|
sessionID,
|
||||||
|
messageID: msg.id,
|
||||||
|
type: "text",
|
||||||
|
text,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function remote(dir: string, url: string): WorkspaceAdaptor {
|
||||||
|
return {
|
||||||
|
name: "remote",
|
||||||
|
description: "remote",
|
||||||
|
configure(info) {
|
||||||
|
return {
|
||||||
|
...info,
|
||||||
|
directory: dir,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async create() {
|
||||||
|
await fs.mkdir(dir, { recursive: true })
|
||||||
|
},
|
||||||
|
async remove() {},
|
||||||
|
target() {
|
||||||
|
return {
|
||||||
|
type: "remote" as const,
|
||||||
|
url,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function local(dir: string): WorkspaceAdaptor {
|
||||||
|
return {
|
||||||
|
name: "local",
|
||||||
|
description: "local",
|
||||||
|
configure(info) {
|
||||||
|
return {
|
||||||
|
...info,
|
||||||
|
directory: dir,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async create() {
|
||||||
|
await fs.mkdir(dir, { recursive: true })
|
||||||
|
},
|
||||||
|
async remove() {},
|
||||||
|
target() {
|
||||||
|
return {
|
||||||
|
type: "local" as const,
|
||||||
|
directory: dir,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("workspace restore route", () => {
|
||||||
|
test("replays session events in batches of 10 and emits progress", async () => {
|
||||||
|
await using tmp = await tmpdir({ git: true })
|
||||||
|
const app = Server.Default().app
|
||||||
|
const dir = path.join(tmp.path, ".restore")
|
||||||
|
const seen: any[] = []
|
||||||
|
const posts: Array<{
|
||||||
|
path: string
|
||||||
|
body: { directory: string; events: Array<{ seq: number; aggregateID: string }> }
|
||||||
|
}> = []
|
||||||
|
const on = (evt: any) => seen.push(evt)
|
||||||
|
GlobalBus.on("event", on)
|
||||||
|
|
||||||
|
const raw = globalThis.fetch
|
||||||
|
const fetch = spyOn(globalThis, "fetch").mockImplementation(
|
||||||
|
Object.assign(
|
||||||
|
async (input: URL | RequestInfo, init?: BunFetchRequestInit | RequestInit) => {
|
||||||
|
const url = new URL(typeof input === "string" || input instanceof URL ? input : input.url)
|
||||||
|
posts.push({
|
||||||
|
path: url.pathname,
|
||||||
|
body: JSON.parse(String(init?.body)),
|
||||||
|
})
|
||||||
|
return Response.json({ sessionID: posts.at(-1)!.body.events[0].aggregateID })
|
||||||
|
},
|
||||||
|
{
|
||||||
|
preconnect: raw.preconnect?.bind(raw),
|
||||||
|
},
|
||||||
|
) as typeof globalThis.fetch,
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const setup = await Instance.provide({
|
||||||
|
directory: tmp.path,
|
||||||
|
fn: async () => {
|
||||||
|
registerAdaptor(Instance.project.id, "worktree", remote(dir, "https://workspace.test/base"))
|
||||||
|
const space = await Workspace.create({
|
||||||
|
type: "worktree",
|
||||||
|
branch: null,
|
||||||
|
extra: null,
|
||||||
|
projectID: Instance.project.id,
|
||||||
|
})
|
||||||
|
const session = await Session.create({})
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
await user(session.id, `msg ${i}`)
|
||||||
|
}
|
||||||
|
const rows = Database.use((db) =>
|
||||||
|
db
|
||||||
|
.select({ seq: EventTable.seq })
|
||||||
|
.from(EventTable)
|
||||||
|
.where(eq(EventTable.aggregate_id, session.id))
|
||||||
|
.orderBy(asc(EventTable.seq))
|
||||||
|
.all(),
|
||||||
|
)
|
||||||
|
return { space, session, rows }
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(setup.rows).toHaveLength(13)
|
||||||
|
|
||||||
|
const res = await app.request(`/experimental/workspace/${setup.space.id}/session-restore`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/json",
|
||||||
|
"x-opencode-directory": tmp.path,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
sessionID: setup.session.id,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(fetch).toHaveBeenCalledTimes(2)
|
||||||
|
expect(res.status).toBe(200)
|
||||||
|
expect(await res.json()).toEqual({ total: 2 })
|
||||||
|
expect(posts).toHaveLength(2)
|
||||||
|
expect(posts[0]?.path).toBe("/base/sync/replay")
|
||||||
|
expect(posts[1]?.path).toBe("/base/sync/replay")
|
||||||
|
expect(posts[0]?.body.directory).toBe(dir)
|
||||||
|
expect(posts[1]?.body.directory).toBe(dir)
|
||||||
|
expect(posts[0]?.body.events).toHaveLength(10)
|
||||||
|
expect(posts[1]?.body.events).toHaveLength(4)
|
||||||
|
expect(posts.flatMap((item) => item.body.events.map((event) => event.seq))).toEqual([
|
||||||
|
...setup.rows.map((row) => row.seq),
|
||||||
|
setup.rows.at(-1)!.seq + 1,
|
||||||
|
])
|
||||||
|
expect(posts[1]?.body.events.at(-1)).toMatchObject({
|
||||||
|
aggregateID: setup.session.id,
|
||||||
|
seq: setup.rows.at(-1)!.seq + 1,
|
||||||
|
type: SyncEvent.versionedType(Session.Event.Updated.type, Session.Event.Updated.version),
|
||||||
|
data: {
|
||||||
|
sessionID: setup.session.id,
|
||||||
|
info: {
|
||||||
|
workspaceID: setup.space.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const restore = seen.filter(
|
||||||
|
(evt) => evt.workspace === setup.space.id && evt.payload.type === Workspace.Event.Restore.type,
|
||||||
|
)
|
||||||
|
expect(restore.map((evt) => evt.payload.properties.step)).toEqual([0, 1, 2])
|
||||||
|
expect(restore.map((evt) => evt.payload.properties.total)).toEqual([2, 2, 2])
|
||||||
|
expect(restore.map((evt) => evt.payload.properties.sessionID)).toEqual([
|
||||||
|
setup.session.id,
|
||||||
|
setup.session.id,
|
||||||
|
setup.session.id,
|
||||||
|
])
|
||||||
|
} finally {
|
||||||
|
GlobalBus.off("event", on)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test("replays locally without posting to a server", async () => {
|
||||||
|
await using tmp = await tmpdir({ git: true })
|
||||||
|
const app = Server.Default().app
|
||||||
|
const dir = path.join(tmp.path, ".restore-local")
|
||||||
|
const seen: any[] = []
|
||||||
|
const on = (evt: any) => seen.push(evt)
|
||||||
|
GlobalBus.on("event", on)
|
||||||
|
|
||||||
|
const fetch = spyOn(globalThis, "fetch")
|
||||||
|
const replayAll = spyOn(SyncEvent, "replayAll")
|
||||||
|
|
||||||
|
try {
|
||||||
|
const setup = await Instance.provide({
|
||||||
|
directory: tmp.path,
|
||||||
|
fn: async () => {
|
||||||
|
registerAdaptor(Instance.project.id, "local-restore", local(dir))
|
||||||
|
const space = await Workspace.create({
|
||||||
|
type: "local-restore",
|
||||||
|
branch: null,
|
||||||
|
extra: null,
|
||||||
|
projectID: Instance.project.id,
|
||||||
|
})
|
||||||
|
const session = await Session.create({})
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
await user(session.id, `msg ${i}`)
|
||||||
|
}
|
||||||
|
return { space, session }
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const res = await app.request(`/experimental/workspace/${setup.space.id}/session-restore`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/json",
|
||||||
|
"x-opencode-directory": tmp.path,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
sessionID: setup.session.id,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(res.status).toBe(200)
|
||||||
|
expect(await res.json()).toEqual({ total: 2 })
|
||||||
|
expect(fetch).not.toHaveBeenCalled()
|
||||||
|
expect(replayAll).toHaveBeenCalledTimes(2)
|
||||||
|
expect((await Session.get(setup.session.id)).workspaceID).toBe(setup.space.id)
|
||||||
|
|
||||||
|
const restore = seen.filter(
|
||||||
|
(evt) => evt.workspace === setup.space.id && evt.payload.type === Workspace.Event.Restore.type,
|
||||||
|
)
|
||||||
|
expect(restore.map((evt) => evt.payload.properties.step)).toEqual([0, 1, 2])
|
||||||
|
} finally {
|
||||||
|
GlobalBus.off("event", on)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -9,6 +9,7 @@ import { EventTable } from "../../src/sync/event.sql"
|
||||||
import { Identifier } from "../../src/id/id"
|
import { Identifier } from "../../src/id/id"
|
||||||
import { Flag } from "../../src/flag/flag"
|
import { Flag } from "../../src/flag/flag"
|
||||||
import { initProjectors } from "../../src/server/projectors"
|
import { initProjectors } from "../../src/server/projectors"
|
||||||
|
import { SyncRoutes } from "../../src/server/routes/sync"
|
||||||
|
|
||||||
const original = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES
|
const original = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES
|
||||||
|
|
||||||
|
|
@ -187,5 +188,74 @@ describe("SyncEvent", () => {
|
||||||
).toThrow(/Unknown event type/)
|
).toThrow(/Unknown event type/)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
test(
|
||||||
|
"sync route accepts later chunks after the first batch",
|
||||||
|
withInstance(async () => {
|
||||||
|
const { Created } = setup()
|
||||||
|
const app = SyncRoutes()
|
||||||
|
const id = Identifier.descending("message")
|
||||||
|
|
||||||
|
const one = await app.request("/replay", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
directory: "/tmp/test",
|
||||||
|
events: [
|
||||||
|
{
|
||||||
|
id: "evt_1",
|
||||||
|
type: SyncEvent.versionedType(Created.type, Created.version),
|
||||||
|
seq: 0,
|
||||||
|
aggregateID: id,
|
||||||
|
data: { id, name: "first" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "evt_2",
|
||||||
|
type: SyncEvent.versionedType(Created.type, Created.version),
|
||||||
|
seq: 1,
|
||||||
|
aggregateID: id,
|
||||||
|
data: { id, name: "second" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
const two = await app.request("/replay", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
directory: "/tmp/test",
|
||||||
|
events: [
|
||||||
|
{
|
||||||
|
id: "evt_3",
|
||||||
|
type: SyncEvent.versionedType(Created.type, Created.version),
|
||||||
|
seq: 2,
|
||||||
|
aggregateID: id,
|
||||||
|
data: { id, name: "third" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "evt_4",
|
||||||
|
type: SyncEvent.versionedType(Created.type, Created.version),
|
||||||
|
seq: 3,
|
||||||
|
aggregateID: id,
|
||||||
|
data: { id, name: "fourth" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(one.status).toBe(200)
|
||||||
|
expect(await one.json()).toEqual({ sessionID: id })
|
||||||
|
expect(two.status).toBe(200)
|
||||||
|
expect(await two.json()).toEqual({ sessionID: id })
|
||||||
|
|
||||||
|
const rows = Database.use((db) => db.select().from(EventTable).all())
|
||||||
|
expect(rows.map((row) => row.seq)).toEqual([0, 1, 2, 3])
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,8 @@ import type {
|
||||||
ExperimentalWorkspaceListResponses,
|
ExperimentalWorkspaceListResponses,
|
||||||
ExperimentalWorkspaceRemoveErrors,
|
ExperimentalWorkspaceRemoveErrors,
|
||||||
ExperimentalWorkspaceRemoveResponses,
|
ExperimentalWorkspaceRemoveResponses,
|
||||||
|
ExperimentalWorkspaceSessionRestoreErrors,
|
||||||
|
ExperimentalWorkspaceSessionRestoreResponses,
|
||||||
ExperimentalWorkspaceStatusResponses,
|
ExperimentalWorkspaceStatusResponses,
|
||||||
FileListResponses,
|
FileListResponses,
|
||||||
FilePartInput,
|
FilePartInput,
|
||||||
|
|
@ -51,6 +53,11 @@ import type {
|
||||||
GlobalDisposeResponses,
|
GlobalDisposeResponses,
|
||||||
GlobalEventResponses,
|
GlobalEventResponses,
|
||||||
GlobalHealthResponses,
|
GlobalHealthResponses,
|
||||||
|
GlobalSyncEventSubscribeResponses,
|
||||||
|
GlobalSyncHistoryListErrors,
|
||||||
|
GlobalSyncHistoryListResponses,
|
||||||
|
GlobalSyncReplayErrors,
|
||||||
|
GlobalSyncReplayResponses,
|
||||||
GlobalUpgradeErrors,
|
GlobalUpgradeErrors,
|
||||||
GlobalUpgradeResponses,
|
GlobalUpgradeResponses,
|
||||||
InstanceDisposeResponses,
|
InstanceDisposeResponses,
|
||||||
|
|
@ -236,6 +243,20 @@ class HeyApiRegistry<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class SyncEvent extends HeyApiClient {
|
||||||
|
/**
|
||||||
|
* Subscribe to global sync events
|
||||||
|
*
|
||||||
|
* Get global sync events
|
||||||
|
*/
|
||||||
|
public subscribe<ThrowOnError extends boolean = false>(options?: Options<never, ThrowOnError>) {
|
||||||
|
return (options?.client ?? this.client).sse.get<GlobalSyncEventSubscribeResponses, unknown, ThrowOnError>({
|
||||||
|
url: "/global/sync-event",
|
||||||
|
...options,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class Config extends HeyApiClient {
|
export class Config extends HeyApiClient {
|
||||||
/**
|
/**
|
||||||
* Get global configuration
|
* Get global configuration
|
||||||
|
|
@ -274,6 +295,38 @@ export class Config extends HeyApiClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class SyncHistory extends HeyApiClient {
|
||||||
|
/**
|
||||||
|
* List sync events
|
||||||
|
*
|
||||||
|
* List sync events for all aggregates. Keys are aggregate IDs the client already knows about, values are the last known sequence ID. Events with seq > value are returned for those aggregates. Aggregates not listed in the input get their full history.
|
||||||
|
*/
|
||||||
|
public list<ThrowOnError extends boolean = false>(
|
||||||
|
parameters?: {
|
||||||
|
body?: {
|
||||||
|
[key: string]: number
|
||||||
|
}
|
||||||
|
},
|
||||||
|
options?: Options<never, ThrowOnError>,
|
||||||
|
) {
|
||||||
|
const params = buildClientParams([parameters], [{ args: [{ key: "body", map: "body" }] }])
|
||||||
|
return (options?.client ?? this.client).get<
|
||||||
|
GlobalSyncHistoryListResponses,
|
||||||
|
GlobalSyncHistoryListErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
url: "/sync/history",
|
||||||
|
...options,
|
||||||
|
...params,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options?.headers,
|
||||||
|
...params.headers,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class Global extends HeyApiClient {
|
export class Global extends HeyApiClient {
|
||||||
/**
|
/**
|
||||||
* Get health
|
* Get health
|
||||||
|
|
@ -335,10 +388,63 @@ export class Global extends HeyApiClient {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replay sync events
|
||||||
|
*
|
||||||
|
* Validate and replay a complete sync event history.
|
||||||
|
*/
|
||||||
|
public syncReplay<ThrowOnError extends boolean = false>(
|
||||||
|
parameters?: {
|
||||||
|
directory?: string
|
||||||
|
events?: Array<{
|
||||||
|
id: string
|
||||||
|
aggregateID: string
|
||||||
|
seq: number
|
||||||
|
type: string
|
||||||
|
data: {
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
},
|
||||||
|
options?: Options<never, ThrowOnError>,
|
||||||
|
) {
|
||||||
|
const params = buildClientParams(
|
||||||
|
[parameters],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
args: [
|
||||||
|
{ in: "body", key: "directory" },
|
||||||
|
{ in: "body", key: "events" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
return (options?.client ?? this.client).post<GlobalSyncReplayResponses, GlobalSyncReplayErrors, ThrowOnError>({
|
||||||
|
url: "/sync/replay",
|
||||||
|
...options,
|
||||||
|
...params,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options?.headers,
|
||||||
|
...params.headers,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private _syncEvent?: SyncEvent
|
||||||
|
get syncEvent(): SyncEvent {
|
||||||
|
return (this._syncEvent ??= new SyncEvent({ client: this.client }))
|
||||||
|
}
|
||||||
|
|
||||||
private _config?: Config
|
private _config?: Config
|
||||||
get config(): Config {
|
get config(): Config {
|
||||||
return (this._config ??= new Config({ client: this.client }))
|
return (this._config ??= new Config({ client: this.client }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _syncHistory?: SyncHistory
|
||||||
|
get syncHistory(): SyncHistory {
|
||||||
|
return (this._syncHistory ??= new SyncHistory({ client: this.client }))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Auth extends HeyApiClient {
|
export class Auth extends HeyApiClient {
|
||||||
|
|
@ -1243,6 +1349,49 @@ export class Workspace extends HeyApiClient {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore session into workspace
|
||||||
|
*
|
||||||
|
* Replay a session's sync events into the target workspace in batches.
|
||||||
|
*/
|
||||||
|
public sessionRestore<ThrowOnError extends boolean = false>(
|
||||||
|
parameters: {
|
||||||
|
id: string
|
||||||
|
directory?: string
|
||||||
|
workspace?: string
|
||||||
|
sessionID?: string
|
||||||
|
},
|
||||||
|
options?: Options<never, ThrowOnError>,
|
||||||
|
) {
|
||||||
|
const params = buildClientParams(
|
||||||
|
[parameters],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
args: [
|
||||||
|
{ in: "path", key: "id" },
|
||||||
|
{ in: "query", key: "directory" },
|
||||||
|
{ in: "query", key: "workspace" },
|
||||||
|
{ in: "body", key: "sessionID" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
return (options?.client ?? this.client).post<
|
||||||
|
ExperimentalWorkspaceSessionRestoreResponses,
|
||||||
|
ExperimentalWorkspaceSessionRestoreErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
url: "/experimental/workspace/{id}/session-restore",
|
||||||
|
...options,
|
||||||
|
...params,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options?.headers,
|
||||||
|
...params.headers,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
private _adaptor?: Adaptor
|
private _adaptor?: Adaptor
|
||||||
get adaptor(): Adaptor {
|
get adaptor(): Adaptor {
|
||||||
return (this._adaptor ??= new Adaptor({ client: this.client }))
|
return (this._adaptor ??= new Adaptor({ client: this.client }))
|
||||||
|
|
@ -1743,7 +1892,6 @@ export class Session2 extends HeyApiClient {
|
||||||
directory?: string
|
directory?: string
|
||||||
workspace?: string
|
workspace?: string
|
||||||
title?: string
|
title?: string
|
||||||
permission?: PermissionRuleset
|
|
||||||
time?: {
|
time?: {
|
||||||
archived?: number
|
archived?: number
|
||||||
}
|
}
|
||||||
|
|
@ -1759,7 +1907,6 @@ export class Session2 extends HeyApiClient {
|
||||||
{ in: "query", key: "directory" },
|
{ in: "query", key: "directory" },
|
||||||
{ in: "query", key: "workspace" },
|
{ in: "query", key: "workspace" },
|
||||||
{ in: "body", key: "title" },
|
{ in: "body", key: "title" },
|
||||||
{ in: "body", key: "permission" },
|
|
||||||
{ in: "body", key: "time" },
|
{ in: "body", key: "time" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -33,13 +33,6 @@ export type EventProjectUpdated = {
|
||||||
properties: Project
|
properties: Project
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EventServerInstanceDisposed = {
|
|
||||||
type: "server.instance.disposed"
|
|
||||||
properties: {
|
|
||||||
directory: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type EventInstallationUpdated = {
|
export type EventInstallationUpdated = {
|
||||||
type: "installation.updated"
|
type: "installation.updated"
|
||||||
properties: {
|
properties: {
|
||||||
|
|
@ -54,6 +47,13 @@ export type EventInstallationUpdateAvailable = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type EventServerInstanceDisposed = {
|
||||||
|
type: "server.instance.disposed"
|
||||||
|
properties: {
|
||||||
|
directory: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export type EventServerConnected = {
|
export type EventServerConnected = {
|
||||||
type: "server.connected"
|
type: "server.connected"
|
||||||
properties: {
|
properties: {
|
||||||
|
|
@ -68,21 +68,6 @@ export type EventGlobalDisposed = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EventFileEdited = {
|
|
||||||
type: "file.edited"
|
|
||||||
properties: {
|
|
||||||
file: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type EventFileWatcherUpdated = {
|
|
||||||
type: "file.watcher.updated"
|
|
||||||
properties: {
|
|
||||||
file: string
|
|
||||||
event: "add" | "change" | "unlink"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type EventLspClientDiagnostics = {
|
export type EventLspClientDiagnostics = {
|
||||||
type: "lsp.client.diagnostics"
|
type: "lsp.client.diagnostics"
|
||||||
properties: {
|
properties: {
|
||||||
|
|
@ -230,133 +215,25 @@ export type EventSessionError = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type QuestionOption = {
|
export type EventFileEdited = {
|
||||||
/**
|
type: "file.edited"
|
||||||
* Display text (1-5 words, concise)
|
properties: {
|
||||||
*/
|
file: string
|
||||||
label: string
|
|
||||||
/**
|
|
||||||
* Explanation of choice
|
|
||||||
*/
|
|
||||||
description: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QuestionInfo = {
|
|
||||||
/**
|
|
||||||
* Complete question
|
|
||||||
*/
|
|
||||||
question: string
|
|
||||||
/**
|
|
||||||
* Very short label (max 30 chars)
|
|
||||||
*/
|
|
||||||
header: string
|
|
||||||
/**
|
|
||||||
* Available choices
|
|
||||||
*/
|
|
||||||
options: Array<QuestionOption>
|
|
||||||
/**
|
|
||||||
* Allow selecting multiple choices
|
|
||||||
*/
|
|
||||||
multiple?: boolean
|
|
||||||
/**
|
|
||||||
* Allow typing a custom answer (default: true)
|
|
||||||
*/
|
|
||||||
custom?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QuestionRequest = {
|
|
||||||
id: string
|
|
||||||
sessionID: string
|
|
||||||
/**
|
|
||||||
* Questions to ask
|
|
||||||
*/
|
|
||||||
questions: Array<QuestionInfo>
|
|
||||||
tool?: {
|
|
||||||
messageID: string
|
|
||||||
callID: string
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EventQuestionAsked = {
|
export type EventFileWatcherUpdated = {
|
||||||
type: "question.asked"
|
type: "file.watcher.updated"
|
||||||
properties: QuestionRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QuestionAnswer = Array<string>
|
|
||||||
|
|
||||||
export type EventQuestionReplied = {
|
|
||||||
type: "question.replied"
|
|
||||||
properties: {
|
properties: {
|
||||||
sessionID: string
|
file: string
|
||||||
requestID: string
|
event: "add" | "change" | "unlink"
|
||||||
answers: Array<QuestionAnswer>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EventQuestionRejected = {
|
export type EventVcsBranchUpdated = {
|
||||||
type: "question.rejected"
|
type: "vcs.branch.updated"
|
||||||
properties: {
|
properties: {
|
||||||
sessionID: string
|
branch?: string
|
||||||
requestID: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Todo = {
|
|
||||||
/**
|
|
||||||
* Brief description of the task
|
|
||||||
*/
|
|
||||||
content: string
|
|
||||||
/**
|
|
||||||
* Current status of the task: pending, in_progress, completed, cancelled
|
|
||||||
*/
|
|
||||||
status: string
|
|
||||||
/**
|
|
||||||
* Priority level of the task: high, medium, low
|
|
||||||
*/
|
|
||||||
priority: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type EventTodoUpdated = {
|
|
||||||
type: "todo.updated"
|
|
||||||
properties: {
|
|
||||||
sessionID: string
|
|
||||||
todos: Array<Todo>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SessionStatus =
|
|
||||||
| {
|
|
||||||
type: "idle"
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: "retry"
|
|
||||||
attempt: number
|
|
||||||
message: string
|
|
||||||
next: number
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: "busy"
|
|
||||||
}
|
|
||||||
|
|
||||||
export type EventSessionStatus = {
|
|
||||||
type: "session.status"
|
|
||||||
properties: {
|
|
||||||
sessionID: string
|
|
||||||
status: SessionStatus
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type EventSessionIdle = {
|
|
||||||
type: "session.idle"
|
|
||||||
properties: {
|
|
||||||
sessionID: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type EventSessionCompacted = {
|
|
||||||
type: "session.compacted"
|
|
||||||
properties: {
|
|
||||||
sessionID: string
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -439,28 +316,169 @@ export type EventCommandExecuted = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EventVcsBranchUpdated = {
|
export type EventWorkspaceReady = {
|
||||||
type: "vcs.branch.updated"
|
type: "workspace.ready"
|
||||||
properties: {
|
|
||||||
branch?: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type EventWorktreeReady = {
|
|
||||||
type: "worktree.ready"
|
|
||||||
properties: {
|
properties: {
|
||||||
name: string
|
name: string
|
||||||
branch: string
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EventWorktreeFailed = {
|
export type EventWorkspaceFailed = {
|
||||||
type: "worktree.failed"
|
type: "workspace.failed"
|
||||||
properties: {
|
properties: {
|
||||||
message: string
|
message: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type EventWorkspaceRestore = {
|
||||||
|
type: "workspace.restore"
|
||||||
|
properties: {
|
||||||
|
workspaceID: string
|
||||||
|
sessionID: string
|
||||||
|
total: number
|
||||||
|
step: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EventWorkspaceStatus = {
|
||||||
|
type: "workspace.status"
|
||||||
|
properties: {
|
||||||
|
workspaceID: string
|
||||||
|
status: "connected" | "connecting" | "disconnected" | "error"
|
||||||
|
error?: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type QuestionOption = {
|
||||||
|
/**
|
||||||
|
* Display text (1-5 words, concise)
|
||||||
|
*/
|
||||||
|
label: string
|
||||||
|
/**
|
||||||
|
* Explanation of choice
|
||||||
|
*/
|
||||||
|
description: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type QuestionInfo = {
|
||||||
|
/**
|
||||||
|
* Complete question
|
||||||
|
*/
|
||||||
|
question: string
|
||||||
|
/**
|
||||||
|
* Very short label (max 30 chars)
|
||||||
|
*/
|
||||||
|
header: string
|
||||||
|
/**
|
||||||
|
* Available choices
|
||||||
|
*/
|
||||||
|
options: Array<QuestionOption>
|
||||||
|
/**
|
||||||
|
* Allow selecting multiple choices
|
||||||
|
*/
|
||||||
|
multiple?: boolean
|
||||||
|
/**
|
||||||
|
* Allow typing a custom answer (default: true)
|
||||||
|
*/
|
||||||
|
custom?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type QuestionRequest = {
|
||||||
|
id: string
|
||||||
|
sessionID: string
|
||||||
|
/**
|
||||||
|
* Questions to ask
|
||||||
|
*/
|
||||||
|
questions: Array<QuestionInfo>
|
||||||
|
tool?: {
|
||||||
|
messageID: string
|
||||||
|
callID: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EventQuestionAsked = {
|
||||||
|
type: "question.asked"
|
||||||
|
properties: QuestionRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
export type QuestionAnswer = Array<string>
|
||||||
|
|
||||||
|
export type EventQuestionReplied = {
|
||||||
|
type: "question.replied"
|
||||||
|
properties: {
|
||||||
|
sessionID: string
|
||||||
|
requestID: string
|
||||||
|
answers: Array<QuestionAnswer>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EventQuestionRejected = {
|
||||||
|
type: "question.rejected"
|
||||||
|
properties: {
|
||||||
|
sessionID: string
|
||||||
|
requestID: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SessionStatus =
|
||||||
|
| {
|
||||||
|
type: "idle"
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "retry"
|
||||||
|
attempt: number
|
||||||
|
message: string
|
||||||
|
next: number
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "busy"
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EventSessionStatus = {
|
||||||
|
type: "session.status"
|
||||||
|
properties: {
|
||||||
|
sessionID: string
|
||||||
|
status: SessionStatus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EventSessionIdle = {
|
||||||
|
type: "session.idle"
|
||||||
|
properties: {
|
||||||
|
sessionID: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EventSessionCompacted = {
|
||||||
|
type: "session.compacted"
|
||||||
|
properties: {
|
||||||
|
sessionID: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Todo = {
|
||||||
|
/**
|
||||||
|
* Brief description of the task
|
||||||
|
*/
|
||||||
|
content: string
|
||||||
|
/**
|
||||||
|
* Current status of the task: pending, in_progress, completed, cancelled
|
||||||
|
*/
|
||||||
|
status: string
|
||||||
|
/**
|
||||||
|
* Priority level of the task: high, medium, low
|
||||||
|
*/
|
||||||
|
priority: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EventTodoUpdated = {
|
||||||
|
type: "todo.updated"
|
||||||
|
properties: {
|
||||||
|
sessionID: string
|
||||||
|
todos: Array<Todo>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export type Pty = {
|
export type Pty = {
|
||||||
id: string
|
id: string
|
||||||
title: string
|
title: string
|
||||||
|
|
@ -500,29 +518,21 @@ export type EventPtyDeleted = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EventWorkspaceReady = {
|
export type EventWorktreeReady = {
|
||||||
type: "workspace.ready"
|
type: "worktree.ready"
|
||||||
properties: {
|
properties: {
|
||||||
name: string
|
name: string
|
||||||
|
branch: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EventWorkspaceFailed = {
|
export type EventWorktreeFailed = {
|
||||||
type: "workspace.failed"
|
type: "worktree.failed"
|
||||||
properties: {
|
properties: {
|
||||||
message: string
|
message: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EventWorkspaceStatus = {
|
|
||||||
type: "workspace.status"
|
|
||||||
properties: {
|
|
||||||
workspaceID: string
|
|
||||||
status: "connected" | "connecting" | "disconnected" | "error"
|
|
||||||
error?: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type OutputFormatText = {
|
export type OutputFormatText = {
|
||||||
type: "text"
|
type: "text"
|
||||||
}
|
}
|
||||||
|
|
@ -971,12 +981,65 @@ export type EventSessionDeleted = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Event =
|
||||||
|
| EventProjectUpdated
|
||||||
|
| EventInstallationUpdated
|
||||||
|
| EventInstallationUpdateAvailable
|
||||||
|
| EventServerInstanceDisposed
|
||||||
|
| EventServerConnected
|
||||||
|
| EventGlobalDisposed
|
||||||
|
| EventLspClientDiagnostics
|
||||||
|
| EventLspUpdated
|
||||||
|
| EventMessagePartDelta
|
||||||
|
| EventPermissionAsked
|
||||||
|
| EventPermissionReplied
|
||||||
|
| EventSessionDiff
|
||||||
|
| EventSessionError
|
||||||
|
| EventFileEdited
|
||||||
|
| EventFileWatcherUpdated
|
||||||
|
| EventVcsBranchUpdated
|
||||||
|
| EventTuiPromptAppend
|
||||||
|
| EventTuiCommandExecute
|
||||||
|
| EventTuiToastShow
|
||||||
|
| EventTuiSessionSelect
|
||||||
|
| EventMcpToolsChanged
|
||||||
|
| EventMcpBrowserOpenFailed
|
||||||
|
| EventCommandExecuted
|
||||||
|
| EventWorkspaceReady
|
||||||
|
| EventWorkspaceFailed
|
||||||
|
| EventWorkspaceRestore
|
||||||
|
| EventWorkspaceStatus
|
||||||
|
| EventQuestionAsked
|
||||||
|
| EventQuestionReplied
|
||||||
|
| EventQuestionRejected
|
||||||
|
| EventSessionStatus
|
||||||
|
| EventSessionIdle
|
||||||
|
| EventSessionCompacted
|
||||||
|
| EventTodoUpdated
|
||||||
|
| EventPtyCreated
|
||||||
|
| EventPtyUpdated
|
||||||
|
| EventPtyExited
|
||||||
|
| EventPtyDeleted
|
||||||
|
| EventWorktreeReady
|
||||||
|
| EventWorktreeFailed
|
||||||
|
| EventMessageUpdated
|
||||||
|
| EventMessageRemoved
|
||||||
|
| EventMessagePartUpdated
|
||||||
|
| EventMessagePartRemoved
|
||||||
|
| EventSessionCreated
|
||||||
|
| EventSessionUpdated
|
||||||
|
| EventSessionDeleted
|
||||||
|
|
||||||
|
export type GlobalEvent = {
|
||||||
|
directory: string
|
||||||
|
project?: string
|
||||||
|
workspace?: string
|
||||||
|
payload: Event
|
||||||
|
}
|
||||||
|
|
||||||
export type SyncEventMessageUpdated = {
|
export type SyncEventMessageUpdated = {
|
||||||
type: "sync"
|
type: "message.updated.1"
|
||||||
name: "message.updated.1"
|
aggregate: "sessionID"
|
||||||
id: string
|
|
||||||
seq: number
|
|
||||||
aggregateID: "sessionID"
|
|
||||||
data: {
|
data: {
|
||||||
sessionID: string
|
sessionID: string
|
||||||
info: Message
|
info: Message
|
||||||
|
|
@ -984,11 +1047,8 @@ export type SyncEventMessageUpdated = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SyncEventMessageRemoved = {
|
export type SyncEventMessageRemoved = {
|
||||||
type: "sync"
|
type: "message.removed.1"
|
||||||
name: "message.removed.1"
|
aggregate: "sessionID"
|
||||||
id: string
|
|
||||||
seq: number
|
|
||||||
aggregateID: "sessionID"
|
|
||||||
data: {
|
data: {
|
||||||
sessionID: string
|
sessionID: string
|
||||||
messageID: string
|
messageID: string
|
||||||
|
|
@ -996,11 +1056,8 @@ export type SyncEventMessageRemoved = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SyncEventMessagePartUpdated = {
|
export type SyncEventMessagePartUpdated = {
|
||||||
type: "sync"
|
type: "message.part.updated.1"
|
||||||
name: "message.part.updated.1"
|
aggregate: "sessionID"
|
||||||
id: string
|
|
||||||
seq: number
|
|
||||||
aggregateID: "sessionID"
|
|
||||||
data: {
|
data: {
|
||||||
sessionID: string
|
sessionID: string
|
||||||
part: Part
|
part: Part
|
||||||
|
|
@ -1009,11 +1066,8 @@ export type SyncEventMessagePartUpdated = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SyncEventMessagePartRemoved = {
|
export type SyncEventMessagePartRemoved = {
|
||||||
type: "sync"
|
type: "message.part.removed.1"
|
||||||
name: "message.part.removed.1"
|
aggregate: "sessionID"
|
||||||
id: string
|
|
||||||
seq: number
|
|
||||||
aggregateID: "sessionID"
|
|
||||||
data: {
|
data: {
|
||||||
sessionID: string
|
sessionID: string
|
||||||
messageID: string
|
messageID: string
|
||||||
|
|
@ -1022,11 +1076,8 @@ export type SyncEventMessagePartRemoved = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SyncEventSessionCreated = {
|
export type SyncEventSessionCreated = {
|
||||||
type: "sync"
|
type: "session.created.1"
|
||||||
name: "session.created.1"
|
aggregate: "sessionID"
|
||||||
id: string
|
|
||||||
seq: number
|
|
||||||
aggregateID: "sessionID"
|
|
||||||
data: {
|
data: {
|
||||||
sessionID: string
|
sessionID: string
|
||||||
info: Session
|
info: Session
|
||||||
|
|
@ -1034,11 +1085,8 @@ export type SyncEventSessionCreated = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SyncEventSessionUpdated = {
|
export type SyncEventSessionUpdated = {
|
||||||
type: "sync"
|
type: "session.updated.1"
|
||||||
name: "session.updated.1"
|
aggregate: "sessionID"
|
||||||
id: string
|
|
||||||
seq: number
|
|
||||||
aggregateID: "sessionID"
|
|
||||||
data: {
|
data: {
|
||||||
sessionID: string
|
sessionID: string
|
||||||
info: {
|
info: {
|
||||||
|
|
@ -1077,75 +1125,16 @@ export type SyncEventSessionUpdated = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SyncEventSessionDeleted = {
|
export type SyncEventSessionDeleted = {
|
||||||
type: "sync"
|
type: "session.deleted.1"
|
||||||
name: "session.deleted.1"
|
aggregate: "sessionID"
|
||||||
id: string
|
|
||||||
seq: number
|
|
||||||
aggregateID: "sessionID"
|
|
||||||
data: {
|
data: {
|
||||||
sessionID: string
|
sessionID: string
|
||||||
info: Session
|
info: Session
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GlobalEvent = {
|
export type SyncEvent = {
|
||||||
directory: string
|
payload: SyncEvent
|
||||||
project?: string
|
|
||||||
workspace?: string
|
|
||||||
payload:
|
|
||||||
| EventProjectUpdated
|
|
||||||
| EventServerInstanceDisposed
|
|
||||||
| EventInstallationUpdated
|
|
||||||
| EventInstallationUpdateAvailable
|
|
||||||
| EventServerConnected
|
|
||||||
| EventGlobalDisposed
|
|
||||||
| EventFileEdited
|
|
||||||
| EventFileWatcherUpdated
|
|
||||||
| EventLspClientDiagnostics
|
|
||||||
| EventLspUpdated
|
|
||||||
| EventMessagePartDelta
|
|
||||||
| EventPermissionAsked
|
|
||||||
| EventPermissionReplied
|
|
||||||
| EventSessionDiff
|
|
||||||
| EventSessionError
|
|
||||||
| EventQuestionAsked
|
|
||||||
| EventQuestionReplied
|
|
||||||
| EventQuestionRejected
|
|
||||||
| EventTodoUpdated
|
|
||||||
| EventSessionStatus
|
|
||||||
| EventSessionIdle
|
|
||||||
| EventSessionCompacted
|
|
||||||
| EventTuiPromptAppend
|
|
||||||
| EventTuiCommandExecute
|
|
||||||
| EventTuiToastShow
|
|
||||||
| EventTuiSessionSelect
|
|
||||||
| EventMcpToolsChanged
|
|
||||||
| EventMcpBrowserOpenFailed
|
|
||||||
| EventCommandExecuted
|
|
||||||
| EventVcsBranchUpdated
|
|
||||||
| EventWorktreeReady
|
|
||||||
| EventWorktreeFailed
|
|
||||||
| EventPtyCreated
|
|
||||||
| EventPtyUpdated
|
|
||||||
| EventPtyExited
|
|
||||||
| EventPtyDeleted
|
|
||||||
| EventWorkspaceReady
|
|
||||||
| EventWorkspaceFailed
|
|
||||||
| EventWorkspaceStatus
|
|
||||||
| EventMessageUpdated
|
|
||||||
| EventMessageRemoved
|
|
||||||
| EventMessagePartUpdated
|
|
||||||
| EventMessagePartRemoved
|
|
||||||
| EventSessionCreated
|
|
||||||
| EventSessionUpdated
|
|
||||||
| EventSessionDeleted
|
|
||||||
| SyncEventMessageUpdated
|
|
||||||
| SyncEventMessageRemoved
|
|
||||||
| SyncEventMessagePartUpdated
|
|
||||||
| SyncEventMessagePartRemoved
|
|
||||||
| SyncEventSessionCreated
|
|
||||||
| SyncEventSessionUpdated
|
|
||||||
| SyncEventSessionDeleted
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -2004,54 +1993,6 @@ export type File = {
|
||||||
status: "added" | "deleted" | "modified"
|
status: "added" | "deleted" | "modified"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Event =
|
|
||||||
| EventProjectUpdated
|
|
||||||
| EventServerInstanceDisposed
|
|
||||||
| EventInstallationUpdated
|
|
||||||
| EventInstallationUpdateAvailable
|
|
||||||
| EventServerConnected
|
|
||||||
| EventGlobalDisposed
|
|
||||||
| EventFileEdited
|
|
||||||
| EventFileWatcherUpdated
|
|
||||||
| EventLspClientDiagnostics
|
|
||||||
| EventLspUpdated
|
|
||||||
| EventMessagePartDelta
|
|
||||||
| EventPermissionAsked
|
|
||||||
| EventPermissionReplied
|
|
||||||
| EventSessionDiff
|
|
||||||
| EventSessionError
|
|
||||||
| EventQuestionAsked
|
|
||||||
| EventQuestionReplied
|
|
||||||
| EventQuestionRejected
|
|
||||||
| EventTodoUpdated
|
|
||||||
| EventSessionStatus
|
|
||||||
| EventSessionIdle
|
|
||||||
| EventSessionCompacted
|
|
||||||
| EventTuiPromptAppend
|
|
||||||
| EventTuiCommandExecute
|
|
||||||
| EventTuiToastShow
|
|
||||||
| EventTuiSessionSelect
|
|
||||||
| EventMcpToolsChanged
|
|
||||||
| EventMcpBrowserOpenFailed
|
|
||||||
| EventCommandExecuted
|
|
||||||
| EventVcsBranchUpdated
|
|
||||||
| EventWorktreeReady
|
|
||||||
| EventWorktreeFailed
|
|
||||||
| EventPtyCreated
|
|
||||||
| EventPtyUpdated
|
|
||||||
| EventPtyExited
|
|
||||||
| EventPtyDeleted
|
|
||||||
| EventWorkspaceReady
|
|
||||||
| EventWorkspaceFailed
|
|
||||||
| EventWorkspaceStatus
|
|
||||||
| EventMessageUpdated
|
|
||||||
| EventMessageRemoved
|
|
||||||
| EventMessagePartUpdated
|
|
||||||
| EventMessagePartRemoved
|
|
||||||
| EventSessionCreated
|
|
||||||
| EventSessionUpdated
|
|
||||||
| EventSessionDeleted
|
|
||||||
|
|
||||||
export type McpStatusConnected = {
|
export type McpStatusConnected = {
|
||||||
status: "connected"
|
status: "connected"
|
||||||
}
|
}
|
||||||
|
|
@ -2183,6 +2124,23 @@ export type GlobalEventResponses = {
|
||||||
|
|
||||||
export type GlobalEventResponse = GlobalEventResponses[keyof GlobalEventResponses]
|
export type GlobalEventResponse = GlobalEventResponses[keyof GlobalEventResponses]
|
||||||
|
|
||||||
|
export type GlobalSyncEventSubscribeData = {
|
||||||
|
body?: never
|
||||||
|
path?: never
|
||||||
|
query?: never
|
||||||
|
url: "/global/sync-event"
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GlobalSyncEventSubscribeResponses = {
|
||||||
|
/**
|
||||||
|
* Event stream
|
||||||
|
*/
|
||||||
|
200: SyncEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GlobalSyncEventSubscribeResponse =
|
||||||
|
GlobalSyncEventSubscribeResponses[keyof GlobalSyncEventSubscribeResponses]
|
||||||
|
|
||||||
export type GlobalConfigGetData = {
|
export type GlobalConfigGetData = {
|
||||||
body?: never
|
body?: never
|
||||||
path?: never
|
path?: never
|
||||||
|
|
@ -2275,6 +2233,79 @@ export type GlobalUpgradeResponses = {
|
||||||
|
|
||||||
export type GlobalUpgradeResponse = GlobalUpgradeResponses[keyof GlobalUpgradeResponses]
|
export type GlobalUpgradeResponse = GlobalUpgradeResponses[keyof GlobalUpgradeResponses]
|
||||||
|
|
||||||
|
export type GlobalSyncReplayData = {
|
||||||
|
body?: {
|
||||||
|
directory: string
|
||||||
|
events: Array<{
|
||||||
|
id: string
|
||||||
|
aggregateID: string
|
||||||
|
seq: number
|
||||||
|
type: string
|
||||||
|
data: {
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
path?: never
|
||||||
|
query?: never
|
||||||
|
url: "/sync/replay"
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GlobalSyncReplayErrors = {
|
||||||
|
/**
|
||||||
|
* Bad request
|
||||||
|
*/
|
||||||
|
400: BadRequestError
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GlobalSyncReplayError = GlobalSyncReplayErrors[keyof GlobalSyncReplayErrors]
|
||||||
|
|
||||||
|
export type GlobalSyncReplayResponses = {
|
||||||
|
/**
|
||||||
|
* Replayed sync events
|
||||||
|
*/
|
||||||
|
200: {
|
||||||
|
sessionID: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GlobalSyncReplayResponse = GlobalSyncReplayResponses[keyof GlobalSyncReplayResponses]
|
||||||
|
|
||||||
|
export type GlobalSyncHistoryListData = {
|
||||||
|
body?: {
|
||||||
|
[key: string]: number
|
||||||
|
}
|
||||||
|
path?: never
|
||||||
|
query?: never
|
||||||
|
url: "/sync/history"
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GlobalSyncHistoryListErrors = {
|
||||||
|
/**
|
||||||
|
* Bad request
|
||||||
|
*/
|
||||||
|
400: BadRequestError
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GlobalSyncHistoryListError = GlobalSyncHistoryListErrors[keyof GlobalSyncHistoryListErrors]
|
||||||
|
|
||||||
|
export type GlobalSyncHistoryListResponses = {
|
||||||
|
/**
|
||||||
|
* Sync events
|
||||||
|
*/
|
||||||
|
200: Array<{
|
||||||
|
id: string
|
||||||
|
aggregate_id: string
|
||||||
|
seq: number
|
||||||
|
type: string
|
||||||
|
data: {
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GlobalSyncHistoryListResponse = GlobalSyncHistoryListResponses[keyof GlobalSyncHistoryListResponses]
|
||||||
|
|
||||||
export type AuthRemoveData = {
|
export type AuthRemoveData = {
|
||||||
body?: never
|
body?: never
|
||||||
path: {
|
path: {
|
||||||
|
|
@ -3000,6 +3031,42 @@ export type ExperimentalWorkspaceRemoveResponses = {
|
||||||
export type ExperimentalWorkspaceRemoveResponse =
|
export type ExperimentalWorkspaceRemoveResponse =
|
||||||
ExperimentalWorkspaceRemoveResponses[keyof ExperimentalWorkspaceRemoveResponses]
|
ExperimentalWorkspaceRemoveResponses[keyof ExperimentalWorkspaceRemoveResponses]
|
||||||
|
|
||||||
|
export type ExperimentalWorkspaceSessionRestoreData = {
|
||||||
|
body?: {
|
||||||
|
sessionID: string
|
||||||
|
}
|
||||||
|
path: {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
query?: {
|
||||||
|
directory?: string
|
||||||
|
workspace?: string
|
||||||
|
}
|
||||||
|
url: "/experimental/workspace/{id}/session-restore"
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ExperimentalWorkspaceSessionRestoreErrors = {
|
||||||
|
/**
|
||||||
|
* Bad request
|
||||||
|
*/
|
||||||
|
400: BadRequestError
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ExperimentalWorkspaceSessionRestoreError =
|
||||||
|
ExperimentalWorkspaceSessionRestoreErrors[keyof ExperimentalWorkspaceSessionRestoreErrors]
|
||||||
|
|
||||||
|
export type ExperimentalWorkspaceSessionRestoreResponses = {
|
||||||
|
/**
|
||||||
|
* Session replay started
|
||||||
|
*/
|
||||||
|
200: {
|
||||||
|
total: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ExperimentalWorkspaceSessionRestoreResponse =
|
||||||
|
ExperimentalWorkspaceSessionRestoreResponses[keyof ExperimentalWorkspaceSessionRestoreResponses]
|
||||||
|
|
||||||
export type WorktreeRemoveData = {
|
export type WorktreeRemoveData = {
|
||||||
body?: WorktreeRemoveInput
|
body?: WorktreeRemoveInput
|
||||||
path?: never
|
path?: never
|
||||||
|
|
@ -3343,7 +3410,6 @@ export type SessionGetResponse = SessionGetResponses[keyof SessionGetResponses]
|
||||||
export type SessionUpdateData = {
|
export type SessionUpdateData = {
|
||||||
body?: {
|
body?: {
|
||||||
title?: string
|
title?: string
|
||||||
permission?: PermissionRuleset
|
|
||||||
time?: {
|
time?: {
|
||||||
archived?: number
|
archived?: number
|
||||||
}
|
}
|
||||||
|
|
|
||||||
189
sync-routes.ts
Normal file
189
sync-routes.ts
Normal file
|
|
@ -0,0 +1,189 @@
|
||||||
|
import z from "zod"
|
||||||
|
import { Hono } from "hono"
|
||||||
|
import { describeRoute, validator, resolver } from "hono-openapi"
|
||||||
|
import { SyncEvent } from "@/sync"
|
||||||
|
import { Database, asc, and, not, or, lte, eq } from "@/storage/db"
|
||||||
|
import { EventTable } from "@/sync/event.sql"
|
||||||
|
import { Log } from "@/util/log"
|
||||||
|
import { lazy } from "@/util/lazy"
|
||||||
|
import { Instance } from "@/project/instance"
|
||||||
|
import { InstanceBootstrap } from "../../project/bootstrap"
|
||||||
|
import { errors } from "../error"
|
||||||
|
import { streamQueue } from "../stream-queue"
|
||||||
|
|
||||||
|
const log = Log.create({ service: "server" })
|
||||||
|
|
||||||
|
const ReplayEvent = z.object({
|
||||||
|
id: z.string(),
|
||||||
|
aggregateID: z.string(),
|
||||||
|
seq: z.number().int().min(0),
|
||||||
|
type: z.string(),
|
||||||
|
data: z.record(z.string(), z.unknown()),
|
||||||
|
})
|
||||||
|
|
||||||
|
export const SyncRoutes = lazy(() =>
|
||||||
|
new Hono()
|
||||||
|
.get(
|
||||||
|
"/event",
|
||||||
|
describeRoute({
|
||||||
|
summary: "Subscribe to sync events",
|
||||||
|
description: "Get sync events",
|
||||||
|
operationId: "sync.event",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Event stream",
|
||||||
|
content: {
|
||||||
|
"text/event-stream": {
|
||||||
|
schema: resolver(
|
||||||
|
z
|
||||||
|
.object({
|
||||||
|
payload: SyncEvent.payloads(),
|
||||||
|
})
|
||||||
|
.meta({
|
||||||
|
ref: "SyncEvent",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (c) => {
|
||||||
|
log.info("sync event connected")
|
||||||
|
c.header("X-Accel-Buffering", "no")
|
||||||
|
c.header("X-Content-Type-Options", "nosniff")
|
||||||
|
return streamQueue(c, {
|
||||||
|
connect: (q) => {
|
||||||
|
log.info("sync event connected")
|
||||||
|
|
||||||
|
q.push(
|
||||||
|
JSON.stringify({
|
||||||
|
type: "server.connected",
|
||||||
|
properties: {},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
heartbeat: (q) => {
|
||||||
|
q.push(
|
||||||
|
JSON.stringify({
|
||||||
|
type: "server.heartbeat",
|
||||||
|
properties: {},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
subscribe: (q) => {
|
||||||
|
const unsub = SyncEvent.subscribeAll(({ def, event }) => {
|
||||||
|
q.push(JSON.stringify({ ...event, type: SyncEvent.versionedType(def.type, def.version) }))
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unsub()
|
||||||
|
log.info("sync event disconnected")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.post(
|
||||||
|
"/replay",
|
||||||
|
describeRoute({
|
||||||
|
summary: "Replay sync events",
|
||||||
|
description: "Validate and replay a complete sync event history.",
|
||||||
|
operationId: "global.sync-replay",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Replayed sync events",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(
|
||||||
|
z.object({
|
||||||
|
sessionID: z.string(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...errors(400),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
validator(
|
||||||
|
"json",
|
||||||
|
z.object({
|
||||||
|
directory: z.string(),
|
||||||
|
events: z.array(ReplayEvent).min(1),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
async (c) => {
|
||||||
|
const body = c.req.valid("json")
|
||||||
|
const events = body.events
|
||||||
|
const source = events[0].aggregateID
|
||||||
|
if (events.some((item) => item.aggregateID !== source)) {
|
||||||
|
throw new Error("Replay events must belong to the same session")
|
||||||
|
}
|
||||||
|
for (const [i, item] of events.entries()) {
|
||||||
|
if (item.seq !== i) throw new Error(`Replay sequence mismatch at index ${i}: expected ${i}, got ${item.seq}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Instance.provide({
|
||||||
|
directory: body.directory,
|
||||||
|
init: InstanceBootstrap,
|
||||||
|
async fn() {
|
||||||
|
for (const item of events) {
|
||||||
|
SyncEvent.replay(item)
|
||||||
|
}
|
||||||
|
return c.json({ sessionID: source })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.get(
|
||||||
|
"/history",
|
||||||
|
describeRoute({
|
||||||
|
summary: "List sync events",
|
||||||
|
description: "List sync events for all aggregates. Keys are aggregate IDs the client already knows about, values are the last known sequence ID. Events with seq > value are returned for those aggregates. Aggregates not listed in the input get their full history.",
|
||||||
|
operationId: "global.sync-history.list",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Sync events",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(
|
||||||
|
z.array(
|
||||||
|
z.object({
|
||||||
|
id: z.string(),
|
||||||
|
aggregate_id: z.string(),
|
||||||
|
seq: z.number(),
|
||||||
|
type: z.string(),
|
||||||
|
data: z.record(z.string(), z.unknown()),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...errors(400),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
validator(
|
||||||
|
"json",
|
||||||
|
z.record(z.string(), z.number().int().min(0)),
|
||||||
|
),
|
||||||
|
async (c) => {
|
||||||
|
const body = c.req.valid("json")
|
||||||
|
const exclude = Object.entries(body)
|
||||||
|
const where = exclude.length > 0
|
||||||
|
? not(or(...exclude.map(([id, seq]) => and(eq(EventTable.aggregate_id, id), lte(EventTable.seq, seq))))!)
|
||||||
|
: undefined
|
||||||
|
const rows = Database.use((db) =>
|
||||||
|
db
|
||||||
|
.select()
|
||||||
|
.from(EventTable)
|
||||||
|
.where(where)
|
||||||
|
.orderBy(asc(EventTable.seq))
|
||||||
|
.all(),
|
||||||
|
)
|
||||||
|
return c.json(rows)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue