diff --git a/STATS.md b/STATS.md index 67f236ebe2..8a3e0d5538 100644 --- a/STATS.md +++ b/STATS.md @@ -167,3 +167,4 @@ | 2025-12-09 | 1,011,488 (+10,590) | 973,922 (+16,773) | 1,985,410 (+27,363) | | 2025-12-10 | 1,025,891 (+14,403) | 991,708 (+17,786) | 2,017,599 (+32,189) | | 2025-12-11 | 1,045,110 (+19,219) | 1,010,559 (+18,851) | 2,055,669 (+38,070) | +| 2025-12-12 | 1,061,340 (+16,230) | 1,030,838 (+20,279) | 2,092,178 (+36,509) | diff --git a/bun.lock b/bun.lock index 5b3d864de5..9d5819b153 100644 --- a/bun.lock +++ b/bun.lock @@ -131,6 +131,7 @@ "@opencode-ai/util": "workspace:*", "@shikijs/transformers": "3.9.2", "@solid-primitives/active-element": "2.1.3", + "@solid-primitives/audio": "1.4.2", "@solid-primitives/event-bus": "1.1.2", "@solid-primitives/resize-observer": "2.1.3", "@solid-primitives/scroll": "2.1.3", @@ -355,9 +356,12 @@ "@tauri-apps/api": "^2", "@tauri-apps/plugin-dialog": "~2", "@tauri-apps/plugin-opener": "^2", + "@tauri-apps/plugin-os": "~2", "@tauri-apps/plugin-process": "~2", "@tauri-apps/plugin-shell": "~2", + "@tauri-apps/plugin-store": "~2", "@tauri-apps/plugin-updater": "~2", + "@tauri-apps/plugin-window-state": "~2", "solid-js": "catalog:", }, "devDependencies": { @@ -1546,6 +1550,8 @@ "@solid-primitives/active-element": ["@solid-primitives/active-element@2.1.3", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-9t5K4aR2naVDj950XU8OjnLgOg94a8k5wr6JNOPK+N5ESLsJDq42c1ZP8UKpewi1R+wplMMxiM6OPKRzbxJY7A=="], + "@solid-primitives/audio": ["@solid-primitives/audio@1.4.2", "", { "dependencies": { "@solid-primitives/static-store": "^0.1.2", "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-UMD3ORQfI5Ky8yuKPxidDiEazsjv/dsoiKK5yZxLnsgaeNR1Aym3/77h/qT1jBYeXUgj4DX6t7NMpFUSVr14OQ=="], + "@solid-primitives/event-bus": ["@solid-primitives/event-bus@1.1.2", "", { "dependencies": { "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-l+n10/51neGcMaP3ypYt21bXfoeWh8IaC8k7fYuY3ww2a8S1Zv2N2a7FF5Qn+waTu86l0V8/nRHjkyqVIZBYwA=="], "@solid-primitives/event-listener": ["@solid-primitives/event-listener@2.4.3", "", { "dependencies": { "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-h4VqkYFv6Gf+L7SQj+Y6puigL/5DIi7x5q07VZET7AWcS+9/G3WfIE9WheniHWJs51OEkRB43w6lDys5YeFceg=="], @@ -1658,12 +1664,18 @@ "@tauri-apps/plugin-opener": ["@tauri-apps/plugin-opener@2.5.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-ei/yRRoCklWHImwpCcDK3VhNXx+QXM9793aQ64YxpqVF0BDuuIlXhZgiAkc15wnPVav+IbkYhmDJIv5R326Mew=="], + "@tauri-apps/plugin-os": ["@tauri-apps/plugin-os@2.3.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-n+nXWeuSeF9wcEsSPmRnBEGrRgOy6jjkSU+UVCOV8YUGKb2erhDOxis7IqRXiRVHhY8XMKks00BJ0OAdkpf6+A=="], + "@tauri-apps/plugin-process": ["@tauri-apps/plugin-process@2.3.1", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-nCa4fGVaDL/B9ai03VyPOjfAHRHSBz5v6F/ObsB73r/dA3MHHhZtldaDMIc0V/pnUw9ehzr2iEG+XkSEyC0JJA=="], "@tauri-apps/plugin-shell": ["@tauri-apps/plugin-shell@2.3.3", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-Xod+pRcFxmOWFWEnqH5yZcA7qwAMuaaDkMR1Sply+F8VfBj++CGnj2xf5UoialmjZ2Cvd8qrvSCbU+7GgNVsKQ=="], + "@tauri-apps/plugin-store": ["@tauri-apps/plugin-store@2.4.1", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-ckGSEzZ5Ii4Hf2D5x25Oqnm2Zf9MfDWAzR+volY0z/OOBz6aucPKEY0F649JvQ0Vupku6UJo7ugpGRDOFOunkA=="], + "@tauri-apps/plugin-updater": ["@tauri-apps/plugin-updater@2.9.0", "", { "dependencies": { "@tauri-apps/api": "^2.6.0" } }, "sha512-j++sgY8XpeDvzImTrzWA08OqqGqgkNyxczLD7FjNJJx/uXxMZFz5nDcfkyoI/rCjYuj2101Tci/r/HFmOmoxCg=="], + "@tauri-apps/plugin-window-state": ["@tauri-apps/plugin-window-state@2.4.1", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-OuvdrzyY8Q5Dbzpj+GcrnV1iCeoZbcFdzMjanZMMcAEUNy/6PH5pxZPXpaZLOR7whlzXiuzx0L9EKZbH7zpdRw=="], + "@thisbeyond/solid-dnd": ["@thisbeyond/solid-dnd@0.7.5", "", { "peerDependencies": { "solid-js": "^1.5" } }, "sha512-DfI5ff+yYGpK9M21LhYwIPlbP2msKxN2ARwuu6GF8tT1GgNVDTI8VCQvH4TJFoVApP9d44izmAcTh/iTCH2UUw=="], "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="], diff --git a/flake.lock b/flake.lock index 4822d9da57..58344d82c4 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1765270179, - "narHash": "sha256-g2a4MhRKu4ymR4xwo+I+auTknXt/+j37Lnf0Mvfl1rE=", + "lastModified": 1765425892, + "narHash": "sha256-jlQpSkg2sK6IJVzTQBDyRxQZgKADC2HKMRfGCSgNMHo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "677fbe97984e7af3175b6c121f3c39ee5c8d62c9", + "rev": "5d6bdbddb4695a62f0d00a3620b37a15275a5093", "type": "github" }, "original": { diff --git a/infra/console.ts b/infra/console.ts index 0a98ab0723..8f54823f84 100644 --- a/infra/console.ts +++ b/infra/console.ts @@ -102,6 +102,7 @@ const ZEN_MODELS = [ new sst.Secret("ZEN_MODELS2"), new sst.Secret("ZEN_MODELS3"), new sst.Secret("ZEN_MODELS4"), + new sst.Secret("ZEN_MODELS5"), ] const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY") const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", { diff --git a/nix/hashes.json b/nix/hashes.json index 53a696f851..b640219471 100644 --- a/nix/hashes.json +++ b/nix/hashes.json @@ -1,3 +1,3 @@ { - "nodeModules": "sha256-3GaqUwomnIUW8MqUi1jDVPHQ/C5Z+D9wMR//tAGxvSQ=" + "nodeModules": "sha256-3CG0wAMQp2E6ghPUXbYaYifJorp9b1WvCtHD+o8Nhck=" } diff --git a/packages/console/app/src/lib/github.ts b/packages/console/app/src/lib/github.ts index bc49d2e629..cc266f58c4 100644 --- a/packages/console/app/src/lib/github.ts +++ b/packages/console/app/src/lib/github.ts @@ -26,6 +26,7 @@ export const github = query(async () => { release: { name: release.name, url: release.html_url, + tag_name: release.tag_name, }, contributors: contributorCount, } diff --git a/packages/console/app/src/routes/download/index.tsx b/packages/console/app/src/routes/download/index.tsx index a7dc974435..31ce49617b 100644 --- a/packages/console/app/src/routes/download/index.tsx +++ b/packages/console/app/src/routes/download/index.tsx @@ -8,15 +8,7 @@ import { Faq } from "~/component/faq" import desktopAppIcon from "../../asset/lander/opencode-desktop-icon.png" import { Legal } from "~/component/legal" import { config } from "~/config" -import { createMemo } from "solid-js" - -const getLatestRelease = query(async () => { - "use server" - const response = await fetch("https://api.github.com/repos/sst/opencode/releases/latest") - if (!response.ok) return null - const data = await response.json() - return data.tag_name as string -}, "latest-release") +import { github } from "~/lib/github" function CopyStatus() { return ( @@ -28,14 +20,14 @@ function CopyStatus() { } export default function Download() { - const release = createAsync(() => getLatestRelease(), { + const githubData = createAsync(() => github(), { deferStream: true, }) - const download = createMemo(() => { - const version = release() ?? "v1.0.150" + const download = () => { + const version = githubData()?.release.tag_name if (!version) return null return `https://github.com/sst/opencode/releases/download/${version}` - }) + } const handleCopyClick = (command: string) => (event: Event) => { const button = event.currentTarget as HTMLButtonElement navigator.clipboard.writeText(command) diff --git a/packages/console/app/src/routes/workspace/[id]/model-section.tsx b/packages/console/app/src/routes/workspace/[id]/model-section.tsx index 30815336d6..e760ccea25 100644 --- a/packages/console/app/src/routes/workspace/[id]/model-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/model-section.tsx @@ -43,9 +43,12 @@ const getModelsInfo = query(async (workspaceID: string) => { const pA = getPriority(idA) const pB = getPriority(idB) if (pA !== pB) return pA - pB - return modelA.name.localeCompare(modelB.name) + + const modelAName = Array.isArray(modelA) ? modelA[0].name : modelA.name + const modelBName = Array.isArray(modelB) ? modelB[0].name : modelB.name + return modelAName.localeCompare(modelBName) }) - .map(([id, model]) => ({ id, name: model.name })), + .map(([id, model]) => ({ id, name: Array.isArray(model) ? model[0].name : model.name })), disabled: await Model.listDisabled(), } }, workspaceID) diff --git a/packages/console/app/src/routes/zen/util/handler.ts b/packages/console/app/src/routes/zen/util/handler.ts index 7d7767b8df..5e9c877cc3 100644 --- a/packages/console/app/src/routes/zen/util/handler.ts +++ b/packages/console/app/src/routes/zen/util/handler.ts @@ -57,15 +57,17 @@ export async function handler( const sessionId = input.request.headers.get("x-opencode-session") ?? "" const requestId = input.request.headers.get("x-opencode-request") ?? "" const projectId = input.request.headers.get("x-opencode-project") ?? "" + const ocClient = input.request.headers.get("x-opencode-client") ?? "" logger.metric({ is_tream: isStream, session: sessionId, request: requestId, + client: ocClient, }) const zenData = ZenData.list() const modelInfo = validateModel(zenData, model) const dataDumper = createDataDumper(sessionId, requestId, projectId) - const trialLimiter = createTrialLimiter(modelInfo.trial?.limit, ip) + const trialLimiter = createTrialLimiter(modelInfo.trial, ip, ocClient) const isTrial = await trialLimiter?.isTrial() const rateLimiter = createRateLimiter(modelInfo.id, modelInfo.rateLimit, ip) await rateLimiter?.check() @@ -286,11 +288,14 @@ export async function handler( } function validateModel(zenData: ZenData, reqModel: string) { - if (!(reqModel in zenData.models)) { - throw new ModelError(`Model ${reqModel} not supported`) - } + if (!(reqModel in zenData.models)) throw new ModelError(`Model ${reqModel} not supported`) + const modelId = reqModel as keyof typeof zenData.models - const modelData = zenData.models[modelId] + const modelData = Array.isArray(zenData.models[modelId]) + ? zenData.models[modelId].find((model) => opts.format === model.formatFilter) + : zenData.models[modelId] + + if (!modelData) throw new ModelError(`Model ${reqModel} not supported for format ${opts.format}`) logger.metric({ model: modelId }) diff --git a/packages/console/app/src/routes/zen/util/trialLimiter.ts b/packages/console/app/src/routes/zen/util/trialLimiter.ts index 15561c9f6b..531e5cf0c3 100644 --- a/packages/console/app/src/routes/zen/util/trialLimiter.ts +++ b/packages/console/app/src/routes/zen/util/trialLimiter.ts @@ -1,12 +1,18 @@ import { Database, eq, sql } from "@opencode-ai/console-core/drizzle/index.js" import { IpTable } from "@opencode-ai/console-core/schema/ip.sql.js" import { UsageInfo } from "./provider/provider" +import { ZenData } from "@opencode-ai/console-core/model.js" -export function createTrialLimiter(limit: number | undefined, ip: string) { - if (!limit) return +export function createTrialLimiter(trial: ZenData.Trial | undefined, ip: string, client: string) { + if (!trial) return if (!ip) return - let trial: boolean + const limit = + trial.limits.find((limit) => limit.client === client)?.limit ?? + trial.limits.find((limit) => limit.client === undefined)?.limit + if (!limit) return + + let _isTrial: boolean return { isTrial: async () => { @@ -20,11 +26,11 @@ export function createTrialLimiter(limit: number | undefined, ip: string) { .then((rows) => rows[0]), ) - trial = (data?.usage ?? 0) < limit - return trial + _isTrial = (data?.usage ?? 0) < limit + return _isTrial }, track: async (usageInfo: UsageInfo) => { - if (!trial) return + if (!_isTrial) return const usage = usageInfo.inputTokens + usageInfo.outputTokens + diff --git a/packages/console/core/script/promote-models.ts b/packages/console/core/script/promote-models.ts index 0ff859df8e..bebef5cfb5 100755 --- a/packages/console/core/script/promote-models.ts +++ b/packages/console/core/script/promote-models.ts @@ -16,16 +16,19 @@ const value1 = lines.find((line) => line.startsWith("ZEN_MODELS1"))?.split("=")[ const value2 = lines.find((line) => line.startsWith("ZEN_MODELS2"))?.split("=")[1] const value3 = lines.find((line) => line.startsWith("ZEN_MODELS3"))?.split("=")[1] const value4 = lines.find((line) => line.startsWith("ZEN_MODELS4"))?.split("=")[1] +const value5 = lines.find((line) => line.startsWith("ZEN_MODELS5"))?.split("=")[1] if (!value1) throw new Error("ZEN_MODELS1 not found") if (!value2) throw new Error("ZEN_MODELS2 not found") if (!value3) throw new Error("ZEN_MODELS3 not found") if (!value4) throw new Error("ZEN_MODELS4 not found") +if (!value5) throw new Error("ZEN_MODELS5 not found") // validate value -ZenData.validate(JSON.parse(value1 + value2 + value3 + value4)) +ZenData.validate(JSON.parse(value1 + value2 + value3 + value4 + value5)) // update the secret await $`bun sst secret set ZEN_MODELS1 ${value1} --stage ${stage}` await $`bun sst secret set ZEN_MODELS2 ${value2} --stage ${stage}` await $`bun sst secret set ZEN_MODELS3 ${value3} --stage ${stage}` await $`bun sst secret set ZEN_MODELS4 ${value4} --stage ${stage}` +await $`bun sst secret set ZEN_MODELS5 ${value5} --stage ${stage}` diff --git a/packages/console/core/script/pull-models.ts b/packages/console/core/script/pull-models.ts index a89e3951c8..afa865625b 100755 --- a/packages/console/core/script/pull-models.ts +++ b/packages/console/core/script/pull-models.ts @@ -16,16 +16,19 @@ const value1 = lines.find((line) => line.startsWith("ZEN_MODELS1"))?.split("=")[ const value2 = lines.find((line) => line.startsWith("ZEN_MODELS2"))?.split("=")[1] const value3 = lines.find((line) => line.startsWith("ZEN_MODELS3"))?.split("=")[1] const value4 = lines.find((line) => line.startsWith("ZEN_MODELS4"))?.split("=")[1] +const value5 = lines.find((line) => line.startsWith("ZEN_MODELS5"))?.split("=")[1] if (!value1) throw new Error("ZEN_MODELS1 not found") if (!value2) throw new Error("ZEN_MODELS2 not found") if (!value3) throw new Error("ZEN_MODELS3 not found") if (!value4) throw new Error("ZEN_MODELS4 not found") +if (!value5) throw new Error("ZEN_MODELS5 not found") // validate value -ZenData.validate(JSON.parse(value1 + value2 + value3 + value4)) +ZenData.validate(JSON.parse(value1 + value2 + value3 + value4 + value5)) // update the secret await $`bun sst secret set ZEN_MODELS1 ${value1}` await $`bun sst secret set ZEN_MODELS2 ${value2}` await $`bun sst secret set ZEN_MODELS3 ${value3}` await $`bun sst secret set ZEN_MODELS4 ${value4}` +await $`bun sst secret set ZEN_MODELS5 ${value5}` diff --git a/packages/console/core/script/update-models.ts b/packages/console/core/script/update-models.ts index a8523a5f20..5d40b4d5a3 100755 --- a/packages/console/core/script/update-models.ts +++ b/packages/console/core/script/update-models.ts @@ -14,15 +14,17 @@ const oldValue1 = lines.find((line) => line.startsWith("ZEN_MODELS1"))?.split("= const oldValue2 = lines.find((line) => line.startsWith("ZEN_MODELS2"))?.split("=")[1] const oldValue3 = lines.find((line) => line.startsWith("ZEN_MODELS3"))?.split("=")[1] const oldValue4 = lines.find((line) => line.startsWith("ZEN_MODELS4"))?.split("=")[1] +const oldValue5 = lines.find((line) => line.startsWith("ZEN_MODELS5"))?.split("=")[1] if (!oldValue1) throw new Error("ZEN_MODELS1 not found") if (!oldValue2) throw new Error("ZEN_MODELS2 not found") if (!oldValue3) throw new Error("ZEN_MODELS3 not found") if (!oldValue4) throw new Error("ZEN_MODELS4 not found") +if (!oldValue5) throw new Error("ZEN_MODELS5 not found") // store the prettified json to a temp file const filename = `models-${Date.now()}.json` const tempFile = Bun.file(path.join(os.tmpdir(), filename)) -await tempFile.write(JSON.stringify(JSON.parse(oldValue1 + oldValue2 + oldValue3 + oldValue4), null, 2)) +await tempFile.write(JSON.stringify(JSON.parse(oldValue1 + oldValue2 + oldValue3 + oldValue4 + oldValue5), null, 2)) console.log("tempFile", tempFile.name) // open temp file in vim and read the file on close @@ -31,12 +33,15 @@ const newValue = JSON.stringify(JSON.parse(await tempFile.text())) ZenData.validate(JSON.parse(newValue)) // update the secret -const chunk = Math.ceil(newValue.length / 4) +const chunk = Math.ceil(newValue.length / 5) const newValue1 = newValue.slice(0, chunk) const newValue2 = newValue.slice(chunk, chunk * 2) const newValue3 = newValue.slice(chunk * 2, chunk * 3) -const newValue4 = newValue.slice(chunk * 3) +const newValue4 = newValue.slice(chunk * 3, chunk * 4) +const newValue5 = newValue.slice(chunk * 4) + await $`bun sst secret set ZEN_MODELS1 ${newValue1}` await $`bun sst secret set ZEN_MODELS2 ${newValue2}` await $`bun sst secret set ZEN_MODELS3 ${newValue3}` await $`bun sst secret set ZEN_MODELS4 ${newValue4}` +await $`bun sst secret set ZEN_MODELS5 ${newValue5}` diff --git a/packages/console/core/src/model.ts b/packages/console/core/src/model.ts index 47ba3e9d83..55d6c895c5 100644 --- a/packages/console/core/src/model.ts +++ b/packages/console/core/src/model.ts @@ -9,7 +9,17 @@ import { Resource } from "@opencode-ai/console-resource" export namespace ZenData { const FormatSchema = z.enum(["anthropic", "google", "openai", "oa-compat"]) + const TrialSchema = z.object({ + provider: z.string(), + limits: z.array( + z.object({ + limit: z.number(), + client: z.enum(["cli", "desktop"]).optional(), + }), + ), + }) export type Format = z.infer + export type Trial = z.infer const ModelCostSchema = z.object({ input: z.number(), @@ -26,12 +36,7 @@ export namespace ZenData { allowAnonymous: z.boolean().optional(), byokProvider: z.enum(["openai", "anthropic", "google"]).optional(), stickyProvider: z.boolean().optional(), - trial: z - .object({ - limit: z.number(), - provider: z.string(), - }) - .optional(), + trial: TrialSchema.optional(), rateLimit: z.number().optional(), fallbackProvider: z.string().optional(), providers: z.array( @@ -53,7 +58,7 @@ export namespace ZenData { }) const ModelsSchema = z.object({ - models: z.record(z.string(), ModelSchema), + models: z.record(z.string(), z.union([ModelSchema, z.array(ModelSchema.extend({ formatFilter: FormatSchema }))])), providers: z.record(z.string(), ProviderSchema), }) @@ -63,7 +68,11 @@ export namespace ZenData { export const list = fn(z.void(), () => { const json = JSON.parse( - Resource.ZEN_MODELS1.value + Resource.ZEN_MODELS2.value + Resource.ZEN_MODELS3.value + Resource.ZEN_MODELS4.value, + Resource.ZEN_MODELS1.value + + Resource.ZEN_MODELS2.value + + Resource.ZEN_MODELS3.value + + Resource.ZEN_MODELS4.value + + Resource.ZEN_MODELS5.value, ) return ModelsSchema.parse(json) }) diff --git a/packages/console/core/sst-env.d.ts b/packages/console/core/sst-env.d.ts index 0b09bfd0cd..632ea3fbe7 100644 --- a/packages/console/core/sst-env.d.ts +++ b/packages/console/core/sst-env.d.ts @@ -50,10 +50,6 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } - "Enterprise": { - "type": "sst.cloudflare.SolidStart" - "url": string - } "GITHUB_APP_ID": { "type": "sst.sst.Secret" "value": string @@ -94,6 +90,10 @@ declare module "sst" { "type": "sst.sst.Linkable" "value": string } + "Teams": { + "type": "sst.cloudflare.SolidStart" + "url": string + } "Web": { "type": "sst.cloudflare.Astro" "url": string @@ -114,6 +114,10 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "ZEN_MODELS5": { + "type": "sst.sst.Secret" + "value": string + } } } // cloudflare diff --git a/packages/console/function/sst-env.d.ts b/packages/console/function/sst-env.d.ts index 0b09bfd0cd..632ea3fbe7 100644 --- a/packages/console/function/sst-env.d.ts +++ b/packages/console/function/sst-env.d.ts @@ -50,10 +50,6 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } - "Enterprise": { - "type": "sst.cloudflare.SolidStart" - "url": string - } "GITHUB_APP_ID": { "type": "sst.sst.Secret" "value": string @@ -94,6 +90,10 @@ declare module "sst" { "type": "sst.sst.Linkable" "value": string } + "Teams": { + "type": "sst.cloudflare.SolidStart" + "url": string + } "Web": { "type": "sst.cloudflare.Astro" "url": string @@ -114,6 +114,10 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "ZEN_MODELS5": { + "type": "sst.sst.Secret" + "value": string + } } } // cloudflare diff --git a/packages/console/resource/sst-env.d.ts b/packages/console/resource/sst-env.d.ts index 0b09bfd0cd..632ea3fbe7 100644 --- a/packages/console/resource/sst-env.d.ts +++ b/packages/console/resource/sst-env.d.ts @@ -50,10 +50,6 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } - "Enterprise": { - "type": "sst.cloudflare.SolidStart" - "url": string - } "GITHUB_APP_ID": { "type": "sst.sst.Secret" "value": string @@ -94,6 +90,10 @@ declare module "sst" { "type": "sst.sst.Linkable" "value": string } + "Teams": { + "type": "sst.cloudflare.SolidStart" + "url": string + } "Web": { "type": "sst.cloudflare.Astro" "url": string @@ -114,6 +114,10 @@ declare module "sst" { "type": "sst.sst.Secret" "value": string } + "ZEN_MODELS5": { + "type": "sst.sst.Secret" + "value": string + } } } // cloudflare diff --git a/packages/desktop/index.html b/packages/desktop/index.html index b9d3e53512..9803517a07 100644 --- a/packages/desktop/index.html +++ b/packages/desktop/index.html @@ -14,7 +14,7 @@ - + -
+
diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 1d12a9cb9f..a2b995a4ab 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -35,6 +35,7 @@ "@opencode-ai/util": "workspace:*", "@shikijs/transformers": "3.9.2", "@solid-primitives/active-element": "2.1.3", + "@solid-primitives/audio": "1.4.2", "@solid-primitives/event-bus": "1.1.2", "@solid-primitives/resize-observer": "2.1.3", "@solid-primitives/scroll": "2.1.3", diff --git a/packages/desktop/src/app.tsx b/packages/desktop/src/app.tsx index a1ff90d269..bf9dfd3b78 100644 --- a/packages/desktop/src/app.tsx +++ b/packages/desktop/src/app.tsx @@ -14,6 +14,7 @@ import { LayoutProvider } from "./context/layout" import { GlobalSDKProvider } from "./context/global-sdk" import { SessionProvider } from "./context/session" import { Show } from "solid-js" +import { NotificationProvider } from "./context/notification" declare global { interface Window { @@ -37,25 +38,27 @@ export function App() { - - - - - - } /> - ( - - - - - - )} - /> - - - + + + + + + + } /> + ( + + + + + + )} + /> + + + + diff --git a/packages/desktop/src/components/header.tsx b/packages/desktop/src/components/header.tsx new file mode 100644 index 0000000000..cc4d018166 --- /dev/null +++ b/packages/desktop/src/components/header.tsx @@ -0,0 +1,113 @@ +import { useGlobalSync } from "@/context/global-sync" +import { useLayout } from "@/context/layout" +import { Session } from "@opencode-ai/sdk/v2/client" +import { Button } from "@opencode-ai/ui/button" +import { Icon } from "@opencode-ai/ui/icon" +import { Mark } from "@opencode-ai/ui/logo" +import { Select } from "@opencode-ai/ui/select" +import { Tooltip } from "@opencode-ai/ui/tooltip" +import { base64Decode } from "@opencode-ai/util/encode" +import { getFilename } from "@opencode-ai/util/path" +import { A, useParams } from "@solidjs/router" +import { createMemo, Show } from "solid-js" + +export function Header(props: { + navigateToProject: (directory: string) => void + navigateToSession: (session: Session | undefined) => void +}) { + const globalSync = useGlobalSync() + const layout = useLayout() + const params = useParams() + const currentDirectory = createMemo(() => base64Decode(params.dir ?? "")) + const store = createMemo(() => globalSync.child(currentDirectory())[0]) + const sessions = createMemo(() => store().session ?? []) + const currentSession = createMemo(() => sessions().find((s) => s.id === params.id)) + + return ( +
+ + + +
+ 0}> +
+
+ +
/
+ project.worktree)} - current={currentDirectory()} - label={(x) => getFilename(x)} - onSelect={(x) => (x ? navigateToProject(x) : undefined)} - class="text-14-regular text-text-base" - variant="ghost" - > - {/* @ts-ignore */} - {(i) => ( -
- -
{getFilename(i)}
-
- )} - -
/
-