From cc7acd90ab2fda54f06ff687a46d7364e479dc32 Mon Sep 17 00:00:00 2001 From: Jérôme Benoit Date: Thu, 16 Apr 2026 17:43:15 +0200 Subject: fix(nix): add shared package to bun install filters (#22665) --- packages/shared/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'packages/shared') diff --git a/packages/shared/package.json b/packages/shared/package.json index ac2d8f209..bdfca12a9 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -17,10 +17,10 @@ }, "imports": {}, "devDependencies": { + "@tsconfig/bun": "catalog:", "@types/semver": "catalog:", "@types/bun": "catalog:", - "@types/npmcli__arborist": "6.3.3", - "@tsconfig/bun": "catalog:" + "@types/npmcli__arborist": "6.3.3" }, "dependencies": { "@effect/platform-node": "catalog:", -- cgit v1.2.3 From bfffc3c2c6349d9199dd1a73260612b5ec2da88d Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 16 Apr 2026 12:19:43 -0400 Subject: tui: ensure TUI plugins load with proper project context when multiple directories are open Fixes potential plugin resolution issues when switching between projects by wrapping plugin loading in Instance.provide(). This ensures each plugin resolves dependencies relative to its correct project directory instead of inheriting context from whatever instance happened to be active. Also reorganizes config loading code into focused modules (command.ts, managed.ts, plugin.ts) to make the codebase easier to maintain and test. --- .../opencode/src/cli/cmd/tui/plugin/runtime.ts | 72 ++++--- packages/opencode/src/config/command.ts | 76 +++++++ packages/opencode/src/config/config.ts | 233 +++------------------ packages/opencode/src/config/index.ts | 2 + packages/opencode/src/config/managed.ts | 71 +++++++ packages/opencode/src/config/paths.ts | 5 +- packages/opencode/test/config/config.test.ts | 29 ++- packages/shared/src/npm.ts | 47 +++-- 8 files changed, 265 insertions(+), 270 deletions(-) create mode 100644 packages/opencode/src/config/command.ts create mode 100644 packages/opencode/src/config/managed.ts (limited to 'packages/shared') diff --git a/packages/opencode/src/cli/cmd/tui/plugin/runtime.ts b/packages/opencode/src/cli/cmd/tui/plugin/runtime.ts index af37ffbd7..ac1c0fc3b 100644 --- a/packages/opencode/src/cli/cmd/tui/plugin/runtime.ts +++ b/packages/opencode/src/cli/cmd/tui/plugin/runtime.ts @@ -16,6 +16,7 @@ import { TuiConfig } from "@/cli/cmd/tui/config/tui" import { Log } from "@/util" import { errorData, errorMessage } from "@/util/error" import { isRecord } from "@/util/record" +import { Instance } from "@/project/instance" import { readPackageThemes, readPluginId, @@ -789,7 +790,13 @@ async function addPluginBySpec(state: RuntimeState | undefined, raw: string) { state.pending.delete(spec) return true } - const ready = await resolveExternalPlugins([cfg], () => TuiConfig.waitForDependencies()) + const ready = await Instance.provide({ + directory: state.directory, + fn: () => resolveExternalPlugins([cfg], () => TuiConfig.waitForDependencies()), + }).catch((error) => { + fail("failed to add tui plugin", { path: next, error }) + return [] as PluginLoad[] + }) if (!ready.length) { return false } @@ -980,37 +987,42 @@ export namespace TuiPluginRuntime { } runtime = next try { - const records = Flag.OPENCODE_PURE ? [] : (config.plugin_origins ?? []) - if (Flag.OPENCODE_PURE && config.plugin_origins?.length) { - log.info("skipping external tui plugins in pure mode", { count: config.plugin_origins.length }) - } + await Instance.provide({ + directory: cwd, + fn: async () => { + const records = Flag.OPENCODE_PURE ? [] : (config.plugin_origins ?? []) + if (Flag.OPENCODE_PURE && config.plugin_origins?.length) { + log.info("skipping external tui plugins in pure mode", { count: config.plugin_origins.length }) + } - for (const item of INTERNAL_TUI_PLUGINS) { - log.info("loading internal tui plugin", { id: item.id }) - const entry = loadInternalPlugin(item) - const meta = createMeta(entry.source, entry.spec, entry.target, undefined, entry.id) - addPluginEntry(next, { - id: entry.id, - load: entry, - meta, - themes: {}, - plugin: entry.module.tui, - enabled: true, - }) - } + for (const item of INTERNAL_TUI_PLUGINS) { + log.info("loading internal tui plugin", { id: item.id }) + const entry = loadInternalPlugin(item) + const meta = createMeta(entry.source, entry.spec, entry.target, undefined, entry.id) + addPluginEntry(next, { + id: entry.id, + load: entry, + meta, + themes: {}, + plugin: entry.module.tui, + enabled: true, + }) + } - const ready = await resolveExternalPlugins(records, () => TuiConfig.waitForDependencies()) - await addExternalPluginEntries(next, ready) - - applyInitialPluginEnabledState(next, config) - for (const plugin of next.plugins) { - if (!plugin.enabled) continue - // Keep plugin execution sequential for deterministic side effects: - // command registration order affects keybind/command precedence, - // route registration is last-wins when ids collide, - // and hook chains rely on stable plugin ordering. - await activatePluginEntry(next, plugin, false) - } + const ready = await resolveExternalPlugins(records, () => TuiConfig.waitForDependencies()) + await addExternalPluginEntries(next, ready) + + applyInitialPluginEnabledState(next, config) + for (const plugin of next.plugins) { + if (!plugin.enabled) continue + // Keep plugin execution sequential for deterministic side effects: + // command registration order affects keybind/command precedence, + // route registration is last-wins when ids collide, + // and hook chains rely on stable plugin ordering. + await activatePluginEntry(next, plugin, false) + } + }, + }) } catch (error) { fail("failed to load tui plugins", { directory: cwd, error }) } diff --git a/packages/opencode/src/config/command.ts b/packages/opencode/src/config/command.ts new file mode 100644 index 000000000..4b2d58f3f --- /dev/null +++ b/packages/opencode/src/config/command.ts @@ -0,0 +1,76 @@ +import { Log } from "../util" +import path from "path" +import z from "zod" +import { NamedError } from "@opencode-ai/shared/util/error" +import { Glob } from "@opencode-ai/shared/util/glob" +import { Bus } from "@/bus" +import * as ConfigMarkdown from "./markdown" +import { InvalidError } from "./paths" + +const ModelId = z.string().meta({ $ref: "https://models.dev/model-schema.json#/$defs/Model" }) + +const log = Log.create({ service: "config" }) + +function rel(item: string, patterns: string[]) { + const normalizedItem = item.replaceAll("\\", "/") + for (const pattern of patterns) { + const index = normalizedItem.indexOf(pattern) + if (index === -1) continue + return normalizedItem.slice(index + pattern.length) + } +} + +function trim(file: string) { + const ext = path.extname(file) + return ext.length ? file.slice(0, -ext.length) : file +} + +export namespace ConfigCommand { + export const Info = z.object({ + template: z.string(), + description: z.string().optional(), + agent: z.string().optional(), + model: ModelId.optional(), + subtask: z.boolean().optional(), + }) + + export type Info = z.infer + + export async function load(dir: string) { + const result: Record = {} + for (const item of await Glob.scan("{command,commands}/**/*.md", { + cwd: dir, + absolute: true, + dot: true, + symlink: true, + })) { + const md = await ConfigMarkdown.parse(item).catch(async (err) => { + const message = ConfigMarkdown.FrontmatterError.isInstance(err) + ? err.data.message + : `Failed to parse command ${item}` + const { Session } = await import("@/session") + void Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) + log.error("failed to load command", { command: item, err }) + return undefined + }) + if (!md) continue + + const patterns = ["/.opencode/command/", "/.opencode/commands/", "/command/", "/commands/"] + const file = rel(item, patterns) ?? path.basename(item) + const name = trim(file) + + const config = { + name, + ...md.data, + template: md.content.trim(), + } + const parsed = Info.safeParse(config) + if (parsed.success) { + result[config.name] = parsed.data + continue + } + throw new InvalidError({ path: item, issues: parsed.error.issues }, { cause: parsed.error }) + } + return result + } +} diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 97e7a662d..3922357f2 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -2,9 +2,8 @@ import { Log } from "../util" import path from "path" import { pathToFileURL } from "url" import os from "os" -import { Process } from "../util" import z from "zod" -import { mergeDeep, pipe, unique } from "remeda" +import { mergeDeep, pipe } from "remeda" import { Global } from "../global" import fsNode from "fs/promises" import { NamedError } from "@opencode-ai/shared/util/error" @@ -35,10 +34,11 @@ import { AppFileSystem } from "@opencode-ai/shared/filesystem" import { InstanceState } from "@/effect" import { Context, Duration, Effect, Exit, Fiber, Layer, Option } from "effect" import { EffectFlock } from "@opencode-ai/shared/util/effect-flock" - -import { isPathPluginSpec, parsePluginSpecifier, resolvePathPluginTarget } from "@/plugin/shared" import { InstanceRef } from "@/effect/instance-ref" import { Npm } from "@opencode-ai/shared/npm" +import { ConfigPlugin } from "./plugin" +import { ConfigManaged } from "./managed" +import { ConfigCommand } from "./command" const ModelId = z.string().meta({ $ref: "https://models.dev/model-schema.json#/$defs/Model" }) const PluginOptions = z.record(z.string(), z.unknown()) @@ -55,78 +55,6 @@ export type PluginOrigin = { const log = Log.create({ service: "config" }) -// Managed settings directory for enterprise deployments (highest priority, admin-controlled) -// These settings override all user and project settings -function systemManagedConfigDir(): string { - switch (process.platform) { - case "darwin": - return "/Library/Application Support/opencode" - case "win32": - return path.join(process.env.ProgramData || "C:\\ProgramData", "opencode") - default: - return "/etc/opencode" - } -} - -export function managedConfigDir() { - return process.env.OPENCODE_TEST_MANAGED_CONFIG_DIR || systemManagedConfigDir() -} - -const managedDir = managedConfigDir() - -const MANAGED_PLIST_DOMAIN = "ai.opencode.managed" - -// Keys injected by macOS/MDM into the managed plist that are not OpenCode config -const PLIST_META = new Set([ - "PayloadDisplayName", - "PayloadIdentifier", - "PayloadType", - "PayloadUUID", - "PayloadVersion", - "_manualProfile", -]) - -/** - * Parse raw JSON (from plutil conversion of a managed plist) into OpenCode config. - * Strips MDM metadata keys before parsing through the config schema. - * Pure function — no OS interaction, safe to unit test directly. - */ -export function parseManagedPlist(json: string, source: string): Info { - const raw = JSON.parse(json) - for (const key of Object.keys(raw)) { - if (PLIST_META.has(key)) delete raw[key] - } - return parseConfig(JSON.stringify(raw), source) -} - -/** - * Read macOS managed preferences deployed via .mobileconfig / MDM (Jamf, Kandji, etc). - * MDM-installed profiles write to /Library/Managed Preferences/ which is only writable by root. - * User-scoped plists are checked first, then machine-scoped. - */ -async function readManagedPreferences(): Promise { - if (process.platform !== "darwin") return {} - - const domain = MANAGED_PLIST_DOMAIN - const user = os.userInfo().username - const paths = [ - path.join("/Library/Managed Preferences", user, `${domain}.plist`), - path.join("/Library/Managed Preferences", `${domain}.plist`), - ] - - for (const plist of paths) { - if (!existsSync(plist)) continue - log.info("reading macOS managed preferences", { path: plist }) - const result = await Process.run(["plutil", "-convert", "json", "-o", "-", plist], { nothrow: true }) - if (result.code !== 0) { - log.warn("failed to convert managed preferences plist", { path: plist }) - continue - } - return parseManagedPlist(result.stdout.toString(), `mobileconfig:${plist}`) - } - return {} -} - // Custom merge function that concatenates array fields instead of replacing them function mergeConfigConcatArrays(target: Info, source: Info): Info { const merged = mergeDeep(target, source) @@ -154,44 +82,6 @@ function trim(file: string) { return ext.length ? file.slice(0, -ext.length) : file } -async function loadCommand(dir: string) { - const result: Record = {} - for (const item of await Glob.scan("{command,commands}/**/*.md", { - cwd: dir, - absolute: true, - dot: true, - symlink: true, - })) { - const md = await ConfigMarkdown.parse(item).catch(async (err) => { - const message = ConfigMarkdown.FrontmatterError.isInstance(err) - ? err.data.message - : `Failed to parse command ${item}` - const { Session } = await import("@/session") - void Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() }) - log.error("failed to load command", { command: item, err }) - return undefined - }) - if (!md) continue - - const patterns = ["/.opencode/command/", "/.opencode/commands/", "/command/", "/commands/"] - const file = rel(item, patterns) ?? path.basename(item) - const name = trim(file) - - const config = { - name, - ...md.data, - template: md.content.trim(), - } - const parsed = Command.safeParse(config) - if (parsed.success) { - result[config.name] = parsed.data - continue - } - throw new InvalidError({ path: item, issues: parsed.error.issues }, { cause: parsed.error }) - } - return result -} - async function loadAgent(dir: string) { const result: Record = {} @@ -267,60 +157,6 @@ async function loadMode(dir: string) { return result } -async function loadPlugin(dir: string) { - const plugins: PluginSpec[] = [] - - for (const item of await Glob.scan("{plugin,plugins}/*.{ts,js}", { - cwd: dir, - absolute: true, - dot: true, - symlink: true, - })) { - plugins.push(pathToFileURL(item).href) - } - return plugins -} - -export function pluginSpecifier(plugin: PluginSpec): string { - return Array.isArray(plugin) ? plugin[0] : plugin -} - -export function pluginOptions(plugin: PluginSpec): PluginOptions | undefined { - return Array.isArray(plugin) ? plugin[1] : undefined -} - -export async function resolvePluginSpec(plugin: PluginSpec, configFilepath: string): Promise { - const spec = pluginSpecifier(plugin) - if (!isPathPluginSpec(spec)) return plugin - - const base = path.dirname(configFilepath) - const file = (() => { - if (spec.startsWith("file://")) return spec - if (path.isAbsolute(spec) || /^[A-Za-z]:[\\/]/.test(spec)) return pathToFileURL(spec).href - return pathToFileURL(path.resolve(base, spec)).href - })() - - const resolved = await resolvePathPluginTarget(file).catch(() => file) - - if (Array.isArray(plugin)) return [resolved, plugin[1]] - return resolved -} - -export function deduplicatePluginOrigins(plugins: PluginOrigin[]): PluginOrigin[] { - const seen = new Set() - const list: PluginOrigin[] = [] - - for (const plugin of plugins.toReversed()) { - const spec = pluginSpecifier(plugin.spec) - const name = spec.startsWith("file://") ? spec : parsePluginSpecifier(spec).pkg - if (seen.has(name)) continue - seen.add(name) - list.push(plugin) - } - - return list.toReversed() -} - export const McpLocal = z .object({ type: z.literal("local").describe("Type of MCP server connection"), @@ -453,15 +289,6 @@ export const Permission = z }) export type Permission = z.infer -export const Command = z.object({ - template: z.string(), - description: z.string().optional(), - agent: z.string().optional(), - model: ModelId.optional(), - subtask: z.boolean().optional(), -}) -export type Command = z.infer - export const Skills = z.object({ paths: z.array(z.string()).optional().describe("Additional paths to skill folders"), urls: z @@ -854,7 +681,7 @@ export const Info = z logLevel: Log.Level.optional().describe("Log level"), server: Server.optional().describe("Server configuration for opencode serve and web commands"), command: z - .record(z.string(), Command) + .record(z.string(), ConfigCommand.Info) .optional() .describe("Command configuration, see https://opencode.ai/docs/commands"), skills: Skills.optional().describe("Additional skill folder paths"), @@ -1095,7 +922,7 @@ function writable(info: Info) { return next } -function parseConfig(text: string, filepath: string): Info { +export function parseConfig(text: string, filepath: string): Info { const errors: JsoncParseError[] = [] const data = parseJsonc(text, errors, { allowTrailingComma: true }) if (errors.length) { @@ -1193,7 +1020,7 @@ export const layer = Layer.effect( if (data.plugin && isFile) { const list = data.plugin for (let i = 0; i < list.length; i++) { - list[i] = yield* Effect.promise(() => resolvePluginSpec(list[i], options.path)) + list[i] = yield* Effect.promise(() => ConfigPlugin.resolvePluginSpec(list[i], options.path)) } } return data @@ -1253,7 +1080,7 @@ export const layer = Layer.effect( return yield* cachedGlobal }) - const setupConfigDir = Effect.fnUntraced(function* (dir: string) { + const ensureGitignore = Effect.fn("Config.ensureGitignore")(function* (dir: string) { const gitignore = path.join(dir, ".gitignore") const hasIgnore = yield* fs.existsSafe(gitignore) if (!hasIgnore) { @@ -1262,9 +1089,6 @@ export const layer = Layer.effect( ["node_modules", "package.json", "package-lock.json", "bun.lock", ".gitignore"].join("\n"), ) } - yield* npmSvc.install(dir, { - add: ["@opencode-ai/plugin" + (InstallationLocal ? "" : "@" + InstallationVersion)], - }) }) const loadInstanceState = Effect.fn("Config.loadInstanceState")(function* (ctx: InstanceContext) { @@ -1284,7 +1108,7 @@ export const layer = Layer.effect( const track = Effect.fnUntraced(function* (source: string, list: PluginSpec[] | undefined, kind?: PluginScope) { if (!list?.length) return const hit = kind ?? (yield* scope(source)) - const plugins = deduplicatePluginOrigins([ + const plugins = ConfigPlugin.deduplicatePluginOrigins([ ...(result.plugin_origins ?? []), ...list.map((spec) => ({ spec, source, scope: hit })), ]) @@ -1347,7 +1171,7 @@ export const layer = Layer.effect( const deps: Fiber.Fiber[] = [] - for (const dir of unique(directories)) { + for (const dir of directories) { if (dir.endsWith(".opencode") || dir === Flag.OPENCODE_CONFIG_DIR) { for (const file of ["opencode.json", "opencode.jsonc"]) { const source = path.join(dir, file) @@ -1359,24 +1183,30 @@ export const layer = Layer.effect( } } - const dep = yield* setupConfigDir(dir).pipe( - Effect.exit, - Effect.tap((exit) => - Exit.isFailure(exit) - ? Effect.sync(() => { - log.warn("background dependency install failed", { dir, error: String(exit.cause) }) - }) - : Effect.void, - ), - Effect.asVoid, - Effect.forkScoped, - ) + yield* ensureGitignore(dir).pipe(Effect.forkScoped) + + const dep = yield* npmSvc + .install(dir, { + add: ["@opencode-ai/plugin" + (InstallationLocal ? "" : "@" + InstallationVersion)], + }) + .pipe( + Effect.exit, + Effect.tap((exit) => + Exit.isFailure(exit) + ? Effect.sync(() => { + log.warn("background dependency install failed", { dir, error: String(exit.cause) }) + }) + : Effect.void, + ), + Effect.asVoid, + Effect.forkDetach, + ) deps.push(dep) - result.command = mergeDeep(result.command ?? {}, yield* Effect.promise(() => loadCommand(dir))) + result.command = mergeDeep(result.command ?? {}, yield* Effect.promise(() => ConfigCommand.load(dir))) result.agent = mergeDeep(result.agent, yield* Effect.promise(() => loadAgent(dir))) result.agent = mergeDeep(result.agent, yield* Effect.promise(() => loadMode(dir))) - const list = yield* Effect.promise(() => loadPlugin(dir)) + const list = yield* Effect.promise(() => ConfigPlugin.load(dir)) yield* track(dir, list) } @@ -1429,6 +1259,7 @@ export const layer = Layer.effect( ) } + const managedDir = ConfigManaged.managedConfigDir() if (existsSync(managedDir)) { for (const file of ["opencode.json", "opencode.jsonc"]) { const source = path.join(managedDir, file) @@ -1437,7 +1268,7 @@ export const layer = Layer.effect( } // macOS managed preferences (.mobileconfig deployed via MDM) override everything - result = mergeConfigConcatArrays(result, yield* Effect.promise(() => readManagedPreferences())) + result = mergeConfigConcatArrays(result, yield* Effect.promise(() => ConfigManaged.readManagedPreferences())) for (const [name, mode] of Object.entries(result.mode ?? {})) { result.agent = mergeDeep(result.agent ?? {}, { diff --git a/packages/opencode/src/config/index.ts b/packages/opencode/src/config/index.ts index fbcca1aa9..8380d370d 100644 --- a/packages/opencode/src/config/index.ts +++ b/packages/opencode/src/config/index.ts @@ -1,3 +1,5 @@ export * as Config from "./config" +export * as ConfigCommand from "./command" +export { ConfigManaged } from "./managed" export * as ConfigMarkdown from "./markdown" export * as ConfigPaths from "./paths" diff --git a/packages/opencode/src/config/managed.ts b/packages/opencode/src/config/managed.ts new file mode 100644 index 000000000..61c535185 --- /dev/null +++ b/packages/opencode/src/config/managed.ts @@ -0,0 +1,71 @@ +import { existsSync } from "fs" +import os from "os" +import path from "path" +import { type Info, parseConfig } from "./config" +import { Log, Process } from "../util" + +const log = Log.create({ service: "config" }) + +const MANAGED_PLIST_DOMAIN = "ai.opencode.managed" + +// Keys injected by macOS/MDM into the managed plist that are not OpenCode config +const PLIST_META = new Set([ + "PayloadDisplayName", + "PayloadIdentifier", + "PayloadType", + "PayloadUUID", + "PayloadVersion", + "_manualProfile", +]) + +function systemManagedConfigDir(): string { + switch (process.platform) { + case "darwin": + return "/Library/Application Support/opencode" + case "win32": + return path.join(process.env.ProgramData || "C:\\ProgramData", "opencode") + default: + return "/etc/opencode" + } +} + +function managedConfigDir() { + return process.env.OPENCODE_TEST_MANAGED_CONFIG_DIR || systemManagedConfigDir() +} + +function parseManagedPlist(json: string, source: string): Info { + const raw = JSON.parse(json) + for (const key of Object.keys(raw)) { + if (PLIST_META.has(key)) delete raw[key] + } + return parseConfig(JSON.stringify(raw), source) +} + +async function readManagedPreferences(): Promise { + if (process.platform !== "darwin") return {} + + const user = os.userInfo().username + const paths = [ + path.join("/Library/Managed Preferences", user, `${MANAGED_PLIST_DOMAIN}.plist`), + path.join("/Library/Managed Preferences", `${MANAGED_PLIST_DOMAIN}.plist`), + ] + + for (const plist of paths) { + if (!existsSync(plist)) continue + log.info("reading macOS managed preferences", { path: plist }) + const result = await Process.run(["plutil", "-convert", "json", "-o", "-", plist], { nothrow: true }) + if (result.code !== 0) { + log.warn("failed to convert managed preferences plist", { path: plist }) + continue + } + return parseManagedPlist(result.stdout.toString(), `mobileconfig:${plist}`) + } + + return {} +} + +export const ConfigManaged = { + managedConfigDir, + parseManagedPlist, + readManagedPreferences, +} diff --git a/packages/opencode/src/config/paths.ts b/packages/opencode/src/config/paths.ts index eeb9d62d3..fabd3fd5f 100644 --- a/packages/opencode/src/config/paths.ts +++ b/packages/opencode/src/config/paths.ts @@ -6,13 +6,14 @@ import { NamedError } from "@opencode-ai/shared/util/error" import { Filesystem } from "@/util" import { Flag } from "@/flag/flag" import { Global } from "@/global" +import { unique } from "remeda" export async function projectFiles(name: string, directory: string, worktree?: string) { return Filesystem.findUp([`${name}.json`, `${name}.jsonc`], directory, worktree, { rootFirst: true }) } export async function directories(directory: string, worktree?: string) { - return [ + return unique([ Global.Path.config, ...(!Flag.OPENCODE_DISABLE_PROJECT_CONFIG ? await Array.fromAsync( @@ -31,7 +32,7 @@ export async function directories(directory: string, worktree?: string) { }), )), ...(Flag.OPENCODE_CONFIG_DIR ? [Flag.OPENCODE_CONFIG_DIR] : []), - ] + ]) } export function fileInDirectory(dir: string, name: string) { diff --git a/packages/opencode/test/config/config.test.ts b/packages/opencode/test/config/config.test.ts index 1f3631244..303fa8ba0 100644 --- a/packages/opencode/test/config/config.test.ts +++ b/packages/opencode/test/config/config.test.ts @@ -1,7 +1,7 @@ -import { test, expect, describe, mock, afterEach, beforeEach, spyOn } from "bun:test" -import { Deferred, Effect, Fiber, Layer, Option } from "effect" +import { test, expect, describe, mock, afterEach, beforeEach } from "bun:test" +import { Effect, Layer, Option } from "effect" import { NodeFileSystem, NodePath } from "@effect/platform-node" -import { Config } from "../../src/config" +import { Config, ConfigManaged } from "../../src/config" import { EffectFlock } from "@opencode-ai/shared/util/effect-flock" import { Instance } from "../../src/project/instance" @@ -10,7 +10,7 @@ import { AccessToken, Account, AccountID, OrgID } from "../../src/account" import { AppFileSystem } from "@opencode-ai/shared/filesystem" import { Env } from "../../src/env" import { provideTmpdirInstance } from "../fixture/fixture" -import { tmpdir, tmpdirScoped } from "../fixture/fixture" +import { tmpdir } from "../fixture/fixture" import * as CrossSpawnSpawner from "../../src/effect/cross-spawn-spawner" import { testEffect } from "../lib/effect" @@ -24,7 +24,6 @@ import { pathToFileURL } from "url" import { Global } from "../../src/global" import { ProjectID } from "../../src/project/schema" import { Filesystem } from "../../src/util" -import * as Network from "../../src/util/network" import { ConfigPlugin } from "@/config/plugin" import { Npm } from "@opencode-ai/shared/npm" @@ -1860,14 +1859,14 @@ describe("resolvePluginSpec", () => { }) const file = path.join(tmp.path, "opencode.json") - const hit = await Config.resolvePluginSpec("./plugin", file) - expect(Config.pluginSpecifier(hit)).toBe(pathToFileURL(path.join(tmp.path, "plugin", "index.ts")).href) + const hit = await ConfigPlugin.resolvePluginSpec("./plugin", file) + expect(ConfigPlugin.pluginSpecifier(hit)).toBe(pathToFileURL(path.join(tmp.path, "plugin", "index.ts")).href) }) }) describe("deduplicatePluginOrigins", () => { const dedupe = (plugins: Config.PluginSpec[]) => - Config.deduplicatePluginOrigins( + ConfigPlugin.deduplicatePluginOrigins( plugins.map((spec) => ({ spec, source: "", @@ -1937,8 +1936,8 @@ describe("deduplicatePluginOrigins", () => { const config = await load() const plugins = config.plugin ?? [] - expect(plugins.some((p) => Config.pluginSpecifier(p) === "my-plugin@1.0.0")).toBe(true) - expect(plugins.some((p) => Config.pluginSpecifier(p).startsWith("file://"))).toBe(true) + expect(plugins.some((p) => ConfigPlugin.pluginSpecifier(p) === "my-plugin@1.0.0")).toBe(true) + expect(plugins.some((p) => ConfigPlugin.pluginSpecifier(p).startsWith("file://"))).toBe(true) }, }) }) @@ -2209,7 +2208,7 @@ describe("OPENCODE_CONFIG_CONTENT token substitution", () => { // parseManagedPlist unit tests — pure function, no OS interaction test("parseManagedPlist strips MDM metadata keys", async () => { - const config = await Config.parseManagedPlist( + const config = await ConfigManaged.parseManagedPlist( JSON.stringify({ PayloadDisplayName: "OpenCode Managed", PayloadIdentifier: "ai.opencode.managed.test", @@ -2231,7 +2230,7 @@ test("parseManagedPlist strips MDM metadata keys", async () => { }) test("parseManagedPlist parses server settings", async () => { - const config = await Config.parseManagedPlist( + const config = await ConfigManaged.parseManagedPlist( JSON.stringify({ $schema: "https://opencode.ai/config.json", server: { hostname: "127.0.0.1", mdns: false }, @@ -2245,7 +2244,7 @@ test("parseManagedPlist parses server settings", async () => { }) test("parseManagedPlist parses permission rules", async () => { - const config = await Config.parseManagedPlist( + const config = await ConfigManaged.parseManagedPlist( JSON.stringify({ $schema: "https://opencode.ai/config.json", permission: { @@ -2269,7 +2268,7 @@ test("parseManagedPlist parses permission rules", async () => { }) test("parseManagedPlist parses enabled_providers", async () => { - const config = await Config.parseManagedPlist( + const config = await ConfigManaged.parseManagedPlist( JSON.stringify({ $schema: "https://opencode.ai/config.json", enabled_providers: ["anthropic", "google"], @@ -2280,7 +2279,7 @@ test("parseManagedPlist parses enabled_providers", async () => { }) test("parseManagedPlist handles empty config", async () => { - const config = await Config.parseManagedPlist( + const config = await ConfigManaged.parseManagedPlist( JSON.stringify({ $schema: "https://opencode.ai/config.json" }), "test:mobileconfig", ) diff --git a/packages/shared/src/npm.ts b/packages/shared/src/npm.ts index 955cafa19..e4f42227d 100644 --- a/packages/shared/src/npm.ts +++ b/packages/shared/src/npm.ts @@ -142,7 +142,7 @@ export namespace Npm { yield* flock.acquire(`npm-install:${dir}`) - const reify = Effect.fnUntraced(function* () { + const reify = Effect.fn("Npm.reify")(function* () { const { Arborist } = yield* Effect.promise(() => import("@npmcli/arborist")) const arb = new Arborist({ path: dir, @@ -176,28 +176,31 @@ export namespace Npm { const pkgAny = pkg as any const lockAny = lock as any - const declared = new Set([ - ...Object.keys(pkgAny?.dependencies || {}), - ...Object.keys(pkgAny?.devDependencies || {}), - ...Object.keys(pkgAny?.peerDependencies || {}), - ...Object.keys(pkgAny?.optionalDependencies || {}), - ...(input?.add || []), - ]) - - const root = lockAny?.packages?.[""] || {} - const locked = new Set([ - ...Object.keys(root?.dependencies || {}), - ...Object.keys(root?.devDependencies || {}), - ...Object.keys(root?.peerDependencies || {}), - ...Object.keys(root?.optionalDependencies || {}), - ]) - - for (const name of declared) { - if (!locked.has(name)) { - yield* reify() - return + yield* Effect.gen(function* () { + const declared = new Set([ + ...Object.keys(pkgAny?.dependencies || {}), + ...Object.keys(pkgAny?.devDependencies || {}), + ...Object.keys(pkgAny?.peerDependencies || {}), + ...Object.keys(pkgAny?.optionalDependencies || {}), + ...(input?.add || []), + ]) + + const root = lockAny?.packages?.[""] || {} + const locked = new Set([ + ...Object.keys(root?.dependencies || {}), + ...Object.keys(root?.devDependencies || {}), + ...Object.keys(root?.peerDependencies || {}), + ...Object.keys(root?.optionalDependencies || {}), + ]) + + for (const name of declared) { + if (!locked.has(name)) { + yield* reify() + return + } } - } + }).pipe(Effect.withSpan("Npm.checkDirty")) + return }, Effect.scoped) const which = Effect.fn("Npm.which")(function* (pkg: string) { -- cgit v1.2.3 From c60862fc9e0a3378fe7be7c5079545c0d5c8d405 Mon Sep 17 00:00:00 2001 From: Thomas Butler <58192340+trbutler4@users.noreply.github.com> Date: Thu, 16 Apr 2026 13:21:04 -0500 Subject: fix: add missing glob dependency (#22851) --- bun.lock | 1 + nix/hashes.json | 2 +- packages/shared/package.json | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) (limited to 'packages/shared') diff --git a/bun.lock b/bun.lock index 644de37f2..3d82a64af 100644 --- a/bun.lock +++ b/bun.lock @@ -516,6 +516,7 @@ "@effect/platform-node": "catalog:", "@npmcli/arborist": "catalog:", "effect": "catalog:", + "glob": "13.0.5", "mime-types": "3.0.2", "minimatch": "10.2.5", "semver": "catalog:", diff --git a/nix/hashes.json b/nix/hashes.json index e9795e5c3..c844031be 100644 --- a/nix/hashes.json +++ b/nix/hashes.json @@ -1,6 +1,6 @@ { "nodeModules": { - "x86_64-linux": "sha256-NJAK+cPjwn+2ojDLyyDmBQyx2pD+rILetp7VCylgjek=", + "x86_64-linux": "sha256-b9tsgqQDXd2uM/j+rZnvkoXbXzB4iYCEasXsy9kgIl4=", "aarch64-linux": "sha256-q8NTtFQJoyM7TTvErGA6RtmUscxoZKD/mj9N6S5YhkA=", "aarch64-darwin": "sha256-/ccoSZNLef6j9j14HzpVqhKCR+czM3mhPKPH51mHO24=", "x86_64-darwin": "sha256-6Pd10sMHL/5ZoWNvGPwPn4/AIs1TKjt/3gFyrVpBaE0=" diff --git a/packages/shared/package.json b/packages/shared/package.json index bdfca12a9..4d10a30a3 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -26,6 +26,7 @@ "@effect/platform-node": "catalog:", "@npmcli/arborist": "catalog:", "effect": "catalog:", + "glob": "13.0.5", "mime-types": "3.0.2", "minimatch": "10.2.5", "semver": "catalog:", -- cgit v1.2.3 From 25a9de301ad83ac7f6c8ec5ed67d81ee4d2a0221 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 16 Apr 2026 16:21:47 -0400 Subject: core: eager load config on startup for better traces and refactor npm install for improved error reporting Config is now loaded eagerly during project bootstrap so users can see config loading in traces during startup. This helps diagnose configuration issues earlier in the initialization flow. NPM installation logic has been refactored with a unified reify function and improved InstallFailedError that includes both the packages being installed and the target directory. This provides users with complete context when package installations fail, making it easier to identify which dependency or project directory caused the issue. --- packages/opencode/src/effect/bootstrap-runtime.ts | 2 + packages/opencode/src/project/bootstrap.ts | 4 + packages/shared/src/npm.ts | 126 ++++++++++------------ 3 files changed, 60 insertions(+), 72 deletions(-) (limited to 'packages/shared') diff --git a/packages/opencode/src/effect/bootstrap-runtime.ts b/packages/opencode/src/effect/bootstrap-runtime.ts index 89cc07156..62b71e58b 100644 --- a/packages/opencode/src/effect/bootstrap-runtime.ts +++ b/packages/opencode/src/effect/bootstrap-runtime.ts @@ -10,9 +10,11 @@ import { File } from "@/file" import { Vcs } from "@/project" import { Snapshot } from "@/snapshot" import { Bus } from "@/bus" +import { Config } from "@/config" import * as Observability from "./observability" export const BootstrapLayer = Layer.mergeAll( + Config.defaultLayer, Plugin.defaultLayer, ShareNext.defaultLayer, Format.defaultLayer, diff --git a/packages/opencode/src/project/bootstrap.ts b/packages/opencode/src/project/bootstrap.ts index e506d2fed..a7c071a9f 100644 --- a/packages/opencode/src/project/bootstrap.ts +++ b/packages/opencode/src/project/bootstrap.ts @@ -12,9 +12,13 @@ import { Log } from "@/util" import { FileWatcher } from "@/file/watcher" import { ShareNext } from "@/share" import * as Effect from "effect/Effect" +import { Config } from "@/config" export const InstanceBootstrap = Effect.gen(function* () { Log.Default.info("bootstrapping", { directory: Instance.directory }) + // everything depends on config so eager load it for nice traces + yield* Config.Service.use((svc) => svc.get()) + // Plugin can mutate config so it has to be initialized before anything else. yield* Plugin.Service.use((svc) => svc.init()) yield* Effect.all( [ diff --git a/packages/shared/src/npm.ts b/packages/shared/src/npm.ts index e4f42227d..865e827b3 100644 --- a/packages/shared/src/npm.ts +++ b/packages/shared/src/npm.ts @@ -8,7 +8,8 @@ import { EffectFlock } from "./util/effect-flock" export namespace Npm { export class InstallFailedError extends Schema.TaggedErrorClass()("NpmInstallFailedError", { - pkg: Schema.String, + add: Schema.Array(Schema.String).pipe(Schema.optional), + dir: Schema.String, cause: Schema.optional(Schema.Defect), }) {} @@ -19,7 +20,10 @@ export namespace Npm { export interface Interface { readonly add: (pkg: string) => Effect.Effect - readonly install: (dir: string, input?: { add: string[] }) => Effect.Effect + readonly install: ( + dir: string, + input?: { add: string[] }, + ) => Effect.Effect readonly outdated: (pkg: string, cachedVersion: string) => Effect.Effect readonly which: (pkg: string) => Effect.Effect> } @@ -55,6 +59,37 @@ export namespace Npm { interface ArboristTree { edgesOut: Map } + + const reify = (input: { dir: string; add?: string[] }) => + Effect.gen(function* () { + const { Arborist } = yield* Effect.promise(() => import("@npmcli/arborist")) + const arborist = new Arborist({ + path: input.dir, + binLinks: true, + progress: false, + savePrefix: "", + ignoreScripts: true, + }) + return yield* Effect.tryPromise({ + try: () => + arborist.reify({ + add: input?.add || [], + save: true, + saveType: "prod", + }), + catch: (cause) => + new InstallFailedError({ + cause, + add: input?.add, + dir: input.dir, + }), + }) as Effect.Effect + }).pipe( + Effect.withSpan("Npm.reify", { + attributes: input, + }), + ) + export const layer = Layer.effect( Service, Effect.gen(function* () { @@ -91,45 +126,12 @@ export namespace Npm { }) const add = Effect.fn("Npm.add")(function* (pkg: string) { - const { Arborist } = yield* Effect.promise(() => import("@npmcli/arborist")) const dir = directory(pkg) yield* flock.acquire(`npm-install:${dir}`) - const arborist = new Arborist({ - path: dir, - binLinks: true, - progress: false, - savePrefix: "", - ignoreScripts: true, - }) - - const tree = yield* Effect.tryPromise({ - try: () => arborist.loadVirtual().catch(() => undefined), - catch: () => undefined, - }).pipe(Effect.orElseSucceed(() => undefined)) as Effect.Effect - - if (tree) { - const first = tree.edgesOut.values().next().value?.to - if (first) { - return resolveEntryPoint(first.name, first.path) - } - } - - const result = yield* Effect.tryPromise({ - try: () => - arborist.reify({ - add: [pkg], - save: true, - saveType: "prod", - }), - catch: (cause) => new InstallFailedError({ pkg, cause }), - }) as Effect.Effect - - const first = result.edgesOut.values().next().value?.to - if (!first) { - return yield* new InstallFailedError({ pkg }) - } - + const tree = yield* reify({ dir, add: [pkg] }) + const first = tree.edgesOut.values().next().value?.to + if (!first) return yield* new InstallFailedError({ add: [pkg], dir }) return resolveEntryPoint(first.name, first.path) }, Effect.scoped) @@ -142,41 +144,20 @@ export namespace Npm { yield* flock.acquire(`npm-install:${dir}`) - const reify = Effect.fn("Npm.reify")(function* () { - const { Arborist } = yield* Effect.promise(() => import("@npmcli/arborist")) - const arb = new Arborist({ - path: dir, - binLinks: true, - progress: false, - savePrefix: "", - ignoreScripts: true, - }) - yield* Effect.tryPromise({ - try: () => - arb - .reify({ - add: input?.add || [], - save: true, - saveType: "prod", - }) - .catch(() => {}), - catch: () => {}, - }).pipe(Effect.orElseSucceed(() => {})) - }) - - const nodeModulesExists = yield* afs.existsSafe(path.join(dir, "node_modules")) - if (!nodeModulesExists) { - yield* reify() - return - } - - const pkg = yield* afs.readJson(path.join(dir, "package.json")).pipe(Effect.orElseSucceed(() => ({}))) - const lock = yield* afs.readJson(path.join(dir, "package-lock.json")).pipe(Effect.orElseSucceed(() => ({}))) - - const pkgAny = pkg as any - const lockAny = lock as any + yield* Effect.gen(function* () { + const nodeModulesExists = yield* afs.existsSafe(path.join(dir, "node_modules")) + if (!nodeModulesExists) { + yield* reify({ add: input?.add, dir }) + return + } + }).pipe(Effect.withSpan("Npm.checkNodeModules")) yield* Effect.gen(function* () { + const pkg = yield* afs.readJson(path.join(dir, "package.json")).pipe(Effect.orElseSucceed(() => ({}))) + const lock = yield* afs.readJson(path.join(dir, "package-lock.json")).pipe(Effect.orElseSucceed(() => ({}))) + + const pkgAny = pkg as any + const lockAny = lock as any const declared = new Set([ ...Object.keys(pkgAny?.dependencies || {}), ...Object.keys(pkgAny?.devDependencies || {}), @@ -195,11 +176,12 @@ export namespace Npm { for (const name of declared) { if (!locked.has(name)) { - yield* reify() + yield* reify({ dir, add: input?.add }) return } } }).pipe(Effect.withSpan("Npm.checkDirty")) + return }, Effect.scoped) -- cgit v1.2.3 From 9f201d637034ff0a4c1e518481c76ba1a4f0eeb6 Mon Sep 17 00:00:00 2001 From: opencode Date: Thu, 16 Apr 2026 21:54:54 +0000 Subject: release: v1.4.7 --- bun.lock | 32 ++++++++++++++++---------------- packages/app/package.json | 2 +- packages/console/app/package.json | 2 +- packages/console/core/package.json | 2 +- packages/console/function/package.json | 2 +- packages/console/mail/package.json | 2 +- packages/desktop-electron/package.json | 2 +- packages/desktop/package.json | 2 +- packages/enterprise/package.json | 2 +- packages/extensions/zed/extension.toml | 12 ++++++------ packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/plugin/package.json | 2 +- packages/sdk/js/package.json | 2 +- packages/shared/package.json | 2 +- packages/slack/package.json | 2 +- packages/ui/package.json | 2 +- packages/web/package.json | 2 +- sdks/vscode/package.json | 2 +- 19 files changed, 39 insertions(+), 39 deletions(-) (limited to 'packages/shared') diff --git a/bun.lock b/bun.lock index e236f3f49..63232cb29 100644 --- a/bun.lock +++ b/bun.lock @@ -29,7 +29,7 @@ }, "packages/app": { "name": "@opencode-ai/app", - "version": "1.4.6", + "version": "1.4.7", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -83,7 +83,7 @@ }, "packages/console/app": { "name": "@opencode-ai/console-app", - "version": "1.4.6", + "version": "1.4.7", "dependencies": { "@cloudflare/vite-plugin": "1.15.2", "@ibm/plex": "6.4.1", @@ -117,7 +117,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.4.6", + "version": "1.4.7", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -144,7 +144,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.4.6", + "version": "1.4.7", "dependencies": { "@ai-sdk/anthropic": "3.0.64", "@ai-sdk/openai": "3.0.48", @@ -168,7 +168,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.4.6", + "version": "1.4.7", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -192,7 +192,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.4.6", + "version": "1.4.7", "dependencies": { "@opencode-ai/app": "workspace:*", "@opencode-ai/ui": "workspace:*", @@ -225,7 +225,7 @@ }, "packages/desktop-electron": { "name": "@opencode-ai/desktop-electron", - "version": "1.4.6", + "version": "1.4.7", "dependencies": { "effect": "catalog:", "electron-context-menu": "4.1.2", @@ -268,7 +268,7 @@ }, "packages/enterprise": { "name": "@opencode-ai/enterprise", - "version": "1.4.6", + "version": "1.4.7", "dependencies": { "@opencode-ai/shared": "workspace:*", "@opencode-ai/ui": "workspace:*", @@ -297,7 +297,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.4.6", + "version": "1.4.7", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "catalog:", @@ -313,7 +313,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.4.6", + "version": "1.4.7", "bin": { "opencode": "./bin/opencode", }, @@ -458,7 +458,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.4.6", + "version": "1.4.7", "dependencies": { "@opencode-ai/sdk": "workspace:*", "effect": "catalog:", @@ -493,7 +493,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.4.6", + "version": "1.4.7", "dependencies": { "cross-spawn": "catalog:", }, @@ -508,7 +508,7 @@ }, "packages/shared": { "name": "@opencode-ai/shared", - "version": "1.4.6", + "version": "1.4.7", "bin": { "opencode": "./bin/opencode", }, @@ -532,7 +532,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.4.6", + "version": "1.4.7", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -567,7 +567,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.4.6", + "version": "1.4.7", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -616,7 +616,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.4.6", + "version": "1.4.7", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/app/package.json b/packages/app/package.json index 483c71dc5..2941637d0 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/app", - "version": "1.4.6", + "version": "1.4.7", "description": "", "type": "module", "exports": { diff --git a/packages/console/app/package.json b/packages/console/app/package.json index 062114eeb..8783f3fd0 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-app", - "version": "1.4.6", + "version": "1.4.7", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/console/core/package.json b/packages/console/core/package.json index 760174cf0..cdefd0e60 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "1.4.6", + "version": "1.4.7", "private": true, "type": "module", "license": "MIT", diff --git a/packages/console/function/package.json b/packages/console/function/package.json index 840f32742..898c540ba 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.4.6", + "version": "1.4.7", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index 5aa94224e..46ff28b7d 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.4.6", + "version": "1.4.7", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop-electron/package.json b/packages/desktop-electron/package.json index cc0bad7d7..e1f69b5b2 100644 --- a/packages/desktop-electron/package.json +++ b/packages/desktop-electron/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/desktop-electron", "private": true, - "version": "1.4.6", + "version": "1.4.7", "type": "module", "license": "MIT", "homepage": "https://opencode.ai", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 3fd02ad88..d8eea4ea3 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/desktop", "private": true, - "version": "1.4.6", + "version": "1.4.7", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/enterprise/package.json b/packages/enterprise/package.json index 3c4a835f3..12a72e647 100644 --- a/packages/enterprise/package.json +++ b/packages/enterprise/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/enterprise", - "version": "1.4.6", + "version": "1.4.7", "private": true, "type": "module", "license": "MIT", diff --git a/packages/extensions/zed/extension.toml b/packages/extensions/zed/extension.toml index d6be9c6b6..d164534cf 100644 --- a/packages/extensions/zed/extension.toml +++ b/packages/extensions/zed/extension.toml @@ -1,7 +1,7 @@ id = "opencode" name = "OpenCode" description = "The open source coding agent." -version = "1.4.6" +version = "1.4.7" schema_version = 1 authors = ["Anomaly"] repository = "https://github.com/anomalyco/opencode" @@ -11,26 +11,26 @@ name = "OpenCode" icon = "./icons/opencode.svg" [agent_servers.opencode.targets.darwin-aarch64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.6/opencode-darwin-arm64.zip" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.7/opencode-darwin-arm64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.darwin-x86_64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.6/opencode-darwin-x64.zip" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.7/opencode-darwin-x64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-aarch64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.6/opencode-linux-arm64.tar.gz" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.7/opencode-linux-arm64.tar.gz" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-x86_64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.6/opencode-linux-x64.tar.gz" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.7/opencode-linux-x64.tar.gz" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.windows-x86_64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.6/opencode-windows-x64.zip" +archive = "https://github.com/anomalyco/opencode/releases/download/v1.4.7/opencode-windows-x64.zip" cmd = "./opencode.exe" args = ["acp"] diff --git a/packages/function/package.json b/packages/function/package.json index 941f093fc..36a9ddc32 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.4.6", + "version": "1.4.7", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 3a98d0eb9..1dabd91b8 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.4.6", + "version": "1.4.7", "name": "opencode", "type": "module", "license": "MIT", diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 76fe2e862..6f9a0ea1d 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "1.4.6", + "version": "1.4.7", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 49c441600..53a589314 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "1.4.6", + "version": "1.4.7", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/shared/package.json b/packages/shared/package.json index 4d10a30a3..9dec6bdb6 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.4.6", + "version": "1.4.7", "name": "@opencode-ai/shared", "type": "module", "license": "MIT", diff --git a/packages/slack/package.json b/packages/slack/package.json index 9239ac572..a23500241 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.4.6", + "version": "1.4.7", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/ui/package.json b/packages/ui/package.json index 21974e3ec..cd559041c 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.4.6", + "version": "1.4.7", "type": "module", "license": "MIT", "exports": { diff --git a/packages/web/package.json b/packages/web/package.json index 5ca11ea29..a53ef5193 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -2,7 +2,7 @@ "name": "@opencode-ai/web", "type": "module", "license": "MIT", - "version": "1.4.6", + "version": "1.4.7", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 634583bd3..c499f679f 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "1.4.6", + "version": "1.4.7", "publisher": "sst-dev", "repository": { "type": "git", -- cgit v1.2.3