mirror of
https://github.com/aljazceru/opencode.git
synced 2025-12-24 11:14:23 +01:00
Merge branch 'dev' of https://github.com/sst/opencode into dev
This commit is contained in:
42
bun.lock
42
bun.lock
@@ -39,7 +39,7 @@
|
||||
},
|
||||
"packages/console/core": {
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.0.20",
|
||||
"version": "1.0.23",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
@@ -66,7 +66,7 @@
|
||||
},
|
||||
"packages/console/function": {
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.0.20",
|
||||
"version": "1.0.23",
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "2.0.0",
|
||||
"@ai-sdk/openai": "2.0.2",
|
||||
@@ -90,7 +90,7 @@
|
||||
},
|
||||
"packages/console/mail": {
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.0.20",
|
||||
"version": "1.0.23",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
@@ -111,7 +111,7 @@
|
||||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.0.20",
|
||||
"version": "1.0.23",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
@@ -150,7 +150,7 @@
|
||||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.0.20",
|
||||
"version": "1.0.23",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "22.0.0",
|
||||
@@ -166,7 +166,7 @@
|
||||
},
|
||||
"packages/opencode": {
|
||||
"name": "opencode",
|
||||
"version": "1.0.20",
|
||||
"version": "1.0.23",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
@@ -184,8 +184,8 @@
|
||||
"@opencode-ai/plugin": "workspace:*",
|
||||
"@opencode-ai/script": "workspace:*",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@opentui/core": "0.1.34",
|
||||
"@opentui/solid": "0.1.34",
|
||||
"@opentui/core": "0.1.33",
|
||||
"@opentui/solid": "0.1.33",
|
||||
"@parcel/watcher": "2.5.1",
|
||||
"@pierre/precision-diffs": "catalog:",
|
||||
"@solid-primitives/event-bus": "1.1.2",
|
||||
@@ -243,7 +243,7 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.0.20",
|
||||
"version": "1.0.23",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"zod": "catalog:",
|
||||
@@ -263,7 +263,7 @@
|
||||
},
|
||||
"packages/sdk/js": {
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.0.20",
|
||||
"version": "1.0.23",
|
||||
"devDependencies": {
|
||||
"@hey-api/openapi-ts": "0.81.0",
|
||||
"@tsconfig/node22": "catalog:",
|
||||
@@ -274,7 +274,7 @@
|
||||
},
|
||||
"packages/slack": {
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.0.20",
|
||||
"version": "1.0.23",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@slack/bolt": "^3.17.1",
|
||||
@@ -287,7 +287,7 @@
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.0.20",
|
||||
"version": "1.0.23",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
@@ -317,7 +317,7 @@
|
||||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode-ai/web",
|
||||
"version": "1.0.20",
|
||||
"version": "1.0.23",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
@@ -961,21 +961,21 @@
|
||||
|
||||
"@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=="],
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"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",
|
||||
"start": "vinxi start",
|
||||
"version": "1.0.20"
|
||||
"version": "1.0.23"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ibm/plex": "6.4.1",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.0.20",
|
||||
"version": "1.0.23",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.0.20",
|
||||
"version": "1.0.23",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.0.20",
|
||||
"version": "1.0.23",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.0.20",
|
||||
"version": "1.0.23",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -12,7 +12,9 @@ import Home from "@/pages"
|
||||
const host = import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "127.0.0.1"
|
||||
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")
|
||||
if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.0.20",
|
||||
"version": "1.0.23",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.0.20",
|
||||
"version": "1.0.23",
|
||||
"name": "opencode",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
@@ -54,8 +54,8 @@
|
||||
"@opencode-ai/plugin": "workspace:*",
|
||||
"@opencode-ai/script": "workspace:*",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@opentui/core": "0.1.34",
|
||||
"@opentui/solid": "0.1.34",
|
||||
"@opentui/core": "0.1.33",
|
||||
"@opentui/solid": "0.1.33",
|
||||
"@parcel/watcher": "2.5.1",
|
||||
"@solid-primitives/event-bus": "1.1.2",
|
||||
"@pierre/precision-diffs": "catalog:",
|
||||
|
||||
@@ -10,6 +10,7 @@ export namespace Auth {
|
||||
refresh: z.string(),
|
||||
access: z.string(),
|
||||
expires: z.number(),
|
||||
enterpriseUrl: z.string().optional(),
|
||||
})
|
||||
.meta({ ref: "OAuth" })
|
||||
|
||||
|
||||
@@ -102,178 +102,223 @@ export const AuthLoginCommand = cmd({
|
||||
prompts.outro("Done")
|
||||
return
|
||||
}
|
||||
await ModelsDev.refresh().catch(() => {})
|
||||
const providers = await ModelsDev.get()
|
||||
const priority: Record<string, number> = {
|
||||
opencode: 0,
|
||||
anthropic: 1,
|
||||
"github-copilot": 2,
|
||||
openai: 3,
|
||||
google: 4,
|
||||
openrouter: 5,
|
||||
vercel: 6,
|
||||
}
|
||||
let provider = await prompts.autocomplete({
|
||||
message: "Select provider",
|
||||
maxItems: 8,
|
||||
options: [
|
||||
...pipe(
|
||||
providers,
|
||||
values(),
|
||||
sortBy(
|
||||
(x) => priority[x.id] ?? 99,
|
||||
(x) => x.name ?? x.id,
|
||||
),
|
||||
map((x) => ({
|
||||
label: x.name,
|
||||
value: x.id,
|
||||
hint: priority[x.id] <= 1 ? "recommended" : undefined,
|
||||
})),
|
||||
await ModelsDev.refresh().catch(() => {})
|
||||
const providers = await ModelsDev.get()
|
||||
const priority: Record<string, number> = {
|
||||
opencode: 0,
|
||||
anthropic: 1,
|
||||
"github-copilot": 2,
|
||||
openai: 3,
|
||||
google: 4,
|
||||
openrouter: 5,
|
||||
vercel: 6,
|
||||
}
|
||||
let provider = await prompts.autocomplete({
|
||||
message: "Select provider",
|
||||
maxItems: 8,
|
||||
options: [
|
||||
...pipe(
|
||||
providers,
|
||||
values(),
|
||||
sortBy(
|
||||
(x) => priority[x.id] ?? 99,
|
||||
(x) => x.name ?? x.id,
|
||||
),
|
||||
{
|
||||
value: "other",
|
||||
label: "Other",
|
||||
},
|
||||
],
|
||||
})
|
||||
map((x) => ({
|
||||
label: x.name,
|
||||
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))
|
||||
if (plugin && plugin.auth) {
|
||||
let index = 0
|
||||
if (plugin.auth.methods.length > 1) {
|
||||
const method = await prompts.select({
|
||||
message: "Login method",
|
||||
options: [
|
||||
...plugin.auth.methods.map((x, index) => ({
|
||||
label: x.label,
|
||||
value: index.toString(),
|
||||
})),
|
||||
],
|
||||
})
|
||||
if (prompts.isCancel(method)) throw new UI.CancelledError()
|
||||
index = parseInt(method)
|
||||
}
|
||||
const method = plugin.auth.methods[index]
|
||||
if (method.type === "oauth") {
|
||||
await new Promise((resolve) => setTimeout(resolve, 10))
|
||||
const authorize = await method.authorize()
|
||||
const plugin = await Plugin.list().then((x) => x.find((x) => x.auth?.provider === provider))
|
||||
if (plugin && plugin.auth) {
|
||||
let index = 0
|
||||
if (plugin.auth.methods.length > 1) {
|
||||
const method = await prompts.select({
|
||||
message: "Login method",
|
||||
options: [
|
||||
...plugin.auth.methods.map((x, index) => ({
|
||||
label: x.label,
|
||||
value: index.toString(),
|
||||
})),
|
||||
],
|
||||
})
|
||||
if (prompts.isCancel(method)) throw new UI.CancelledError()
|
||||
index = parseInt(method)
|
||||
}
|
||||
const method = plugin.auth.methods[index]
|
||||
|
||||
if (authorize.url) {
|
||||
prompts.log.info("Go to: " + authorize.url)
|
||||
// Handle prompts for all auth types
|
||||
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 (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") {
|
||||
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 (prompt.type === "select") {
|
||||
const value = await prompts.select({
|
||||
message: prompt.message,
|
||||
options: prompt.options,
|
||||
})
|
||||
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 (prompts.isCancel(value)) throw new UI.CancelledError()
|
||||
inputs[prompt.key] = value
|
||||
} else {
|
||||
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 ("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,
|
||||
})
|
||||
}
|
||||
prompts.log.success("Login successful")
|
||||
if ("key" in result) {
|
||||
await Auth.set(saveProvider, {
|
||||
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()
|
||||
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")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (provider === "other") {
|
||||
provider = await prompts.text({
|
||||
message: "Enter provider id",
|
||||
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 (provider === "other") {
|
||||
provider = await prompts.text({
|
||||
message: "Enter provider id",
|
||||
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 === "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")
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
@@ -358,6 +358,7 @@ function App() {
|
||||
|
||||
event.on(SessionApi.Event.Deleted.type, (evt) => {
|
||||
if (route.data.type === "session" && route.data.sessionID === evt.properties.info.id) {
|
||||
dialog.clear()
|
||||
route.navigate({ type: "home" })
|
||||
toast.show({
|
||||
variant: "info",
|
||||
|
||||
@@ -22,15 +22,16 @@ export type CommandOption = DialogSelectOption & {
|
||||
|
||||
function init() {
|
||||
const [registrations, setRegistrations] = createSignal<Accessor<CommandOption[]>[]>([])
|
||||
const [suspendCount, setSuspendCount] = createSignal(0)
|
||||
const dialog = useDialog()
|
||||
const keybind = useKeybind()
|
||||
const options = createMemo(() => {
|
||||
return registrations().flatMap((x) => x())
|
||||
})
|
||||
const suspended = () => suspendCount() > 0
|
||||
|
||||
let keybinds = true
|
||||
useKeyboard((evt) => {
|
||||
if (!keybinds) return
|
||||
if (suspended()) return
|
||||
for (const option of options()) {
|
||||
if (option.keybind && keybind.match(option.keybind, evt)) {
|
||||
evt.preventDefault()
|
||||
@@ -50,8 +51,9 @@ function init() {
|
||||
}
|
||||
},
|
||||
keybinds(enabled: boolean) {
|
||||
keybinds = enabled
|
||||
setSuspendCount((count) => count + (enabled ? -1 : 1))
|
||||
},
|
||||
suspended,
|
||||
show() {
|
||||
dialog.replace(() => <DialogCommand options={options()} />)
|
||||
},
|
||||
@@ -83,7 +85,10 @@ export function CommandProvider(props: ParentProps) {
|
||||
const keybind = useKeybind()
|
||||
|
||||
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()
|
||||
dialog.replace(() => <DialogCommand options={value.options} />)
|
||||
return
|
||||
|
||||
@@ -72,7 +72,7 @@ export function DialogSessionList() {
|
||||
},
|
||||
})
|
||||
setToDelete(undefined)
|
||||
dialog.clear()
|
||||
// dialog.clear()
|
||||
return
|
||||
}
|
||||
setToDelete(option.value)
|
||||
|
||||
@@ -54,6 +54,12 @@ export function Autocomplete(props: {
|
||||
|
||||
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
|
||||
})
|
||||
|
||||
@@ -373,15 +379,45 @@ export function Autocomplete(props: {
|
||||
return store.visible
|
||||
},
|
||||
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) {
|
||||
if (store.visible) {
|
||||
if (e.name === "up") move(-1)
|
||||
if (e.name === "down") move(1)
|
||||
if (e.name === "escape") hide()
|
||||
if (e.name === "return" || e.name === "tab") select()
|
||||
if (["up", "down", "return", "tab", "escape"].includes(e.name)) e.preventDefault()
|
||||
const name = e.name?.toLowerCase()
|
||||
const ctrlOnly = e.ctrl && !e.meta && !e.shift
|
||||
const isNavUp = name === "up" || (ctrlOnly && name === "p")
|
||||
const isNavDown = name === "down" || (ctrlOnly && name === "n")
|
||||
|
||||
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 (e.name === "@") {
|
||||
|
||||
@@ -115,15 +115,11 @@ export function Prompt(props: PromptProps) {
|
||||
{
|
||||
title: "Clear prompt",
|
||||
value: "prompt.clear",
|
||||
disabled: true,
|
||||
category: "Prompt",
|
||||
disabled: true,
|
||||
onSelect: (dialog) => {
|
||||
input.extmarks.clear()
|
||||
setStore("prompt", {
|
||||
input: "",
|
||||
parts: [],
|
||||
})
|
||||
setStore("extmarkToPartIndex", new Map())
|
||||
input.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) => {
|
||||
setStore(
|
||||
"prompt",
|
||||
produce((draft) => {
|
||||
draft.input += evt.properties.text
|
||||
}),
|
||||
)
|
||||
input.insertText(evt.properties.text)
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
|
||||
@@ -26,12 +26,15 @@ export const WebCommand = cmd({
|
||||
port,
|
||||
hostname,
|
||||
})
|
||||
const url = `https://desktop.dev.opencode.ai?url=${server.url}`
|
||||
UI.empty()
|
||||
UI.println(UI.logo(" "))
|
||||
UI.empty()
|
||||
UI.println(UI.Style.TEXT_INFO_BOLD + " Web interface: ", UI.Style.TEXT_NORMAL, url)
|
||||
open(url).catch(() => {})
|
||||
UI.println(
|
||||
UI.Style.TEXT_INFO_BOLD + " Web interface: ",
|
||||
UI.Style.TEXT_NORMAL,
|
||||
server.url.toString(),
|
||||
)
|
||||
open(server.url.toString()).catch(() => {})
|
||||
await new Promise(() => {})
|
||||
await server.stop()
|
||||
},
|
||||
|
||||
@@ -574,6 +574,7 @@ export namespace Config {
|
||||
.object({
|
||||
apiKey: z.string().optional(),
|
||||
baseURL: z.string().optional(),
|
||||
enterpriseUrl: z.string().optional().describe("GitHub Enterprise URL for copilot authentication"),
|
||||
timeout: z
|
||||
.union([
|
||||
z
|
||||
|
||||
@@ -28,7 +28,7 @@ export namespace Plugin {
|
||||
}
|
||||
const plugins = [...(config.plugin ?? [])]
|
||||
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")
|
||||
}
|
||||
for (let plugin of plugins) {
|
||||
|
||||
@@ -283,6 +283,18 @@ export namespace 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) {
|
||||
const existing = database[providerID]
|
||||
const parsed: ModelsDev.Provider = {
|
||||
@@ -378,14 +390,44 @@ export namespace Provider {
|
||||
if (!plugin.auth) continue
|
||||
const providerID = plugin.auth.provider
|
||||
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)
|
||||
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
|
||||
const options = await plugin.auth.loader(
|
||||
() => Auth.get(providerID) as any,
|
||||
database[plugin.auth.provider],
|
||||
)
|
||||
mergeProvider(plugin.auth.provider, options ?? {}, "custom")
|
||||
|
||||
// Load for the main provider if auth exists
|
||||
if (auth) {
|
||||
const options = await plugin.auth.loader(
|
||||
() => 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
|
||||
@@ -458,7 +500,8 @@ export namespace Provider {
|
||||
: installedPath
|
||||
const mod = await import(modPath)
|
||||
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) => {
|
||||
const { signal, ...rest } = init ?? {}
|
||||
|
||||
@@ -468,7 +511,8 @@ export namespace Provider {
|
||||
|
||||
const combined = signals.length > 1 ? AbortSignal.any(signals) : signals[0]
|
||||
|
||||
return fetch(input, {
|
||||
const fetchFn = customFetch ?? fetch
|
||||
return fetchFn(input, {
|
||||
...rest,
|
||||
signal: combined,
|
||||
// @ts-ignore see here: https://github.com/oven-sh/bun/issues/16682
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
import { Hono } from "hono"
|
||||
import { cors } from "hono/cors"
|
||||
import { stream, streamSSE } from "hono/streaming"
|
||||
import { proxy } from "hono/proxy"
|
||||
import { Session } from "../session"
|
||||
import z from "zod"
|
||||
import { Provider } from "../provider/provider"
|
||||
@@ -757,6 +758,34 @@ export namespace Server {
|
||||
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(
|
||||
"/session/:id/message/:messageID",
|
||||
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() {
|
||||
|
||||
@@ -15,8 +15,8 @@ import { MessageV2 } from "./message-v2"
|
||||
import { Instance } from "../project/instance"
|
||||
import { SessionPrompt } from "./prompt"
|
||||
import { fn } from "@/util/fn"
|
||||
import { Snapshot } from "@/snapshot"
|
||||
import { Command } from "../command"
|
||||
import { Snapshot } from "@/snapshot"
|
||||
|
||||
export namespace Session {
|
||||
const log = Log.create({ service: "session" })
|
||||
@@ -42,7 +42,9 @@ export namespace Session {
|
||||
parentID: Identifier.schema("session").optional(),
|
||||
summary: z
|
||||
.object({
|
||||
diffs: Snapshot.FileDiff.array(),
|
||||
additions: z.number(),
|
||||
deletions: z.number(),
|
||||
diffs: Snapshot.FileDiff.array().optional(),
|
||||
})
|
||||
.optional(),
|
||||
share: z
|
||||
@@ -258,6 +260,11 @@ export namespace Session {
|
||||
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) => {
|
||||
const result = [] as MessageV2.WithParts[]
|
||||
for (const p of await Storage.list(["message", sessionID])) {
|
||||
|
||||
@@ -11,6 +11,7 @@ import { SystemPrompt } from "./system"
|
||||
import { Log } from "@/util/log"
|
||||
import path from "path"
|
||||
import { Instance } from "@/project/instance"
|
||||
import { Storage } from "@/storage/storage"
|
||||
|
||||
export namespace SessionSummary {
|
||||
const log = Log.create({ service: "session.summary" })
|
||||
@@ -44,9 +45,11 @@ export namespace SessionSummary {
|
||||
)
|
||||
await Session.update(input.sessionID, (draft) => {
|
||||
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[] }) {
|
||||
|
||||
@@ -85,7 +85,9 @@ export namespace Storage {
|
||||
const session = await Bun.file(sessionFile).json()
|
||||
await Bun.write(dest, JSON.stringify(session))
|
||||
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,
|
||||
absolute: true,
|
||||
})) {
|
||||
@@ -98,12 +100,12 @@ export namespace Storage {
|
||||
await Bun.write(dest, JSON.stringify(message))
|
||||
|
||||
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(
|
||||
{
|
||||
cwd: fullProjectDir,
|
||||
absolute: true,
|
||||
},
|
||||
)) {
|
||||
for await (const partFile of new Bun.Glob(
|
||||
`storage/session/part/${session.id}/${message.id}/*.json`,
|
||||
).scan({
|
||||
cwd: fullProjectDir,
|
||||
absolute: true,
|
||||
})) {
|
||||
const dest = path.join(dir, "part", message.id, path.basename(partFile))
|
||||
const part = await Bun.file(partFile).json()
|
||||
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 () => {
|
||||
@@ -128,9 +153,7 @@ export namespace Storage {
|
||||
for (let index = migration; index < MIGRATIONS.length; index++) {
|
||||
log.info("running migration", { index })
|
||||
const migration = MIGRATIONS[index]
|
||||
await migration(dir).catch((e) => {
|
||||
log.error("failed to run migration", { error: e, index })
|
||||
})
|
||||
await migration(dir).catch(() => log.error("failed to run migration", { index }))
|
||||
await Bun.write(path.join(dir, "migration"), (index + 1).toString())
|
||||
}
|
||||
return {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.0.20",
|
||||
"version": "1.0.23",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit",
|
||||
|
||||
@@ -39,13 +39,35 @@ export interface Hooks {
|
||||
| {
|
||||
type: "oauth"
|
||||
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 } & (
|
||||
| {
|
||||
method: "auto"
|
||||
callback(): Promise<
|
||||
| ({
|
||||
type: "success"
|
||||
provider?: string
|
||||
} & (
|
||||
| {
|
||||
refresh: string
|
||||
@@ -64,6 +86,7 @@ export interface Hooks {
|
||||
callback(code: string): Promise<
|
||||
| ({
|
||||
type: "success"
|
||||
provider?: 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"
|
||||
}
|
||||
>
|
||||
}
|
||||
)[]
|
||||
}
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.0.20",
|
||||
"version": "1.0.23",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit",
|
||||
|
||||
@@ -55,6 +55,7 @@ import type {
|
||||
SessionShareErrors,
|
||||
SessionDiffData,
|
||||
SessionDiffResponses,
|
||||
SessionDiffErrors,
|
||||
SessionSummarizeData,
|
||||
SessionSummarizeResponses,
|
||||
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>(
|
||||
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",
|
||||
...options,
|
||||
})
|
||||
|
||||
@@ -163,7 +163,7 @@ export type KeybindsConfig = {
|
||||
*/
|
||||
history_previous?: string
|
||||
/**
|
||||
* Previous history item
|
||||
* Next history item
|
||||
*/
|
||||
history_next?: string
|
||||
/**
|
||||
@@ -405,6 +405,10 @@ export type Config = {
|
||||
options?: {
|
||||
apiKey?: 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.
|
||||
*/
|
||||
@@ -527,7 +531,9 @@ export type Session = {
|
||||
directory: string
|
||||
parentID?: string
|
||||
summary?: {
|
||||
diffs: Array<FileDiff>
|
||||
additions: number
|
||||
deletions: number
|
||||
diffs?: Array<FileDiff>
|
||||
}
|
||||
share?: {
|
||||
url: string
|
||||
@@ -1133,6 +1139,7 @@ export type OAuth = {
|
||||
refresh: string
|
||||
access: string
|
||||
expires: number
|
||||
enterpriseUrl?: string
|
||||
}
|
||||
|
||||
export type ApiAuth = {
|
||||
@@ -1882,6 +1889,9 @@ export type SessionShareResponse = SessionShareResponses[keyof SessionShareRespo
|
||||
export type SessionDiffData = {
|
||||
body?: never
|
||||
path: {
|
||||
/**
|
||||
* Session ID
|
||||
*/
|
||||
id: string
|
||||
}
|
||||
query?: {
|
||||
@@ -1891,9 +1901,22 @@ export type SessionDiffData = {
|
||||
url: "/session/{id}/diff"
|
||||
}
|
||||
|
||||
export type SessionDiffErrors = {
|
||||
/**
|
||||
* Bad request
|
||||
*/
|
||||
400: BadRequestError
|
||||
/**
|
||||
* Not found
|
||||
*/
|
||||
404: NotFoundError
|
||||
}
|
||||
|
||||
export type SessionDiffError = SessionDiffErrors[keyof SessionDiffErrors]
|
||||
|
||||
export type SessionDiffResponses = {
|
||||
/**
|
||||
* Successfully retrieved diff
|
||||
* List of diffs
|
||||
*/
|
||||
200: Array<FileDiff>
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.0.20",
|
||||
"version": "1.0.23",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "bun run src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.0.20",
|
||||
"version": "1.0.23",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/components/index.ts",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@opencode-ai/web",
|
||||
"type": "module",
|
||||
"version": "1.0.20",
|
||||
"version": "1.0.23",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "opencode",
|
||||
"displayName": "opencode",
|
||||
"description": "opencode for VS Code",
|
||||
"version": "1.0.20",
|
||||
"version": "1.0.23",
|
||||
"publisher": "sst-dev",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
Reference in New Issue
Block a user