Merge branch 'dev' of https://github.com/sst/opencode into dev

This commit is contained in:
David Hill
2025-11-04 21:36:46 +00:00
33 changed files with 548 additions and 248 deletions

View File

@@ -39,7 +39,7 @@
}, },
"packages/console/core": { "packages/console/core": {
"name": "@opencode-ai/console-core", "name": "@opencode-ai/console-core",
"version": "1.0.20", "version": "1.0.23",
"dependencies": { "dependencies": {
"@aws-sdk/client-sts": "3.782.0", "@aws-sdk/client-sts": "3.782.0",
"@jsx-email/render": "1.1.1", "@jsx-email/render": "1.1.1",
@@ -66,7 +66,7 @@
}, },
"packages/console/function": { "packages/console/function": {
"name": "@opencode-ai/console-function", "name": "@opencode-ai/console-function",
"version": "1.0.20", "version": "1.0.23",
"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",
@@ -90,7 +90,7 @@
}, },
"packages/console/mail": { "packages/console/mail": {
"name": "@opencode-ai/console-mail", "name": "@opencode-ai/console-mail",
"version": "1.0.20", "version": "1.0.23",
"dependencies": { "dependencies": {
"@jsx-email/all": "2.2.3", "@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3", "@jsx-email/cli": "1.4.3",
@@ -111,7 +111,7 @@
}, },
"packages/desktop": { "packages/desktop": {
"name": "@opencode-ai/desktop", "name": "@opencode-ai/desktop",
"version": "1.0.20", "version": "1.0.23",
"dependencies": { "dependencies": {
"@kobalte/core": "catalog:", "@kobalte/core": "catalog:",
"@opencode-ai/sdk": "workspace:*", "@opencode-ai/sdk": "workspace:*",
@@ -150,7 +150,7 @@
}, },
"packages/function": { "packages/function": {
"name": "@opencode-ai/function", "name": "@opencode-ai/function",
"version": "1.0.20", "version": "1.0.23",
"dependencies": { "dependencies": {
"@octokit/auth-app": "8.0.1", "@octokit/auth-app": "8.0.1",
"@octokit/rest": "22.0.0", "@octokit/rest": "22.0.0",
@@ -166,7 +166,7 @@
}, },
"packages/opencode": { "packages/opencode": {
"name": "opencode", "name": "opencode",
"version": "1.0.20", "version": "1.0.23",
"bin": { "bin": {
"opencode": "./bin/opencode", "opencode": "./bin/opencode",
}, },
@@ -184,8 +184,8 @@
"@opencode-ai/plugin": "workspace:*", "@opencode-ai/plugin": "workspace:*",
"@opencode-ai/script": "workspace:*", "@opencode-ai/script": "workspace:*",
"@opencode-ai/sdk": "workspace:*", "@opencode-ai/sdk": "workspace:*",
"@opentui/core": "0.1.34", "@opentui/core": "0.1.33",
"@opentui/solid": "0.1.34", "@opentui/solid": "0.1.33",
"@parcel/watcher": "2.5.1", "@parcel/watcher": "2.5.1",
"@pierre/precision-diffs": "catalog:", "@pierre/precision-diffs": "catalog:",
"@solid-primitives/event-bus": "1.1.2", "@solid-primitives/event-bus": "1.1.2",
@@ -243,7 +243,7 @@
}, },
"packages/plugin": { "packages/plugin": {
"name": "@opencode-ai/plugin", "name": "@opencode-ai/plugin",
"version": "1.0.20", "version": "1.0.23",
"dependencies": { "dependencies": {
"@opencode-ai/sdk": "workspace:*", "@opencode-ai/sdk": "workspace:*",
"zod": "catalog:", "zod": "catalog:",
@@ -263,7 +263,7 @@
}, },
"packages/sdk/js": { "packages/sdk/js": {
"name": "@opencode-ai/sdk", "name": "@opencode-ai/sdk",
"version": "1.0.20", "version": "1.0.23",
"devDependencies": { "devDependencies": {
"@hey-api/openapi-ts": "0.81.0", "@hey-api/openapi-ts": "0.81.0",
"@tsconfig/node22": "catalog:", "@tsconfig/node22": "catalog:",
@@ -274,7 +274,7 @@
}, },
"packages/slack": { "packages/slack": {
"name": "@opencode-ai/slack", "name": "@opencode-ai/slack",
"version": "1.0.20", "version": "1.0.23",
"dependencies": { "dependencies": {
"@opencode-ai/sdk": "workspace:*", "@opencode-ai/sdk": "workspace:*",
"@slack/bolt": "^3.17.1", "@slack/bolt": "^3.17.1",
@@ -287,7 +287,7 @@
}, },
"packages/ui": { "packages/ui": {
"name": "@opencode-ai/ui", "name": "@opencode-ai/ui",
"version": "1.0.20", "version": "1.0.23",
"dependencies": { "dependencies": {
"@kobalte/core": "catalog:", "@kobalte/core": "catalog:",
"@opencode-ai/sdk": "workspace:*", "@opencode-ai/sdk": "workspace:*",
@@ -317,7 +317,7 @@
}, },
"packages/web": { "packages/web": {
"name": "@opencode-ai/web", "name": "@opencode-ai/web",
"version": "1.0.20", "version": "1.0.23",
"dependencies": { "dependencies": {
"@astrojs/cloudflare": "12.6.3", "@astrojs/cloudflare": "12.6.3",
"@astrojs/markdown-remark": "6.3.1", "@astrojs/markdown-remark": "6.3.1",
@@ -961,21 +961,21 @@
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
"@opentui/core": ["@opentui/core@0.1.34", "", { "dependencies": { "bun-ffi-structs": "^0.1.0", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.34", "@opentui/core-darwin-x64": "0.1.34", "@opentui/core-linux-arm64": "0.1.34", "@opentui/core-linux-x64": "0.1.34", "@opentui/core-win32-arm64": "0.1.34", "@opentui/core-win32-x64": "0.1.34", "bun-webgpu": "0.1.3", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-rsqEbHXIFL6JEZs/2dCHn7efnJaGByqpI3mMtt+cJvyt7ZiGU9y+JwryFb9rE8KZMtwsUWN1ECz58ufy6iJvzA=="], "@opentui/core": ["@opentui/core@0.1.33", "", { "dependencies": { "bun-ffi-structs": "^0.1.0", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.33", "@opentui/core-darwin-x64": "0.1.33", "@opentui/core-linux-arm64": "0.1.33", "@opentui/core-linux-x64": "0.1.33", "@opentui/core-win32-arm64": "0.1.33", "@opentui/core-win32-x64": "0.1.33", "bun-webgpu": "0.1.3", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-vwHdrPIqnsY6YnG2JTNhenHSsx+HUPYrQTBZdmEfCj9ROGVzKgUKbSDH1xGK2OtSNRb2KVBg4XaMpq0bie6afQ=="],
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.34", "", { "os": "darwin", "cpu": "arm64" }, "sha512-P/Pw66vJ1W5pIVg7D5bUlMPBTarXh0S/conHRaeybBZoO+8G04A6x9ufeaD/L4HCE0iR0huSoHGDB1VxZUL2Zg=="], "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.33", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JBvzcP2V7fT9KxFAMenHRd/t72qPP5IL5kzge2uok1T7t2nw3Wa+CWI5s6FYP42p2b1W9qZkv5Fno5gA7OAYuQ=="],
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.34", "", { "os": "darwin", "cpu": "x64" }, "sha512-JKfDC2qI1AmY4u504FKfrSdP0qOJIn+rI7kj0C0ydpvj1Wd2c6ImOsbnny70372Uq/m3EXxPE3Hq/66DL4P94A=="], "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.33", "", { "os": "darwin", "cpu": "x64" }, "sha512-x7DY6VCkAky10z/2o4UkkuNW/nIvoX7uAh3dJOHWZCLbiKywSFvFk3QZVVcH5BMk4tOOophYTzika4s4HpaeMg=="],
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.34", "", { "os": "linux", "cpu": "arm64" }, "sha512-E1xAuz0xx7lmh7tZmexP/4Aceyzmpuo4c9UoNd844Aweu/AlmjsmaOMOBLA77I94RSbEuGKJt9WAPyiSZbgwVw=="], "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.33", "", { "os": "linux", "cpu": "arm64" }, "sha512-bBc1EdkVxsLBtqGjXM2BYpBJLa57ogcrSADSZbc5cQkPu0muSGzUwBbVnVZJUjWEfk6n5jcd4dDmLezVoQga0A=="],
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.34", "", { "os": "linux", "cpu": "x64" }, "sha512-VZxgdOUR8h2l3LUPex0A02pLsw9+P4RouL7sJ2Ul/sXvvi/b2ptzJvGQluynV6yHa2etYklZWDyWyMJmF8OKzw=="], "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.33", "", { "os": "linux", "cpu": "x64" }, "sha512-3oVL5mrLlKLUc1lc4v7xS3BJ9N7PnnimbGwAvlnVpfaAygotAs1XkPcjsUe6ItMnSJyi0FWiDHUE2+GiDtM5Nw=="],
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.34", "", { "os": "win32", "cpu": "arm64" }, "sha512-4HXGcYdAHodhm0VnL3nn9uYFvmUhKHiN2vSMDy5KO2NZ49O1IXcS001g/TKryv0hcK1kIUBkq+RH/0vrieCAJw=="], "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.33", "", { "os": "win32", "cpu": "arm64" }, "sha512-Q68v7wssE+r0OG1KIGfi7m3fnu8KOK4ZNg9ML6EwE47VF9/bqgUe+6fPiXh5mmHzTwof7nAOdXCf052av5/upQ=="],
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.34", "", { "os": "win32", "cpu": "x64" }, "sha512-ptuIL6QO7LVFGI6ouZ01fw+AQfjJC+DURjsqiQhoaS/iunFefZY0q83V7ZWgv0nYlhRm+E2yWjRNNzCySJlTaQ=="], "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.33", "", { "os": "win32", "cpu": "x64" }, "sha512-PvuchmUnbMCUXXMzfle/WTzhNGIdJ6RGCCoclx3YVUyNUVuUicPf42OEV+td2m81/Hr3CgcLn98HYX1TLIzPrw=="],
"@opentui/solid": ["@opentui/solid@0.1.34", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.34", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-GPT+EeC6vcDnb4aUJ2K4t01GlbNoMZUfMTiIif55JSjXTKURzdDLL4mOhxar1+iJqwubYHEu/nC1GkTiGWIJoQ=="], "@opentui/solid": ["@opentui/solid@0.1.33", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.33", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-bWSALdGJ2j51zwZ2gK1ZIBxFgauHq+V1ejEnyd4XamYMdWfpAKU+AUWDVLbpx1T9XG1oAnycJZfYX7BsZdVOOg=="],
"@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="], "@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],

View File

@@ -7,7 +7,7 @@
"dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev", "dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev",
"build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json", "build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json",
"start": "vinxi start", "start": "vinxi start",
"version": "1.0.20" "version": "1.0.23"
}, },
"dependencies": { "dependencies": {
"@ibm/plex": "6.4.1", "@ibm/plex": "6.4.1",

View File

@@ -1,7 +1,7 @@
{ {
"$schema": "https://json.schemastore.org/package.json", "$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/console-core", "name": "@opencode-ai/console-core",
"version": "1.0.20", "version": "1.0.23",
"private": true, "private": true,
"type": "module", "type": "module",
"dependencies": { "dependencies": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@opencode-ai/console-function", "name": "@opencode-ai/console-function",
"version": "1.0.20", "version": "1.0.23",
"$schema": "https://json.schemastore.org/package.json", "$schema": "https://json.schemastore.org/package.json",
"private": true, "private": true,
"type": "module", "type": "module",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@opencode-ai/console-mail", "name": "@opencode-ai/console-mail",
"version": "1.0.20", "version": "1.0.23",
"dependencies": { "dependencies": {
"@jsx-email/all": "2.2.3", "@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3", "@jsx-email/cli": "1.4.3",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@opencode-ai/desktop", "name": "@opencode-ai/desktop",
"version": "1.0.20", "version": "1.0.23",
"description": "", "description": "",
"type": "module", "type": "module",
"scripts": { "scripts": {

View File

@@ -12,7 +12,9 @@ import Home from "@/pages"
const host = import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "127.0.0.1" const host = import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "127.0.0.1"
const port = import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096" const port = import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096"
const url = new URLSearchParams(document.location.search).get("url") || `http://${host}:${port}` const url =
new URLSearchParams(document.location.search).get("url") ||
(location.hostname.includes("opencode.ai") ? `http://${host}:${port}` : "/")
const root = document.getElementById("root") const root = document.getElementById("root")
if (import.meta.env.DEV && !(root instanceof HTMLElement)) { if (import.meta.env.DEV && !(root instanceof HTMLElement)) {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@opencode-ai/function", "name": "@opencode-ai/function",
"version": "1.0.20", "version": "1.0.23",
"$schema": "https://json.schemastore.org/package.json", "$schema": "https://json.schemastore.org/package.json",
"private": true, "private": true,
"type": "module", "type": "module",

View File

@@ -1,6 +1,6 @@
{ {
"$schema": "https://json.schemastore.org/package.json", "$schema": "https://json.schemastore.org/package.json",
"version": "1.0.20", "version": "1.0.23",
"name": "opencode", "name": "opencode",
"type": "module", "type": "module",
"private": true, "private": true,
@@ -54,8 +54,8 @@
"@opencode-ai/plugin": "workspace:*", "@opencode-ai/plugin": "workspace:*",
"@opencode-ai/script": "workspace:*", "@opencode-ai/script": "workspace:*",
"@opencode-ai/sdk": "workspace:*", "@opencode-ai/sdk": "workspace:*",
"@opentui/core": "0.1.34", "@opentui/core": "0.1.33",
"@opentui/solid": "0.1.34", "@opentui/solid": "0.1.33",
"@parcel/watcher": "2.5.1", "@parcel/watcher": "2.5.1",
"@solid-primitives/event-bus": "1.1.2", "@solid-primitives/event-bus": "1.1.2",
"@pierre/precision-diffs": "catalog:", "@pierre/precision-diffs": "catalog:",

View File

@@ -10,6 +10,7 @@ export namespace Auth {
refresh: z.string(), refresh: z.string(),
access: z.string(), access: z.string(),
expires: z.number(), expires: z.number(),
enterpriseUrl: z.string().optional(),
}) })
.meta({ ref: "OAuth" }) .meta({ ref: "OAuth" })

View File

@@ -102,178 +102,223 @@ export const AuthLoginCommand = cmd({
prompts.outro("Done") prompts.outro("Done")
return return
} }
await ModelsDev.refresh().catch(() => {}) await ModelsDev.refresh().catch(() => {})
const providers = await ModelsDev.get() const providers = await ModelsDev.get()
const priority: Record<string, number> = { const priority: Record<string, number> = {
opencode: 0, opencode: 0,
anthropic: 1, anthropic: 1,
"github-copilot": 2, "github-copilot": 2,
openai: 3, openai: 3,
google: 4, google: 4,
openrouter: 5, openrouter: 5,
vercel: 6, vercel: 6,
} }
let provider = await prompts.autocomplete({ let provider = await prompts.autocomplete({
message: "Select provider", message: "Select provider",
maxItems: 8, maxItems: 8,
options: [ options: [
...pipe( ...pipe(
providers, providers,
values(), values(),
sortBy( sortBy(
(x) => priority[x.id] ?? 99, (x) => priority[x.id] ?? 99,
(x) => x.name ?? x.id, (x) => x.name ?? x.id,
),
map((x) => ({
label: x.name,
value: x.id,
hint: priority[x.id] <= 1 ? "recommended" : undefined,
})),
), ),
{ map((x) => ({
value: "other", label: x.name,
label: "Other", value: x.id,
}, hint: priority[x.id] <= 1 ? "recommended" : undefined,
], })),
}) ),
{
value: "other",
label: "Other",
},
],
})
if (prompts.isCancel(provider)) throw new UI.CancelledError() if (prompts.isCancel(provider)) throw new UI.CancelledError()
const plugin = await Plugin.list().then((x) => x.find((x) => x.auth?.provider === provider)) const plugin = await Plugin.list().then((x) => x.find((x) => x.auth?.provider === provider))
if (plugin && plugin.auth) { if (plugin && plugin.auth) {
let index = 0 let index = 0
if (plugin.auth.methods.length > 1) { if (plugin.auth.methods.length > 1) {
const method = await prompts.select({ const method = await prompts.select({
message: "Login method", message: "Login method",
options: [ options: [
...plugin.auth.methods.map((x, index) => ({ ...plugin.auth.methods.map((x, index) => ({
label: x.label, label: x.label,
value: index.toString(), value: index.toString(),
})), })),
], ],
}) })
if (prompts.isCancel(method)) throw new UI.CancelledError() if (prompts.isCancel(method)) throw new UI.CancelledError()
index = parseInt(method) index = parseInt(method)
} }
const method = plugin.auth.methods[index] const method = plugin.auth.methods[index]
if (method.type === "oauth") {
await new Promise((resolve) => setTimeout(resolve, 10))
const authorize = await method.authorize()
if (authorize.url) { // Handle prompts for all auth types
prompts.log.info("Go to: " + authorize.url) await new Promise((resolve) => setTimeout(resolve, 10))
const inputs: Record<string, string> = {}
if (method.prompts) {
for (const prompt of method.prompts) {
if (prompt.condition && !prompt.condition(inputs)) {
continue
} }
if (prompt.type === "select") {
if (authorize.method === "auto") { const value = await prompts.select({
if (authorize.instructions) { message: prompt.message,
prompts.log.info(authorize.instructions) options: prompt.options,
}
const spinner = prompts.spinner()
spinner.start("Waiting for authorization...")
const result = await authorize.callback()
if (result.type === "failed") {
spinner.stop("Failed to authorize", 1)
}
if (result.type === "success") {
if ("refresh" in result) {
await Auth.set(provider, {
type: "oauth",
refresh: result.refresh,
access: result.access,
expires: result.expires,
})
}
if ("key" in result) {
await Auth.set(provider, {
type: "api",
key: result.key,
})
}
spinner.stop("Login successful")
}
}
if (authorize.method === "code") {
const code = await prompts.text({
message: "Paste the authorization code here: ",
validate: (x) => (x && x.length > 0 ? undefined : "Required"),
}) })
if (prompts.isCancel(code)) throw new UI.CancelledError() if (prompts.isCancel(value)) throw new UI.CancelledError()
const result = await authorize.callback(code) inputs[prompt.key] = value
if (result.type === "failed") { } else {
prompts.log.error("Failed to authorize") const value = await prompts.text({
message: prompt.message,
placeholder: prompt.placeholder,
validate: prompt.validate ? (v) => prompt.validate!(v ?? "") : undefined,
})
if (prompts.isCancel(value)) throw new UI.CancelledError()
inputs[prompt.key] = value
}
}
}
if (method.type === "oauth") {
const authorize = await method.authorize(inputs)
if (authorize.url) {
prompts.log.info("Go to: " + authorize.url)
}
if (authorize.method === "auto") {
if (authorize.instructions) {
prompts.log.info(authorize.instructions)
}
const spinner = prompts.spinner()
spinner.start("Waiting for authorization...")
const result = await authorize.callback()
if (result.type === "failed") {
spinner.stop("Failed to authorize", 1)
}
if (result.type === "success") {
const saveProvider = result.provider ?? provider
if ("refresh" in result) {
const { type: _, provider: __, refresh, access, expires, ...extraFields } = result
await Auth.set(saveProvider, {
type: "oauth",
refresh,
access,
expires,
...extraFields,
})
} }
if (result.type === "success") { if ("key" in result) {
if ("refresh" in result) { await Auth.set(saveProvider, {
await Auth.set(provider, { type: "api",
type: "oauth", key: result.key,
refresh: result.refresh, })
access: result.access,
expires: result.expires,
})
}
if ("key" in result) {
await Auth.set(provider, {
type: "api",
key: result.key,
})
}
prompts.log.success("Login successful")
} }
spinner.stop("Login successful")
}
}
if (authorize.method === "code") {
const code = await prompts.text({
message: "Paste the authorization code here: ",
validate: (x) => (x && x.length > 0 ? undefined : "Required"),
})
if (prompts.isCancel(code)) throw new UI.CancelledError()
const result = await authorize.callback(code)
if (result.type === "failed") {
prompts.log.error("Failed to authorize")
}
if (result.type === "success") {
const saveProvider = result.provider ?? provider
if ("refresh" in result) {
const { type: _, provider: __, refresh, access, expires, ...extraFields } = result
await Auth.set(saveProvider, {
type: "oauth",
refresh,
access,
expires,
...extraFields,
})
}
if ("key" in result) {
await Auth.set(saveProvider, {
type: "api",
key: result.key,
})
}
prompts.log.success("Login successful")
}
}
prompts.outro("Done")
return
}
if (method.type === "api") {
if (method.authorize) {
const result = await method.authorize(inputs)
if (result.type === "failed") {
prompts.log.error("Failed to authorize")
}
if (result.type === "success") {
const saveProvider = result.provider ?? provider
await Auth.set(saveProvider, {
type: "api",
key: result.key,
})
prompts.log.success("Login successful")
} }
prompts.outro("Done") prompts.outro("Done")
return return
} }
} }
}
if (provider === "other") { if (provider === "other") {
provider = await prompts.text({ provider = await prompts.text({
message: "Enter provider id", message: "Enter provider id",
validate: (x) => (x && x.match(/^[0-9a-z-]+$/) ? undefined : "a-z, 0-9 and hyphens only"), validate: (x) => (x && x.match(/^[0-9a-z-]+$/) ? undefined : "a-z, 0-9 and hyphens only"),
})
if (prompts.isCancel(provider)) throw new UI.CancelledError()
provider = provider.replace(/^@ai-sdk\//, "")
if (prompts.isCancel(provider)) throw new UI.CancelledError()
prompts.log.warn(
`This only stores a credential for ${provider} - you will need configure it in opencode.json, check the docs for examples.`,
)
}
if (provider === "amazon-bedrock") {
prompts.log.info(
"Amazon bedrock can be configured with standard AWS environment variables like AWS_BEARER_TOKEN_BEDROCK, AWS_PROFILE or AWS_ACCESS_KEY_ID",
)
prompts.outro("Done")
return
}
if (provider === "google-vertex") {
prompts.log.info(
"Google Cloud Vertex AI uses Application Default Credentials. Set GOOGLE_APPLICATION_CREDENTIALS or run 'gcloud auth application-default login'. Optionally set GOOGLE_CLOUD_PROJECT and GOOGLE_CLOUD_LOCATION (or VERTEX_LOCATION)",
)
prompts.outro("Done")
return
}
if (provider === "opencode") {
prompts.log.info("Create an api key at https://opencode.ai/auth")
}
if (provider === "vercel") {
prompts.log.info("You can create an api key at https://vercel.link/ai-gateway-token")
}
const key = await prompts.password({
message: "Enter your API key",
validate: (x) => (x && x.length > 0 ? undefined : "Required"),
})
if (prompts.isCancel(key)) throw new UI.CancelledError()
await Auth.set(provider, {
type: "api",
key,
}) })
if (prompts.isCancel(provider)) throw new UI.CancelledError()
provider = provider.replace(/^@ai-sdk\//, "")
if (prompts.isCancel(provider)) throw new UI.CancelledError()
prompts.log.warn(
`This only stores a credential for ${provider} - you will need configure it in opencode.json, check the docs for examples.`,
)
}
if (provider === "amazon-bedrock") {
prompts.log.info(
"Amazon bedrock can be configured with standard AWS environment variables like AWS_BEARER_TOKEN_BEDROCK, AWS_PROFILE or AWS_ACCESS_KEY_ID",
)
prompts.outro("Done") prompts.outro("Done")
return
}
if (provider === "opencode") {
prompts.log.info("Create an api key at https://opencode.ai/auth")
}
if (provider === "vercel") {
prompts.log.info("You can create an api key at https://vercel.link/ai-gateway-token")
}
const key = await prompts.password({
message: "Enter your API key",
validate: (x) => (x && x.length > 0 ? undefined : "Required"),
})
if (prompts.isCancel(key)) throw new UI.CancelledError()
await Auth.set(provider, {
type: "api",
key,
})
prompts.outro("Done")
}, },
}) })
}, },

View File

@@ -358,6 +358,7 @@ function App() {
event.on(SessionApi.Event.Deleted.type, (evt) => { event.on(SessionApi.Event.Deleted.type, (evt) => {
if (route.data.type === "session" && route.data.sessionID === evt.properties.info.id) { if (route.data.type === "session" && route.data.sessionID === evt.properties.info.id) {
dialog.clear()
route.navigate({ type: "home" }) route.navigate({ type: "home" })
toast.show({ toast.show({
variant: "info", variant: "info",

View File

@@ -22,15 +22,16 @@ export type CommandOption = DialogSelectOption & {
function init() { function init() {
const [registrations, setRegistrations] = createSignal<Accessor<CommandOption[]>[]>([]) const [registrations, setRegistrations] = createSignal<Accessor<CommandOption[]>[]>([])
const [suspendCount, setSuspendCount] = createSignal(0)
const dialog = useDialog() const dialog = useDialog()
const keybind = useKeybind() const keybind = useKeybind()
const options = createMemo(() => { const options = createMemo(() => {
return registrations().flatMap((x) => x()) return registrations().flatMap((x) => x())
}) })
const suspended = () => suspendCount() > 0
let keybinds = true
useKeyboard((evt) => { useKeyboard((evt) => {
if (!keybinds) return if (suspended()) return
for (const option of options()) { for (const option of options()) {
if (option.keybind && keybind.match(option.keybind, evt)) { if (option.keybind && keybind.match(option.keybind, evt)) {
evt.preventDefault() evt.preventDefault()
@@ -50,8 +51,9 @@ function init() {
} }
}, },
keybinds(enabled: boolean) { keybinds(enabled: boolean) {
keybinds = enabled setSuspendCount((count) => count + (enabled ? -1 : 1))
}, },
suspended,
show() { show() {
dialog.replace(() => <DialogCommand options={options()} />) dialog.replace(() => <DialogCommand options={options()} />)
}, },
@@ -83,7 +85,10 @@ export function CommandProvider(props: ParentProps) {
const keybind = useKeybind() const keybind = useKeybind()
useKeyboard((evt) => { useKeyboard((evt) => {
if (keybind.match("command_list", evt) && dialog.stack.length === 0) { if (value.suspended()) return
if (dialog.stack.length > 0) return
if (evt.defaultPrevented) return
if (keybind.match("command_list", evt)) {
evt.preventDefault() evt.preventDefault()
dialog.replace(() => <DialogCommand options={value.options} />) dialog.replace(() => <DialogCommand options={value.options} />)
return return

View File

@@ -72,7 +72,7 @@ export function DialogSessionList() {
}, },
}) })
setToDelete(undefined) setToDelete(undefined)
dialog.clear() // dialog.clear()
return return
} }
setToDelete(option.value) setToDelete(option.value)

View File

@@ -54,6 +54,12 @@ export function Autocomplete(props: {
const val = props.input().getTextRange(store.index + 1, props.input().cursorOffset + 1) const val = props.input().getTextRange(store.index + 1, props.input().cursorOffset + 1)
// If the filter contains a space, hide the autocomplete
if (val.includes(" ")) {
hide()
return undefined
}
return val return val
}) })
@@ -373,15 +379,45 @@ export function Autocomplete(props: {
return store.visible return store.visible
}, },
onInput() { onInput() {
if (store.visible && props.input().cursorOffset <= store.index) hide() if (store.visible) {
if (props.input().cursorOffset <= store.index) {
hide()
return
}
// Check if a space was typed after the trigger character
const currentText = props.input().getTextRange(store.index + 1, props.input().cursorOffset + 1)
if (currentText.includes(" ")) {
hide()
}
}
}, },
onKeyDown(e: KeyEvent) { onKeyDown(e: KeyEvent) {
if (store.visible) { if (store.visible) {
if (e.name === "up") move(-1) const name = e.name?.toLowerCase()
if (e.name === "down") move(1) const ctrlOnly = e.ctrl && !e.meta && !e.shift
if (e.name === "escape") hide() const isNavUp = name === "up" || (ctrlOnly && name === "p")
if (e.name === "return" || e.name === "tab") select() const isNavDown = name === "down" || (ctrlOnly && name === "n")
if (["up", "down", "return", "tab", "escape"].includes(e.name)) e.preventDefault()
if (isNavUp) {
move(-1)
e.preventDefault()
return
}
if (isNavDown) {
move(1)
e.preventDefault()
return
}
if (name === "escape") {
hide()
e.preventDefault()
return
}
if (name === "return" || name === "tab") {
select()
e.preventDefault()
return
}
} }
if (!store.visible) { if (!store.visible) {
if (e.name === "@") { if (e.name === "@") {

View File

@@ -115,15 +115,11 @@ export function Prompt(props: PromptProps) {
{ {
title: "Clear prompt", title: "Clear prompt",
value: "prompt.clear", value: "prompt.clear",
disabled: true,
category: "Prompt", category: "Prompt",
disabled: true,
onSelect: (dialog) => { onSelect: (dialog) => {
input.extmarks.clear() input.extmarks.clear()
setStore("prompt", { input.clear()
input: "",
parts: [],
})
setStore("extmarkToPartIndex", new Map())
dialog.clear() dialog.clear()
}, },
}, },
@@ -156,16 +152,27 @@ export function Prompt(props: PromptProps) {
} }
}, },
}, },
{
title: "Interrupt session",
value: "session.interrupt",
keybind: "session_interrupt",
category: "Session",
disabled: true,
onSelect: (dialog) => {
if (!props.sessionID) return
sdk.client.session.abort({
path: {
id: props.sessionID,
},
})
dialog.clear()
},
},
] ]
}) })
sdk.event.on(TuiEvent.PromptAppend.type, (evt) => { sdk.event.on(TuiEvent.PromptAppend.type, (evt) => {
setStore( input.insertText(evt.properties.text)
"prompt",
produce((draft) => {
draft.input += evt.properties.text
}),
)
}) })
createEffect(() => { createEffect(() => {

View File

@@ -26,12 +26,15 @@ export const WebCommand = cmd({
port, port,
hostname, hostname,
}) })
const url = `https://desktop.dev.opencode.ai?url=${server.url}`
UI.empty() UI.empty()
UI.println(UI.logo(" ")) UI.println(UI.logo(" "))
UI.empty() UI.empty()
UI.println(UI.Style.TEXT_INFO_BOLD + " Web interface: ", UI.Style.TEXT_NORMAL, url) UI.println(
open(url).catch(() => {}) UI.Style.TEXT_INFO_BOLD + " Web interface: ",
UI.Style.TEXT_NORMAL,
server.url.toString(),
)
open(server.url.toString()).catch(() => {})
await new Promise(() => {}) await new Promise(() => {})
await server.stop() await server.stop()
}, },

View File

@@ -574,6 +574,7 @@ export namespace Config {
.object({ .object({
apiKey: z.string().optional(), apiKey: z.string().optional(),
baseURL: z.string().optional(), baseURL: z.string().optional(),
enterpriseUrl: z.string().optional().describe("GitHub Enterprise URL for copilot authentication"),
timeout: z timeout: z
.union([ .union([
z z

View File

@@ -28,7 +28,7 @@ export namespace Plugin {
} }
const plugins = [...(config.plugin ?? [])] const plugins = [...(config.plugin ?? [])]
if (!Flag.OPENCODE_DISABLE_DEFAULT_PLUGINS) { if (!Flag.OPENCODE_DISABLE_DEFAULT_PLUGINS) {
plugins.push("opencode-copilot-auth@0.0.3") plugins.push("opencode-copilot-auth@0.0.4")
plugins.push("opencode-anthropic-auth@0.0.2") plugins.push("opencode-anthropic-auth@0.0.2")
} }
for (let plugin of plugins) { for (let plugin of plugins) {

View File

@@ -283,6 +283,18 @@ export namespace Provider {
const configProviders = Object.entries(config.provider ?? {}) const configProviders = Object.entries(config.provider ?? {})
// Add GitHub Copilot Enterprise provider that inherits from GitHub Copilot
if (database["github-copilot"]) {
const githubCopilot = database["github-copilot"]
database["github-copilot-enterprise"] = {
...githubCopilot,
id: "github-copilot-enterprise",
name: "GitHub Copilot Enterprise",
// Enterprise uses a different API endpoint - will be set dynamically based on auth
api: undefined,
}
}
for (const [providerID, provider] of configProviders) { for (const [providerID, provider] of configProviders) {
const existing = database[providerID] const existing = database[providerID]
const parsed: ModelsDev.Provider = { const parsed: ModelsDev.Provider = {
@@ -378,14 +390,44 @@ export namespace Provider {
if (!plugin.auth) continue if (!plugin.auth) continue
const providerID = plugin.auth.provider const providerID = plugin.auth.provider
if (disabled.has(providerID)) continue if (disabled.has(providerID)) continue
// For github-copilot plugin, check if auth exists for either github-copilot or github-copilot-enterprise
let hasAuth = false
const auth = await Auth.get(providerID) const auth = await Auth.get(providerID)
if (!auth) continue if (auth) hasAuth = true
// Special handling for github-copilot: also check for enterprise auth
if (providerID === "github-copilot" && !hasAuth) {
const enterpriseAuth = await Auth.get("github-copilot-enterprise")
if (enterpriseAuth) hasAuth = true
}
if (!hasAuth) continue
if (!plugin.auth.loader) continue if (!plugin.auth.loader) continue
const options = await plugin.auth.loader(
() => Auth.get(providerID) as any, // Load for the main provider if auth exists
database[plugin.auth.provider], if (auth) {
) const options = await plugin.auth.loader(
mergeProvider(plugin.auth.provider, options ?? {}, "custom") () => Auth.get(providerID) as any,
database[plugin.auth.provider],
)
mergeProvider(plugin.auth.provider, options ?? {}, "custom")
}
// If this is github-copilot plugin, also register for github-copilot-enterprise if auth exists
if (providerID === "github-copilot") {
const enterpriseProviderID = "github-copilot-enterprise"
if (!disabled.has(enterpriseProviderID)) {
const enterpriseAuth = await Auth.get(enterpriseProviderID)
if (enterpriseAuth) {
const enterpriseOptions = await plugin.auth.loader(
() => Auth.get(enterpriseProviderID) as any,
database[enterpriseProviderID],
)
mergeProvider(enterpriseProviderID, enterpriseOptions ?? {}, "custom")
}
}
}
} }
// load config // load config
@@ -458,7 +500,8 @@ export namespace Provider {
: installedPath : installedPath
const mod = await import(modPath) const mod = await import(modPath)
if (options["timeout"] !== undefined && options["timeout"] !== null) { if (options["timeout"] !== undefined && options["timeout"] !== null) {
// Only override fetch if user explicitly sets timeout // Preserve custom fetch if it exists, wrap it with timeout logic
const customFetch = options["fetch"]
options["fetch"] = async (input: any, init?: BunFetchRequestInit) => { options["fetch"] = async (input: any, init?: BunFetchRequestInit) => {
const { signal, ...rest } = init ?? {} const { signal, ...rest } = init ?? {}
@@ -468,7 +511,8 @@ export namespace Provider {
const combined = signals.length > 1 ? AbortSignal.any(signals) : signals[0] const combined = signals.length > 1 ? AbortSignal.any(signals) : signals[0]
return fetch(input, { const fetchFn = customFetch ?? fetch
return fetchFn(input, {
...rest, ...rest,
signal: combined, signal: combined,
// @ts-ignore see here: https://github.com/oven-sh/bun/issues/16682 // @ts-ignore see here: https://github.com/oven-sh/bun/issues/16682

View File

@@ -10,6 +10,7 @@ import {
import { Hono } from "hono" import { Hono } from "hono"
import { cors } from "hono/cors" import { cors } from "hono/cors"
import { stream, streamSSE } from "hono/streaming" import { stream, streamSSE } from "hono/streaming"
import { proxy } from "hono/proxy"
import { Session } from "../session" import { Session } from "../session"
import z from "zod" import z from "zod"
import { Provider } from "../provider/provider" import { Provider } from "../provider/provider"
@@ -757,6 +758,34 @@ export namespace Server {
return c.json(messages) return c.json(messages)
}, },
) )
.get(
"/session/:id/diff",
describeRoute({
description: "Get the diff for this session",
operationId: "session.diff",
responses: {
200: {
description: "List of diffs",
content: {
"application/json": {
schema: resolver(Snapshot.FileDiff.array()),
},
},
},
...errors(400, 404),
},
}),
validator(
"param",
z.object({
id: z.string().meta({ description: "Session ID" }),
}),
),
async (c) => {
const diff = await Session.diff(c.req.valid("param").id)
return c.json(diff)
},
)
.get( .get(
"/session/:id/message/:messageID", "/session/:id/message/:messageID",
describeRoute({ describeRoute({
@@ -1696,7 +1725,15 @@ export namespace Server {
}) })
}) })
}, },
), )
.all("/*", async (c) => {
return proxy(`https://desktop.dev.opencode.ai${c.req.path}`, {
...c.req,
headers: {
host: "desktop.dev.opencode.ai",
},
})
}),
) )
export async function openapi() { export async function openapi() {

View File

@@ -15,8 +15,8 @@ import { MessageV2 } from "./message-v2"
import { Instance } from "../project/instance" import { Instance } from "../project/instance"
import { SessionPrompt } from "./prompt" import { SessionPrompt } from "./prompt"
import { fn } from "@/util/fn" import { fn } from "@/util/fn"
import { Snapshot } from "@/snapshot"
import { Command } from "../command" import { Command } from "../command"
import { Snapshot } from "@/snapshot"
export namespace Session { export namespace Session {
const log = Log.create({ service: "session" }) const log = Log.create({ service: "session" })
@@ -42,7 +42,9 @@ export namespace Session {
parentID: Identifier.schema("session").optional(), parentID: Identifier.schema("session").optional(),
summary: z summary: z
.object({ .object({
diffs: Snapshot.FileDiff.array(), additions: z.number(),
deletions: z.number(),
diffs: Snapshot.FileDiff.array().optional(),
}) })
.optional(), .optional(),
share: z share: z
@@ -258,6 +260,11 @@ export namespace Session {
return result return result
} }
export const diff = fn(Identifier.schema("session"), async (sessionID) => {
const diffs = await Storage.read<Snapshot.FileDiff[]>(["session_diff", sessionID])
return diffs ?? []
})
export const messages = fn(Identifier.schema("session"), async (sessionID) => { export const messages = fn(Identifier.schema("session"), async (sessionID) => {
const result = [] as MessageV2.WithParts[] const result = [] as MessageV2.WithParts[]
for (const p of await Storage.list(["message", sessionID])) { for (const p of await Storage.list(["message", sessionID])) {

View File

@@ -11,6 +11,7 @@ import { SystemPrompt } from "./system"
import { Log } from "@/util/log" import { Log } from "@/util/log"
import path from "path" import path from "path"
import { Instance } from "@/project/instance" import { Instance } from "@/project/instance"
import { Storage } from "@/storage/storage"
export namespace SessionSummary { export namespace SessionSummary {
const log = Log.create({ service: "session.summary" }) const log = Log.create({ service: "session.summary" })
@@ -44,9 +45,11 @@ export namespace SessionSummary {
) )
await Session.update(input.sessionID, (draft) => { await Session.update(input.sessionID, (draft) => {
draft.summary = { draft.summary = {
diffs, additions: diffs.reduce((sum, x) => sum + x.additions, 0),
deletions: diffs.reduce((sum, x) => sum + x.deletions, 0),
} }
}) })
await Storage.write(["session_diff", input.sessionID], diffs)
} }
async function summarizeMessage(input: { messageID: string; messages: MessageV2.WithParts[] }) { async function summarizeMessage(input: { messageID: string; messages: MessageV2.WithParts[] }) {

View File

@@ -85,7 +85,9 @@ export namespace Storage {
const session = await Bun.file(sessionFile).json() const session = await Bun.file(sessionFile).json()
await Bun.write(dest, JSON.stringify(session)) await Bun.write(dest, JSON.stringify(session))
log.info(`migrating messages for session ${session.id}`) log.info(`migrating messages for session ${session.id}`)
for await (const msgFile of new Bun.Glob(`storage/session/message/${session.id}/*.json`).scan({ for await (const msgFile of new Bun.Glob(
`storage/session/message/${session.id}/*.json`,
).scan({
cwd: fullProjectDir, cwd: fullProjectDir,
absolute: true, absolute: true,
})) { })) {
@@ -98,12 +100,12 @@ export namespace Storage {
await Bun.write(dest, JSON.stringify(message)) await Bun.write(dest, JSON.stringify(message))
log.info(`migrating parts for message ${message.id}`) log.info(`migrating parts for message ${message.id}`)
for await (const partFile of new Bun.Glob(`storage/session/part/${session.id}/${message.id}/*.json`).scan( for await (const partFile of new Bun.Glob(
{ `storage/session/part/${session.id}/${message.id}/*.json`,
cwd: fullProjectDir, ).scan({
absolute: true, cwd: fullProjectDir,
}, absolute: true,
)) { })) {
const dest = path.join(dir, "part", message.id, path.basename(partFile)) const dest = path.join(dir, "part", message.id, path.basename(partFile))
const part = await Bun.file(partFile).json() const part = await Bun.file(partFile).json()
log.info("copying", { log.info("copying", {
@@ -117,6 +119,29 @@ export namespace Storage {
} }
} }
}, },
async (dir) => {
for await (const item of new Bun.Glob("session/*/*.json").scan({
cwd: dir,
absolute: true,
})) {
const session = await Bun.file(item).json()
if (!session.projectID) continue
if (!session.summary?.diffs) continue
const { diffs } = session.summary
await Bun.file(path.join(dir, "session_diff", session.id + ".json")).write(
JSON.stringify(diffs),
)
await Bun.file(path.join(dir, "session", session.projectID, session.id + ".json")).write(
JSON.stringify({
...session,
summary: {
additions: diffs.reduce((sum: any, x: any) => sum + x.additions, 0),
deletions: diffs.reduce((sum: any, x: any) => sum + x.deletions, 0),
},
}),
)
}
},
] ]
const state = lazy(async () => { const state = lazy(async () => {
@@ -128,9 +153,7 @@ export namespace Storage {
for (let index = migration; index < MIGRATIONS.length; index++) { for (let index = migration; index < MIGRATIONS.length; index++) {
log.info("running migration", { index }) log.info("running migration", { index })
const migration = MIGRATIONS[index] const migration = MIGRATIONS[index]
await migration(dir).catch((e) => { await migration(dir).catch(() => log.error("failed to run migration", { index }))
log.error("failed to run migration", { error: e, index })
})
await Bun.write(path.join(dir, "migration"), (index + 1).toString()) await Bun.write(path.join(dir, "migration"), (index + 1).toString())
} }
return { return {

View File

@@ -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": "1.0.20", "version": "1.0.23",
"type": "module", "type": "module",
"scripts": { "scripts": {
"typecheck": "tsgo --noEmit", "typecheck": "tsgo --noEmit",

View File

@@ -39,13 +39,35 @@ export interface Hooks {
| { | {
type: "oauth" type: "oauth"
label: string label: string
authorize(): Promise< prompts?: Array<
| {
type: "text"
key: string
message: string
placeholder?: string
validate?: (value: string) => string | undefined
condition?: (inputs: Record<string, string>) => boolean
}
| {
type: "select"
key: string
message: string
options: Array<{
label: string
value: string
hint?: string
}>
condition?: (inputs: Record<string, string>) => boolean
}
>
authorize(inputs?: Record<string, string>): Promise<
{ url: string; instructions: string } & ( { url: string; instructions: string } & (
| { | {
method: "auto" method: "auto"
callback(): Promise< callback(): Promise<
| ({ | ({
type: "success" type: "success"
provider?: string
} & ( } & (
| { | {
refresh: string refresh: string
@@ -64,6 +86,7 @@ export interface Hooks {
callback(code: string): Promise< callback(code: string): Promise<
| ({ | ({
type: "success" type: "success"
provider?: string
} & ( } & (
| { | {
refresh: string refresh: string
@@ -80,7 +103,41 @@ export interface Hooks {
) )
> >
} }
| { type: "api"; label: string } | {
type: "api"
label: string
prompts?: Array<
| {
type: "text"
key: string
message: string
placeholder?: string
validate?: (value: string) => string | undefined
condition?: (inputs: Record<string, string>) => boolean
}
| {
type: "select"
key: string
message: string
options: Array<{
label: string
value: string
hint?: string
}>
condition?: (inputs: Record<string, string>) => boolean
}
>
authorize?(inputs?: Record<string, string>): Promise<
| {
type: "success"
key: string
provider?: string
}
| {
type: "failed"
}
>
}
)[] )[]
} }
/** /**

View File

@@ -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": "1.0.20", "version": "1.0.23",
"type": "module", "type": "module",
"scripts": { "scripts": {
"typecheck": "tsgo --noEmit", "typecheck": "tsgo --noEmit",

View File

@@ -55,6 +55,7 @@ import type {
SessionShareErrors, SessionShareErrors,
SessionDiffData, SessionDiffData,
SessionDiffResponses, SessionDiffResponses,
SessionDiffErrors,
SessionSummarizeData, SessionSummarizeData,
SessionSummarizeResponses, SessionSummarizeResponses,
SessionSummarizeErrors, SessionSummarizeErrors,
@@ -475,12 +476,16 @@ class Session extends _HeyApiClient {
} }
/** /**
* Get the diff that resulted from this user message * Get the diff for this session
*/ */
public diff<ThrowOnError extends boolean = false>( public diff<ThrowOnError extends boolean = false>(
options: Options<SessionDiffData, ThrowOnError>, options: Options<SessionDiffData, ThrowOnError>,
) { ) {
return (options.client ?? this._client).get<SessionDiffResponses, unknown, ThrowOnError>({ return (options.client ?? this._client).get<
SessionDiffResponses,
SessionDiffErrors,
ThrowOnError
>({
url: "/session/{id}/diff", url: "/session/{id}/diff",
...options, ...options,
}) })

View File

@@ -163,7 +163,7 @@ export type KeybindsConfig = {
*/ */
history_previous?: string history_previous?: string
/** /**
* Previous history item * Next history item
*/ */
history_next?: string history_next?: string
/** /**
@@ -405,6 +405,10 @@ export type Config = {
options?: { options?: {
apiKey?: string apiKey?: string
baseURL?: string baseURL?: string
/**
* GitHub Enterprise URL for copilot authentication
*/
enterpriseUrl?: string
/** /**
* Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout. * Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout.
*/ */
@@ -527,7 +531,9 @@ export type Session = {
directory: string directory: string
parentID?: string parentID?: string
summary?: { summary?: {
diffs: Array<FileDiff> additions: number
deletions: number
diffs?: Array<FileDiff>
} }
share?: { share?: {
url: string url: string
@@ -1133,6 +1139,7 @@ export type OAuth = {
refresh: string refresh: string
access: string access: string
expires: number expires: number
enterpriseUrl?: string
} }
export type ApiAuth = { export type ApiAuth = {
@@ -1882,6 +1889,9 @@ export type SessionShareResponse = SessionShareResponses[keyof SessionShareRespo
export type SessionDiffData = { export type SessionDiffData = {
body?: never body?: never
path: { path: {
/**
* Session ID
*/
id: string id: string
} }
query?: { query?: {
@@ -1891,9 +1901,22 @@ export type SessionDiffData = {
url: "/session/{id}/diff" url: "/session/{id}/diff"
} }
export type SessionDiffErrors = {
/**
* Bad request
*/
400: BadRequestError
/**
* Not found
*/
404: NotFoundError
}
export type SessionDiffError = SessionDiffErrors[keyof SessionDiffErrors]
export type SessionDiffResponses = { export type SessionDiffResponses = {
/** /**
* Successfully retrieved diff * List of diffs
*/ */
200: Array<FileDiff> 200: Array<FileDiff>
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@opencode-ai/slack", "name": "@opencode-ai/slack",
"version": "1.0.20", "version": "1.0.23",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "bun run src/index.ts", "dev": "bun run src/index.ts",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@opencode-ai/ui", "name": "@opencode-ai/ui",
"version": "1.0.20", "version": "1.0.23",
"type": "module", "type": "module",
"exports": { "exports": {
".": "./src/components/index.ts", ".": "./src/components/index.ts",

View File

@@ -1,7 +1,7 @@
{ {
"name": "@opencode-ai/web", "name": "@opencode-ai/web",
"type": "module", "type": "module",
"version": "1.0.20", "version": "1.0.23",
"scripts": { "scripts": {
"dev": "astro dev", "dev": "astro dev",
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",

View File

@@ -2,7 +2,7 @@
"name": "opencode", "name": "opencode",
"displayName": "opencode", "displayName": "opencode",
"description": "opencode for VS Code", "description": "opencode for VS Code",
"version": "1.0.20", "version": "1.0.23",
"publisher": "sst-dev", "publisher": "sst-dev",
"repository": { "repository": {
"type": "git", "type": "git",