diff --git a/packages/opencode/src/cli/cmd/account.ts b/packages/opencode/src/cli/cmd/account.ts index 8ad42c5eb..2b1df897e 100644 --- a/packages/opencode/src/cli/cmd/account.ts +++ b/packages/opencode/src/cli/cmd/account.ts @@ -92,6 +92,11 @@ interface OrgChoice { label: string } +const isActiveOrgChoice = ( + active: Option.Option<{ id: AccountID; active_org_id: OrgID | null }>, + choice: { accountID: AccountID; orgID: OrgID }, +) => Option.isSome(active) && active.value.id === choice.accountID && active.value.active_org_id === choice.orgID + const switchEffect = Effect.fn("switch")(function* () { const service = yield* AccountService @@ -99,11 +104,10 @@ const switchEffect = Effect.fn("switch")(function* () { if (groups.length === 0) return yield* println("Not logged in") const active = yield* service.active() - const activeOrgID = Option.flatMap(active, (a) => Option.fromNullishOr(a.active_org_id)) const opts = groups.flatMap((group) => group.orgs.map((org) => { - const isActive = Option.isSome(activeOrgID) && activeOrgID.value === org.id + const isActive = isActiveOrgChoice(active, { accountID: group.account.id, orgID: org.id }) return { value: { orgID: org.id, accountID: group.account.id, label: org.name }, label: isActive @@ -132,11 +136,10 @@ const orgsEffect = Effect.fn("orgs")(function* () { if (!groups.some((group) => group.orgs.length > 0)) return yield* println("No orgs found") const active = yield* service.active() - const activeOrgID = Option.flatMap(active, (a) => Option.fromNullishOr(a.active_org_id)) for (const group of groups) { for (const org of group.orgs) { - const isActive = Option.isSome(activeOrgID) && activeOrgID.value === org.id + const isActive = isActiveOrgChoice(active, { accountID: group.account.id, orgID: org.id }) const dot = isActive ? UI.Style.TEXT_SUCCESS + "●" + UI.Style.TEXT_NORMAL : " " const name = isActive ? UI.Style.TEXT_HIGHLIGHT_BOLD + org.name + UI.Style.TEXT_NORMAL : org.name const email = UI.Style.TEXT_DIM + group.account.email + UI.Style.TEXT_NORMAL diff --git a/packages/opencode/src/cli/cmd/tui/util/editor.ts b/packages/opencode/src/cli/cmd/tui/util/editor.ts index 6d32c63c0..9eaae99fc 100644 --- a/packages/opencode/src/cli/cmd/tui/util/editor.ts +++ b/packages/opencode/src/cli/cmd/tui/util/editor.ts @@ -17,17 +17,21 @@ export namespace Editor { await Filesystem.write(filepath, opts.value) opts.renderer.suspend() opts.renderer.currentRenderBuffer.clear() - const parts = editor.split(" ") - const proc = Process.spawn([...parts, filepath], { - stdin: "inherit", - stdout: "inherit", - stderr: "inherit", - }) - await proc.exited - const content = await Filesystem.readText(filepath) - opts.renderer.currentRenderBuffer.clear() - opts.renderer.resume() - opts.renderer.requestRender() - return content || undefined + try { + const parts = editor.split(" ") + const proc = Process.spawn([...parts, filepath], { + stdin: "inherit", + stdout: "inherit", + stderr: "inherit", + shell: process.platform === "win32", + }) + await proc.exited + const content = await Filesystem.readText(filepath) + return content || undefined + } finally { + opts.renderer.currentRenderBuffer.clear() + opts.renderer.resume() + opts.renderer.requestRender() + } } } diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 5caeb9156..96b65e9da 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -1053,7 +1053,12 @@ export namespace Config { }) .optional(), plugin: z.string().array().optional(), - snapshot: z.boolean().optional(), + snapshot: z + .boolean() + .optional() + .describe( + "Enable or disable snapshot tracking. When false, filesystem snapshots are not recorded and undoing or reverting will not undo/revert file changes. Defaults to true.", + ), share: z .enum(["manual", "auto", "disabled"]) .optional() diff --git a/packages/opencode/src/plugin/copilot.ts b/packages/opencode/src/plugin/copilot.ts index ddb4d9046..9a55c7376 100644 --- a/packages/opencode/src/plugin/copilot.ts +++ b/packages/opencode/src/plugin/copilot.ts @@ -185,12 +185,9 @@ export async function CopilotAuthPlugin(input: PluginInput): Promise { const deploymentType = inputs.deploymentType || "github.com" let domain = "github.com" - let actualProvider = "github-copilot" - if (deploymentType === "enterprise") { const enterpriseUrl = inputs.enterpriseUrl domain = normalizeDomain(enterpriseUrl!) - actualProvider = "github-copilot-enterprise" } const urls = getUrls(domain) @@ -262,8 +259,7 @@ export async function CopilotAuthPlugin(input: PluginInput): Promise { expires: 0, } - if (actualProvider === "github-copilot-enterprise") { - result.provider = "github-copilot-enterprise" + if (deploymentType === "enterprise") { result.enterpriseUrl = domain } diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 349073197..5c5bcc0f2 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -197,16 +197,6 @@ export namespace Provider { options: {}, } }, - "github-copilot-enterprise": async () => { - return { - autoload: false, - async getModel(sdk: any, modelID: string, _options?: Record) { - if (useLanguageModel(sdk)) return sdk.languageModel(modelID) - return shouldUseCopilotResponsesApi(modelID) ? sdk.responses(modelID) : sdk.chat(modelID) - }, - options: {}, - } - }, azure: async (provider) => { const resource = iife(() => { const name = provider.options?.resourceName @@ -863,20 +853,6 @@ 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: ProviderID.githubCopilotEnterprise, - name: "GitHub Copilot Enterprise", - models: mapValues(githubCopilot.models, (model) => ({ - ...model, - providerID: ProviderID.githubCopilotEnterprise, - })), - } - } - function mergeProvider(providerID: ProviderID, provider: Partial) { const existing = providers[providerID] if (existing) { @@ -1003,46 +979,14 @@ export namespace Provider { const providerID = ProviderID.make(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) hasAuth = true - - // Special handling for github-copilot: also check for enterprise auth - if (providerID === ProviderID.githubCopilot && !hasAuth) { - const enterpriseAuth = await Auth.get("github-copilot-enterprise") - if (enterpriseAuth) hasAuth = true - } - - if (!hasAuth) continue + if (!auth) continue if (!plugin.auth.loader) continue - // 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]) - const opts = options ?? {} - const patch: Partial = providers[providerID] ? { options: opts } : { source: "custom", options: opts } - mergeProvider(providerID, patch) - } - - // If this is github-copilot plugin, also register for github-copilot-enterprise if auth exists - if (providerID === ProviderID.githubCopilot) { - const enterpriseProviderID = ProviderID.githubCopilotEnterprise - 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], - ) - const opts = enterpriseOptions ?? {} - const patch: Partial = providers[enterpriseProviderID] - ? { options: opts } - : { source: "custom", options: opts } - mergeProvider(enterpriseProviderID, patch) - } - } - } + const options = await plugin.auth.loader(() => Auth.get(providerID) as any, database[plugin.auth.provider]) + const opts = options ?? {} + const patch: Partial = providers[providerID] ? { options: opts } : { source: "custom", options: opts } + mergeProvider(providerID, patch) } for (const [id, fn] of Object.entries(CUSTOM_LOADERS)) { diff --git a/packages/opencode/src/provider/schema.ts b/packages/opencode/src/provider/schema.ts index 9eac235ce..15a919d8e 100644 --- a/packages/opencode/src/provider/schema.ts +++ b/packages/opencode/src/provider/schema.ts @@ -18,7 +18,6 @@ export const ProviderID = providerIdSchema.pipe( google: schema.makeUnsafe("google"), googleVertex: schema.makeUnsafe("google-vertex"), githubCopilot: schema.makeUnsafe("github-copilot"), - githubCopilotEnterprise: schema.makeUnsafe("github-copilot-enterprise"), amazonBedrock: schema.makeUnsafe("amazon-bedrock"), azure: schema.makeUnsafe("azure"), openrouter: schema.makeUnsafe("openrouter"), diff --git a/packages/opencode/src/util/process.ts b/packages/opencode/src/util/process.ts index 049096937..9b37432c3 100644 --- a/packages/opencode/src/util/process.ts +++ b/packages/opencode/src/util/process.ts @@ -3,6 +3,7 @@ import { buffer } from "node:stream/consumers" export namespace Process { export type Stdio = "inherit" | "pipe" | "ignore" + export type Shell = boolean | string export interface Options { cwd?: string @@ -10,6 +11,7 @@ export namespace Process { stdin?: Stdio stdout?: Stdio stderr?: Stdio + shell?: Shell abort?: AbortSignal kill?: NodeJS.Signals | number timeout?: number @@ -60,6 +62,7 @@ export namespace Process { cwd: opts.cwd, env: opts.env === null ? {} : opts.env ? { ...process.env, ...opts.env } : undefined, stdio: [opts.stdin ?? "ignore", opts.stdout ?? "ignore", opts.stderr ?? "ignore"], + shell: opts.shell, windowsHide: process.platform === "win32", }) diff --git a/packages/opencode/test/preload.ts b/packages/opencode/test/preload.ts index 1ebd273d2..e253183d8 100644 --- a/packages/opencode/test/preload.ts +++ b/packages/opencode/test/preload.ts @@ -44,6 +44,7 @@ process.env["OPENCODE_TEST_HOME"] = testHome // Set test managed config directory to isolate tests from system managed settings const testManagedConfigDir = path.join(dir, "managed") process.env["OPENCODE_TEST_MANAGED_CONFIG_DIR"] = testManagedConfigDir +process.env["OPENCODE_DISABLE_DEFAULT_PLUGINS"] = "true" // Write the cache version file to prevent global/index.ts from clearing the cache const cacheDir = path.join(dir, "cache", "opencode") diff --git a/packages/ui/src/components/line-comment-styles.ts b/packages/ui/src/components/line-comment-styles.ts index d5be67554..f89d039de 100644 --- a/packages/ui/src/components/line-comment-styles.ts +++ b/packages/ui/src/components/line-comment-styles.ts @@ -76,8 +76,10 @@ export const lineCommentStyles = ` right: auto; margin-left: 8px; flex: 0 1 600px; + min-width: 0; width: min(100%, 600px); max-width: min(100%, 600px); + box-sizing: border-box; } [data-component="line-comment"][data-inline] [data-slot="line-comment-popover"][data-inline-body] { @@ -113,6 +115,7 @@ export const lineCommentStyles = ` [data-component="line-comment"] [data-slot="line-comment-text"] { flex: 1; + min-width: 0; font-family: var(--font-family-sans); font-size: var(--font-size-base); font-weight: var(--font-weight-regular); @@ -120,6 +123,7 @@ export const lineCommentStyles = ` letter-spacing: var(--letter-spacing-normal); color: var(--text-strong); white-space: pre-wrap; + overflow-wrap: anywhere; } [data-component="line-comment"] [data-slot="line-comment-tools"] { @@ -169,10 +173,14 @@ export const lineCommentStyles = ` align-items: center; gap: 8px; padding-left: 8px; + min-width: 0; + box-sizing: border-box; } [data-component="line-comment"] [data-slot="line-comment-editor-label"] { margin-right: auto; + min-width: 0; + overflow-wrap: anywhere; } [data-component="line-comment"] [data-slot="line-comment-action"] { diff --git a/packages/ui/src/components/line-comment.tsx b/packages/ui/src/components/line-comment.tsx index ff5d1df00..75c4bf7e9 100644 --- a/packages/ui/src/components/line-comment.tsx +++ b/packages/ui/src/components/line-comment.tsx @@ -268,7 +268,11 @@ export const LineCommentEditor = (props: LineCommentEditorProps) => { type="button" data-slot="line-comment-action" data-variant="ghost" - on:click={split.onCancel as any} + on:mousedown={(e) => e.preventDefault()} + on:click={(e) => { + e.stopPropagation() + split.onCancel() + }} > {split.cancelLabel ?? i18n.t("ui.common.cancel")} @@ -277,7 +281,11 @@ export const LineCommentEditor = (props: LineCommentEditorProps) => { data-slot="line-comment-action" data-variant="primary" disabled={text().trim().length === 0} - on:click={submit as any} + on:mousedown={(e) => e.preventDefault()} + on:click={(e) => { + e.stopPropagation() + submit() + }} > {split.submitLabel ?? i18n.t("ui.lineComment.submit")}