chore: merge dev into v2 (#34788)

Co-authored-by: Brendan Allan <14191578+Brendonovich@users.noreply.github.com>
Co-authored-by: Kit Langton <kit.langton@gmail.com>
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: Affan Ali <93028901+affanali2k3@users.noreply.github.com>
Co-authored-by: affanali2k3 <affanalikhanxx@gmail.com>
Co-authored-by: Frank <frank@anoma.ly>
Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: 𝓛𝓲𝓽𝓽𝓵𝓮 𝓕𝓻𝓪𝓷𝓴 <little-frank@opencord.local>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: Jay V <air@live.ca>
Co-authored-by: Dax Raad <d@ironbay.co>
Co-authored-by: Aarav Sareen <96787824+arvsrn@users.noreply.github.com>
Co-authored-by: OpeOginni <107570612+OpeOginni@users.noreply.github.com>
Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com>
Co-authored-by: Ben Guthrie <benjee.012@gmail.com>
Co-authored-by: Dax <mail@thdxr.com>
Co-authored-by: Filip <34747899+neriousy@users.noreply.github.com>
Co-authored-by: Max Anderson <max.a.anderson95@gmail.com>
Co-authored-by: Brendan Allan <git@brendonovich.dev>
Co-authored-by: Jack <jack@anoma.ly>
Co-authored-by: Shoubhit Dash <shoubhit2005@gmail.com>
Co-authored-by: Dustin Deus <deusdustin@gmail.com>
Co-authored-by: starptech <starptech@starptechs-MBP.fritz.box>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
Co-authored-by: usrnk1 <7547651+usrnk1@users.noreply.github.com>
Co-authored-by: Jay <53023+jayair@users.noreply.github.com>
Co-authored-by: runvip <164729189+runvip@users.noreply.github.com>
Co-authored-by: opencode <opencode@sst.dev>
Co-authored-by: Julian Coy <julian@ex-machina.co>
Co-authored-by: Vladimir Glafirov <vglafirov@gitlab.com>
This commit is contained in:
James Long
2026-07-01 17:12:00 -04:00
committed by GitHub
parent 23f27d63b6
commit 5e2b06579a
590 changed files with 15772 additions and 5530 deletions
@@ -1,11 +1,11 @@
import { expect } from "bun:test"
import { Effect, Layer } from "effect"
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { Effect } from "effect"
import { Config } from "@/config/config"
import { Agent as AgentSvc } from "../../src/agent/agent"
import { testEffect } from "../lib/effect"
const it = testEffect(Layer.mergeAll(Config.defaultLayer, AgentSvc.defaultLayer, CrossSpawnSpawner.defaultLayer))
const it = testEffect(LayerNode.compile(LayerNode.group([Config.node, AgentSvc.node])))
it.instance(
"agent color parsed from project config",
+20 -34
View File
@@ -1,13 +1,14 @@
import { test, expect, describe, afterEach, beforeEach, spyOn } from "bun:test"
import { ConfigV1 } from "@opencode-ai/core/v1/config/config"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { httpClient } from "@opencode-ai/core/effect/app-node-platform"
import { Cause, Effect, Exit, Layer, Option } from "effect"
import { NamedError } from "@opencode-ai/core/util/error"
import { FetchHttpClient, HttpClient, HttpClientResponse } from "effect/unstable/http"
import { NodeFileSystem, NodePath } from "@effect/platform-node"
import { Config } from "@/config/config"
import { ConfigManaged } from "@/config/managed"
import { ConfigParse } from "../../src/config/parse"
import { EffectFlock } from "@opencode-ai/core/util/effect-flock"
import { Npm } from "@opencode-ai/core/npm"
import { InstanceRef } from "../../src/effect/instance-ref"
import type { InstanceContext } from "../../src/project/instance-context"
@@ -41,13 +42,6 @@ import { AccountTest } from "../fake/account"
import { AuthTest } from "../fake/auth"
import { NpmTest } from "../fake/npm"
/** Infra layer that provides FileSystem, Path, ChildProcessSpawner for test fixtures */
const infra = CrossSpawnSpawner.defaultLayer.pipe(
Layer.provideMerge(Layer.mergeAll(NodeFileSystem.layer, NodePath.layer)),
)
const testFlock = EffectFlock.defaultLayer
const unexpectedHttp = HttpClient.make((request) =>
Effect.die(`unexpected http request: ${request.method} ${request.url}`),
)
@@ -104,16 +98,12 @@ const configLayer = (
client?: HttpClient.HttpClient
} = {},
) =>
Config.layer.pipe(
Layer.provide(testFlock),
Layer.provide(Env.defaultLayer),
Layer.provide(options.auth ?? AuthTest.empty),
Layer.provide(options.account ?? AccountTest.empty),
Layer.provideMerge(infra),
Layer.provide(NpmTest.noop),
Layer.provide(Layer.succeed(HttpClient.HttpClient, options.client ?? unexpectedHttp)),
Layer.provideMerge(FSUtil.defaultLayer),
)
LayerNode.compile(LayerNode.group([Config.node, FSUtil.node, Env.node, CrossSpawnSpawner.node]), [
[Auth.node, options.auth ?? AuthTest.empty],
[Account.node, options.account ?? AccountTest.empty],
[Npm.node, NpmTest.noop],
[httpClient, Layer.succeed(HttpClient.HttpClient, options.client ?? unexpectedHttp)],
])
const layer = configLayer()
@@ -171,7 +161,7 @@ const withInstanceDir = <A, E, R>(dir: string, effect: Effect.Effect<A, E, R>) =
Effect.provideService(TestInstance, { directory: dir }),
provideInstanceEffect(dir),
Effect.provide(testInstanceStoreLayer),
Effect.provide(CrossSpawnSpawner.defaultLayer),
Effect.provide(LayerNode.compile(CrossSpawnSpawner.node)),
)
const withGlobalConfigDir = <A, E, R>(dir: string, effect: Effect.Effect<A, E, R>) =>
@@ -325,7 +315,7 @@ it.effect("creates global jsonc config with schema when no global configs exist"
const content = yield* FSUtil.use.readFileString(path.join(dir, "opencode.jsonc"))
expect(content).toContain('"$schema": "https://opencode.ai/config.json"')
}).pipe(Effect.provide(testInstanceStoreLayer), Effect.provide(CrossSpawnSpawner.defaultLayer)),
}).pipe(Effect.provide(testInstanceStoreLayer), Effect.provide(LayerNode.compile(CrossSpawnSpawner.node))),
),
)
@@ -340,7 +330,7 @@ it.effect("does not create global config when OPENCODE_CONFIG_DIR is set", () =>
yield* Config.use.get().pipe(provideInstanceEffect(dir))
expect(yield* FSUtil.use.existsSafe(path.join(dir, "opencode.jsonc"))).toBe(false)
}).pipe(Effect.provide(testInstanceStoreLayer), Effect.provide(CrossSpawnSpawner.defaultLayer)),
}).pipe(Effect.provide(testInstanceStoreLayer), Effect.provide(LayerNode.compile(CrossSpawnSpawner.node))),
),
)
}),
@@ -930,7 +920,7 @@ it.effect("does not try to install dependencies in read-only OPENCODE_CONFIG_DIR
yield* Effect.addFinalizer(() => FSUtil.use.chmod(readonly, 0o755).pipe(Effect.ignore))
yield* withProcessEnv("OPENCODE_CONFIG_DIR", readonly, Config.use.get().pipe(provideInstanceEffect(dir)))
}).pipe(Effect.provide(testInstanceStoreLayer), Effect.provide(CrossSpawnSpawner.defaultLayer)),
}).pipe(Effect.provide(testInstanceStoreLayer), Effect.provide(LayerNode.compile(CrossSpawnSpawner.node))),
)
it.effect("installs dependencies in writable OPENCODE_CONFIG_DIR", () =>
@@ -948,7 +938,7 @@ it.effect("installs dependencies in writable OPENCODE_CONFIG_DIR", () =>
)
expect(yield* FSUtil.use.readFileString(path.join(configDir, ".gitignore"))).toContain("package-lock.json")
}).pipe(Effect.provide(testInstanceStoreLayer), Effect.provide(CrossSpawnSpawner.defaultLayer)),
}).pipe(Effect.provide(testInstanceStoreLayer), Effect.provide(LayerNode.compile(CrossSpawnSpawner.node))),
)
// Note: deduplication and serialization of npm installs is now handled by the
@@ -1533,16 +1523,12 @@ test("remote well-known config can use FetchHttpClient layer", async () => {
Effect.scoped,
Effect.provide(
Layer.mergeAll(
Config.layer.pipe(
Layer.provide(testFlock),
Layer.provide(FSUtil.defaultLayer),
Layer.provide(Env.defaultLayer),
Layer.provide(wellKnownAuth(server.url.origin)),
Layer.provide(AccountTest.empty),
Layer.provideMerge(infra),
Layer.provide(NpmTest.noop),
Layer.provide(FetchHttpClient.layer),
),
LayerNode.compile(LayerNode.group([Config.node, FSUtil.node, Env.node, CrossSpawnSpawner.node]), [
[Auth.node, wellKnownAuth(server.url.origin)],
[Account.node, AccountTest.empty],
[Npm.node, NpmTest.noop],
[httpClient, FetchHttpClient.layer],
]),
testInstanceStoreLayer,
),
),
+9 -3
View File
@@ -1,6 +1,8 @@
import { expect } from "bun:test"
import path from "path"
import { pathToFileURL } from "url"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { Effect, Layer } from "effect"
import { FSUtil } from "@opencode-ai/core/fs-util"
import { Global } from "@opencode-ai/core/global"
@@ -11,7 +13,7 @@ import { TuiConfig } from "../../src/config/tui"
import { TestInstance } from "../fixture/fixture"
import { testEffect } from "../lib/effect"
const it = testEffect(Layer.mergeAll(Config.defaultLayer, FSUtil.defaultLayer))
const it = testEffect(LayerNode.compile(LayerNode.group([Config.node, FSUtil.node])))
const winIt = process.platform === "win32" ? it.instance : it.instance.skip
const globalConfigFiles = ["opencode.json", "opencode.jsonc", "tui.json", "tui.jsonc"].map((file) =>
@@ -69,12 +71,16 @@ const withPlatform = <A, E, R>(platform: typeof process.platform, self: Effect.E
const getTuiConfig = (directory: string) =>
TuiConfig.Service.use((svc) => svc.get()).pipe(
Effect.provide(TuiConfig.defaultLayer.pipe(Layer.provide(Layer.succeed(CurrentWorkingDirectory, directory)))),
Effect.provide(
AppNodeBuilder.build(TuiConfig.node).pipe(Layer.provide(Layer.succeed(CurrentWorkingDirectory, directory))),
),
)
const getTuiPluginOrigins = (directory: string) =>
TuiConfig.Service.use((svc) => svc.pluginOrigins()).pipe(
Effect.provide(TuiConfig.defaultLayer.pipe(Layer.provide(Layer.succeed(CurrentWorkingDirectory, directory)))),
Effect.provide(
AppNodeBuilder.build(TuiConfig.node).pipe(Layer.provide(Layer.succeed(CurrentWorkingDirectory, directory))),
),
)
it.instance("keeps server and tui plugin merge semantics aligned", () =>