Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b5bd146ea | ||
|
|
836c2060c7 | ||
|
|
6357136ca5 | ||
|
|
0a0b363587 | ||
|
|
f5f6167146 | ||
|
|
f1684c9e15 | ||
|
|
f597b7287b | ||
|
|
95fabc1407 | ||
|
|
315c366e11 | ||
|
|
5d68a7c2e0 | ||
|
|
1b2d3bf659 | ||
|
|
24e4f5b051 | ||
|
|
2992c5a6bf | ||
|
|
ca2660ccf8 | ||
|
|
6b4bd590ac | ||
|
|
60ba42af15 | ||
|
|
f22827bdfa | ||
|
|
f9b5b6d129 | ||
|
|
cc66e06101 | ||
|
|
d4c8d95ec6 | ||
|
|
0fd312346b | ||
|
|
b80046120c | ||
|
|
07ed2a8391 | ||
|
|
e9f52934e9 | ||
|
|
732b67f8ce | ||
|
|
d47bb96784 | ||
|
|
6456350564 | ||
|
|
c5c0a2ca6e | ||
|
|
3706b2bca7 | ||
|
|
1f57b9a70f | ||
|
|
004f53f741 | ||
|
|
cf29ec0a59 | ||
|
|
b5e08acdf7 | ||
|
|
7ddeeeb4f8 | ||
|
|
0f1697b2ab | ||
|
|
6e626afdcb | ||
|
|
0fe94c1616 | ||
|
|
a42b004c72 | ||
|
|
35f57768fd | ||
|
|
9a90ce84fb | ||
|
|
133fe41cd5 | ||
|
|
74c1085103 | ||
|
|
497fc170fd | ||
|
|
3edab60560 | ||
|
|
3f2ac2b9b0 | ||
|
|
1577b44087 | ||
|
|
39f52f48f2 | ||
|
|
4fadbcfb90 | ||
|
|
08c5c401ba | ||
|
|
ba2e86c7ef | ||
|
|
6d056789c7 | ||
|
|
5d508cc9c2 | ||
|
|
d9233872b9 | ||
|
|
aa4dba1541 | ||
|
|
947a3e8aff | ||
|
|
9a3186317b | ||
|
|
b1e584ca1d | ||
|
|
bca523eb63 | ||
|
|
2ff4cd2c2b | ||
|
|
d686269377 | ||
|
|
491abd6b5b | ||
|
|
4518f96e3d | ||
|
|
a9dcbedf99 | ||
|
|
9231043eb4 | ||
|
|
04dcd87170 | ||
|
|
c31fd9ed79 | ||
|
|
2989d92794 | ||
|
|
256d074411 | ||
|
|
8b01676ec0 | ||
|
|
34c6c8494a | ||
|
|
522bed6b7d | ||
|
|
dda672284c | ||
|
|
6018364164 | ||
|
|
bc0d438cee | ||
|
|
abef91c223 | ||
|
|
1bbf6d38e5 | ||
|
|
c9c9db1e8d | ||
|
|
b11fe9fbc6 | ||
|
|
60f3d413de | ||
|
|
1df2d78b85 |
@@ -32,7 +32,7 @@ jobs:
|
|||||||
"webfetch": "deny"
|
"webfetch": "deny"
|
||||||
}
|
}
|
||||||
run: |
|
run: |
|
||||||
opencode run -m anthropic/claude-sonnet-4-20250514 "A new issue has been created: '${{ github.event.issue.title }}'
|
opencode run -m anthropic/claude-sonnet-4-20250514 "A new issue has been created:'
|
||||||
|
|
||||||
Issue number:
|
Issue number:
|
||||||
${{ github.event.issue.number }}
|
${{ github.event.issue.number }}
|
||||||
|
|||||||
@@ -8,22 +8,20 @@ jobs:
|
|||||||
opencode:
|
opencode:
|
||||||
if: |
|
if: |
|
||||||
contains(github.event.comment.body, ' /oc') ||
|
contains(github.event.comment.body, ' /oc') ||
|
||||||
startsWith(github.event.comment.body, '/oc') ||
|
contains(github.event.comment.body, ' /opencode')
|
||||||
contains(github.event.comment.body, ' /opencode') ||
|
|
||||||
startsWith(github.event.comment.body, '/opencode')
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
|
||||||
id-token: write
|
id-token: write
|
||||||
|
contents: read
|
||||||
|
pull-requests: read
|
||||||
|
issues: read
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
|
||||||
fetch-depth: 1
|
|
||||||
|
|
||||||
- name: Run opencode
|
- name: Run opencode
|
||||||
uses: sst/opencode/github@latest
|
uses: sst/opencode/github@latest
|
||||||
env:
|
env:
|
||||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
|
||||||
with:
|
with:
|
||||||
model: anthropic/claude-sonnet-4-20250514
|
model: opencode/sonic
|
||||||
@@ -27,3 +27,5 @@ Check out the /packages/web/src/content/docs/docs/index.mdx as an example.
|
|||||||
|
|
||||||
For JS or TS code snippets remove trailing semicolons and any trailing commas
|
For JS or TS code snippets remove trailing semicolons and any trailing commas
|
||||||
that might not be needed.
|
that might not be needed.
|
||||||
|
|
||||||
|
If you are making a commit prefix the commit message with `docs:`
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
commit and push
|
||||||
|
|
||||||
|
make sure it includes a prefix like
|
||||||
|
docs:
|
||||||
|
tui:
|
||||||
|
core:
|
||||||
|
ci:
|
||||||
|
ignore:
|
||||||
|
wip:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
description: hello world
|
||||||
|
---
|
||||||
|
|
||||||
|
hey there $ARGUMENTS
|
||||||
|
|
||||||
|
!`ls`
|
||||||
|
check out @README.md
|
||||||
@@ -107,4 +107,4 @@ The other confusingly named repo has no relation to this one. You can [read the
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Join our community** [Discord](https://discord.gg/opencode) | [YouTube](https://www.youtube.com/c/sst-dev) | [X.com](https://x.com/SST_dev)
|
**Join our community** [Discord](https://discord.gg/opencode) | [YouTube](https://www.youtube.com/c/sst-dev) | [X.com](https://x.com/anomaly_inv)
|
||||||
|
|||||||
@@ -54,3 +54,8 @@
|
|||||||
| 2025-08-18 | 204,559 (+3,260) | 187,399 (+1,130) | 391,958 (+4,390) |
|
| 2025-08-18 | 204,559 (+3,260) | 187,399 (+1,130) | 391,958 (+4,390) |
|
||||||
| 2025-08-19 | 209,814 (+5,255) | 189,668 (+2,269) | 399,482 (+7,524) |
|
| 2025-08-19 | 209,814 (+5,255) | 189,668 (+2,269) | 399,482 (+7,524) |
|
||||||
| 2025-08-20 | 214,497 (+4,683) | 191,481 (+1,813) | 405,978 (+6,496) |
|
| 2025-08-20 | 214,497 (+4,683) | 191,481 (+1,813) | 405,978 (+6,496) |
|
||||||
|
| 2025-08-21 | 220,465 (+5,968) | 194,784 (+3,303) | 415,249 (+9,271) |
|
||||||
|
| 2025-08-22 | 225,899 (+5,434) | 197,204 (+2,420) | 423,103 (+7,854) |
|
||||||
|
| 2025-08-23 | 229,005 (+3,106) | 199,238 (+2,034) | 428,243 (+5,140) |
|
||||||
|
| 2025-08-24 | 232,098 (+3,093) | 201,157 (+1,919) | 433,255 (+5,012) |
|
||||||
|
| 2025-08-25 | 236,607 (+4,509) | 202,650 (+1,493) | 439,257 (+6,002) |
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ibm/plex": "6.4.1",
|
"@ibm/plex": "6.4.1",
|
||||||
"@openauthjs/openauth": "0.0.0-20250322224806",
|
"@openauthjs/openauth": "0.0.0-20250322224806",
|
||||||
|
"@opencode/cloud-core": "workspace:*",
|
||||||
"@solidjs/meta": "^0.29.4",
|
"@solidjs/meta": "^0.29.4",
|
||||||
"@solidjs/router": "^0.15.0",
|
"@solidjs/router": "^0.15.0",
|
||||||
"@solidjs/start": "^1.1.0",
|
"@solidjs/start": "^1.1.0",
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
},
|
},
|
||||||
"cloud/core": {
|
"cloud/core": {
|
||||||
"name": "@opencode/cloud-core",
|
"name": "@opencode/cloud-core",
|
||||||
"version": "0.5.5",
|
"version": "0.5.15",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-sts": "3.782.0",
|
"@aws-sdk/client-sts": "3.782.0",
|
||||||
"drizzle-orm": "0.41.0",
|
"drizzle-orm": "0.41.0",
|
||||||
@@ -39,7 +40,7 @@
|
|||||||
},
|
},
|
||||||
"cloud/function": {
|
"cloud/function": {
|
||||||
"name": "@opencode/cloud-function",
|
"name": "@opencode/cloud-function",
|
||||||
"version": "0.5.5",
|
"version": "0.5.15",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/anthropic": "2.0.0",
|
"@ai-sdk/anthropic": "2.0.0",
|
||||||
"@ai-sdk/openai": "2.0.2",
|
"@ai-sdk/openai": "2.0.2",
|
||||||
@@ -59,7 +60,7 @@
|
|||||||
},
|
},
|
||||||
"cloud/web": {
|
"cloud/web": {
|
||||||
"name": "@opencode/cloud-web",
|
"name": "@opencode/cloud-web",
|
||||||
"version": "0.5.5",
|
"version": "0.5.15",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kobalte/core": "0.13.9",
|
"@kobalte/core": "0.13.9",
|
||||||
"@openauthjs/solid": "0.0.0-20250322224806",
|
"@openauthjs/solid": "0.0.0-20250322224806",
|
||||||
@@ -78,7 +79,7 @@
|
|||||||
},
|
},
|
||||||
"packages/function": {
|
"packages/function": {
|
||||||
"name": "@opencode/function",
|
"name": "@opencode/function",
|
||||||
"version": "0.5.5",
|
"version": "0.5.15",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@octokit/auth-app": "8.0.1",
|
"@octokit/auth-app": "8.0.1",
|
||||||
"@octokit/rest": "22.0.0",
|
"@octokit/rest": "22.0.0",
|
||||||
@@ -93,7 +94,7 @@
|
|||||||
},
|
},
|
||||||
"packages/opencode": {
|
"packages/opencode": {
|
||||||
"name": "opencode",
|
"name": "opencode",
|
||||||
"version": "0.5.5",
|
"version": "0.5.15",
|
||||||
"bin": {
|
"bin": {
|
||||||
"opencode": "./bin/opencode",
|
"opencode": "./bin/opencode",
|
||||||
},
|
},
|
||||||
@@ -143,19 +144,22 @@
|
|||||||
},
|
},
|
||||||
"packages/plugin": {
|
"packages/plugin": {
|
||||||
"name": "@opencode-ai/plugin",
|
"name": "@opencode-ai/plugin",
|
||||||
"version": "0.5.5",
|
"version": "0.5.15",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@hey-api/openapi-ts": "0.80.1",
|
"@hey-api/openapi-ts": "0.81.0",
|
||||||
"@tsconfig/node22": "catalog:",
|
"@tsconfig/node22": "catalog:",
|
||||||
"typescript": "catalog:",
|
"typescript": "catalog:",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"packages/sdk/js": {
|
"packages/sdk/js": {
|
||||||
"name": "@opencode-ai/sdk",
|
"name": "@opencode-ai/sdk",
|
||||||
"version": "0.5.5",
|
"version": "0.5.15",
|
||||||
|
"dependencies": {
|
||||||
|
"@hey-api/openapi-ts": "0.80.1",
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@hey-api/openapi-ts": "0.80.1",
|
"@hey-api/openapi-ts": "0.80.1",
|
||||||
"@tsconfig/node22": "catalog:",
|
"@tsconfig/node22": "catalog:",
|
||||||
@@ -164,7 +168,7 @@
|
|||||||
},
|
},
|
||||||
"packages/web": {
|
"packages/web": {
|
||||||
"name": "@opencode/web",
|
"name": "@opencode/web",
|
||||||
"version": "0.5.5",
|
"version": "0.5.15",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/cloudflare": "12.6.3",
|
"@astrojs/cloudflare": "12.6.3",
|
||||||
"@astrojs/markdown-remark": "6.3.1",
|
"@astrojs/markdown-remark": "6.3.1",
|
||||||
@@ -186,7 +190,7 @@
|
|||||||
"sharp": "0.32.5",
|
"sharp": "0.32.5",
|
||||||
"shiki": "3.4.2",
|
"shiki": "3.4.2",
|
||||||
"solid-js": "1.9.7",
|
"solid-js": "1.9.7",
|
||||||
"toolbeam-docs-theme": "0.4.3",
|
"toolbeam-docs-theme": "0.4.6",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "catalog:",
|
"@types/node": "catalog:",
|
||||||
@@ -203,6 +207,9 @@
|
|||||||
"web-tree-sitter",
|
"web-tree-sitter",
|
||||||
"tree-sitter-bash",
|
"tree-sitter-bash",
|
||||||
],
|
],
|
||||||
|
"overrides": {
|
||||||
|
"zod": "3.25.76",
|
||||||
|
},
|
||||||
"catalog": {
|
"catalog": {
|
||||||
"@hono/zod-validator": "0.4.2",
|
"@hono/zod-validator": "0.4.2",
|
||||||
"@tsconfig/node22": "22.0.2",
|
"@tsconfig/node22": "22.0.2",
|
||||||
@@ -488,7 +495,7 @@
|
|||||||
|
|
||||||
"@hey-api/json-schema-ref-parser": ["@hey-api/json-schema-ref-parser@1.0.6", "", { "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.0", "lodash": "^4.17.21" } }, "sha512-yktiFZoWPtEW8QKS65eqKwA5MTKp88CyiL8q72WynrBs/73SAaxlSWlA2zW/DZlywZ5hX1OYzrCC0wFdvO9c2w=="],
|
"@hey-api/json-schema-ref-parser": ["@hey-api/json-schema-ref-parser@1.0.6", "", { "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.0", "lodash": "^4.17.21" } }, "sha512-yktiFZoWPtEW8QKS65eqKwA5MTKp88CyiL8q72WynrBs/73SAaxlSWlA2zW/DZlywZ5hX1OYzrCC0wFdvO9c2w=="],
|
||||||
|
|
||||||
"@hey-api/openapi-ts": ["@hey-api/openapi-ts@0.80.1", "", { "dependencies": { "@hey-api/json-schema-ref-parser": "1.0.6", "ansi-colors": "4.1.3", "c12": "2.0.1", "color-support": "1.1.3", "commander": "13.0.0", "handlebars": "4.7.8", "open": "10.1.2", "semver": "7.7.2" }, "peerDependencies": { "typescript": "^5.5.3" }, "bin": { "openapi-ts": "bin/index.cjs" } }, "sha512-AC478kg36vmmrseLZNFonZ/cmXXmDzW5yWz4PVg1S8ebJsRtVRJ/QU+mtnXfzf9avN2P0pz/AO4WAe4jyFY2gA=="],
|
"@hey-api/openapi-ts": ["@hey-api/openapi-ts@0.81.0", "", { "dependencies": { "@hey-api/json-schema-ref-parser": "1.0.6", "ansi-colors": "4.1.3", "c12": "2.0.1", "color-support": "1.1.3", "commander": "13.0.0", "handlebars": "4.7.8", "js-yaml": "4.1.0", "open": "10.1.2", "semver": "7.7.2" }, "peerDependencies": { "typescript": "^5.5.3" }, "bin": { "openapi-ts": "bin/index.cjs" } }, "sha512-PoJukNBkUfHOoMDpN33bBETX49TUhy7Hu8Sa0jslOvFndvZ5VjQr4Nl/Dzjb9LG1Lp5HjybyTJMA6a1zYk/q6A=="],
|
||||||
|
|
||||||
"@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=="],
|
||||||
|
|
||||||
@@ -2804,7 +2811,7 @@
|
|||||||
|
|
||||||
"toml": ["toml@3.0.0", "", {}, "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w=="],
|
"toml": ["toml@3.0.0", "", {}, "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w=="],
|
||||||
|
|
||||||
"toolbeam-docs-theme": ["toolbeam-docs-theme@0.4.3", "", { "peerDependencies": { "@astrojs/starlight": "^0.34.3", "astro": "^5.7.13" } }, "sha512-3um/NsSq4xFeKbKrNGPHIzfTixwnEVvroqA8Q+lecnYHHJ5TtiYTggHDqewOW+I67t0J1IVBwVKUPjxiQfIcog=="],
|
"toolbeam-docs-theme": ["toolbeam-docs-theme@0.4.6", "", { "peerDependencies": { "@astrojs/starlight": "^0.34.3", "astro": "^5.7.13" } }, "sha512-s4yKn3PYnmPCl6MHPkQ2MedSgVh7FMAP64DiXnAfCls/H/RR6iTe0/SFgPTprz+HXXaKvv2iPo3XuvmDRY6cxQ=="],
|
||||||
|
|
||||||
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
|
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
|
||||||
|
|
||||||
@@ -3062,8 +3069,6 @@
|
|||||||
|
|
||||||
"@astrojs/mdx/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
|
"@astrojs/mdx/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
|
||||||
|
|
||||||
"@astrojs/sitemap/zod": ["zod@3.25.49", "", {}, "sha512-JMMPMy9ZBk3XFEdbM3iL1brx4NUSejd6xr3ELrrGEfGb355gjhiAWtG3K5o+AViV/3ZfkIrCzXsZn6SbLwTR8Q=="],
|
|
||||||
|
|
||||||
"@astrojs/solid-js/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="],
|
"@astrojs/solid-js/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="],
|
||||||
|
|
||||||
"@astrojs/solid-js/vite-plugin-solid": ["vite-plugin-solid@2.11.8", "", { "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" }, "optionalPeers": ["@testing-library/jest-dom"] }, "sha512-hFrCxBfv3B1BmFqnJF4JOCYpjrmi/zwyeKjcomQ0khh8HFyQ8SbuBWQ7zGojfrz6HUOBFrJBNySDi/JgAHytWg=="],
|
"@astrojs/solid-js/vite-plugin-solid": ["vite-plugin-solid@2.11.8", "", { "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" }, "optionalPeers": ["@testing-library/jest-dom"] }, "sha512-hFrCxBfv3B1BmFqnJF4JOCYpjrmi/zwyeKjcomQ0khh8HFyQ8SbuBWQ7zGojfrz6HUOBFrJBNySDi/JgAHytWg=="],
|
||||||
@@ -3160,8 +3165,6 @@
|
|||||||
|
|
||||||
"@mdx-js/mdx/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
|
"@mdx-js/mdx/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
|
||||||
|
|
||||||
"@modelcontextprotocol/sdk/zod": ["zod@3.25.49", "", {}, "sha512-JMMPMy9ZBk3XFEdbM3iL1brx4NUSejd6xr3ELrrGEfGb355gjhiAWtG3K5o+AViV/3ZfkIrCzXsZn6SbLwTR8Q=="],
|
|
||||||
|
|
||||||
"@netlify/dev-utils/find-up": ["find-up@7.0.0", "", { "dependencies": { "locate-path": "^7.2.0", "path-exists": "^5.0.0", "unicorn-magic": "^0.1.0" } }, "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g=="],
|
"@netlify/dev-utils/find-up": ["find-up@7.0.0", "", { "dependencies": { "locate-path": "^7.2.0", "path-exists": "^5.0.0", "unicorn-magic": "^0.1.0" } }, "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g=="],
|
||||||
|
|
||||||
"@netlify/dev-utils/uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="],
|
"@netlify/dev-utils/uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="],
|
||||||
@@ -3208,6 +3211,8 @@
|
|||||||
|
|
||||||
"@openauthjs/solid/@openauthjs/openauth": ["@openauthjs/openauth@0.4.2", "", { "dependencies": { "@standard-schema/spec": "1.0.0-beta.3", "aws4fetch": "1.0.20", "jose": "5.9.6" }, "peerDependencies": { "arctic": "^2.2.2", "hono": "^4.0.0" } }, "sha512-8+Bia559iffrZXfQ0LWXrVVVriochS88pDtB8indyQ1S+40MQgDBu8aBzKt+fgSrTmoQGCTT+wlOXgbjc9qIcw=="],
|
"@openauthjs/solid/@openauthjs/openauth": ["@openauthjs/openauth@0.4.2", "", { "dependencies": { "@standard-schema/spec": "1.0.0-beta.3", "aws4fetch": "1.0.20", "jose": "5.9.6" }, "peerDependencies": { "arctic": "^2.2.2", "hono": "^4.0.0" } }, "sha512-8+Bia559iffrZXfQ0LWXrVVVriochS88pDtB8indyQ1S+40MQgDBu8aBzKt+fgSrTmoQGCTT+wlOXgbjc9qIcw=="],
|
||||||
|
|
||||||
|
"@opencode-ai/sdk/@hey-api/openapi-ts": ["@hey-api/openapi-ts@0.80.1", "", { "dependencies": { "@hey-api/json-schema-ref-parser": "1.0.6", "ansi-colors": "4.1.3", "c12": "2.0.1", "color-support": "1.1.3", "commander": "13.0.0", "handlebars": "4.7.8", "open": "10.1.2", "semver": "7.7.2" }, "peerDependencies": { "typescript": "^5.5.3" }, "bin": { "openapi-ts": "bin/index.cjs" } }, "sha512-AC478kg36vmmrseLZNFonZ/cmXXmDzW5yWz4PVg1S8ebJsRtVRJ/QU+mtnXfzf9avN2P0pz/AO4WAe4jyFY2gA=="],
|
||||||
|
|
||||||
"@opencode/cloud-web/solid-js": ["solid-js@1.9.5", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "^1.1.0", "seroval-plugins": "^1.1.0" } }, "sha512-ogI3DaFcyn6UhYhrgcyRAMbu/buBJitYQASZz5WzfQVPP10RD2AbCoRZ517psnezrasyCbWzIxZ6kVqet768xw=="],
|
"@opencode/cloud-web/solid-js": ["solid-js@1.9.5", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "^1.1.0", "seroval-plugins": "^1.1.0" } }, "sha512-ogI3DaFcyn6UhYhrgcyRAMbu/buBJitYQASZz5WzfQVPP10RD2AbCoRZ517psnezrasyCbWzIxZ6kVqet768xw=="],
|
||||||
|
|
||||||
"@opentelemetry/instrumentation-grpc/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.27.0", "", {}, "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg=="],
|
"@opentelemetry/instrumentation-grpc/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.27.0", "", {}, "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg=="],
|
||||||
@@ -3338,8 +3343,6 @@
|
|||||||
|
|
||||||
"astro/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="],
|
"astro/vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="],
|
||||||
|
|
||||||
"astro/zod": ["zod@3.25.49", "", {}, "sha512-JMMPMy9ZBk3XFEdbM3iL1brx4NUSejd6xr3ELrrGEfGb355gjhiAWtG3K5o+AViV/3ZfkIrCzXsZn6SbLwTR8Q=="],
|
|
||||||
|
|
||||||
"babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="],
|
"babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="],
|
||||||
|
|
||||||
"bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
|
"bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
|
||||||
@@ -3416,8 +3419,6 @@
|
|||||||
|
|
||||||
"miniflare/youch": ["youch@4.1.0-beta.10", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@poppinss/dumper": "^0.6.4", "@speed-highlight/core": "^1.2.7", "cookie": "^1.0.2", "youch-core": "^0.3.3" } }, "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ=="],
|
"miniflare/youch": ["youch@4.1.0-beta.10", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@poppinss/dumper": "^0.6.4", "@speed-highlight/core": "^1.2.7", "cookie": "^1.0.2", "youch-core": "^0.3.3" } }, "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ=="],
|
||||||
|
|
||||||
"miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="],
|
|
||||||
|
|
||||||
"minipass-flush/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
|
"minipass-flush/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
|
||||||
|
|
||||||
"minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
|
"minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
|
||||||
@@ -3462,8 +3463,6 @@
|
|||||||
|
|
||||||
"opencontrol/hono": ["hono@4.7.4", "", {}, "sha512-Pst8FuGqz3L7tFF+u9Pu70eI0xa5S3LPUmrNd5Jm8nTHze9FxLTK9Kaj5g/k4UcwuJSXTP65SyHOPLrffpcAJg=="],
|
"opencontrol/hono": ["hono@4.7.4", "", {}, "sha512-Pst8FuGqz3L7tFF+u9Pu70eI0xa5S3LPUmrNd5Jm8nTHze9FxLTK9Kaj5g/k4UcwuJSXTP65SyHOPLrffpcAJg=="],
|
||||||
|
|
||||||
"opencontrol/zod": ["zod@3.24.2", "", {}, "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="],
|
|
||||||
|
|
||||||
"opencontrol/zod-to-json-schema": ["zod-to-json-schema@3.24.3", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A=="],
|
"opencontrol/zod-to-json-schema": ["zod-to-json-schema@3.24.3", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A=="],
|
||||||
|
|
||||||
"openid-client/jose": ["jose@4.15.9", "", {}, "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA=="],
|
"openid-client/jose": ["jose@4.15.9", "", {}, "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA=="],
|
||||||
@@ -3882,8 +3881,6 @@
|
|||||||
|
|
||||||
"opencontrol/@modelcontextprotocol/sdk/pkce-challenge": ["pkce-challenge@4.1.0", "", {}, "sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ=="],
|
"opencontrol/@modelcontextprotocol/sdk/pkce-challenge": ["pkce-challenge@4.1.0", "", {}, "sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ=="],
|
||||||
|
|
||||||
"opencontrol/@modelcontextprotocol/sdk/zod": ["zod@3.25.49", "", {}, "sha512-JMMPMy9ZBk3XFEdbM3iL1brx4NUSejd6xr3ELrrGEfGb355gjhiAWtG3K5o+AViV/3ZfkIrCzXsZn6SbLwTR8Q=="],
|
|
||||||
|
|
||||||
"opencontrol/@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.24.5", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g=="],
|
"opencontrol/@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.24.5", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g=="],
|
||||||
|
|
||||||
"prebuild-install/tar-fs/tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="],
|
"prebuild-install/tar-fs/tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="],
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"dev": "vinxi dev --host 0.0.0.0",
|
"dev": "vinxi dev --host 0.0.0.0",
|
||||||
"build": "vinxi build",
|
"build": "vinxi build",
|
||||||
"start": "vinxi start",
|
"start": "vinxi start",
|
||||||
"version": "0.5.9"
|
"version": "0.5.25"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ibm/plex": "6.4.1",
|
"@ibm/plex": "6.4.1",
|
||||||
@@ -14,7 +14,8 @@
|
|||||||
"@solidjs/router": "^0.15.0",
|
"@solidjs/router": "^0.15.0",
|
||||||
"@solidjs/start": "^1.1.0",
|
"@solidjs/start": "^1.1.0",
|
||||||
"solid-js": "^1.9.5",
|
"solid-js": "^1.9.5",
|
||||||
"vinxi": "^0.5.7"
|
"vinxi": "^0.5.7",
|
||||||
|
"@opencode/cloud-core": "workspace:*"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=22"
|
"node": ">=22"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { MetaProvider, Title } from "@solidjs/meta";
|
import { MetaProvider, Title } from "@solidjs/meta";
|
||||||
import { Router } from "@solidjs/router";
|
import { Router } from "@solidjs/router";
|
||||||
import { FileRoutes } from "@solidjs/start/router";
|
import { FileRoutes } from "@solidjs/start/router";
|
||||||
import { Suspense } from "solid-js";
|
import { ErrorBoundary, Suspense } from "solid-js";
|
||||||
import "@ibm/plex/css/ibm-plex.css";
|
import "@ibm/plex/css/ibm-plex.css";
|
||||||
import "./app.css";
|
import "./app.css";
|
||||||
|
|
||||||
@@ -11,7 +11,9 @@ export default function App() {
|
|||||||
root={props => (
|
root={props => (
|
||||||
<MetaProvider>
|
<MetaProvider>
|
||||||
<Title>SolidStart - Basic</Title>
|
<Title>SolidStart - Basic</Title>
|
||||||
<Suspense>{props.children}</Suspense>
|
<ErrorBoundary fallback={<div>Something went wrong</div>}>
|
||||||
|
<Suspense>{props.children}</Suspense>
|
||||||
|
</ErrorBoundary>
|
||||||
</MetaProvider>
|
</MetaProvider>
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,9 +1,90 @@
|
|||||||
|
|
||||||
|
|
||||||
import { useSession } from "vinxi/http"
|
import { useSession } from "vinxi/http"
|
||||||
import { createClient } from "@openauthjs/openauth/client"
|
import { createClient } from "@openauthjs/openauth/client"
|
||||||
|
import { getRequestEvent } from "solid-js/web"
|
||||||
|
import { and, Database, eq, inArray } from "@opencode/cloud-core/drizzle/index.js"
|
||||||
|
import { WorkspaceTable } from "@opencode/cloud-core/schema/workspace.sql.js"
|
||||||
|
import { UserTable } from "@opencode/cloud-core/schema/user.sql.js"
|
||||||
|
import { query, redirect } from "@solidjs/router"
|
||||||
|
import { AccountTable } from "@opencode/cloud-core/schema/account.sql.js"
|
||||||
|
import { Actor } from "@opencode/cloud-core/actor.js"
|
||||||
|
|
||||||
|
export async function withActor<T>(fn: () => T) {
|
||||||
|
const actor = await getActor()
|
||||||
|
return Actor.provide(actor.type, actor.properties, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getActor = query(async (): Promise<Actor.Info> => {
|
||||||
|
"use server"
|
||||||
|
const evt = getRequestEvent()
|
||||||
|
const url = new URL(evt!.request.headers.get("referer") ?? evt!.request.url)
|
||||||
|
const auth = await useAuthSession()
|
||||||
|
const [workspaceHint] = url.pathname.split("/").filter((x) => x.length > 0)
|
||||||
|
if (!workspaceHint) {
|
||||||
|
if (auth.data.current) {
|
||||||
|
const current = auth.data.account[auth.data.current]
|
||||||
|
return {
|
||||||
|
type: "account",
|
||||||
|
properties: {
|
||||||
|
email: current.email,
|
||||||
|
accountID: current.id,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Object.keys(auth.data.account).length > 0) {
|
||||||
|
const current = Object.values(auth.data.account)[0]
|
||||||
|
await auth.update(val => ({
|
||||||
|
...val,
|
||||||
|
current: current.id,
|
||||||
|
}))
|
||||||
|
return {
|
||||||
|
type: "account",
|
||||||
|
properties: {
|
||||||
|
email: current.email,
|
||||||
|
accountID: current.id,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: "public",
|
||||||
|
properties: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const accounts = Object.keys(auth.data.account)
|
||||||
|
const result = await Database.transaction(async (tx) => {
|
||||||
|
return await tx.select({
|
||||||
|
user: UserTable
|
||||||
|
})
|
||||||
|
.from(AccountTable)
|
||||||
|
.innerJoin(UserTable, and(eq(UserTable.email, AccountTable.email)))
|
||||||
|
.innerJoin(WorkspaceTable, eq(WorkspaceTable.id, UserTable.workspaceID))
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
inArray(AccountTable.id, accounts),
|
||||||
|
eq(WorkspaceTable.id, workspaceHint),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.limit(1)
|
||||||
|
.execute()
|
||||||
|
.then((x) => x[0])
|
||||||
|
})
|
||||||
|
if (result) {
|
||||||
|
return {
|
||||||
|
type: "user",
|
||||||
|
properties: {
|
||||||
|
userID: result.user.id,
|
||||||
|
workspaceID: result.user.workspaceID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw redirect("/auth/authorize")
|
||||||
|
}, "actor")
|
||||||
|
|
||||||
|
|
||||||
export const AuthClient = createClient({
|
export const AuthClient = createClient({
|
||||||
clientID: "app",
|
clientID: "app",
|
||||||
issuer: "https://auth.dev.opencode.ai",
|
issuer: import.meta.env.VITE_AUTH_URL,
|
||||||
})
|
})
|
||||||
|
|
||||||
export interface AuthSession {
|
export interface AuthSession {
|
||||||
@@ -15,7 +96,6 @@ export interface AuthSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useAuthSession() {
|
export function useAuthSession() {
|
||||||
"use server"
|
|
||||||
|
|
||||||
return useSession<AuthSession>({
|
return useSession<AuthSession>({
|
||||||
password: "0".repeat(32),
|
password: "0".repeat(32),
|
||||||
@@ -26,3 +106,4 @@ export function useAuthSession() {
|
|||||||
|
|
||||||
export function AuthProvider() {
|
export function AuthProvider() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { createAsync, query } from "@solidjs/router"
|
||||||
|
import { getActor, withActor } from "~/context/auth"
|
||||||
|
|
||||||
|
const getPosts = query(async () => {
|
||||||
|
"use server"
|
||||||
|
return withActor(() => {
|
||||||
|
return "ok"
|
||||||
|
})
|
||||||
|
}, "posts")
|
||||||
|
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
const actor = createAsync(async () => getActor())
|
||||||
|
return <div>{JSON.stringify(actor())}</div>
|
||||||
|
}
|
||||||
@@ -5,11 +5,6 @@ export async function GET(input: APIEvent) {
|
|||||||
const url = new URL(input.request.url)
|
const url = new URL(input.request.url)
|
||||||
const code = url.searchParams.get("code")
|
const code = url.searchParams.get("code")
|
||||||
if (!code) throw new Error("No code found")
|
if (!code) throw new Error("No code found")
|
||||||
const redirectURI = `${url.origin}${url.pathname}`
|
|
||||||
console.log({
|
|
||||||
redirectURI,
|
|
||||||
code,
|
|
||||||
})
|
|
||||||
const result = await AuthClient.exchange(code, `${url.origin}${url.pathname}`)
|
const result = await AuthClient.exchange(code, `${url.origin}${url.pathname}`)
|
||||||
if (result.err) {
|
if (result.err) {
|
||||||
throw new Error(result.err.message)
|
throw new Error(result.err.message)
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import IMG_SPLASH from "../asset/screenshot-splash.webp"
|
|||||||
import IMG_VSCODE from "../asset/screenshot-vscode.webp"
|
import IMG_VSCODE from "../asset/screenshot-vscode.webp"
|
||||||
import IMG_GITHUB from "../asset/screenshot-github.webp"
|
import IMG_GITHUB from "../asset/screenshot-github.webp"
|
||||||
import { IconCopy, IconCheck } from "../component/icon"
|
import { IconCopy, IconCheck } from "../component/icon"
|
||||||
|
import { createAsync, query, redirect, RouteDefinition } from "@solidjs/router"
|
||||||
|
import { getActor, withActor } from "~/context/auth"
|
||||||
|
import { Account } from "@opencode/cloud-core/account.js"
|
||||||
|
|
||||||
function CopyStatus() {
|
function CopyStatus() {
|
||||||
return (
|
return (
|
||||||
@@ -16,7 +19,22 @@ function CopyStatus() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isLoggedIn = query(async () => {
|
||||||
|
"use server"
|
||||||
|
const actor = await getActor()
|
||||||
|
if (actor.type === "account") {
|
||||||
|
const workspaces = await withActor(() => Account.workspaces())
|
||||||
|
throw redirect("/" + workspaces[0].id)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}, "isLoggedIn")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
|
createAsync(() => isLoggedIn(), {
|
||||||
|
deferStream: true,
|
||||||
|
})
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const commands = document.querySelectorAll("[data-copy]")
|
const commands = document.querySelectorAll("[data-copy]")
|
||||||
for (const button of commands) {
|
for (const button of commands) {
|
||||||
|
|||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
/* This file is auto-generated by SST. Do not edit. */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
/* deno-fmt-ignore-file */
|
||||||
|
|
||||||
|
/// <reference path="../../sst-env.d.ts" />
|
||||||
|
|
||||||
|
import "sst"
|
||||||
|
export {}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"name": "@opencode/cloud-core",
|
"name": "@opencode/cloud-core",
|
||||||
"version": "0.5.9",
|
"version": "0.5.25",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ export namespace Actor {
|
|||||||
properties: {
|
properties: {
|
||||||
userID: string
|
userID: string
|
||||||
workspaceID: string
|
workspaceID: string
|
||||||
email: string
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Resource } from "sst"
|
|||||||
export * from "drizzle-orm"
|
export * from "drizzle-orm"
|
||||||
import postgres from "postgres"
|
import postgres from "postgres"
|
||||||
|
|
||||||
function createClient() {
|
const createClient = memo(() => {
|
||||||
const client = postgres({
|
const client = postgres({
|
||||||
idle_timeout: 30000,
|
idle_timeout: 30000,
|
||||||
connect_timeout: 30000,
|
connect_timeout: 30000,
|
||||||
@@ -19,12 +19,13 @@ function createClient() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return drizzle(client, {})
|
return drizzle(client, {})
|
||||||
}
|
})
|
||||||
|
|
||||||
import { PgTransaction, type PgTransactionConfig } from "drizzle-orm/pg-core"
|
import { PgTransaction, type PgTransactionConfig } from "drizzle-orm/pg-core"
|
||||||
import type { ExtractTablesWithRelations } from "drizzle-orm"
|
import type { ExtractTablesWithRelations } from "drizzle-orm"
|
||||||
import type { PostgresJsQueryResultHKT } from "drizzle-orm/postgres-js"
|
import type { PostgresJsQueryResultHKT } from "drizzle-orm/postgres-js"
|
||||||
import { Context } from "../context"
|
import { Context } from "../context"
|
||||||
|
import { memo } from "../util/memo"
|
||||||
|
|
||||||
export namespace Database {
|
export namespace Database {
|
||||||
export type Transaction = PgTransaction<
|
export type Transaction = PgTransaction<
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export function memo<T>(fn: () => T) {
|
||||||
|
let value: T | undefined
|
||||||
|
let loaded = false
|
||||||
|
|
||||||
|
return (): T => {
|
||||||
|
if (loaded) return value as T
|
||||||
|
loaded = true
|
||||||
|
value = fn()
|
||||||
|
return value as T
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode/cloud-function",
|
"name": "@opencode/cloud-function",
|
||||||
"version": "0.5.9",
|
"version": "0.5.25",
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ import { Resource } from "sst"
|
|||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
import { issuer } from "@openauthjs/openauth"
|
import { issuer } from "@openauthjs/openauth"
|
||||||
import { createSubjects } from "@openauthjs/openauth/subject"
|
import { createSubjects } from "@openauthjs/openauth/subject"
|
||||||
import { CodeProvider } from "@openauthjs/openauth/provider/code"
|
|
||||||
import { GithubProvider } from "@openauthjs/openauth/provider/github"
|
import { GithubProvider } from "@openauthjs/openauth/provider/github"
|
||||||
import { GoogleOidcProvider } from "@openauthjs/openauth/provider/google"
|
import { GoogleOidcProvider } from "@openauthjs/openauth/provider/google"
|
||||||
import { CloudflareStorage } from "@openauthjs/openauth/storage/cloudflare"
|
import { CloudflareStorage } from "@openauthjs/openauth/storage/cloudflare"
|
||||||
import { Account } from "@opencode/cloud-core/account.js"
|
import { Account } from "@opencode/cloud-core/account.js"
|
||||||
|
import { Workspace } from "@opencode/cloud-core/workspace.js"
|
||||||
|
import { Actor } from "@opencode/cloud-core/actor.js"
|
||||||
|
|
||||||
type Env = {
|
type Env = {
|
||||||
AuthStorage: KVNamespace
|
AuthStorage: KVNamespace
|
||||||
@@ -117,6 +118,12 @@ export default {
|
|||||||
email: email!,
|
email: email!,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
await Actor.provide("account", { accountID, email }, async () => {
|
||||||
|
const workspaces = await Account.workspaces()
|
||||||
|
if (workspaces.length === 0) {
|
||||||
|
await Workspace.create()
|
||||||
|
}
|
||||||
|
})
|
||||||
return ctx.subject("account", accountID, { accountID, email })
|
return ctx.subject("account", accountID, { accountID, email })
|
||||||
},
|
},
|
||||||
}).fetch(request, env, ctx)
|
}).fetch(request, env, ctx)
|
||||||
|
|||||||
Vendored
-4
@@ -14,10 +14,6 @@ declare module "sst" {
|
|||||||
"type": "sst.sst.Linkable"
|
"type": "sst.sst.Linkable"
|
||||||
"value": string
|
"value": string
|
||||||
}
|
}
|
||||||
"Console": {
|
|
||||||
"type": "sst.cloudflare.StaticSite"
|
|
||||||
"url": string
|
|
||||||
}
|
|
||||||
"DATABASE_PASSWORD": {
|
"DATABASE_PASSWORD": {
|
||||||
"type": "sst.sst.Secret"
|
"type": "sst.sst.Secret"
|
||||||
"value": string
|
"value": string
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode/cloud-web",
|
"name": "@opencode/cloud-web",
|
||||||
"version": "0.5.9",
|
"version": "0.5.25",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "",
|
"description": "",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
/* This file is auto-generated by SST. Do not edit. */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
/* deno-fmt-ignore-file */
|
||||||
|
|
||||||
|
/// <reference path="../sst-env.d.ts" />
|
||||||
|
|
||||||
|
import "sst"
|
||||||
|
export {}
|
||||||
+2
-2
@@ -25,8 +25,8 @@ export const api = new sst.cloudflare.Worker("Api", {
|
|||||||
])
|
])
|
||||||
args.migrations = {
|
args.migrations = {
|
||||||
// Note: when releasing the next tag, make sure all stages use tag v2
|
// Note: when releasing the next tag, make sure all stages use tag v2
|
||||||
oldTag: $app.stage === "production" ? "" : "v1",
|
oldTag: $app.stage === "production" || $app.stage === "thdxr" ? "" : "v1",
|
||||||
newTag: $app.stage === "production" ? "" : "v1",
|
newTag: $app.stage === "production" || $app.stage === "thdxr" ? "" : "v1",
|
||||||
//newSqliteClasses: ["SyncServer"],
|
//newSqliteClasses: ["SyncServer"],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
+14
-1
@@ -10,7 +10,7 @@ const DATABASE_USERNAME = new sst.Secret("DATABASE_USERNAME")
|
|||||||
const DATABASE_PASSWORD = new sst.Secret("DATABASE_PASSWORD")
|
const DATABASE_PASSWORD = new sst.Secret("DATABASE_PASSWORD")
|
||||||
export const database = new sst.Linkable("Database", {
|
export const database = new sst.Linkable("Database", {
|
||||||
properties: {
|
properties: {
|
||||||
host: "aws-us-east-2-1.pg.psdb.cloud",
|
host: `aws-us-east-2-${$app.stage === "thdxr" ? "2" : "1"}.pg.psdb.cloud`,
|
||||||
database: "postgres",
|
database: "postgres",
|
||||||
username: DATABASE_USERNAME.value,
|
username: DATABASE_USERNAME.value,
|
||||||
password: DATABASE_PASSWORD.value,
|
password: DATABASE_PASSWORD.value,
|
||||||
@@ -106,6 +106,7 @@ export const gateway = new sst.cloudflare.Worker("GatewayApi", {
|
|||||||
// CONSOLE
|
// CONSOLE
|
||||||
////////////////
|
////////////////
|
||||||
|
|
||||||
|
/*
|
||||||
export const console = new sst.cloudflare.x.StaticSite("Console", {
|
export const console = new sst.cloudflare.x.StaticSite("Console", {
|
||||||
domain: `console.${domain}`,
|
domain: `console.${domain}`,
|
||||||
path: "cloud/web",
|
path: "cloud/web",
|
||||||
@@ -119,3 +120,15 @@ export const console = new sst.cloudflare.x.StaticSite("Console", {
|
|||||||
VITE_AUTH_URL: auth.url.apply((url) => url!),
|
VITE_AUTH_URL: auth.url.apply((url) => url!),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
*/
|
||||||
|
|
||||||
|
new sst.x.DevCommand("Solid", {
|
||||||
|
link: [database],
|
||||||
|
dev: {
|
||||||
|
directory: "cloud/app",
|
||||||
|
command: "bun dev",
|
||||||
|
},
|
||||||
|
environment: {
|
||||||
|
VITE_AUTH_URL: auth.url.apply((url) => url!),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|||||||
+1
-4
@@ -1,10 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://opencode.ai/config.json",
|
"$schema": "https://opencode.ai/config.json",
|
||||||
|
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"context7": {
|
|
||||||
"type": "remote",
|
|
||||||
"url": "https://mcp.context7.com/sse"
|
|
||||||
},
|
|
||||||
"weather": {
|
"weather": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"command": ["opencode", "x", "@h1deya/mcp-server-weather"]
|
"command": ["opencode", "x", "@h1deya/mcp-server-weather"]
|
||||||
|
|||||||
@@ -51,5 +51,8 @@
|
|||||||
"tree-sitter-bash",
|
"tree-sitter-bash",
|
||||||
"web-tree-sitter"
|
"web-tree-sitter"
|
||||||
],
|
],
|
||||||
|
"overrides": {
|
||||||
|
"zod": "3.25.76"
|
||||||
|
},
|
||||||
"patchedDependencies": {}
|
"patchedDependencies": {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode/function",
|
"name": "@opencode/function",
|
||||||
"version": "0.5.9",
|
"version": "0.5.25",
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
Vendored
-4
@@ -14,10 +14,6 @@ declare module "sst" {
|
|||||||
"type": "sst.sst.Linkable"
|
"type": "sst.sst.Linkable"
|
||||||
"value": string
|
"value": string
|
||||||
}
|
}
|
||||||
"Console": {
|
|
||||||
"type": "sst.cloudflare.StaticSite"
|
|
||||||
"url": string
|
|
||||||
}
|
|
||||||
"DATABASE_PASSWORD": {
|
"DATABASE_PASSWORD": {
|
||||||
"type": "sst.sst.Secret"
|
"type": "sst.sst.Secret"
|
||||||
"value": string
|
"value": string
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"version": "0.5.9",
|
"version": "0.5.25",
|
||||||
"name": "opencode",
|
"name": "opencode",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
|||||||
@@ -97,12 +97,11 @@ if (!snapshot) {
|
|||||||
const macX64Sha = await $`sha256sum ./dist/opencode-darwin-x64.zip | cut -d' ' -f1`.text().then((x) => x.trim())
|
const macX64Sha = await $`sha256sum ./dist/opencode-darwin-x64.zip | cut -d' ' -f1`.text().then((x) => x.trim())
|
||||||
const macArm64Sha = await $`sha256sum ./dist/opencode-darwin-arm64.zip | cut -d' ' -f1`.text().then((x) => x.trim())
|
const macArm64Sha = await $`sha256sum ./dist/opencode-darwin-arm64.zip | cut -d' ' -f1`.text().then((x) => x.trim())
|
||||||
|
|
||||||
/* AUR package - commented out as AUR is down
|
const binaryPkgbuild = [
|
||||||
const pkgbuild = [
|
|
||||||
"# Maintainer: dax",
|
"# Maintainer: dax",
|
||||||
"# Maintainer: adam",
|
"# Maintainer: adam",
|
||||||
"",
|
"",
|
||||||
"pkgname='${pkg}'",
|
"pkgname='opencode-bin'",
|
||||||
`pkgver=${version.split("-")[0]}`,
|
`pkgver=${version.split("-")[0]}`,
|
||||||
"options=('!debug' '!strip')",
|
"options=('!debug' '!strip')",
|
||||||
"pkgrel=1",
|
"pkgrel=1",
|
||||||
@@ -126,17 +125,63 @@ if (!snapshot) {
|
|||||||
"",
|
"",
|
||||||
].join("\n")
|
].join("\n")
|
||||||
|
|
||||||
for (const pkg of ["opencode-bin"]) {
|
// Source-based PKGBUILD for opencode
|
||||||
|
const sourcePkgbuild = [
|
||||||
|
"# Maintainer: dax",
|
||||||
|
"# Maintainer: adam",
|
||||||
|
"",
|
||||||
|
"pkgname='opencode'",
|
||||||
|
`pkgver=${version.split("-")[0]}`,
|
||||||
|
"options=('!debug' '!strip')",
|
||||||
|
"pkgrel=1",
|
||||||
|
"pkgdesc='The AI coding agent built for the terminal.'",
|
||||||
|
"url='https://github.com/sst/opencode'",
|
||||||
|
"arch=('aarch64' 'x86_64')",
|
||||||
|
"license=('MIT')",
|
||||||
|
"provides=('opencode')",
|
||||||
|
"conflicts=('opencode-bin')",
|
||||||
|
"depends=('fzf' 'ripgrep')",
|
||||||
|
"makedepends=('git' 'bun-bin' 'go')",
|
||||||
|
"",
|
||||||
|
`source=("opencode-\${pkgver}.tar.gz::https://github.com/sst/opencode/archive/v${version}.tar.gz")`,
|
||||||
|
`sha256sums=('SKIP')`,
|
||||||
|
"",
|
||||||
|
"build() {",
|
||||||
|
` cd "opencode-\${pkgver}"`,
|
||||||
|
` bun install`,
|
||||||
|
" cd packages/tui",
|
||||||
|
` CGO_ENABLED=0 go build -ldflags="-s -w -X main.Version=\${pkgver}" -o tui cmd/opencode/main.go`,
|
||||||
|
" cd ../opencode",
|
||||||
|
` bun build --define OPENCODE_TUI_PATH="'$(realpath ../tui/tui)'" --define OPENCODE_VERSION="'\${pkgver}'" --compile --target=bun-linux-x64 --outfile=opencode ./src/index.ts`,
|
||||||
|
"}",
|
||||||
|
"",
|
||||||
|
"package() {",
|
||||||
|
` cd "opencode-\${pkgver}/packages/opencode"`,
|
||||||
|
' install -Dm755 ./opencode "${pkgdir}/usr/bin/opencode"',
|
||||||
|
"}",
|
||||||
|
"",
|
||||||
|
].join("\n")
|
||||||
|
|
||||||
|
for (const [pkg, pkgbuild] of [
|
||||||
|
["opencode-bin", binaryPkgbuild],
|
||||||
|
["opencode", sourcePkgbuild],
|
||||||
|
]) {
|
||||||
await $`rm -rf ./dist/aur-${pkg}`
|
await $`rm -rf ./dist/aur-${pkg}`
|
||||||
await $`git clone ssh://aur@aur.archlinux.org/${pkg}.git ./dist/aur-${pkg}`
|
while (true) {
|
||||||
|
try {
|
||||||
|
await $`git clone ssh://aur@aur.archlinux.org/${pkg}.git ./dist/aur-${pkg}`
|
||||||
|
break
|
||||||
|
} catch (e) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
await $`cd ./dist/aur-${pkg} && git checkout master`
|
await $`cd ./dist/aur-${pkg} && git checkout master`
|
||||||
await Bun.file(`./dist/aur-${pkg}/PKGBUILD`).write(pkgbuild.replace("${pkg}", pkg))
|
await Bun.file(`./dist/aur-${pkg}/PKGBUILD`).write(pkgbuild)
|
||||||
await $`cd ./dist/aur-${pkg} && makepkg --printsrcinfo > .SRCINFO`
|
await $`cd ./dist/aur-${pkg} && makepkg --printsrcinfo > .SRCINFO`
|
||||||
await $`cd ./dist/aur-${pkg} && git add PKGBUILD .SRCINFO`
|
await $`cd ./dist/aur-${pkg} && git add PKGBUILD .SRCINFO`
|
||||||
await $`cd ./dist/aur-${pkg} && git commit -m "Update to v${version}"`
|
await $`cd ./dist/aur-${pkg} && git commit -m "Update to v${version}"`
|
||||||
if (!dry) await $`cd ./dist/aur-${pkg} && git push`
|
if (!dry) await $`cd ./dist/aur-${pkg} && git push`
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
// Homebrew formula
|
// Homebrew formula
|
||||||
const homebrewFormula = [
|
const homebrewFormula = [
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { Config } from "../src/config/config"
|
|||||||
import { zodToJsonSchema } from "zod-to-json-schema"
|
import { zodToJsonSchema } from "zod-to-json-schema"
|
||||||
|
|
||||||
const file = process.argv[2]
|
const file = process.argv[2]
|
||||||
|
console.log(file)
|
||||||
|
|
||||||
const result = zodToJsonSchema(Config.Info, {
|
const result = zodToJsonSchema(Config.Info, {
|
||||||
/**
|
/**
|
||||||
@@ -31,5 +32,13 @@ const result = zodToJsonSchema(Config.Info, {
|
|||||||
|
|
||||||
return jsonSchema
|
return jsonSchema
|
||||||
},
|
},
|
||||||
})
|
}) as Record<string, unknown> & {
|
||||||
|
allowComments?: boolean
|
||||||
|
allowTrailingCommas?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
// used for json lsps since config supports jsonc
|
||||||
|
result.allowComments = true
|
||||||
|
result.allowTrailingCommas = true
|
||||||
|
|
||||||
await Bun.write(file, JSON.stringify(result, null, 2))
|
await Bun.write(file, JSON.stringify(result, null, 2))
|
||||||
|
|||||||
@@ -48,6 +48,15 @@ export namespace Agent {
|
|||||||
}
|
}
|
||||||
const agentPermission = mergeAgentPermissions(defaultPermission, cfg.permission ?? {})
|
const agentPermission = mergeAgentPermissions(defaultPermission, cfg.permission ?? {})
|
||||||
|
|
||||||
|
const planPermission = mergeAgentPermissions(
|
||||||
|
{
|
||||||
|
edit: "ask",
|
||||||
|
bash: "ask",
|
||||||
|
webfetch: "allow",
|
||||||
|
},
|
||||||
|
cfg.permission ?? {},
|
||||||
|
)
|
||||||
|
|
||||||
const result: Record<string, Info> = {
|
const result: Record<string, Info> = {
|
||||||
general: {
|
general: {
|
||||||
name: "general",
|
name: "general",
|
||||||
@@ -74,11 +83,8 @@ export namespace Agent {
|
|||||||
plan: {
|
plan: {
|
||||||
name: "plan",
|
name: "plan",
|
||||||
options: {},
|
options: {},
|
||||||
permission: agentPermission,
|
permission: planPermission,
|
||||||
tools: {
|
tools: {
|
||||||
write: false,
|
|
||||||
edit: false,
|
|
||||||
patch: false,
|
|
||||||
...defaultTools,
|
...defaultTools,
|
||||||
},
|
},
|
||||||
mode: "primary",
|
mode: "primary",
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ import { Snapshot } from "../snapshot"
|
|||||||
|
|
||||||
export async function bootstrap<T>(input: App.Input, cb: (app: App.Info) => Promise<T>) {
|
export async function bootstrap<T>(input: App.Input, cb: (app: App.Info) => Promise<T>) {
|
||||||
return App.provide(input, async (app) => {
|
return App.provide(input, async (app) => {
|
||||||
|
await Plugin.init()
|
||||||
Share.init()
|
Share.init()
|
||||||
Format.init()
|
Format.init()
|
||||||
Plugin.init()
|
|
||||||
ConfigHooks.init()
|
ConfigHooks.init()
|
||||||
LSP.init()
|
LSP.init()
|
||||||
Snapshot.init()
|
Snapshot.init()
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Auth } from "../../auth"
|
import { Auth } from "../../auth"
|
||||||
import { cmd } from "./cmd"
|
import { cmd } from "./cmd"
|
||||||
import * as prompts from "@clack/prompts"
|
import * as prompts from "@clack/prompts"
|
||||||
import open from "open"
|
|
||||||
import { UI } from "../ui"
|
import { UI } from "../ui"
|
||||||
import { ModelsDev } from "../../provider/models"
|
import { ModelsDev } from "../../provider/models"
|
||||||
import { map, pipe, sortBy, values } from "remeda"
|
import { map, pipe, sortBy, values } from "remeda"
|
||||||
@@ -159,9 +158,6 @@ export const AuthLoginCommand = cmd({
|
|||||||
const authorize = await method.authorize()
|
const authorize = await method.authorize()
|
||||||
|
|
||||||
if (authorize.url) {
|
if (authorize.url) {
|
||||||
try {
|
|
||||||
await open(authorize.url)
|
|
||||||
} catch (e) {}
|
|
||||||
prompts.log.info("Go to: " + authorize.url)
|
prompts.log.info("Go to: " + authorize.url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export const ServeCommand = cmd({
|
|||||||
alias: ["p"],
|
alias: ["p"],
|
||||||
type: "number",
|
type: "number",
|
||||||
describe: "port to listen on",
|
describe: "port to listen on",
|
||||||
default: 4096,
|
default: 0,
|
||||||
})
|
})
|
||||||
.option("hostname", {
|
.option("hostname", {
|
||||||
alias: ["h"],
|
alias: ["h"],
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import z from "zod"
|
||||||
|
import { App } from "../app/app"
|
||||||
|
import { Config } from "../config/config"
|
||||||
|
|
||||||
|
export namespace Command {
|
||||||
|
export const Info = z
|
||||||
|
.object({
|
||||||
|
name: z.string(),
|
||||||
|
description: z.string().optional(),
|
||||||
|
agent: z.string().optional(),
|
||||||
|
model: z.string().optional(),
|
||||||
|
template: z.string(),
|
||||||
|
})
|
||||||
|
.openapi({
|
||||||
|
ref: "Command",
|
||||||
|
})
|
||||||
|
export type Info = z.infer<typeof Info>
|
||||||
|
|
||||||
|
const state = App.state("command", async () => {
|
||||||
|
const cfg = await Config.get()
|
||||||
|
|
||||||
|
const result: Record<string, Info> = {}
|
||||||
|
|
||||||
|
for (const [name, command] of Object.entries(cfg.command ?? {})) {
|
||||||
|
result[name] = {
|
||||||
|
name,
|
||||||
|
agent: command.agent,
|
||||||
|
model: command.model,
|
||||||
|
description: command.description,
|
||||||
|
template: command.template,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
|
||||||
|
export async function get(name: string) {
|
||||||
|
return state().then((x) => x[name])
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function list() {
|
||||||
|
return state().then((x) => Object.values(x))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -107,6 +107,32 @@ export namespace Config {
|
|||||||
}
|
}
|
||||||
throw new InvalidError({ path: item }, { cause: parsed.error })
|
throw new InvalidError({ path: item }, { cause: parsed.error })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load command markdown files
|
||||||
|
result.command = result.command || {}
|
||||||
|
const markdownCommands = [
|
||||||
|
...(await Filesystem.globUp("command/*.md", Global.Path.config, Global.Path.config)),
|
||||||
|
...(await Filesystem.globUp(".opencode/command/*.md", app.path.cwd, app.path.root)),
|
||||||
|
]
|
||||||
|
for (const item of markdownCommands) {
|
||||||
|
const content = await Bun.file(item).text()
|
||||||
|
const md = matter(content)
|
||||||
|
if (!md.data) continue
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
name: path.basename(item, ".md"),
|
||||||
|
...md.data,
|
||||||
|
template: md.content.trim(),
|
||||||
|
}
|
||||||
|
const parsed = Command.safeParse(config)
|
||||||
|
if (parsed.success) {
|
||||||
|
result.command = mergeDeep(result.command, {
|
||||||
|
[config.name]: parsed.data,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
throw new InvalidError({ path: item }, { cause: parsed.error })
|
||||||
|
}
|
||||||
// Migrate deprecated mode field to agent field
|
// Migrate deprecated mode field to agent field
|
||||||
for (const [name, mode] of Object.entries(result.mode)) {
|
for (const [name, mode] of Object.entries(result.mode)) {
|
||||||
result.agent = mergeDeep(result.agent ?? {}, {
|
result.agent = mergeDeep(result.agent ?? {}, {
|
||||||
@@ -192,6 +218,14 @@ export namespace Config {
|
|||||||
export const Permission = z.union([z.literal("ask"), z.literal("allow"), z.literal("deny")])
|
export const Permission = z.union([z.literal("ask"), z.literal("allow"), z.literal("deny")])
|
||||||
export type Permission = z.infer<typeof Permission>
|
export type Permission = z.infer<typeof Permission>
|
||||||
|
|
||||||
|
export const Command = z.object({
|
||||||
|
template: z.string(),
|
||||||
|
description: z.string().optional(),
|
||||||
|
agent: z.string().optional(),
|
||||||
|
model: z.string().optional(),
|
||||||
|
})
|
||||||
|
export type Command = z.infer<typeof Command>
|
||||||
|
|
||||||
export const Agent = z
|
export const Agent = z
|
||||||
.object({
|
.object({
|
||||||
model: z.string().optional(),
|
model: z.string().optional(),
|
||||||
@@ -305,6 +339,7 @@ export namespace Config {
|
|||||||
theme: z.string().optional().describe("Theme name to use for the interface"),
|
theme: z.string().optional().describe("Theme name to use for the interface"),
|
||||||
keybinds: Keybinds.optional().describe("Custom keybind configurations"),
|
keybinds: Keybinds.optional().describe("Custom keybind configurations"),
|
||||||
tui: TUI.optional().describe("TUI specific settings"),
|
tui: TUI.optional().describe("TUI specific settings"),
|
||||||
|
command: z.record(z.string(), Command).optional(),
|
||||||
plugin: z.string().array().optional(),
|
plugin: z.string().array().optional(),
|
||||||
snapshot: z.boolean().optional(),
|
snapshot: z.boolean().optional(),
|
||||||
share: z
|
share: z
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export namespace Flag {
|
|||||||
export const OPENCODE_DISABLE_AUTOUPDATE = truthy("OPENCODE_DISABLE_AUTOUPDATE")
|
export const OPENCODE_DISABLE_AUTOUPDATE = truthy("OPENCODE_DISABLE_AUTOUPDATE")
|
||||||
export const OPENCODE_PERMISSION = process.env["OPENCODE_PERMISSION"]
|
export const OPENCODE_PERMISSION = process.env["OPENCODE_PERMISSION"]
|
||||||
export const OPENCODE_DISABLE_DEFAULT_PLUGINS = truthy("OPENCODE_DISABLE_DEFAULT_PLUGINS")
|
export const OPENCODE_DISABLE_DEFAULT_PLUGINS = truthy("OPENCODE_DISABLE_DEFAULT_PLUGINS")
|
||||||
|
export const OPENCODE_DISABLE_LSP_DOWNLOAD = truthy("OPENCODE_DISABLE_LSP_DOWNLOAD")
|
||||||
|
|
||||||
function truthy(key: string) {
|
function truthy(key: string) {
|
||||||
const value = process.env[key]?.toLowerCase()
|
const value = process.env[key]?.toLowerCase()
|
||||||
|
|||||||
@@ -68,19 +68,29 @@ export namespace Format {
|
|||||||
|
|
||||||
for (const item of await getFormatter(ext)) {
|
for (const item of await getFormatter(ext)) {
|
||||||
log.info("running", { command: item.command })
|
log.info("running", { command: item.command })
|
||||||
const proc = Bun.spawn({
|
try {
|
||||||
cmd: item.command.map((x) => x.replace("$FILE", file)),
|
const proc = Bun.spawn({
|
||||||
cwd: App.info().path.cwd,
|
cmd: item.command.map((x) => x.replace("$FILE", file)),
|
||||||
env: { ...process.env, ...item.environment },
|
cwd: App.info().path.cwd,
|
||||||
stdout: "ignore",
|
env: { ...process.env, ...item.environment },
|
||||||
stderr: "ignore",
|
stdout: "ignore",
|
||||||
})
|
stderr: "ignore",
|
||||||
const exit = await proc.exited
|
})
|
||||||
if (exit !== 0)
|
const exit = await proc.exited
|
||||||
|
if (exit !== 0)
|
||||||
|
log.error("failed", {
|
||||||
|
command: item.command,
|
||||||
|
...item.environment,
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
log.error("failed", {
|
log.error("failed", {
|
||||||
|
error,
|
||||||
command: item.command,
|
command: item.command,
|
||||||
...item.environment,
|
...item.environment,
|
||||||
})
|
})
|
||||||
|
// re-raising
|
||||||
|
throw error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ const version = await Bun.file(path.join(Global.Path.cache, "version"))
|
|||||||
.catch(() => "0")
|
.catch(() => "0")
|
||||||
|
|
||||||
if (version !== CACHE_VERSION) {
|
if (version !== CACHE_VERSION) {
|
||||||
await fs.rm(Global.Path.cache, { recursive: true, force: true })
|
try {
|
||||||
|
const contents = await fs.readdir(Global.Path.cache)
|
||||||
|
await Promise.all(
|
||||||
|
contents.map((item) => fs.rm(path.join(Global.Path.cache, item), { recursive: true, force: true })),
|
||||||
|
)
|
||||||
|
} catch (e) {}
|
||||||
await Bun.file(path.join(Global.Path.cache, "version")).write(CACHE_VERSION)
|
await Bun.file(path.join(Global.Path.cache, "version")).write(CACHE_VERSION)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,7 +126,11 @@ export namespace LSP {
|
|||||||
result.push(match)
|
result.push(match)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const handle = await server.spawn(App.info(), root)
|
const handle = await server.spawn(App.info(), root).catch((err) => {
|
||||||
|
s.broken.add(root + server.id)
|
||||||
|
log.error(`Failed to spawn LSP server ${server.id}`, { error: err })
|
||||||
|
return undefined
|
||||||
|
})
|
||||||
if (!handle) continue
|
if (!handle) continue
|
||||||
const client = await LSPClient.create({
|
const client = await LSPClient.create({
|
||||||
serverID: server.id,
|
serverID: server.id,
|
||||||
@@ -135,7 +139,7 @@ export namespace LSP {
|
|||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
s.broken.add(root + server.id)
|
s.broken.add(root + server.id)
|
||||||
handle.process.kill()
|
handle.process.kill()
|
||||||
log.error("", { error: err })
|
log.error(`Failed to initialize LSP client ${server.id}`, { error: err })
|
||||||
})
|
})
|
||||||
if (!client) continue
|
if (!client) continue
|
||||||
s.clients.push(client)
|
s.clients.push(client)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { BunProc } from "../bun"
|
|||||||
import { $ } from "bun"
|
import { $ } from "bun"
|
||||||
import fs from "fs/promises"
|
import fs from "fs/promises"
|
||||||
import { Filesystem } from "../util/filesystem"
|
import { Filesystem } from "../util/filesystem"
|
||||||
|
import { Flag } from "../flag/flag"
|
||||||
|
|
||||||
export namespace LSPServer {
|
export namespace LSPServer {
|
||||||
const log = Log.create({ service: "lsp.server" })
|
const log = Log.create({ service: "lsp.server" })
|
||||||
@@ -95,6 +96,7 @@ export namespace LSPServer {
|
|||||||
"vue-language-server.js",
|
"vue-language-server.js",
|
||||||
)
|
)
|
||||||
if (!(await Bun.file(js).exists())) {
|
if (!(await Bun.file(js).exists())) {
|
||||||
|
if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return
|
||||||
await Bun.spawn([BunProc.which(), "install", "@vue/language-server"], {
|
await Bun.spawn([BunProc.which(), "install", "@vue/language-server"], {
|
||||||
cwd: Global.Path.bin,
|
cwd: Global.Path.bin,
|
||||||
env: {
|
env: {
|
||||||
@@ -148,6 +150,7 @@ export namespace LSPServer {
|
|||||||
if (!eslint) return
|
if (!eslint) return
|
||||||
const serverPath = path.join(Global.Path.bin, "vscode-eslint", "server", "out", "eslintServer.js")
|
const serverPath = path.join(Global.Path.bin, "vscode-eslint", "server", "out", "eslintServer.js")
|
||||||
if (!(await Bun.file(serverPath).exists())) {
|
if (!(await Bun.file(serverPath).exists())) {
|
||||||
|
if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return
|
||||||
log.info("downloading and building VS Code ESLint server")
|
log.info("downloading and building VS Code ESLint server")
|
||||||
const response = await fetch("https://github.com/microsoft/vscode-eslint/archive/refs/heads/main.zip")
|
const response = await fetch("https://github.com/microsoft/vscode-eslint/archive/refs/heads/main.zip")
|
||||||
if (!response.ok) return
|
if (!response.ok) return
|
||||||
@@ -200,6 +203,8 @@ export namespace LSPServer {
|
|||||||
})
|
})
|
||||||
if (!bin) {
|
if (!bin) {
|
||||||
if (!Bun.which("go")) return
|
if (!Bun.which("go")) return
|
||||||
|
if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return
|
||||||
|
|
||||||
log.info("installing gopls")
|
log.info("installing gopls")
|
||||||
const proc = Bun.spawn({
|
const proc = Bun.spawn({
|
||||||
cmd: ["go", "install", "golang.org/x/tools/gopls@latest"],
|
cmd: ["go", "install", "golang.org/x/tools/gopls@latest"],
|
||||||
@@ -241,6 +246,7 @@ export namespace LSPServer {
|
|||||||
log.info("Ruby not found, please install Ruby first")
|
log.info("Ruby not found, please install Ruby first")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return
|
||||||
log.info("installing ruby-lsp")
|
log.info("installing ruby-lsp")
|
||||||
const proc = Bun.spawn({
|
const proc = Bun.spawn({
|
||||||
cmd: ["gem", "install", "ruby-lsp", "--bindir", Global.Path.bin],
|
cmd: ["gem", "install", "ruby-lsp", "--bindir", Global.Path.bin],
|
||||||
@@ -276,6 +282,7 @@ export namespace LSPServer {
|
|||||||
if (!binary) {
|
if (!binary) {
|
||||||
const js = path.join(Global.Path.bin, "node_modules", "pyright", "dist", "pyright-langserver.js")
|
const js = path.join(Global.Path.bin, "node_modules", "pyright", "dist", "pyright-langserver.js")
|
||||||
if (!(await Bun.file(js).exists())) {
|
if (!(await Bun.file(js).exists())) {
|
||||||
|
if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return
|
||||||
await Bun.spawn([BunProc.which(), "install", "pyright"], {
|
await Bun.spawn([BunProc.which(), "install", "pyright"], {
|
||||||
cwd: Global.Path.bin,
|
cwd: Global.Path.bin,
|
||||||
env: {
|
env: {
|
||||||
@@ -323,6 +330,7 @@ export namespace LSPServer {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return
|
||||||
log.info("downloading elixir-ls from GitHub releases")
|
log.info("downloading elixir-ls from GitHub releases")
|
||||||
|
|
||||||
const response = await fetch("https://github.com/elixir-lsp/elixir-ls/archive/refs/heads/master.zip")
|
const response = await fetch("https://github.com/elixir-lsp/elixir-ls/archive/refs/heads/master.zip")
|
||||||
@@ -372,6 +380,7 @@ export namespace LSPServer {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return
|
||||||
log.info("downloading zls from GitHub releases")
|
log.info("downloading zls from GitHub releases")
|
||||||
|
|
||||||
const releaseResponse = await fetch("https://api.github.com/repos/zigtools/zls/releases/latest")
|
const releaseResponse = await fetch("https://api.github.com/repos/zigtools/zls/releases/latest")
|
||||||
@@ -475,6 +484,7 @@ export namespace LSPServer {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return
|
||||||
log.info("installing csharp-ls via dotnet tool")
|
log.info("installing csharp-ls via dotnet tool")
|
||||||
const proc = Bun.spawn({
|
const proc = Bun.spawn({
|
||||||
cmd: ["dotnet", "tool", "install", "csharp-ls", "--tool-path", Global.Path.bin],
|
cmd: ["dotnet", "tool", "install", "csharp-ls", "--tool-path", Global.Path.bin],
|
||||||
@@ -527,6 +537,7 @@ export namespace LSPServer {
|
|||||||
PATH: process.env["PATH"] + ":" + Global.Path.bin,
|
PATH: process.env["PATH"] + ":" + Global.Path.bin,
|
||||||
})
|
})
|
||||||
if (!bin) {
|
if (!bin) {
|
||||||
|
if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return
|
||||||
log.info("downloading clangd from GitHub releases")
|
log.info("downloading clangd from GitHub releases")
|
||||||
|
|
||||||
const releaseResponse = await fetch("https://api.github.com/repos/clangd/clangd/releases/latest")
|
const releaseResponse = await fetch("https://api.github.com/repos/clangd/clangd/releases/latest")
|
||||||
|
|||||||
@@ -68,7 +68,12 @@ export namespace Plugin {
|
|||||||
return state().then((x) => x.hooks)
|
return state().then((x) => x.hooks)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function init() {
|
export async function init() {
|
||||||
|
const hooks = await state().then((x) => x.hooks)
|
||||||
|
const config = await Config.get()
|
||||||
|
for (const hook of hooks) {
|
||||||
|
await hook.config?.(config)
|
||||||
|
}
|
||||||
Bus.subscribeAll(async (input) => {
|
Bus.subscribeAll(async (input) => {
|
||||||
const hooks = await state().then((x) => x.hooks)
|
const hooks = await state().then((x) => x.hooks)
|
||||||
for (const hook of hooks) {
|
for (const hook of hooks) {
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ export namespace Provider {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async opencode() {
|
async opencode(input) {
|
||||||
return {
|
return {
|
||||||
autoload: true,
|
autoload: Object.keys(input.models).length > 0,
|
||||||
options: {},
|
options: {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import { Permission } from "../permission"
|
|||||||
import { lazy } from "../util/lazy"
|
import { lazy } from "../util/lazy"
|
||||||
import { Agent } from "../agent/agent"
|
import { Agent } from "../agent/agent"
|
||||||
import { Auth } from "../auth"
|
import { Auth } from "../auth"
|
||||||
|
import { Command } from "../command"
|
||||||
|
|
||||||
const ERRORS = {
|
const ERRORS = {
|
||||||
400: {
|
400: {
|
||||||
@@ -102,7 +103,7 @@ export namespace Server {
|
|||||||
200: {
|
200: {
|
||||||
description: "Event stream",
|
description: "Event stream",
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"text/event-stream": {
|
||||||
schema: resolver(
|
schema: resolver(
|
||||||
Bus.payloads().openapi({
|
Bus.payloads().openapi({
|
||||||
ref: "Event",
|
ref: "Event",
|
||||||
@@ -611,7 +612,12 @@ export namespace Server {
|
|||||||
description: "Created message",
|
description: "Created message",
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: resolver(MessageV2.Assistant),
|
schema: resolver(
|
||||||
|
z.object({
|
||||||
|
info: MessageV2.Assistant,
|
||||||
|
parts: MessageV2.Part.array(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -631,6 +637,41 @@ export namespace Server {
|
|||||||
return c.json(msg)
|
return c.json(msg)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
.post(
|
||||||
|
"/session/:id/command",
|
||||||
|
describeRoute({
|
||||||
|
description: "Send a new command to a session",
|
||||||
|
operationId: "session.command",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "Created message",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(
|
||||||
|
z.object({
|
||||||
|
info: MessageV2.Assistant,
|
||||||
|
parts: MessageV2.Part.array(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
zValidator(
|
||||||
|
"param",
|
||||||
|
z.object({
|
||||||
|
id: z.string().openapi({ description: "Session ID" }),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
zValidator("json", Session.CommandInput.omit({ sessionID: true })),
|
||||||
|
async (c) => {
|
||||||
|
const sessionID = c.req.valid("param").id
|
||||||
|
const body = c.req.valid("json")
|
||||||
|
const msg = await Session.command({ ...body, sessionID })
|
||||||
|
return c.json(msg)
|
||||||
|
},
|
||||||
|
)
|
||||||
.post(
|
.post(
|
||||||
"/session/:id/shell",
|
"/session/:id/shell",
|
||||||
describeRoute({
|
describeRoute({
|
||||||
@@ -653,7 +694,7 @@ export namespace Server {
|
|||||||
id: z.string().openapi({ description: "Session ID" }),
|
id: z.string().openapi({ description: "Session ID" }),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
zValidator("json", Session.CommandInput.omit({ sessionID: true })),
|
zValidator("json", Session.ShellInput.omit({ sessionID: true })),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const sessionID = c.req.valid("param").id
|
const sessionID = c.req.valid("param").id
|
||||||
const body = c.req.valid("json")
|
const body = c.req.valid("json")
|
||||||
@@ -750,6 +791,27 @@ export namespace Server {
|
|||||||
return c.json(true)
|
return c.json(true)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
.get(
|
||||||
|
"/command",
|
||||||
|
describeRoute({
|
||||||
|
description: "List all commands",
|
||||||
|
operationId: "command.list",
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: "List of commands",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: resolver(Command.Info.array()),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (c) => {
|
||||||
|
const commands = await Command.list()
|
||||||
|
return c.json(commands)
|
||||||
|
},
|
||||||
|
)
|
||||||
.get(
|
.get(
|
||||||
"/config/providers",
|
"/config/providers",
|
||||||
describeRoute({
|
describeRoute({
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
|
|
||||||
import PROMPT_INITIALIZE from "../session/prompt/initialize.txt"
|
import PROMPT_INITIALIZE from "../session/prompt/initialize.txt"
|
||||||
import PROMPT_PLAN from "../session/prompt/plan.txt"
|
import PROMPT_PLAN from "../session/prompt/plan.txt"
|
||||||
|
import BUILD_SWITCH from "../session/prompt/build-switch.txt"
|
||||||
|
|
||||||
import { App } from "../app/app"
|
import { App } from "../app/app"
|
||||||
import { Bus } from "../bus"
|
import { Bus } from "../bus"
|
||||||
@@ -46,6 +47,8 @@ import { Permission } from "../permission"
|
|||||||
import { Wildcard } from "../util/wildcard"
|
import { Wildcard } from "../util/wildcard"
|
||||||
import { ulid } from "ulid"
|
import { ulid } from "ulid"
|
||||||
import { defer } from "../util/defer"
|
import { defer } from "../util/defer"
|
||||||
|
import { Command } from "../command"
|
||||||
|
import { $ } from "bun"
|
||||||
|
|
||||||
export namespace Session {
|
export namespace Session {
|
||||||
const log = Log.create({ service: "session" })
|
const log = Log.create({ service: "session" })
|
||||||
@@ -734,7 +737,7 @@ export namespace Session {
|
|||||||
messageID: userMsg.id,
|
messageID: userMsg.id,
|
||||||
sessionID: input.sessionID,
|
sessionID: input.sessionID,
|
||||||
type: "text",
|
type: "text",
|
||||||
text: "You are now in build mode and are permitted to make edits",
|
text: BUILD_SWITCH,
|
||||||
synthetic: true,
|
synthetic: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1024,13 +1027,13 @@ export namespace Session {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CommandInput = z.object({
|
export const ShellInput = z.object({
|
||||||
sessionID: Identifier.schema("session"),
|
sessionID: Identifier.schema("session"),
|
||||||
agent: z.string(),
|
agent: z.string(),
|
||||||
command: z.string(),
|
command: z.string(),
|
||||||
})
|
})
|
||||||
export type CommandInput = z.infer<typeof CommandInput>
|
export type ShellInput = z.infer<typeof ShellInput>
|
||||||
export async function shell(input: CommandInput) {
|
export async function shell(input: ShellInput) {
|
||||||
using abort = lock(input.sessionID)
|
using abort = lock(input.sessionID)
|
||||||
const msg: MessageV2.Assistant = {
|
const msg: MessageV2.Assistant = {
|
||||||
id: Identifier.ascending("message"),
|
id: Identifier.ascending("message"),
|
||||||
@@ -1154,6 +1157,76 @@ export namespace Session {
|
|||||||
return { info: msg, parts: [part] }
|
return { info: msg, parts: [part] }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const CommandInput = z.object({
|
||||||
|
messageID: Identifier.schema("message").optional(),
|
||||||
|
sessionID: Identifier.schema("session"),
|
||||||
|
agent: z.string().optional(),
|
||||||
|
model: z.string().optional(),
|
||||||
|
arguments: z.string(),
|
||||||
|
command: z.string(),
|
||||||
|
})
|
||||||
|
export type CommandInput = z.infer<typeof CommandInput>
|
||||||
|
const bashRegex = /!`([^`]+)`/g
|
||||||
|
const fileRegex = /@([^\s]+)/g
|
||||||
|
|
||||||
|
export async function command(input: CommandInput) {
|
||||||
|
const command = await Command.get(input.command)
|
||||||
|
const agent = command.agent ?? input.agent ?? "build"
|
||||||
|
const fmtModel = (model: { providerID: string; modelID: string }) => `${model.providerID}/${model.modelID}`
|
||||||
|
|
||||||
|
const model =
|
||||||
|
command.model ??
|
||||||
|
(command.agent && (await Agent.get(command.agent).then((x) => (x.model ? fmtModel(x.model) : undefined)))) ??
|
||||||
|
input.model ??
|
||||||
|
(input.agent && (await Agent.get(input.agent).then((x) => (x.model ? fmtModel(x.model) : undefined)))) ??
|
||||||
|
fmtModel(await Provider.defaultModel())
|
||||||
|
|
||||||
|
let template = command.template.replace("$ARGUMENTS", input.arguments)
|
||||||
|
|
||||||
|
const bash = Array.from(template.matchAll(bashRegex))
|
||||||
|
if (bash.length > 0) {
|
||||||
|
const results = await Promise.all(
|
||||||
|
bash.map(async ([, cmd]) => {
|
||||||
|
try {
|
||||||
|
return await $`${{ raw: cmd }}`.nothrow().text()
|
||||||
|
} catch (error) {
|
||||||
|
return `Error executing command: ${error instanceof Error ? error.message : String(error)}`
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
let index = 0
|
||||||
|
template = template.replace(bashRegex, () => results[index++])
|
||||||
|
}
|
||||||
|
|
||||||
|
const parts = [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: template,
|
||||||
|
},
|
||||||
|
] as ChatInput["parts"]
|
||||||
|
|
||||||
|
const matches = template.matchAll(fileRegex)
|
||||||
|
const app = App.info()
|
||||||
|
|
||||||
|
for (const match of matches) {
|
||||||
|
const file = path.join(app.path.cwd, match[1])
|
||||||
|
parts.push({
|
||||||
|
type: "file",
|
||||||
|
url: `file://${file}`,
|
||||||
|
filename: match[1],
|
||||||
|
mime: "text/plain",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return chat({
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
messageID: input.messageID,
|
||||||
|
...Provider.parseModel(model!),
|
||||||
|
agent,
|
||||||
|
parts,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function createProcessor(assistantMsg: MessageV2.Assistant, model: ModelsDev.Model) {
|
function createProcessor(assistantMsg: MessageV2.Assistant, model: ModelsDev.Model) {
|
||||||
const toolcalls: Record<string, MessageV2.ToolPart> = {}
|
const toolcalls: Record<string, MessageV2.ToolPart> = {}
|
||||||
let snapshot: string | undefined
|
let snapshot: string | undefined
|
||||||
@@ -1611,7 +1684,7 @@ export namespace Session {
|
|||||||
const tokens = {
|
const tokens = {
|
||||||
input: usage.inputTokens ?? 0,
|
input: usage.inputTokens ?? 0,
|
||||||
output: usage.outputTokens ?? 0,
|
output: usage.outputTokens ?? 0,
|
||||||
reasoning: 0,
|
reasoning: usage?.reasoningTokens ?? 0,
|
||||||
cache: {
|
cache: {
|
||||||
write: (metadata?.["anthropic"]?.["cacheCreationInputTokens"] ??
|
write: (metadata?.["anthropic"]?.["cacheCreationInputTokens"] ??
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
Your operational mode has changed from plan to build. You are no longer in read-only mode. You are permitted to make file changes as necessary and utilize your arsenal of tools as needed.
|
||||||
@@ -86,7 +86,7 @@ export namespace ToolRegistry {
|
|||||||
result["webfetch"] = false
|
result["webfetch"] = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modelID.includes("qwen")) {
|
if (modelID.toLowerCase().includes("qwen")) {
|
||||||
result["todowrite"] = false
|
result["todowrite"] = false
|
||||||
result["todoread"] = false
|
result["todoread"] = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,12 @@ export namespace Log {
|
|||||||
...extra,
|
...extra,
|
||||||
})
|
})
|
||||||
.filter(([_, value]) => value !== undefined && value !== null)
|
.filter(([_, value]) => value !== undefined && value !== null)
|
||||||
.map(([key, value]) => `${key}=${typeof value === "object" ? JSON.stringify(value) : value}`)
|
.map(([key, value]) => {
|
||||||
|
const prefix = `${key}=`
|
||||||
|
if (value instanceof Error) return prefix + value.message
|
||||||
|
if (typeof value === "object") return prefix + JSON.stringify(value)
|
||||||
|
return prefix + value
|
||||||
|
})
|
||||||
.join(" ")
|
.join(" ")
|
||||||
const next = new Date()
|
const next = new Date()
|
||||||
const diff = next.getTime() - last
|
const diff = next.getTime() - last
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"name": "@opencode-ai/plugin",
|
"name": "@opencode-ai/plugin",
|
||||||
"version": "0.5.9",
|
"version": "0.5.25",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "tsc --noEmit"
|
||||||
@@ -19,8 +19,8 @@
|
|||||||
"@opencode-ai/sdk": "workspace:*"
|
"@opencode-ai/sdk": "workspace:*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "catalog:",
|
"@hey-api/openapi-ts": "0.81.0",
|
||||||
"@hey-api/openapi-ts": "0.80.1",
|
"@tsconfig/node22": "catalog:",
|
||||||
"@tsconfig/node22": "catalog:"
|
"typescript": "catalog:"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import type {
|
|||||||
UserMessage,
|
UserMessage,
|
||||||
Part,
|
Part,
|
||||||
Auth,
|
Auth,
|
||||||
|
Config,
|
||||||
} from "@opencode-ai/sdk"
|
} from "@opencode-ai/sdk"
|
||||||
import type { BunShell } from "./shell"
|
import type { BunShell } from "./shell"
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ export type Plugin = (input: PluginInput) => Promise<Hooks>
|
|||||||
|
|
||||||
export interface Hooks {
|
export interface Hooks {
|
||||||
event?: (input: { event: Event }) => Promise<void>
|
event?: (input: { event: Event }) => Promise<void>
|
||||||
|
config?: (input: Config) => Promise<void>
|
||||||
auth?: {
|
auth?: {
|
||||||
provider: string
|
provider: string
|
||||||
loader?: (auth: () => Promise<Auth>, provider: Provider) => Promise<Record<string, any>>
|
loader?: (auth: () => Promise<Auth>, provider: Provider) => Promise<Record<string, any>>
|
||||||
|
|||||||
@@ -6,6 +6,11 @@
|
|||||||
"module": "preserve",
|
"module": "preserve",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
|
"lib": [
|
||||||
|
"es2022",
|
||||||
|
"dom",
|
||||||
|
"dom.iterable"
|
||||||
|
],
|
||||||
"customConditions": [
|
"customConditions": [
|
||||||
"development"
|
"development"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
configured_endpoints: 39
|
configured_endpoints: 41
|
||||||
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-e4b6496e5f2c68fa8b3ea1b88e40041eaf5ce2652001344df80bf130675d1766.yml
|
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-6d8e9dfd438cac63fc7d689ea29adfff81ff8880c2d8e1e10fc36f375a721594.yml
|
||||||
openapi_spec_hash: df474311dc9e4a89cd483bd8b8d971d8
|
openapi_spec_hash: 7ac6028dd5957c67a98d91e790863c80
|
||||||
config_hash: eab3723c4c2232a6ba1821151259d6da
|
config_hash: fb625e876313a9f8f31532348fa91f59
|
||||||
|
|||||||
+14
-1
@@ -70,6 +70,16 @@ Methods:
|
|||||||
|
|
||||||
- <code title="get /config">client.Config.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#ConfigService.Get">Get</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>) (<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#Config">Config</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
- <code title="get /config">client.Config.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#ConfigService.Get">Get</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>) (<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#Config">Config</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||||
|
|
||||||
|
# Command
|
||||||
|
|
||||||
|
Response Types:
|
||||||
|
|
||||||
|
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#Command">Command</a>
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
|
||||||
|
- <code title="get /command">client.Command.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#CommandService.List">List</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>) ([]<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#Command">Command</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||||
|
|
||||||
# Session
|
# Session
|
||||||
|
|
||||||
Params Types:
|
Params Types:
|
||||||
@@ -105,6 +115,8 @@ Response Types:
|
|||||||
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#ToolStatePending">ToolStatePending</a>
|
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#ToolStatePending">ToolStatePending</a>
|
||||||
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#ToolStateRunning">ToolStateRunning</a>
|
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#ToolStateRunning">ToolStateRunning</a>
|
||||||
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#UserMessage">UserMessage</a>
|
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#UserMessage">UserMessage</a>
|
||||||
|
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionChatResponse">SessionChatResponse</a>
|
||||||
|
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionCommandResponse">SessionCommandResponse</a>
|
||||||
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionMessageResponse">SessionMessageResponse</a>
|
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionMessageResponse">SessionMessageResponse</a>
|
||||||
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionMessagesResponse">SessionMessagesResponse</a>
|
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionMessagesResponse">SessionMessagesResponse</a>
|
||||||
|
|
||||||
@@ -115,8 +127,9 @@ Methods:
|
|||||||
- <code title="get /session">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.List">List</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>) ([]<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#Session">Session</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
- <code title="get /session">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.List">List</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>) ([]<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#Session">Session</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||||
- <code title="delete /session/{id}">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Delete">Delete</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>) (<a href="https://pkg.go.dev/builtin#bool">bool</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
- <code title="delete /session/{id}">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Delete">Delete</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>) (<a href="https://pkg.go.dev/builtin#bool">bool</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||||
- <code title="post /session/{id}/abort">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Abort">Abort</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>) (<a href="https://pkg.go.dev/builtin#bool">bool</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
- <code title="post /session/{id}/abort">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Abort">Abort</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>) (<a href="https://pkg.go.dev/builtin#bool">bool</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||||
- <code title="post /session/{id}/message">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Chat">Chat</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, body <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionChatParams">SessionChatParams</a>) (<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#AssistantMessage">AssistantMessage</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
- <code title="post /session/{id}/message">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Chat">Chat</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, body <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionChatParams">SessionChatParams</a>) (<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionChatResponse">SessionChatResponse</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||||
- <code title="get /session/{id}/children">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Children">Children</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>) ([]<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#Session">Session</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
- <code title="get /session/{id}/children">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Children">Children</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>) ([]<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#Session">Session</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||||
|
- <code title="post /session/{id}/command">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Command">Command</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, body <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionCommandParams">SessionCommandParams</a>) (<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionCommandResponse">SessionCommandResponse</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||||
- <code title="get /session/{id}">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Get">Get</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>) (<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#Session">Session</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
- <code title="get /session/{id}">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Get">Get</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>) (<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#Session">Session</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||||
- <code title="post /session/{id}/init">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Init">Init</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, body <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionInitParams">SessionInitParams</a>) (<a href="https://pkg.go.dev/builtin#bool">bool</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
- <code title="post /session/{id}/init">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Init">Init</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, body <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionInitParams">SessionInitParams</a>) (<a href="https://pkg.go.dev/builtin#bool">bool</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||||
- <code title="get /session/{id}/message/{messageID}">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Message">Message</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, messageID <a href="https://pkg.go.dev/builtin#string">string</a>) (<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionMessageResponse">SessionMessageResponse</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
- <code title="get /session/{id}/message/{messageID}">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Message">Message</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, messageID <a href="https://pkg.go.dev/builtin#string">string</a>) (<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionMessageResponse">SessionMessageResponse</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ type Client struct {
|
|||||||
Find *FindService
|
Find *FindService
|
||||||
File *FileService
|
File *FileService
|
||||||
Config *ConfigService
|
Config *ConfigService
|
||||||
|
Command *CommandService
|
||||||
Session *SessionService
|
Session *SessionService
|
||||||
Tui *TuiService
|
Tui *TuiService
|
||||||
}
|
}
|
||||||
@@ -49,6 +50,7 @@ func NewClient(opts ...option.RequestOption) (r *Client) {
|
|||||||
r.Find = NewFindService(opts...)
|
r.Find = NewFindService(opts...)
|
||||||
r.File = NewFileService(opts...)
|
r.File = NewFileService(opts...)
|
||||||
r.Config = NewConfigService(opts...)
|
r.Config = NewConfigService(opts...)
|
||||||
|
r.Command = NewCommandService(opts...)
|
||||||
r.Session = NewSessionService(opts...)
|
r.Session = NewSessionService(opts...)
|
||||||
r.Tui = NewTuiService(opts...)
|
r.Tui = NewTuiService(opts...)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
||||||
|
|
||||||
|
package opencode
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/sst/opencode-sdk-go/internal/apijson"
|
||||||
|
"github.com/sst/opencode-sdk-go/internal/requestconfig"
|
||||||
|
"github.com/sst/opencode-sdk-go/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CommandService contains methods and other services that help with interacting
|
||||||
|
// with the opencode API.
|
||||||
|
//
|
||||||
|
// Note, unlike clients, this service does not read variables from the environment
|
||||||
|
// automatically. You should not instantiate this service directly, and instead use
|
||||||
|
// the [NewCommandService] method instead.
|
||||||
|
type CommandService struct {
|
||||||
|
Options []option.RequestOption
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCommandService generates a new service that applies the given options to each
|
||||||
|
// request. These options are applied after the parent client's options (if there
|
||||||
|
// is one), and before any request-specific options.
|
||||||
|
func NewCommandService(opts ...option.RequestOption) (r *CommandService) {
|
||||||
|
r = &CommandService{}
|
||||||
|
r.Options = opts
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// List all commands
|
||||||
|
func (r *CommandService) List(ctx context.Context, opts ...option.RequestOption) (res *[]Command, err error) {
|
||||||
|
opts = append(r.Options[:], opts...)
|
||||||
|
path := "command"
|
||||||
|
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, nil, &res, opts...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type Command struct {
|
||||||
|
Name string `json:"name,required"`
|
||||||
|
Template string `json:"template,required"`
|
||||||
|
Agent string `json:"agent"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Model string `json:"model"`
|
||||||
|
JSON commandJSON `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// commandJSON contains the JSON metadata for the struct [Command]
|
||||||
|
type commandJSON struct {
|
||||||
|
Name apijson.Field
|
||||||
|
Template apijson.Field
|
||||||
|
Agent apijson.Field
|
||||||
|
Description apijson.Field
|
||||||
|
Model apijson.Field
|
||||||
|
raw string
|
||||||
|
ExtraFields map[string]apijson.Field
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Command) UnmarshalJSON(data []byte) (err error) {
|
||||||
|
return apijson.UnmarshalRoot(data, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r commandJSON) RawJSON() string {
|
||||||
|
return r.raw
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
||||||
|
|
||||||
|
package opencode_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/sst/opencode-sdk-go"
|
||||||
|
"github.com/sst/opencode-sdk-go/internal/testutil"
|
||||||
|
"github.com/sst/opencode-sdk-go/option"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommandList(t *testing.T) {
|
||||||
|
t.Skip("skipped: tests are disabled for the time being")
|
||||||
|
baseURL := "http://localhost:4010"
|
||||||
|
if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok {
|
||||||
|
baseURL = envURL
|
||||||
|
}
|
||||||
|
if !testutil.CheckTestServer(t, baseURL) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client := opencode.NewClient(
|
||||||
|
option.WithBaseURL(baseURL),
|
||||||
|
)
|
||||||
|
_, err := client.Command.List(context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
var apierr *opencode.Error
|
||||||
|
if errors.As(err, &apierr) {
|
||||||
|
t.Log(string(apierr.DumpRequest(true)))
|
||||||
|
}
|
||||||
|
t.Fatalf("err should be nil: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,7 +49,8 @@ type Config struct {
|
|||||||
// automatically
|
// automatically
|
||||||
Autoshare bool `json:"autoshare"`
|
Autoshare bool `json:"autoshare"`
|
||||||
// Automatically update to the latest version
|
// Automatically update to the latest version
|
||||||
Autoupdate bool `json:"autoupdate"`
|
Autoupdate bool `json:"autoupdate"`
|
||||||
|
Command map[string]ConfigCommand `json:"command"`
|
||||||
// Disable providers that are loaded automatically
|
// Disable providers that are loaded automatically
|
||||||
DisabledProviders []string `json:"disabled_providers"`
|
DisabledProviders []string `json:"disabled_providers"`
|
||||||
Experimental ConfigExperimental `json:"experimental"`
|
Experimental ConfigExperimental `json:"experimental"`
|
||||||
@@ -79,7 +80,8 @@ type Config struct {
|
|||||||
SmallModel string `json:"small_model"`
|
SmallModel string `json:"small_model"`
|
||||||
Snapshot bool `json:"snapshot"`
|
Snapshot bool `json:"snapshot"`
|
||||||
// Theme name to use for the interface
|
// Theme name to use for the interface
|
||||||
Theme string `json:"theme"`
|
Theme string `json:"theme"`
|
||||||
|
Tools map[string]bool `json:"tools"`
|
||||||
// TUI specific settings
|
// TUI specific settings
|
||||||
Tui ConfigTui `json:"tui"`
|
Tui ConfigTui `json:"tui"`
|
||||||
// Custom username to display in conversations instead of system username
|
// Custom username to display in conversations instead of system username
|
||||||
@@ -93,6 +95,7 @@ type configJSON struct {
|
|||||||
Agent apijson.Field
|
Agent apijson.Field
|
||||||
Autoshare apijson.Field
|
Autoshare apijson.Field
|
||||||
Autoupdate apijson.Field
|
Autoupdate apijson.Field
|
||||||
|
Command apijson.Field
|
||||||
DisabledProviders apijson.Field
|
DisabledProviders apijson.Field
|
||||||
Experimental apijson.Field
|
Experimental apijson.Field
|
||||||
Formatter apijson.Field
|
Formatter apijson.Field
|
||||||
@@ -110,6 +113,7 @@ type configJSON struct {
|
|||||||
SmallModel apijson.Field
|
SmallModel apijson.Field
|
||||||
Snapshot apijson.Field
|
Snapshot apijson.Field
|
||||||
Theme apijson.Field
|
Theme apijson.Field
|
||||||
|
Tools apijson.Field
|
||||||
Tui apijson.Field
|
Tui apijson.Field
|
||||||
Username apijson.Field
|
Username apijson.Field
|
||||||
raw string
|
raw string
|
||||||
@@ -662,6 +666,32 @@ func (r ConfigAgentPlanPermissionWebfetch) IsKnown() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ConfigCommand struct {
|
||||||
|
Template string `json:"template,required"`
|
||||||
|
Agent string `json:"agent"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Model string `json:"model"`
|
||||||
|
JSON configCommandJSON `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// configCommandJSON contains the JSON metadata for the struct [ConfigCommand]
|
||||||
|
type configCommandJSON struct {
|
||||||
|
Template apijson.Field
|
||||||
|
Agent apijson.Field
|
||||||
|
Description apijson.Field
|
||||||
|
Model apijson.Field
|
||||||
|
raw string
|
||||||
|
ExtraFields map[string]apijson.Field
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ConfigCommand) UnmarshalJSON(data []byte) (err error) {
|
||||||
|
return apijson.UnmarshalRoot(data, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r configCommandJSON) RawJSON() string {
|
||||||
|
return r.raw
|
||||||
|
}
|
||||||
|
|
||||||
type ConfigExperimental struct {
|
type ConfigExperimental struct {
|
||||||
Hook ConfigExperimentalHook `json:"hook"`
|
Hook ConfigExperimentalHook `json:"hook"`
|
||||||
JSON configExperimentalJSON `json:"-"`
|
JSON configExperimentalJSON `json:"-"`
|
||||||
@@ -1760,6 +1790,8 @@ type KeybindsConfig struct {
|
|||||||
SessionNew string `json:"session_new,required"`
|
SessionNew string `json:"session_new,required"`
|
||||||
// Share current session
|
// Share current session
|
||||||
SessionShare string `json:"session_share,required"`
|
SessionShare string `json:"session_share,required"`
|
||||||
|
// Show session timeline
|
||||||
|
SessionTimeline string `json:"session_timeline,required"`
|
||||||
// Unshare current session
|
// Unshare current session
|
||||||
SessionUnshare string `json:"session_unshare,required"`
|
SessionUnshare string `json:"session_unshare,required"`
|
||||||
// @deprecated use agent_cycle. Next agent
|
// @deprecated use agent_cycle. Next agent
|
||||||
@@ -1821,6 +1853,7 @@ type keybindsConfigJSON struct {
|
|||||||
SessionList apijson.Field
|
SessionList apijson.Field
|
||||||
SessionNew apijson.Field
|
SessionNew apijson.Field
|
||||||
SessionShare apijson.Field
|
SessionShare apijson.Field
|
||||||
|
SessionTimeline apijson.Field
|
||||||
SessionUnshare apijson.Field
|
SessionUnshare apijson.Field
|
||||||
SwitchAgent apijson.Field
|
SwitchAgent apijson.Field
|
||||||
SwitchAgentReverse apijson.Field
|
SwitchAgentReverse apijson.Field
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ func (r *EventService) ListStreaming(ctx context.Context, opts ...option.Request
|
|||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
opts = append(r.Options[:], opts...)
|
opts = append(r.Options[:], opts...)
|
||||||
|
opts = append([]option.RequestOption{option.WithHeader("Accept", "text/event-stream")}, opts...)
|
||||||
path := "event"
|
path := "event"
|
||||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, nil, &raw, opts...)
|
err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, nil, &raw, opts...)
|
||||||
return ssestream.NewStream[EventListResponse](ssestream.NewDecoder(raw), err)
|
return ssestream.NewStream[EventListResponse](ssestream.NewDecoder(raw), err)
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ func (r *SessionService) Abort(ctx context.Context, id string, opts ...option.Re
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create and send a new message to a session
|
// Create and send a new message to a session
|
||||||
func (r *SessionService) Chat(ctx context.Context, id string, body SessionChatParams, opts ...option.RequestOption) (res *AssistantMessage, err error) {
|
func (r *SessionService) Chat(ctx context.Context, id string, body SessionChatParams, opts ...option.RequestOption) (res *SessionChatResponse, err error) {
|
||||||
opts = append(r.Options[:], opts...)
|
opts = append(r.Options[:], opts...)
|
||||||
if id == "" {
|
if id == "" {
|
||||||
err = errors.New("missing required id parameter")
|
err = errors.New("missing required id parameter")
|
||||||
@@ -114,6 +114,18 @@ func (r *SessionService) Children(ctx context.Context, id string, opts ...option
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send a new command to a session
|
||||||
|
func (r *SessionService) Command(ctx context.Context, id string, body SessionCommandParams, opts ...option.RequestOption) (res *SessionCommandResponse, err error) {
|
||||||
|
opts = append(r.Options[:], opts...)
|
||||||
|
if id == "" {
|
||||||
|
err = errors.New("missing required id parameter")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
path := fmt.Sprintf("session/%s/command", id)
|
||||||
|
err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Get session
|
// Get session
|
||||||
func (r *SessionService) Get(ctx context.Context, id string, opts ...option.RequestOption) (res *Session, err error) {
|
func (r *SessionService) Get(ctx context.Context, id string, opts ...option.RequestOption) (res *Session, err error) {
|
||||||
opts = append(r.Options[:], opts...)
|
opts = append(r.Options[:], opts...)
|
||||||
@@ -2278,6 +2290,52 @@ func (r userMessageTimeJSON) RawJSON() string {
|
|||||||
return r.raw
|
return r.raw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SessionChatResponse struct {
|
||||||
|
Info AssistantMessage `json:"info,required"`
|
||||||
|
Parts []Part `json:"parts,required"`
|
||||||
|
JSON sessionChatResponseJSON `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// sessionChatResponseJSON contains the JSON metadata for the struct
|
||||||
|
// [SessionChatResponse]
|
||||||
|
type sessionChatResponseJSON struct {
|
||||||
|
Info apijson.Field
|
||||||
|
Parts apijson.Field
|
||||||
|
raw string
|
||||||
|
ExtraFields map[string]apijson.Field
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *SessionChatResponse) UnmarshalJSON(data []byte) (err error) {
|
||||||
|
return apijson.UnmarshalRoot(data, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r sessionChatResponseJSON) RawJSON() string {
|
||||||
|
return r.raw
|
||||||
|
}
|
||||||
|
|
||||||
|
type SessionCommandResponse struct {
|
||||||
|
Info AssistantMessage `json:"info,required"`
|
||||||
|
Parts []Part `json:"parts,required"`
|
||||||
|
JSON sessionCommandResponseJSON `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// sessionCommandResponseJSON contains the JSON metadata for the struct
|
||||||
|
// [SessionCommandResponse]
|
||||||
|
type sessionCommandResponseJSON struct {
|
||||||
|
Info apijson.Field
|
||||||
|
Parts apijson.Field
|
||||||
|
raw string
|
||||||
|
ExtraFields map[string]apijson.Field
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *SessionCommandResponse) UnmarshalJSON(data []byte) (err error) {
|
||||||
|
return apijson.UnmarshalRoot(data, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r sessionCommandResponseJSON) RawJSON() string {
|
||||||
|
return r.raw
|
||||||
|
}
|
||||||
|
|
||||||
type SessionMessageResponse struct {
|
type SessionMessageResponse struct {
|
||||||
Info Message `json:"info,required"`
|
Info Message `json:"info,required"`
|
||||||
Parts []Part `json:"parts,required"`
|
Parts []Part `json:"parts,required"`
|
||||||
@@ -2396,6 +2454,18 @@ func (r SessionChatParamsPartsType) IsKnown() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SessionCommandParams struct {
|
||||||
|
Arguments param.Field[string] `json:"arguments,required"`
|
||||||
|
Command param.Field[string] `json:"command,required"`
|
||||||
|
Agent param.Field[string] `json:"agent"`
|
||||||
|
MessageID param.Field[string] `json:"messageID"`
|
||||||
|
Model param.Field[string] `json:"model"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r SessionCommandParams) MarshalJSON() (data []byte, err error) {
|
||||||
|
return apijson.MarshalRoot(r)
|
||||||
|
}
|
||||||
|
|
||||||
type SessionInitParams struct {
|
type SessionInitParams struct {
|
||||||
MessageID param.Field[string] `json:"messageID,required"`
|
MessageID param.Field[string] `json:"messageID,required"`
|
||||||
ModelID param.Field[string] `json:"modelID,required"`
|
ModelID param.Field[string] `json:"modelID,required"`
|
||||||
|
|||||||
@@ -199,6 +199,38 @@ func TestSessionChildren(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSessionCommandWithOptionalParams(t *testing.T) {
|
||||||
|
t.Skip("skipped: tests are disabled for the time being")
|
||||||
|
baseURL := "http://localhost:4010"
|
||||||
|
if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok {
|
||||||
|
baseURL = envURL
|
||||||
|
}
|
||||||
|
if !testutil.CheckTestServer(t, baseURL) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client := opencode.NewClient(
|
||||||
|
option.WithBaseURL(baseURL),
|
||||||
|
)
|
||||||
|
_, err := client.Session.Command(
|
||||||
|
context.TODO(),
|
||||||
|
"id",
|
||||||
|
opencode.SessionCommandParams{
|
||||||
|
Arguments: opencode.F("arguments"),
|
||||||
|
Command: opencode.F("command"),
|
||||||
|
Agent: opencode.F("agent"),
|
||||||
|
MessageID: opencode.F("msg"),
|
||||||
|
Model: opencode.F("model"),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
var apierr *opencode.Error
|
||||||
|
if errors.As(err, &apierr) {
|
||||||
|
t.Log(string(apierr.DumpRequest(true)))
|
||||||
|
}
|
||||||
|
t.Fatalf("err should be nil: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSessionGet(t *testing.T) {
|
func TestSessionGet(t *testing.T) {
|
||||||
t.Skip("skipped: tests are disabled for the time being")
|
t.Skip("skipped: tests are disabled for the time being")
|
||||||
baseURL := "http://localhost:4010"
|
baseURL := "http://localhost:4010"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"name": "@opencode-ai/sdk",
|
"name": "@opencode-ai/sdk",
|
||||||
"version": "0.5.9",
|
"version": "0.5.25",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "tsc --noEmit"
|
||||||
@@ -10,6 +10,14 @@
|
|||||||
".": {
|
".": {
|
||||||
"development": "./src/index.ts",
|
"development": "./src/index.ts",
|
||||||
"import": "./dist/index.js"
|
"import": "./dist/index.js"
|
||||||
|
},
|
||||||
|
"./client": {
|
||||||
|
"development": "./src/client.ts",
|
||||||
|
"import": "./dist/client.js"
|
||||||
|
},
|
||||||
|
"./server": {
|
||||||
|
"development": "./src/server.ts",
|
||||||
|
"import": "./dist/server.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
@@ -19,5 +27,8 @@
|
|||||||
"typescript": "catalog:",
|
"typescript": "catalog:",
|
||||||
"@hey-api/openapi-ts": "0.80.1",
|
"@hey-api/openapi-ts": "0.80.1",
|
||||||
"@tsconfig/node22": "catalog:"
|
"@tsconfig/node22": "catalog:"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@hey-api/openapi-ts": "0.80.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export * from "./gen/types.gen.js"
|
||||||
|
export { type Config as OpencodeClientConfig, OpencodeClient }
|
||||||
|
|
||||||
|
import { createClient } from "./gen/client/client.js"
|
||||||
|
import { type Config } from "./gen/client/types.js"
|
||||||
|
import { OpencodeClient } from "./gen/sdk.gen.js"
|
||||||
|
|
||||||
|
export function createOpencodeClient(config?: Config) {
|
||||||
|
const client = createClient(config)
|
||||||
|
return new OpencodeClient({ client })
|
||||||
|
}
|
||||||
@@ -39,6 +39,8 @@ import type {
|
|||||||
SessionChatResponses,
|
SessionChatResponses,
|
||||||
SessionMessageData,
|
SessionMessageData,
|
||||||
SessionMessageResponses,
|
SessionMessageResponses,
|
||||||
|
SessionCommandData,
|
||||||
|
SessionCommandResponses,
|
||||||
SessionShellData,
|
SessionShellData,
|
||||||
SessionShellResponses,
|
SessionShellResponses,
|
||||||
SessionRevertData,
|
SessionRevertData,
|
||||||
@@ -47,6 +49,8 @@ import type {
|
|||||||
SessionUnrevertResponses,
|
SessionUnrevertResponses,
|
||||||
PostSessionByIdPermissionsByPermissionIdData,
|
PostSessionByIdPermissionsByPermissionIdData,
|
||||||
PostSessionByIdPermissionsByPermissionIdResponses,
|
PostSessionByIdPermissionsByPermissionIdResponses,
|
||||||
|
CommandListData,
|
||||||
|
CommandListResponses,
|
||||||
ConfigProvidersData,
|
ConfigProvidersData,
|
||||||
ConfigProvidersResponses,
|
ConfigProvidersResponses,
|
||||||
FindTextData,
|
FindTextData,
|
||||||
@@ -355,6 +359,20 @@ class Session extends _HeyApiClient {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a new command to a session
|
||||||
|
*/
|
||||||
|
public command<ThrowOnError extends boolean = false>(options: Options<SessionCommandData, ThrowOnError>) {
|
||||||
|
return (options.client ?? this._client).post<SessionCommandResponses, unknown, ThrowOnError>({
|
||||||
|
url: "/session/{id}/command",
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options.headers,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run a shell command
|
* Run a shell command
|
||||||
*/
|
*/
|
||||||
@@ -394,6 +412,18 @@ class Session extends _HeyApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Command extends _HeyApiClient {
|
||||||
|
/**
|
||||||
|
* List all commands
|
||||||
|
*/
|
||||||
|
public list<ThrowOnError extends boolean = false>(options?: Options<CommandListData, ThrowOnError>) {
|
||||||
|
return (options?.client ?? this._client).get<CommandListResponses, unknown, ThrowOnError>({
|
||||||
|
url: "/command",
|
||||||
|
...options,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class Find extends _HeyApiClient {
|
class Find extends _HeyApiClient {
|
||||||
/**
|
/**
|
||||||
* Find text in files
|
* Find text in files
|
||||||
@@ -592,6 +622,7 @@ export class OpencodeClient extends _HeyApiClient {
|
|||||||
app = new App({ client: this._client })
|
app = new App({ client: this._client })
|
||||||
config = new Config({ client: this._client })
|
config = new Config({ client: this._client })
|
||||||
session = new Session({ client: this._client })
|
session = new Session({ client: this._client })
|
||||||
|
command = new Command({ client: this._client })
|
||||||
find = new Find({ client: this._client })
|
find = new Find({ client: this._client })
|
||||||
file = new File({ client: this._client })
|
file = new File({ client: this._client })
|
||||||
tui = new Tui({ client: this._client })
|
tui = new Tui({ client: this._client })
|
||||||
|
|||||||
@@ -585,6 +585,14 @@ export type Config = {
|
|||||||
*/
|
*/
|
||||||
scroll_speed: number
|
scroll_speed: number
|
||||||
}
|
}
|
||||||
|
command?: {
|
||||||
|
[key: string]: {
|
||||||
|
template: string
|
||||||
|
description?: string
|
||||||
|
agent?: string
|
||||||
|
model?: string
|
||||||
|
}
|
||||||
|
}
|
||||||
plugin?: Array<string>
|
plugin?: Array<string>
|
||||||
snapshot?: boolean
|
snapshot?: boolean
|
||||||
/**
|
/**
|
||||||
@@ -1110,6 +1118,14 @@ export type AgentPartInput = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Command = {
|
||||||
|
name: string
|
||||||
|
description?: string
|
||||||
|
agent?: string
|
||||||
|
model?: string
|
||||||
|
template: string
|
||||||
|
}
|
||||||
|
|
||||||
export type Symbol = {
|
export type Symbol = {
|
||||||
name: string
|
name: string
|
||||||
kind: number
|
kind: number
|
||||||
@@ -1527,7 +1543,10 @@ export type SessionChatResponses = {
|
|||||||
/**
|
/**
|
||||||
* Created message
|
* Created message
|
||||||
*/
|
*/
|
||||||
200: AssistantMessage
|
200: {
|
||||||
|
info: AssistantMessage
|
||||||
|
parts: Array<Part>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SessionChatResponse = SessionChatResponses[keyof SessionChatResponses]
|
export type SessionChatResponse = SessionChatResponses[keyof SessionChatResponses]
|
||||||
@@ -1560,6 +1579,36 @@ export type SessionMessageResponses = {
|
|||||||
|
|
||||||
export type SessionMessageResponse = SessionMessageResponses[keyof SessionMessageResponses]
|
export type SessionMessageResponse = SessionMessageResponses[keyof SessionMessageResponses]
|
||||||
|
|
||||||
|
export type SessionCommandData = {
|
||||||
|
body?: {
|
||||||
|
messageID?: string
|
||||||
|
agent?: string
|
||||||
|
model?: string
|
||||||
|
arguments: string
|
||||||
|
command: string
|
||||||
|
}
|
||||||
|
path: {
|
||||||
|
/**
|
||||||
|
* Session ID
|
||||||
|
*/
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
query?: never
|
||||||
|
url: "/session/{id}/command"
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SessionCommandResponses = {
|
||||||
|
/**
|
||||||
|
* Created message
|
||||||
|
*/
|
||||||
|
200: {
|
||||||
|
info: AssistantMessage
|
||||||
|
parts: Array<Part>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SessionCommandResponse = SessionCommandResponses[keyof SessionCommandResponses]
|
||||||
|
|
||||||
export type SessionShellData = {
|
export type SessionShellData = {
|
||||||
body?: {
|
body?: {
|
||||||
agent: string
|
agent: string
|
||||||
@@ -1645,6 +1694,22 @@ export type PostSessionByIdPermissionsByPermissionIdResponses = {
|
|||||||
export type PostSessionByIdPermissionsByPermissionIdResponse =
|
export type PostSessionByIdPermissionsByPermissionIdResponse =
|
||||||
PostSessionByIdPermissionsByPermissionIdResponses[keyof PostSessionByIdPermissionsByPermissionIdResponses]
|
PostSessionByIdPermissionsByPermissionIdResponses[keyof PostSessionByIdPermissionsByPermissionIdResponses]
|
||||||
|
|
||||||
|
export type CommandListData = {
|
||||||
|
body?: never
|
||||||
|
path?: never
|
||||||
|
query?: never
|
||||||
|
url: "/command"
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CommandListResponses = {
|
||||||
|
/**
|
||||||
|
* List of commands
|
||||||
|
*/
|
||||||
|
200: Array<Command>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CommandListResponse = CommandListResponses[keyof CommandListResponses]
|
||||||
|
|
||||||
export type ConfigProvidersData = {
|
export type ConfigProvidersData = {
|
||||||
body?: never
|
body?: never
|
||||||
path?: never
|
path?: never
|
||||||
|
|||||||
@@ -1,35 +1,2 @@
|
|||||||
import { createClient } from "./gen/client/client.js"
|
export * from "./client.js"
|
||||||
import { type Config } from "./gen/client/types.js"
|
export * from "./server.js"
|
||||||
import { OpencodeClient } from "./gen/sdk.gen.js"
|
|
||||||
export * from "./gen/types.gen.js"
|
|
||||||
import { spawn } from "child_process"
|
|
||||||
|
|
||||||
export function createOpencodeClient(config?: Config) {
|
|
||||||
const client = createClient(config)
|
|
||||||
return new OpencodeClient({ client })
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ServerConfig = {
|
|
||||||
host?: string
|
|
||||||
port?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function createOpencodeServer(config?: ServerConfig) {
|
|
||||||
config = Object.assign(
|
|
||||||
{
|
|
||||||
host: "127.0.0.1",
|
|
||||||
port: 4096,
|
|
||||||
},
|
|
||||||
config ?? {},
|
|
||||||
)
|
|
||||||
|
|
||||||
const proc = spawn(`opencode`, [`serve`, `--host=${config.host}`, `--port=${config.port}`])
|
|
||||||
const url = `http://${config.host}:${config.port}`
|
|
||||||
|
|
||||||
return {
|
|
||||||
url,
|
|
||||||
close() {
|
|
||||||
proc.kill()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import { spawn } from "node:child_process"
|
||||||
|
|
||||||
|
export type ServerConfig = {
|
||||||
|
hostname?: string
|
||||||
|
port?: number
|
||||||
|
signal?: AbortSignal
|
||||||
|
timeout?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createOpencodeServer(config?: ServerConfig) {
|
||||||
|
config = Object.assign(
|
||||||
|
{
|
||||||
|
hostname: "127.0.0.1",
|
||||||
|
port: 4096,
|
||||||
|
timeout: 5000,
|
||||||
|
},
|
||||||
|
config ?? {},
|
||||||
|
)
|
||||||
|
|
||||||
|
const proc = spawn(`opencode`, [`serve`, `--hostname=${config.hostname}`, `--port=${config.port}`], {
|
||||||
|
signal: config.signal,
|
||||||
|
})
|
||||||
|
|
||||||
|
const url = await new Promise<string>((resolve, reject) => {
|
||||||
|
const id = setTimeout(() => {
|
||||||
|
reject(new Error(`Timeout waiting for server to start after ${config.timeout}ms`))
|
||||||
|
}, config.timeout)
|
||||||
|
let output = ""
|
||||||
|
proc.stdout?.on("data", (chunk) => {
|
||||||
|
output += chunk.toString()
|
||||||
|
const lines = output.split("\n")
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.startsWith("opencode server listening")) {
|
||||||
|
const match = line.match(/on\s+(https?:\/\/[^\s]+)/)
|
||||||
|
if (!match) {
|
||||||
|
throw new Error(`Failed to parse server url from output: ${line}`)
|
||||||
|
}
|
||||||
|
clearTimeout(id)
|
||||||
|
resolve(match[1])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
proc.stderr?.on("data", (chunk) => {
|
||||||
|
output += chunk.toString()
|
||||||
|
})
|
||||||
|
proc.on("exit", (code) => {
|
||||||
|
clearTimeout(id)
|
||||||
|
let msg = `Server exited with code ${code}`
|
||||||
|
if (output.trim()) {
|
||||||
|
msg += `\nServer output: ${output}`
|
||||||
|
}
|
||||||
|
reject(new Error(msg))
|
||||||
|
})
|
||||||
|
proc.on("error", (error) => {
|
||||||
|
clearTimeout(id)
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
if (config.signal) {
|
||||||
|
config.signal.addEventListener("abort", () => {
|
||||||
|
clearTimeout(id)
|
||||||
|
reject(new Error("Aborted"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
url,
|
||||||
|
close() {
|
||||||
|
proc.kill()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -85,6 +85,12 @@ resources:
|
|||||||
methods:
|
methods:
|
||||||
get: get /config
|
get: get /config
|
||||||
|
|
||||||
|
command:
|
||||||
|
models:
|
||||||
|
command: Command
|
||||||
|
methods:
|
||||||
|
list: get /command
|
||||||
|
|
||||||
session:
|
session:
|
||||||
models:
|
models:
|
||||||
session: Session
|
session: Session
|
||||||
@@ -126,6 +132,7 @@ resources:
|
|||||||
message: get /session/{id}/message/{messageID}
|
message: get /session/{id}/message/{messageID}
|
||||||
messages: get /session/{id}/message
|
messages: get /session/{id}/message
|
||||||
chat: post /session/{id}/message
|
chat: post /session/{id}/message
|
||||||
|
command: post /session/{id}/command
|
||||||
shell: post /session/{id}/shell
|
shell: post /session/{id}/shell
|
||||||
update: patch /session/{id}
|
update: patch /session/{id}
|
||||||
revert: post /session/{id}/revert
|
revert: post /session/{id}/revert
|
||||||
|
|||||||
@@ -84,6 +84,10 @@ type SendPrompt = Prompt
|
|||||||
type SendShell = struct {
|
type SendShell = struct {
|
||||||
Command string
|
Command string
|
||||||
}
|
}
|
||||||
|
type SendCommand = struct {
|
||||||
|
Command string
|
||||||
|
Args string
|
||||||
|
}
|
||||||
type SetEditorContentMsg struct {
|
type SetEditorContentMsg struct {
|
||||||
Text string
|
Text string
|
||||||
}
|
}
|
||||||
@@ -183,6 +187,11 @@ func New(
|
|||||||
|
|
||||||
slog.Debug("Loaded config", "config", configInfo)
|
slog.Debug("Loaded config", "config", configInfo)
|
||||||
|
|
||||||
|
customCommands, err := httpClient.Command.List(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
app := &App{
|
app := &App{
|
||||||
Info: appInfo,
|
Info: appInfo,
|
||||||
Agents: agents,
|
Agents: agents,
|
||||||
@@ -194,7 +203,7 @@ func New(
|
|||||||
AgentIndex: agentIndex,
|
AgentIndex: agentIndex,
|
||||||
Session: &opencode.Session{},
|
Session: &opencode.Session{},
|
||||||
Messages: []Message{},
|
Messages: []Message{},
|
||||||
Commands: commands.LoadFromConfig(configInfo),
|
Commands: commands.LoadFromConfig(configInfo, *customCommands),
|
||||||
InitialModel: initialModel,
|
InitialModel: initialModel,
|
||||||
InitialPrompt: initialPrompt,
|
InitialPrompt: initialPrompt,
|
||||||
InitialAgent: initialAgent,
|
InitialAgent: initialAgent,
|
||||||
@@ -793,6 +802,40 @@ func (a *App) SendPrompt(ctx context.Context, prompt Prompt) (*App, tea.Cmd) {
|
|||||||
return a, tea.Batch(cmds...)
|
return a, tea.Batch(cmds...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) SendCommand(ctx context.Context, command string, args string) (*App, tea.Cmd) {
|
||||||
|
var cmds []tea.Cmd
|
||||||
|
if a.Session.ID == "" {
|
||||||
|
session, err := a.CreateSession(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return a, toast.NewErrorToast(err.Error())
|
||||||
|
}
|
||||||
|
a.Session = session
|
||||||
|
cmds = append(cmds, util.CmdHandler(SessionCreatedMsg{Session: session}))
|
||||||
|
}
|
||||||
|
|
||||||
|
cmds = append(cmds, func() tea.Msg {
|
||||||
|
_, err := a.Client.Session.Command(
|
||||||
|
context.Background(),
|
||||||
|
a.Session.ID,
|
||||||
|
opencode.SessionCommandParams{
|
||||||
|
Command: opencode.F(command),
|
||||||
|
Arguments: opencode.F(args),
|
||||||
|
Agent: opencode.F(a.Agents[a.AgentIndex].Name),
|
||||||
|
Model: opencode.F(a.State.Provider + "/" + a.State.Model),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Failed to execute command", "error", err)
|
||||||
|
return toast.NewErrorToast("Failed to execute command")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// The actual response will come through SSE
|
||||||
|
// For now, just return success
|
||||||
|
return a, tea.Batch(cmds...)
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) SendShell(ctx context.Context, command string) (*App, tea.Cmd) {
|
func (a *App) SendShell(ctx context.Context, command string) (*App, tea.Cmd) {
|
||||||
var cmds []tea.Cmd
|
var cmds []tea.Cmd
|
||||||
if a.Session.ID == "" {
|
if a.Session.ID == "" {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ type Command struct {
|
|||||||
Description string
|
Description string
|
||||||
Keybindings []Keybinding
|
Keybindings []Keybinding
|
||||||
Trigger []string
|
Trigger []string
|
||||||
|
Custom bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Command) Keys() []string {
|
func (c Command) Keys() []string {
|
||||||
@@ -91,11 +92,18 @@ func (r CommandRegistry) Sorted() []Command {
|
|||||||
if b.Name == AppExitCommand {
|
if b.Name == AppExitCommand {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
if a.Custom && !b.Custom {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if !a.Custom && b.Custom {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
return strings.Compare(string(a.Name), string(b.Name))
|
return strings.Compare(string(a.Name), string(b.Name))
|
||||||
})
|
})
|
||||||
return commands
|
return commands
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r CommandRegistry) Matches(msg tea.KeyPressMsg, leader bool) []Command {
|
func (r CommandRegistry) Matches(msg tea.KeyPressMsg, leader bool) []Command {
|
||||||
var matched []Command
|
var matched []Command
|
||||||
for _, command := range r.Sorted() {
|
for _, command := range r.Sorted() {
|
||||||
@@ -182,7 +190,7 @@ func parseBindings(bindings ...string) []Keybinding {
|
|||||||
return parsedBindings
|
return parsedBindings
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadFromConfig(config *opencode.Config) CommandRegistry {
|
func LoadFromConfig(config *opencode.Config, customCommands []opencode.Command) CommandRegistry {
|
||||||
defaults := []Command{
|
defaults := []Command{
|
||||||
{
|
{
|
||||||
Name: AppHelpCommand,
|
Name: AppHelpCommand,
|
||||||
@@ -400,6 +408,16 @@ func LoadFromConfig(config *opencode.Config) CommandRegistry {
|
|||||||
}
|
}
|
||||||
registry[command.Name] = command
|
registry[command.Name] = command
|
||||||
}
|
}
|
||||||
|
for _, command := range customCommands {
|
||||||
|
registry[CommandName(command.Name)] = Command{
|
||||||
|
Name: CommandName(command.Name),
|
||||||
|
Description: command.Description,
|
||||||
|
Trigger: []string{command.Name},
|
||||||
|
Keybindings: []Keybinding{},
|
||||||
|
Custom: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
slog.Info("Loaded commands", "commands", registry)
|
slog.Info("Loaded commands", "commands", registry)
|
||||||
return registry
|
return registry
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -224,10 +224,17 @@ func (m *editorComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
case dialog.CompletionSelectedMsg:
|
case dialog.CompletionSelectedMsg:
|
||||||
switch msg.Item.ProviderID {
|
switch msg.Item.ProviderID {
|
||||||
case "commands":
|
case "commands":
|
||||||
commandName := strings.TrimPrefix(msg.Item.Value, "/")
|
command := msg.Item.RawData.(commands.Command)
|
||||||
|
if command.Custom {
|
||||||
|
m.SetValue("/" + command.PrimaryTrigger() + " ")
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
updated, cmd := m.Clear()
|
updated, cmd := m.Clear()
|
||||||
m = updated.(*editorComponent)
|
m = updated.(*editorComponent)
|
||||||
cmds = append(cmds, cmd)
|
cmds = append(cmds, cmd)
|
||||||
|
|
||||||
|
commandName := strings.TrimPrefix(msg.Item.Value, "/")
|
||||||
cmds = append(cmds, util.CmdHandler(commands.ExecuteCommandMsg(m.app.Commands[commands.CommandName(commandName)])))
|
cmds = append(cmds, util.CmdHandler(commands.ExecuteCommandMsg(m.app.Commands[commands.CommandName(commandName)])))
|
||||||
return m, tea.Batch(cmds...)
|
return m, tea.Batch(cmds...)
|
||||||
case "files":
|
case "files":
|
||||||
@@ -481,6 +488,25 @@ func (m *editorComponent) Submit() (tea.Model, tea.Cmd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var cmds []tea.Cmd
|
var cmds []tea.Cmd
|
||||||
|
if strings.HasPrefix(value, "/") {
|
||||||
|
value = value[1:]
|
||||||
|
commandName := strings.Split(value, " ")[0]
|
||||||
|
command := m.app.Commands[commands.CommandName(commandName)]
|
||||||
|
if command.Custom {
|
||||||
|
args := strings.TrimPrefix(value, command.PrimaryTrigger()+" ")
|
||||||
|
cmds = append(
|
||||||
|
cmds,
|
||||||
|
util.CmdHandler(app.SendCommand{Command: string(command.Name), Args: args}),
|
||||||
|
)
|
||||||
|
|
||||||
|
updated, cmd := m.Clear()
|
||||||
|
m = updated.(*editorComponent)
|
||||||
|
cmds = append(cmds, cmd)
|
||||||
|
|
||||||
|
return m, tea.Batch(cmds...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
attachments := m.textarea.GetAttachments()
|
attachments := m.textarea.GetAttachments()
|
||||||
|
|
||||||
prompt := app.Prompt{Text: value, Attachments: attachments}
|
prompt := app.Prompt{Text: value, Attachments: attachments}
|
||||||
|
|||||||
@@ -174,6 +174,10 @@ func (m *messagesComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
m.viewport.GotoBottom()
|
m.viewport.GotoBottom()
|
||||||
m.tail = true
|
m.tail = true
|
||||||
return m, nil
|
return m, nil
|
||||||
|
case app.SendCommand:
|
||||||
|
m.viewport.GotoBottom()
|
||||||
|
m.tail = true
|
||||||
|
return m, nil
|
||||||
case dialog.ThemeSelectedMsg:
|
case dialog.ThemeSelectedMsg:
|
||||||
m.cache.Clear()
|
m.cache.Clear()
|
||||||
m.loading = true
|
m.loading = true
|
||||||
|
|||||||
@@ -408,6 +408,24 @@ func (a Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
a.app, cmd = a.app.SendPrompt(context.Background(), msg)
|
a.app, cmd = a.app.SendPrompt(context.Background(), msg)
|
||||||
cmds = append(cmds, cmd)
|
cmds = append(cmds, cmd)
|
||||||
}
|
}
|
||||||
|
case app.SendCommand:
|
||||||
|
// If we're in a child session, switch back to parent before sending prompt
|
||||||
|
if a.app.Session.ParentID != "" {
|
||||||
|
parentSession, err := a.app.Client.Session.Get(context.Background(), a.app.Session.ParentID)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Failed to get parent session", "error", err)
|
||||||
|
return a, toast.NewErrorToast("Failed to get parent session")
|
||||||
|
}
|
||||||
|
a.app.Session = parentSession
|
||||||
|
a.app, cmd = a.app.SendCommand(context.Background(), msg.Command, msg.Args)
|
||||||
|
cmds = append(cmds, tea.Sequence(
|
||||||
|
util.CmdHandler(app.SessionSelectedMsg(parentSession)),
|
||||||
|
cmd,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
a.app, cmd = a.app.SendCommand(context.Background(), msg.Command, msg.Args)
|
||||||
|
cmds = append(cmds, cmd)
|
||||||
|
}
|
||||||
case app.SendShell:
|
case app.SendShell:
|
||||||
// If we're in a child session, switch back to parent before sending prompt
|
// If we're in a child session, switch back to parent before sending prompt
|
||||||
if a.app.Session.ParentID != "" {
|
if a.app.Session.ParentID != "" {
|
||||||
@@ -897,9 +915,10 @@ func (a Model) Cleanup() {
|
|||||||
func (a Model) home() (string, int, int) {
|
func (a Model) home() (string, int, int) {
|
||||||
t := theme.CurrentTheme()
|
t := theme.CurrentTheme()
|
||||||
effectiveWidth := a.width - 4
|
effectiveWidth := a.width - 4
|
||||||
baseStyle := styles.NewStyle().Background(t.Background())
|
baseStyle := styles.NewStyle().Foreground(t.Text()).Background(t.Background())
|
||||||
base := baseStyle.Render
|
base := baseStyle.Render
|
||||||
muted := styles.NewStyle().Foreground(t.TextMuted()).Background(t.Background()).Render
|
muted := styles.NewStyle().Foreground(t.TextMuted()).Background(t.Background()).Render
|
||||||
|
highlight := styles.NewStyle().Foreground(t.Accent()).Background(t.Background()).Render
|
||||||
|
|
||||||
open := `
|
open := `
|
||||||
█▀▀█ █▀▀█ █▀▀ █▀▀▄
|
█▀▀█ █▀▀█ █▀▀ █▀▀▄
|
||||||
@@ -934,9 +953,9 @@ func (a Model) home() (string, int, int) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Use limit of 4 for vscode, 6 for others
|
// Use limit of 4 for vscode, 6 for others
|
||||||
limit := 6
|
limit := 4
|
||||||
if util.IsVSCode() {
|
if util.IsVSCode() {
|
||||||
limit = 4
|
limit = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
showVscode := util.IsVSCode()
|
showVscode := util.IsVSCode()
|
||||||
@@ -953,15 +972,23 @@ func (a Model) home() (string, int, int) {
|
|||||||
styles.WhitespaceStyle(t.Background()),
|
styles.WhitespaceStyle(t.Background()),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
grok := highlight("Try Grok Code free for a limited time")
|
||||||
|
grok = lipgloss.PlaceHorizontal(
|
||||||
|
effectiveWidth,
|
||||||
|
lipgloss.Center,
|
||||||
|
grok,
|
||||||
|
styles.WhitespaceStyle(t.Background()),
|
||||||
|
)
|
||||||
|
|
||||||
lines := []string{}
|
lines := []string{}
|
||||||
lines = append(lines, "")
|
lines = append(lines, "")
|
||||||
lines = append(lines, "")
|
|
||||||
lines = append(lines, logoAndVersion)
|
lines = append(lines, logoAndVersion)
|
||||||
lines = append(lines, "")
|
lines = append(lines, "")
|
||||||
lines = append(lines, "")
|
|
||||||
lines = append(lines, cmds)
|
lines = append(lines, cmds)
|
||||||
lines = append(lines, "")
|
lines = append(lines, "")
|
||||||
lines = append(lines, "")
|
lines = append(lines, "")
|
||||||
|
lines = append(lines, grok)
|
||||||
|
lines = append(lines, "")
|
||||||
|
|
||||||
mainHeight := lipgloss.Height(strings.Join(lines, "\n"))
|
mainHeight := lipgloss.Height(strings.Join(lines, "\n"))
|
||||||
|
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export default defineConfig({
|
|||||||
|
|
||||||
{
|
{
|
||||||
label: "Usage",
|
label: "Usage",
|
||||||
items: ["docs/tui", "docs/cli", "docs/ide", "docs/share", "docs/github"],
|
items: ["docs/tui", "docs/cli", "docs/ide", "docs/share", "docs/github", "docs/gitlab"],
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -78,6 +78,7 @@ export default defineConfig({
|
|||||||
"docs/models",
|
"docs/models",
|
||||||
"docs/themes",
|
"docs/themes",
|
||||||
"docs/keybinds",
|
"docs/keybinds",
|
||||||
|
"docs/commands",
|
||||||
"docs/formatters",
|
"docs/formatters",
|
||||||
"docs/permissions",
|
"docs/permissions",
|
||||||
"docs/lsp",
|
"docs/lsp",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode/web",
|
"name": "@opencode/web",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.5.9",
|
"version": "0.5.25",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "astro dev",
|
"dev": "astro dev",
|
||||||
"dev:remote": "sst shell --stage=dev --target=Web astro dev",
|
"dev:remote": "sst shell --stage=dev --target=Web astro dev",
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
"sharp": "0.32.5",
|
"sharp": "0.32.5",
|
||||||
"shiki": "3.4.2",
|
"shiki": "3.4.2",
|
||||||
"solid-js": "1.9.7",
|
"solid-js": "1.9.7",
|
||||||
"toolbeam-docs-theme": "0.4.3"
|
"toolbeam-docs-theme": "0.4.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"opencode": "workspace:*",
|
"opencode": "workspace:*",
|
||||||
|
|||||||
@@ -58,12 +58,11 @@ Build is the **default** primary agent with all tools enabled. This is the stand
|
|||||||
|
|
||||||
_Mode_: `primary`
|
_Mode_: `primary`
|
||||||
|
|
||||||
A restricted agent designed for planning and analysis. In the plan agent, the following tools are disabled by default:
|
A restricted agent designed for planning and analysis. We use a permission system to give you more control and prevent unintended changes.
|
||||||
|
By default, all of the following are set to `ask`:
|
||||||
|
|
||||||
- `write` - Cannot create new files
|
- `file edits`: All writes, patches, and edits
|
||||||
- `edit` - Cannot modify existing files
|
- `bash`: All bash commands
|
||||||
- `patch` - Cannot apply patches
|
|
||||||
- `bash` - Cannot execute shell commands
|
|
||||||
|
|
||||||
This agent is useful when you want the LLM to analyze code, suggest changes, or create plans without making any actual modifications to your codebase.
|
This agent is useful when you want the LLM to analyze code, suggest changes, or create plans without making any actual modifications to your codebase.
|
||||||
|
|
||||||
@@ -533,7 +532,7 @@ For example, with OpenAI's reasoning models, you can control the reasoning effor
|
|||||||
"agent": {
|
"agent": {
|
||||||
"deep-thinker": {
|
"deep-thinker": {
|
||||||
"description": "Agent that uses high reasoning effort for complex problems",
|
"description": "Agent that uses high reasoning effort for complex problems",
|
||||||
"model": "openai/gpt-5-turbo",
|
"model": "openai/gpt-5",
|
||||||
"reasoningEffort": "high",
|
"reasoningEffort": "high",
|
||||||
"textVerbosity": "low"
|
"textVerbosity": "low"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,156 @@
|
|||||||
|
---
|
||||||
|
title: Commands
|
||||||
|
description: Create custom commands for repetitive tasks.
|
||||||
|
---
|
||||||
|
|
||||||
|
Define custom commands to automate repetitive coding tasks.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Create command files
|
||||||
|
|
||||||
|
Create markdown files in the `command/` directory to define custom commands.
|
||||||
|
|
||||||
|
Create `.opencode/command/test.md`:
|
||||||
|
|
||||||
|
```md title=".opencode/command/test.md"
|
||||||
|
---
|
||||||
|
description: Run tests with coverage
|
||||||
|
agent: build
|
||||||
|
model: anthropic/claude-3-5-sonnet-20241022
|
||||||
|
---
|
||||||
|
|
||||||
|
Run the full test suite with coverage report and show any failures.
|
||||||
|
Focus on the failing tests and suggest fixes.
|
||||||
|
```
|
||||||
|
|
||||||
|
The frontmatter defines command properties. The content becomes the template.
|
||||||
|
|
||||||
|
Use the command by typing `/` followed by the command name.
|
||||||
|
|
||||||
|
```bash frame="none"
|
||||||
|
"/test"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Use arguments
|
||||||
|
|
||||||
|
Pass arguments to commands using the `$ARGUMENTS` placeholder.
|
||||||
|
|
||||||
|
Create `.opencode/command/component.md`:
|
||||||
|
|
||||||
|
```md title=".opencode/command/component.md"
|
||||||
|
---
|
||||||
|
description: Create a new component
|
||||||
|
---
|
||||||
|
|
||||||
|
Create a new React component named $ARGUMENTS with TypeScript support.
|
||||||
|
Include proper typing and basic structure.
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the command with arguments:
|
||||||
|
|
||||||
|
```bash frame="none"
|
||||||
|
"/component Button"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Inject shell output
|
||||||
|
|
||||||
|
Use !`command` to inject shell command output into your prompt.
|
||||||
|
|
||||||
|
Create `.opencode/command/analyze-coverage.md`:
|
||||||
|
|
||||||
|
```md title=".opencode/command/analyze-coverage.md"
|
||||||
|
---
|
||||||
|
description: Analyze test coverage
|
||||||
|
---
|
||||||
|
|
||||||
|
Here are the current test results:
|
||||||
|
!`npm test`
|
||||||
|
|
||||||
|
Based on these results, suggest improvements to increase coverage.
|
||||||
|
```
|
||||||
|
|
||||||
|
Create `.opencode/command/review-changes.md`:
|
||||||
|
|
||||||
|
```md title=".opencode/command/review-changes.md"
|
||||||
|
---
|
||||||
|
description: Review recent changes
|
||||||
|
---
|
||||||
|
|
||||||
|
Recent git commits:
|
||||||
|
!`git log --oneline -10`
|
||||||
|
|
||||||
|
Review these changes and suggest any improvements.
|
||||||
|
```
|
||||||
|
|
||||||
|
Commands run in your project's root directory and their output becomes part of the prompt.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Reference files
|
||||||
|
|
||||||
|
Include files in your command using `@` followed by the filename.
|
||||||
|
|
||||||
|
Create `.opencode/command/review-component.md`:
|
||||||
|
|
||||||
|
```md title=".opencode/command/review-component.md"
|
||||||
|
---
|
||||||
|
description: Review component
|
||||||
|
---
|
||||||
|
|
||||||
|
Review the component in @src/components/Button.tsx.
|
||||||
|
Check for performance issues and suggest improvements.
|
||||||
|
```
|
||||||
|
|
||||||
|
The file content gets included in the prompt automatically.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Command properties
|
||||||
|
|
||||||
|
Configure commands with these optional frontmatter properties:
|
||||||
|
|
||||||
|
- **description**: Brief explanation of what the command does
|
||||||
|
- **agent**: Agent to use (defaults to "build")
|
||||||
|
- **model**: Specific model to use for this command
|
||||||
|
|
||||||
|
Create `.opencode/command/code-review.md`:
|
||||||
|
|
||||||
|
```md title=".opencode/command/code-review.md"
|
||||||
|
---
|
||||||
|
description: Code review assistant
|
||||||
|
agent: build
|
||||||
|
model: anthropic/claude-3-5-sonnet-20241022
|
||||||
|
---
|
||||||
|
|
||||||
|
Review the code for best practices and suggest improvements.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Command directory
|
||||||
|
|
||||||
|
Store command files in these locations:
|
||||||
|
|
||||||
|
- `.opencode/command/` - Project-specific commands
|
||||||
|
- `command/` - Global commands in config directory
|
||||||
|
|
||||||
|
Project commands take precedence over global ones.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Built-in commands
|
||||||
|
|
||||||
|
opencode includes several built-in commands:
|
||||||
|
|
||||||
|
- `/init` - Initialize project and create AGENTS.md
|
||||||
|
- `/undo` - Revert the last changes
|
||||||
|
- `/redo` - Restore reverted changes
|
||||||
|
- `/share` - Share the current conversation
|
||||||
|
- `/help` - Show available commands and keybinds
|
||||||
|
|
||||||
|
Use `/help` to see all available commands in your setup.
|
||||||
@@ -9,7 +9,7 @@ you to use opencode at your organization.
|
|||||||
To get started, we recommend:
|
To get started, we recommend:
|
||||||
|
|
||||||
1. Do a trial internally with your team.
|
1. Do a trial internally with your team.
|
||||||
2. [**Contact us**](mailto:hello@sst.dev) to discuss pricing and implementation options.
|
2. [**Contact us**](mailto:hello@anoma.ly) to discuss pricing and implementation options.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ We recommend you disable this for your trial.
|
|||||||
## Deployment
|
## Deployment
|
||||||
|
|
||||||
Once you have completed your trial and you are ready to self-host opencode at
|
Once you have completed your trial and you are ready to self-host opencode at
|
||||||
your organization, you can [**contact us**](mailto:hello@sst.dev) to discuss
|
your organization, you can [**contact us**](mailto:hello@anoma.ly) to discuss
|
||||||
pricing and implementation options.
|
pricing and implementation options.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: GitHub
|
title: GitHub
|
||||||
description: Use opencode in GitHub issues and pull-requests
|
description: Use opencode in GitHub issues and pull-requests.
|
||||||
---
|
---
|
||||||
|
|
||||||
opencode integrates with your GitHub workflow. Mention `/opencode` or `/oc` in your comment, and opencode will execute tasks within your GitHub Actions runner.
|
opencode integrates with your GitHub workflow. Mention `/opencode` or `/oc` in your comment, and opencode will execute tasks within your GitHub Actions runner.
|
||||||
|
|||||||
@@ -0,0 +1,151 @@
|
|||||||
|
---
|
||||||
|
title: GitLab
|
||||||
|
description: Use opencode in GitLab issues and merge requests.
|
||||||
|
---
|
||||||
|
|
||||||
|
opencode integrates with your GitLab workflow.
|
||||||
|
Mention `@opencode` in a comment, and opencode will execute tasks within your GitLab CI pipeline.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Triage issues**: Ask opencode to look into an issue and explain it to you.
|
||||||
|
- **Fix and implement**: Ask opencode to fix an issue or implement a feature.
|
||||||
|
It will work create a new branch and raised a merge request with the changes.
|
||||||
|
- **Secure**: opencode runs on your GitLab runners.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
opencode runs in your GitLab CI/CD pipeline, here's what you'll need to set it up:
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
Check out the [**GitLab docs**](https://docs.gitlab.com/user/duo_agent_platform/agent_assistant/) for up to date instructions.
|
||||||
|
:::
|
||||||
|
|
||||||
|
1. Configure your GitLab environment
|
||||||
|
2. Set up CI/CD
|
||||||
|
3. Get an AI model provider API key
|
||||||
|
4. Create a service account
|
||||||
|
5. Configure CI/CD variables
|
||||||
|
6. Create a flow config file, here's an example:
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Flow configuration</summary>
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
image: node:22-slim
|
||||||
|
commands:
|
||||||
|
- echo "Installing opencode"
|
||||||
|
- npm install --global opencode-ai
|
||||||
|
- echo "Installing glab"
|
||||||
|
- export GITLAB_TOKEN=$GITLAB_TOKEN_OPENCODE
|
||||||
|
- apt-get update --quiet && apt-get install --yes curl wget gpg git && rm --recursive --force /var/lib/apt/lists/*
|
||||||
|
- curl --silent --show-error --location "https://raw.githubusercontent.com/upciti/wakemeops/main/assets/install_repository" | bash
|
||||||
|
- apt-get install --yes glab
|
||||||
|
- echo "Configuring glab"
|
||||||
|
- echo $GITLAB_HOST
|
||||||
|
- echo "Creating opencode auth configuration"
|
||||||
|
- mkdir --parents ~/.local/share/opencode
|
||||||
|
- |
|
||||||
|
cat > ~/.local/share/opencode/auth.json << EOF
|
||||||
|
{
|
||||||
|
"anthropic": {
|
||||||
|
"type": "api",
|
||||||
|
"key": "$ANTHROPIC_API_KEY"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
- echo "Configuring git"
|
||||||
|
- git config --global user.email "opencode@gitlab.com"
|
||||||
|
- git config --global user.name "Opencode"
|
||||||
|
- echo "Testing glab"
|
||||||
|
- glab issue list
|
||||||
|
- echo "Running Opencode"
|
||||||
|
- |
|
||||||
|
opencode run "
|
||||||
|
You are an AI assistant helping with GitLab operations.
|
||||||
|
|
||||||
|
Context: $AI_FLOW_CONTEXT
|
||||||
|
Task: $AI_FLOW_INPUT
|
||||||
|
Event: $AI_FLOW_EVENT
|
||||||
|
|
||||||
|
Please execute the requested task using the available GitLab tools.
|
||||||
|
Be thorough in your analysis and provide clear explanations.
|
||||||
|
|
||||||
|
<important>
|
||||||
|
Please use the glab CLI to access data from GitLab. The glab CLI has already been authenticated. You can run the corresponding commands.
|
||||||
|
|
||||||
|
If you are asked to summarise an MR or issue or asked to provide more information then please post back a note to the MR/Issue so that the user can see it.
|
||||||
|
You don't need to commit or push up changes, those will be done automatically based on the file changes you make.
|
||||||
|
</important>
|
||||||
|
"
|
||||||
|
- git checkout --branch $CI_WORKLOAD_REF origin/$CI_WORKLOAD_REF
|
||||||
|
- echo "Checking for git changes and pushing if any exist"
|
||||||
|
- |
|
||||||
|
if ! git diff --quiet || ! git diff --cached --quiet || [ --not --zero "$(git ls-files --others --exclude-standard)" ]; then
|
||||||
|
echo "Git changes detected, adding and pushing..."
|
||||||
|
git add .
|
||||||
|
if git diff --cached --quiet; then
|
||||||
|
echo "No staged changes to commit"
|
||||||
|
else
|
||||||
|
echo "Committing changes to branch: $CI_WORKLOAD_REF"
|
||||||
|
git commit --message "Codex changes"
|
||||||
|
echo "Pushing changes up to $CI_WORKLOAD_REF"
|
||||||
|
git push https://gitlab-ci-token:$GITLAB_TOKEN@$GITLAB_HOST/gl-demo-ultimate-dev-ai-epic-17570/test-java-project.git $CI_WORKLOAD_REF
|
||||||
|
echo "Changes successfully pushed"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "No git changes detected, skipping push"
|
||||||
|
fi
|
||||||
|
variables:
|
||||||
|
- ANTHROPIC_API_KEY
|
||||||
|
- GITLAB_TOKEN_OPENCODE
|
||||||
|
- GITLAB_HOST
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
You can refer to the [GitLab CLI agents docs](https://docs.gitlab.com/user/duo_agent_platform/agent_assistant/) for detailed instructions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Here are some examples of how you can use opencode in GitLab.
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
You can configure to use a different trigger phrase than `@opencode`.
|
||||||
|
:::
|
||||||
|
|
||||||
|
- **Explain an issue**
|
||||||
|
|
||||||
|
Add this comment in a GitLab issue.
|
||||||
|
|
||||||
|
```
|
||||||
|
@opencode explain this issue
|
||||||
|
```
|
||||||
|
|
||||||
|
opencode will read the issue and reply with a clear explanation.
|
||||||
|
|
||||||
|
- **Fix an issue**
|
||||||
|
|
||||||
|
In a GitLab issue, say:
|
||||||
|
|
||||||
|
```
|
||||||
|
@opencode fix this
|
||||||
|
```
|
||||||
|
|
||||||
|
opencode will create a new branch, implement the changes, and open a merge request with the changes.
|
||||||
|
|
||||||
|
- **Review merge requests**
|
||||||
|
|
||||||
|
Leave the following comment on a GitLab merge request.
|
||||||
|
|
||||||
|
```
|
||||||
|
@opencode review this merge request
|
||||||
|
```
|
||||||
|
|
||||||
|
opencode will review the merge request and provide feedback.
|
||||||
@@ -42,24 +42,24 @@ You can also install it with the following:
|
|||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<TabItem label="npm">
|
<TabItem label="npm">
|
||||||
```bash
|
```bash
|
||||||
npm install -g opencode-ai
|
npm install -g opencode-ai
|
||||||
```
|
```
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem label="Bun">
|
<TabItem label="Bun">
|
||||||
```bash
|
```bash
|
||||||
bun install -g opencode-ai
|
bun install -g opencode-ai
|
||||||
```
|
```
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem label="pnpm">
|
<TabItem label="pnpm">
|
||||||
```bash
|
```bash
|
||||||
pnpm install -g opencode-ai
|
pnpm install -g opencode-ai
|
||||||
```
|
```
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem label="Yarn">
|
<TabItem label="Yarn">
|
||||||
```bash
|
```bash
|
||||||
yarn global add opencode-ai
|
yarn global add opencode-ai
|
||||||
```
|
```
|
||||||
</TabItem>
|
</TabItem>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
@@ -308,4 +308,4 @@ Here's an [example conversation](https://opencode.ai/s/4XP1fce5) with opencode.
|
|||||||
|
|
||||||
And that's it! You are now a pro at using opencode.
|
And that's it! You are now a pro at using opencode.
|
||||||
|
|
||||||
To make it your own, we recommend [picking a theme](/docs/themes), [customizing the keybinds](/docs/keybinds), [configuring code formatters](/docs/formatters), or playing around with the [opencode config](/docs/config).
|
To make it your own, we recommend [picking a theme](/docs/themes), [customizing the keybinds](/docs/keybinds), [configuring code formatters](/docs/formatters), [creating custom commands](/docs/commands), or playing around with the [opencode config](/docs/config).
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ title: LSP Servers
|
|||||||
description: opencode integrates with your LSP servers.
|
description: opencode integrates with your LSP servers.
|
||||||
---
|
---
|
||||||
|
|
||||||
opencode integrates with your Language Server Protocol (LSP) to help the LLM interacts with your codebase. It uses diagnostics to provide feebdack to the LLM. And _go-to-definition_ and _find-references_ to help navigate your codebase.
|
opencode integrates with your Language Server Protocol (LSP) to help the LLM interacts with your codebase. It uses diagnostics to provide feedback to the LLM. And _go-to-definition_ and _find-references_ to help navigate your codebase.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -11,19 +11,26 @@ opencode integrates with your Language Server Protocol (LSP) to help the LLM int
|
|||||||
|
|
||||||
opencode comes with several built-in LSP servers for popular languages:
|
opencode comes with several built-in LSP servers for popular languages:
|
||||||
|
|
||||||
| LSP Server | Extensions | Requirements |
|
| LSP Server | Extensions | Requirements |
|
||||||
| ---------- | -------------------------------------------- | ----------------------------------- |
|
| ---------- | ---------------------------------------------------- | ----------------------------------- |
|
||||||
| typescript | .ts, .tsx, .js, .jsx, .mjs, .cjs, .mts, .cts | `typescript` dependency in project |
|
| typescript | .ts, .tsx, .js, .jsx, .mjs, .cjs, .mts, .cts | `typescript` dependency in project |
|
||||||
| eslint | .ts, .tsx, .js, .jsx, .mjs, .cjs, .mts, .cts | `eslint` dependency in project |
|
| eslint | .ts, .tsx, .js, .jsx, .mjs, .cjs, .mts, .cts, .vue | `eslint` dependency in project |
|
||||||
| gopls | .go | `go` command available |
|
| gopls | .go | `go` command available |
|
||||||
| ruby-lsp | .rb, .rake, .gemspec, .ru | `ruby` and `gem` commands available |
|
| ruby-lsp | .rb, .rake, .gemspec, .ru | `ruby` and `gem` commands available |
|
||||||
| pyright | .py, .pyi | `pyright` dependency installed |
|
| pyright | .py, .pyi | `pyright` dependency installed |
|
||||||
| elixir-ls | .ex, .exs | `elixir` command available |
|
| elixir-ls | .ex, .exs | `elixir` command available |
|
||||||
| zls | .zig, .zon | `zig` command available |
|
| zls | .zig, .zon | `zig` command available |
|
||||||
| csharp | .cs | `.NET SDK` installed |
|
| csharp | .cs | `.NET SDK` installed |
|
||||||
|
| vue | .vue | Auto-installs for Vue projects |
|
||||||
|
| rust | .rs | `rust-analyzer` command available |
|
||||||
|
| clangd | .c, .cpp, .cc, .cxx, .c++, .h, .hpp, .hh, .hxx, .h++ | Auto-installs for C/C++ projects |
|
||||||
|
|
||||||
LSP servers are automatically enabled when one of the above file extensions are detected and the requirements are met.
|
LSP servers are automatically enabled when one of the above file extensions are detected and the requirements are met.
|
||||||
|
|
||||||
|
:::note
|
||||||
|
You can disable automatic LSP server downloads by setting the `OPENCODE_DISABLE_LSP_DOWNLOAD` environment variable to `true`.
|
||||||
|
:::
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## How It Works
|
## How It Works
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ export const typesUrl = `${config.github}/blob/dev/packages/sdk/js/src/gen/types
|
|||||||
|
|
||||||
The opencode [JS/TS SDK](https://www.npmjs.com/package/@opencode-ai/sdk) provides a type-safe client for interacting with the opencode server. You can use it to build custom integrations and control opencode programmatically.
|
The opencode [JS/TS SDK](https://www.npmjs.com/package/@opencode-ai/sdk) provides a type-safe client for interacting with the opencode server. You can use it to build custom integrations and control opencode programmatically.
|
||||||
|
|
||||||
|
[Learn more](/docs/server) about how the opencode server works.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
@@ -34,10 +36,13 @@ const client = createOpencodeClient({
|
|||||||
|
|
||||||
#### Options
|
#### Options
|
||||||
|
|
||||||
| Option | Type | Description | Default |
|
| Option | Type | Description | Default |
|
||||||
| --------- | ---------- | --------------------------- | ----------------------- |
|
| --------------- | ---------- | --------------------------------- | ----------------------- |
|
||||||
| `baseUrl` | `string` | URL of the opencode server | `http://localhost:4096` |
|
| `baseUrl` | `string` | URL of the opencode server | `http://localhost:4096` |
|
||||||
| `fetch` | `function` | Custom fetch implementation | `globalThis.fetch` |
|
| `fetch` | `function` | Custom fetch implementation | `globalThis.fetch` |
|
||||||
|
| `parseAs` | `string` | Response parsing method | `auto` |
|
||||||
|
| `responseStyle` | `string` | Return style: `data` or `fields` | `fields` |
|
||||||
|
| `throwOnError` | `boolean` | Throw errors instead of returning | `false` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -45,18 +50,27 @@ const client = createOpencodeClient({
|
|||||||
|
|
||||||
You can also programmatically start an opencode server:
|
You can also programmatically start an opencode server:
|
||||||
|
|
||||||
```javascript
|
````javascript
|
||||||
import { createOpencodeServer } from "@opencode-ai/sdk"
|
import { createOpencodeServer } from "@opencode-ai/sdk"
|
||||||
|
|
||||||
const server = await createOpencodeServer({
|
const server = await createOpencodeServer({
|
||||||
host: "127.0.0.1",
|
hostname: "127.0.0.1",
|
||||||
port: 4096,
|
port: 4096,
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log(`Server running at ${server.url}`)
|
console.log(`Server running at ${server.url}`)
|
||||||
|
|
||||||
server.close()
|
server.close()
|
||||||
```
|
}
|
||||||
|
|
||||||
|
#### Options
|
||||||
|
|
||||||
|
| Option | Type | Description | Default |
|
||||||
|
| ---------- | -------------- | ------------------------------ | ------------- |
|
||||||
|
| `hostname` | `string` | Server hostname | `127.0.0.1` |
|
||||||
|
| `port` | `number` | Server port | `4096` |
|
||||||
|
| `signal` | `AbortSignal` | Abort signal for cancellation | `undefined` |
|
||||||
|
| `timeout` | `number` | Timeout in ms for server start | `5000` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -66,12 +80,26 @@ The SDK includes TypeScript definitions for all API types. Import them directly:
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import type { Session, Message, Part } from "@opencode-ai/sdk"
|
import type { Session, Message, Part } from "@opencode-ai/sdk"
|
||||||
```
|
````
|
||||||
|
|
||||||
All types are generated from the server's OpenAPI specification and available in the <a href={typesUrl}>types file</a>.
|
All types are generated from the server's OpenAPI specification and available in the <a href={typesUrl}>types file</a>.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
The SDK throws typed errors that you can catch and handle:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
try {
|
||||||
|
const session = await client.session.get({ id: "invalid-id" })
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to get session:", error.message)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## APIs
|
## APIs
|
||||||
|
|
||||||
The SDK exposes all server APIs through a type-safe client interface.
|
The SDK exposes all server APIs through a type-safe client interface.
|
||||||
@@ -284,17 +312,3 @@ for await (const event of eventStream) {
|
|||||||
console.log("Event:", event.type, event.properties)
|
console.log("Event:", event.type, event.properties)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Error handling
|
|
||||||
|
|
||||||
The SDK throws typed errors that you can catch and handle:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
try {
|
|
||||||
const session = await client.session.get({ id: "invalid-id" })
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Failed to get session:", error.message)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ description: Interact with opencode server over HTTP.
|
|||||||
import config from "../../../../config.mjs"
|
import config from "../../../../config.mjs"
|
||||||
export const typesUrl = `${config.github}/blob/dev/packages/sdk/js/src/gen/types.gen.ts`
|
export const typesUrl = `${config.github}/blob/dev/packages/sdk/js/src/gen/types.gen.ts`
|
||||||
|
|
||||||
The `opencode serve` command runs a headless HTTP server that exposes an API that an opencode client, like the TUI, uses. You can use this to create your own opencode client and even control a currently running TUI.
|
The `opencode serve` command runs a headless HTTP server that exposes an OpenAPI endpoint that an opencode client can use.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Usage
|
### Usage
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
opencode serve [--port <number>] [--hostname <string>]
|
opencode serve [--port <number>] [--hostname <string>]
|
||||||
@@ -25,7 +25,32 @@ opencode serve [--port <number>] [--hostname <string>]
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Docs
|
### How it works
|
||||||
|
|
||||||
|
When you run `opencode` it starts a TUI and a server. Where the TUI is the
|
||||||
|
client that talks to the server. The server exposes an OpenAPI 3.1 spec
|
||||||
|
endpoint. This endpoint is also used to generate an [SDK](/docs/sdk).
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
Use the opencode server to interact with opencode programmatically.
|
||||||
|
:::
|
||||||
|
|
||||||
|
This architecture lets opencode support multiple clients and allows you to interact with opencode programmatically.
|
||||||
|
|
||||||
|
You can run `opencode serve` to start a standalone server. If you have the
|
||||||
|
opencode TUI running, `opencode serve` will start a new server.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Connect to an existing server
|
||||||
|
|
||||||
|
When you start the TUI it randomly assigns a port and hostname. You can instead pass in the `--hostname` and `--port` [flags](/docs/cli). Then use this to connect to its server.
|
||||||
|
|
||||||
|
The [`/tui`](#tui) endpoint can be used to drive the TUI through the server. For example, you can prefill or run a prompt. This setup is used by the opencode [IDE](/docs/ide) plugins.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Spec
|
||||||
|
|
||||||
The server publishes an OpenAPI 3.1 spec that can be viewed at:
|
The server publishes an OpenAPI 3.1 spec that can be viewed at:
|
||||||
|
|
||||||
|
|||||||
@@ -254,37 +254,52 @@ Both the `/editor` and `/export` commands use the editor specified in your `EDIT
|
|||||||
<Tabs>
|
<Tabs>
|
||||||
<TabItem label="Linux/macOS">
|
<TabItem label="Linux/macOS">
|
||||||
```bash
|
```bash
|
||||||
export EDITOR=nano # or vim, code, etc.
|
# Example for nano or vim
|
||||||
|
export EDITOR=nano
|
||||||
|
export EDITOR=vim
|
||||||
|
|
||||||
|
# For GUI editors (VS Code, Cursor, VSCodium, Windsurf, Zed, etc.) include --wait
|
||||||
|
export EDITOR="code --wait"
|
||||||
```
|
```
|
||||||
|
|
||||||
To make it permanent, add this to your shell profile;
|
To make it permanent, add this to your shell profile;
|
||||||
`~/.bashrc`, `~/.zshrc`, etc.
|
`~/.bashrc`, `~/.zshrc`, etc.
|
||||||
|
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
||||||
<TabItem label="Windows (CMD)">
|
<TabItem label="Windows (CMD)">
|
||||||
```bash
|
```bash
|
||||||
set EDITOR=notepad # or code, vim, etc.
|
set EDITOR=notepad
|
||||||
|
|
||||||
|
# For GUI editors (VS Code, Cursor, VSCodium, Windsurf, Zed, etc.) include --wait
|
||||||
|
set EDITOR=code --wait
|
||||||
```
|
```
|
||||||
|
|
||||||
To make it permanent, use **System Properties** > **Environment
|
To make it permanent, use **System Properties** > **Environment
|
||||||
Variables**.
|
Variables**.
|
||||||
|
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
||||||
<TabItem label="Windows (PowerShell)">
|
<TabItem label="Windows (PowerShell)">
|
||||||
```bash
|
```powershell
|
||||||
$env:EDITOR = "notepad" # or "code", "vim", etc.
|
$env:EDITOR = "notepad"
|
||||||
|
|
||||||
|
# For GUI editors (VS Code, Cursor, VSCodium, Windsurf, Zed, etc.) include --wait
|
||||||
|
$env:EDITOR = "code --wait"
|
||||||
```
|
```
|
||||||
|
|
||||||
To make it permanent, add this to your PowerShell
|
To make it permanent, add this to your PowerShell profile.
|
||||||
profile.
|
|
||||||
|
|
||||||
</TabItem>
|
</TabItem>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
Popular editor options include:
|
Popular editor options include:
|
||||||
|
|
||||||
- `code` - Visual Studio Code
|
- `code` - Visual Studio Code
|
||||||
|
- `cursor` - Cursor
|
||||||
|
- `windsurf` - Windsurf
|
||||||
- `vim` - Vim editor
|
- `vim` - Vim editor
|
||||||
- `nano` - Nano editor
|
- `nano` - Nano editor
|
||||||
- `notepad` - Windows Notepad
|
- `notepad` - Windows Notepad
|
||||||
- `subl` - Sublime Text
|
- `subl` - Sublime Text
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
Some editors need command-line arguments to run in blocking mode. The --wait flag makes the editor process block until closed, which is necessary for opencode to function correctly.
|
||||||
|
:::
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "opencode",
|
"name": "opencode",
|
||||||
"displayName": "opencode",
|
"displayName": "opencode",
|
||||||
"description": "opencode for VS Code",
|
"description": "opencode for VS Code",
|
||||||
"version": "0.5.9",
|
"version": "0.5.25",
|
||||||
"publisher": "sst-dev",
|
"publisher": "sst-dev",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
Vendored
-4
@@ -27,10 +27,6 @@ declare module "sst" {
|
|||||||
"Bucket": {
|
"Bucket": {
|
||||||
"type": "sst.cloudflare.Bucket"
|
"type": "sst.cloudflare.Bucket"
|
||||||
}
|
}
|
||||||
"Console": {
|
|
||||||
"type": "sst.cloudflare.StaticSite"
|
|
||||||
"url": string
|
|
||||||
}
|
|
||||||
"DATABASE_PASSWORD": {
|
"DATABASE_PASSWORD": {
|
||||||
"type": "sst.sst.Secret"
|
"type": "sst.sst.Secret"
|
||||||
"value": string
|
"value": string
|
||||||
|
|||||||
+5
-1
@@ -1,5 +1,9 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/tsconfig",
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
"extends": "@tsconfig/bun/tsconfig.json",
|
"extends": "@tsconfig/bun/tsconfig.json",
|
||||||
"compilerOptions": {}
|
"compilerOptions": {
|
||||||
|
"customConditions": [
|
||||||
|
"development"
|
||||||
|
],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user