refactor(opencode): roll out serviceUse proxy across 14 services + tests (#28576)
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import { Cache, Clock, Duration, Effect, Layer, Option, Schema, SchemaGetter, Context } from "effect"
|
import { Cache, Clock, Duration, Effect, Layer, Option, Schema, SchemaGetter, Context } from "effect"
|
||||||
|
import { serviceUse } from "@/effect/service-use"
|
||||||
import {
|
import {
|
||||||
FetchHttpClient,
|
FetchHttpClient,
|
||||||
HttpClient,
|
HttpClient,
|
||||||
@@ -181,6 +182,8 @@ export interface Interface {
|
|||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/Account") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/Account") {}
|
||||||
|
|
||||||
|
export const use = serviceUse(Service)
|
||||||
|
|
||||||
export const layer: Layer.Layer<Service, never, AccountRepo.Service | HttpClient.HttpClient> = Layer.effect(
|
export const layer: Layer.Layer<Service, never, AccountRepo.Service | HttpClient.HttpClient> = Layer.effect(
|
||||||
Service,
|
Service,
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { eq } from "drizzle-orm"
|
import { eq } from "drizzle-orm"
|
||||||
|
import { serviceUse } from "@/effect/service-use"
|
||||||
import { Effect, Layer, Option, Schema, Context } from "effect"
|
import { Effect, Layer, Option, Schema, Context } from "effect"
|
||||||
|
|
||||||
import { Database } from "@/storage/db"
|
import { Database } from "@/storage/db"
|
||||||
@@ -38,6 +39,8 @@ export interface Interface {
|
|||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/AccountRepo") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/AccountRepo") {}
|
||||||
|
|
||||||
|
export const use = serviceUse(Service)
|
||||||
|
|
||||||
export const layer: Layer.Layer<Service> = Layer.effect(
|
export const layer: Layer.Layer<Service> = Layer.effect(
|
||||||
Service,
|
Service,
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Config } from "@/config/config"
|
import { Config } from "@/config/config"
|
||||||
|
import { serviceUse } from "@/effect/service-use"
|
||||||
import { Provider } from "@/provider/provider"
|
import { Provider } from "@/provider/provider"
|
||||||
import { ModelID, ProviderID } from "../provider/schema"
|
import { ModelID, ProviderID } from "../provider/schema"
|
||||||
import { generateObject, streamObject, type ModelMessage } from "ai"
|
import { generateObject, streamObject, type ModelMessage } from "ai"
|
||||||
@@ -76,6 +77,8 @@ type State = Omit<Interface, "generate">
|
|||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/Agent") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/Agent") {}
|
||||||
|
|
||||||
|
export const use = serviceUse(Service)
|
||||||
|
|
||||||
export const layer = Layer.effect(
|
export const layer = Layer.effect(
|
||||||
Service,
|
Service,
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import * as Log from "@opencode-ai/core/util/log"
|
import * as Log from "@opencode-ai/core/util/log"
|
||||||
|
import { serviceUse } from "@/effect/service-use"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { pathToFileURL } from "url"
|
import { pathToFileURL } from "url"
|
||||||
import os from "os"
|
import os from "os"
|
||||||
@@ -319,6 +320,8 @@ export interface Interface {
|
|||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/Config") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/Config") {}
|
||||||
|
|
||||||
|
export const use = serviceUse(Service)
|
||||||
|
|
||||||
function globalConfigFile() {
|
function globalConfigFile() {
|
||||||
const candidates = ["opencode.jsonc", "opencode.json", "config.json"].map((file) =>
|
const candidates = ["opencode.jsonc", "opencode.json", "config.json"].map((file) =>
|
||||||
path.join(Global.Path.config, file),
|
path.join(Global.Path.config, file),
|
||||||
|
|||||||
Vendored
+3
@@ -1,4 +1,5 @@
|
|||||||
import { Context, Effect, Layer } from "effect"
|
import { Context, Effect, Layer } from "effect"
|
||||||
|
import { serviceUse } from "@/effect/service-use"
|
||||||
import { InstanceState } from "@/effect/instance-state"
|
import { InstanceState } from "@/effect/instance-state"
|
||||||
|
|
||||||
type State = Record<string, string | undefined>
|
type State = Record<string, string | undefined>
|
||||||
@@ -12,6 +13,8 @@ export interface Interface {
|
|||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/Env") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/Env") {}
|
||||||
|
|
||||||
|
export const use = serviceUse(Service)
|
||||||
|
|
||||||
export const layer = Layer.effect(
|
export const layer = Layer.effect(
|
||||||
Service,
|
Service,
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { BusEvent } from "@/bus/bus-event"
|
import { BusEvent } from "@/bus/bus-event"
|
||||||
|
import { serviceUse } from "@/effect/service-use"
|
||||||
import { InstanceState } from "@/effect/instance-state"
|
import { InstanceState } from "@/effect/instance-state"
|
||||||
|
|
||||||
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
||||||
@@ -326,6 +327,8 @@ export interface Interface {
|
|||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/File") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/File") {}
|
||||||
|
|
||||||
|
export const use = serviceUse(Service)
|
||||||
|
|
||||||
export const layer = Layer.effect(
|
export const layer = Layer.effect(
|
||||||
Service,
|
Service,
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import path from "path"
|
import path from "path"
|
||||||
|
import { serviceUse } from "@/effect/service-use"
|
||||||
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
||||||
import { Cause, Context, Effect, Fiber, Layer, Queue, Schema, Stream } from "effect"
|
import { Cause, Context, Effect, Fiber, Layer, Queue, Schema, Stream } from "effect"
|
||||||
import type { PlatformError } from "effect/PlatformError"
|
import type { PlatformError } from "effect/PlatformError"
|
||||||
@@ -141,6 +142,8 @@ export interface Interface {
|
|||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/Ripgrep") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/Ripgrep") {}
|
||||||
|
|
||||||
|
export const use = serviceUse(Service)
|
||||||
|
|
||||||
function env() {
|
function env() {
|
||||||
const env = sanitizedProcessEnv()
|
const env = sanitizedProcessEnv()
|
||||||
delete env.RIPGREP_CONFIG_PATH
|
delete env.RIPGREP_CONFIG_PATH
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Effect, Layer, Context, Schema } from "effect"
|
import { Effect, Layer, Context, Schema } from "effect"
|
||||||
|
import { serviceUse } from "@/effect/service-use"
|
||||||
import { ChildProcess } from "effect/unstable/process"
|
import { ChildProcess } from "effect/unstable/process"
|
||||||
import { AppProcess } from "@opencode-ai/core/process"
|
import { AppProcess } from "@opencode-ai/core/process"
|
||||||
import { InstanceState } from "@/effect/instance-state"
|
import { InstanceState } from "@/effect/instance-state"
|
||||||
@@ -27,6 +28,8 @@ export interface Interface {
|
|||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/Format") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/Format") {}
|
||||||
|
|
||||||
|
export const use = serviceUse(Service)
|
||||||
|
|
||||||
export const layer = Layer.effect(
|
export const layer = Layer.effect(
|
||||||
Service,
|
Service,
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Effect, Layer, Schema, Context, Stream } from "effect"
|
import { Effect, Layer, Schema, Context, Stream } from "effect"
|
||||||
|
import { serviceUse } from "@/effect/service-use"
|
||||||
import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
|
import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
|
||||||
import { withTransientReadRetry } from "@/util/effect-http-client"
|
import { withTransientReadRetry } from "@/util/effect-http-client"
|
||||||
import { errorMessage } from "@/util/error"
|
import { errorMessage } from "@/util/error"
|
||||||
@@ -89,6 +90,8 @@ export interface Interface {
|
|||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/Installation") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/Installation") {}
|
||||||
|
|
||||||
|
export const use = serviceUse(Service)
|
||||||
|
|
||||||
export const layer: Layer.Layer<Service, never, HttpClient.HttpClient | AppProcess.Service> = Layer.effect(
|
export const layer: Layer.Layer<Service, never, HttpClient.HttpClient | AppProcess.Service> = Layer.effect(
|
||||||
Service,
|
Service,
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import path from "path"
|
import path from "path"
|
||||||
|
import { serviceUse } from "@/effect/service-use"
|
||||||
import { Global } from "@opencode-ai/core/global"
|
import { Global } from "@opencode-ai/core/global"
|
||||||
import { Effect, Layer, Context, Option, Schema } from "effect"
|
import { Effect, Layer, Context, Option, Schema } from "effect"
|
||||||
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
||||||
@@ -51,6 +52,8 @@ export interface Interface {
|
|||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/McpAuth") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/McpAuth") {}
|
||||||
|
|
||||||
|
export const use = serviceUse(Service)
|
||||||
|
|
||||||
export const layer = Layer.effect(
|
export const layer = Layer.effect(
|
||||||
Service,
|
Service,
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { dynamicTool, type Tool, jsonSchema, type JSONSchema7 } from "ai"
|
import { dynamicTool, type Tool, jsonSchema, type JSONSchema7 } from "ai"
|
||||||
|
import { serviceUse } from "@/effect/service-use"
|
||||||
import { Client } from "@modelcontextprotocol/sdk/client/index.js"
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js"
|
||||||
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"
|
||||||
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"
|
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"
|
||||||
@@ -264,6 +265,8 @@ export interface Interface {
|
|||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/MCP") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/MCP") {}
|
||||||
|
|
||||||
|
export const use = serviceUse(Service)
|
||||||
|
|
||||||
export const layer = Layer.effect(
|
export const layer = Layer.effect(
|
||||||
Service,
|
Service,
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { GlobalBus } from "@/bus/global"
|
import { GlobalBus } from "@/bus/global"
|
||||||
|
import { serviceUse } from "@/effect/service-use"
|
||||||
import { WorkspaceContext } from "@/control-plane/workspace-context"
|
import { WorkspaceContext } from "@/control-plane/workspace-context"
|
||||||
import { InstanceRef } from "@/effect/instance-ref"
|
import { InstanceRef } from "@/effect/instance-ref"
|
||||||
import { disposeInstance as runDisposers } from "@/effect/instance-registry"
|
import { disposeInstance as runDisposers } from "@/effect/instance-registry"
|
||||||
@@ -24,6 +25,8 @@ export interface Interface {
|
|||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/InstanceStore") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/InstanceStore") {}
|
||||||
|
|
||||||
|
export const use = serviceUse(Service)
|
||||||
|
|
||||||
interface Entry {
|
interface Entry {
|
||||||
readonly deferred: Deferred.Deferred<InstanceContext>
|
readonly deferred: Deferred.Deferred<InstanceContext>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { AuthOAuthResult, Hooks } from "@opencode-ai/plugin"
|
import type { AuthOAuthResult, Hooks } from "@opencode-ai/plugin"
|
||||||
|
import { serviceUse } from "@/effect/service-use"
|
||||||
import { Auth } from "@/auth"
|
import { Auth } from "@/auth"
|
||||||
import { InstanceState } from "@/effect/instance-state"
|
import { InstanceState } from "@/effect/instance-state"
|
||||||
import { optionalOmitUndefined } from "@opencode-ai/core/schema"
|
import { optionalOmitUndefined } from "@opencode-ai/core/schema"
|
||||||
@@ -102,6 +103,8 @@ interface State {
|
|||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/ProviderAuth") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/ProviderAuth") {}
|
||||||
|
|
||||||
|
export const use = serviceUse(Service)
|
||||||
|
|
||||||
export const layer: Layer.Layer<Service, never, Auth.Service | Plugin.Service> = Layer.effect(
|
export const layer: Layer.Layer<Service, never, Auth.Service | Plugin.Service> = Layer.effect(
|
||||||
Service,
|
Service,
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type * as SDK from "@opencode-ai/sdk/v2"
|
import type * as SDK from "@opencode-ai/sdk/v2"
|
||||||
|
import { serviceUse } from "@/effect/service-use"
|
||||||
import { Effect, Exit, Layer, Option, Schema, Scope, Context, Stream } from "effect"
|
import { Effect, Exit, Layer, Option, Schema, Scope, Context, Stream } from "effect"
|
||||||
import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
|
import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
|
||||||
import { Account } from "@/account/account"
|
import { Account } from "@/account/account"
|
||||||
@@ -76,6 +77,8 @@ export interface Interface {
|
|||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/ShareNext") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/ShareNext") {}
|
||||||
|
|
||||||
|
export const use = serviceUse(Service)
|
||||||
|
|
||||||
const db = <T>(fn: (d: Parameters<typeof Database.use>[0] extends (trx: infer D) => any ? D : never) => T) =>
|
const db = <T>(fn: (d: Parameters<typeof Database.use>[0] extends (trx: infer D) => any ? D : never) => T) =>
|
||||||
Effect.sync(() => Database.use(fn))
|
Effect.sync(() => Database.use(fn))
|
||||||
|
|
||||||
|
|||||||
@@ -18,14 +18,14 @@ const it = testEffect(Layer.merge(AccountRepo.layer, truncate))
|
|||||||
|
|
||||||
it.live("list returns empty when no accounts exist", () =>
|
it.live("list returns empty when no accounts exist", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const accounts = yield* AccountRepo.Service.use((r) => r.list())
|
const accounts = yield* AccountRepo.use.list()
|
||||||
expect(accounts).toEqual([])
|
expect(accounts).toEqual([])
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
it.live("active returns none when no accounts exist", () =>
|
it.live("active returns none when no accounts exist", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const active = yield* AccountRepo.Service.use((r) => r.active())
|
const active = yield* AccountRepo.use.active()
|
||||||
expect(Option.isNone(active)).toBe(true)
|
expect(Option.isNone(active)).toBe(true)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -45,13 +45,13 @@ it.live("persistAccount inserts and getRow retrieves", () =>
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
|
const row = yield* AccountRepo.use.getRow(id)
|
||||||
expect(Option.isSome(row)).toBe(true)
|
expect(Option.isSome(row)).toBe(true)
|
||||||
const value = Option.getOrThrow(row)
|
const value = Option.getOrThrow(row)
|
||||||
expect(value.id).toBe(AccountID.make("user-1"))
|
expect(value.id).toBe(AccountID.make("user-1"))
|
||||||
expect(value.email).toBe("test@example.com")
|
expect(value.email).toBe("test@example.com")
|
||||||
|
|
||||||
const active = yield* AccountRepo.Service.use((r) => r.active())
|
const active = yield* AccountRepo.use.active()
|
||||||
expect(Option.getOrThrow(active).active_org_id).toBe(OrgID.make("org-1"))
|
expect(Option.getOrThrow(active).active_org_id).toBe(OrgID.make("org-1"))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -72,9 +72,9 @@ it.live("persistAccount normalizes trailing slashes in stored server URLs", () =
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
|
const row = yield* AccountRepo.use.getRow(id)
|
||||||
const active = yield* AccountRepo.Service.use((r) => r.active())
|
const active = yield* AccountRepo.use.active()
|
||||||
const list = yield* AccountRepo.Service.use((r) => r.list())
|
const list = yield* AccountRepo.use.list()
|
||||||
|
|
||||||
expect(Option.getOrThrow(row).url).toBe("https://control.example.com")
|
expect(Option.getOrThrow(row).url).toBe("https://control.example.com")
|
||||||
expect(Option.getOrThrow(active).url).toBe("https://control.example.com")
|
expect(Option.getOrThrow(active).url).toBe("https://control.example.com")
|
||||||
@@ -112,7 +112,7 @@ it.live("persistAccount sets the active account and org", () =>
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Last persisted account is active with its org
|
// Last persisted account is active with its org
|
||||||
const active = yield* AccountRepo.Service.use((r) => r.active())
|
const active = yield* AccountRepo.use.active()
|
||||||
expect(Option.isSome(active)).toBe(true)
|
expect(Option.isSome(active)).toBe(true)
|
||||||
expect(Option.getOrThrow(active).id).toBe(AccountID.make("user-2"))
|
expect(Option.getOrThrow(active).id).toBe(AccountID.make("user-2"))
|
||||||
expect(Option.getOrThrow(active).active_org_id).toBe(OrgID.make("org-2"))
|
expect(Option.getOrThrow(active).active_org_id).toBe(OrgID.make("org-2"))
|
||||||
@@ -148,7 +148,7 @@ it.live("list returns all accounts", () =>
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
const accounts = yield* AccountRepo.Service.use((r) => r.list())
|
const accounts = yield* AccountRepo.use.list()
|
||||||
expect(accounts.length).toBe(2)
|
expect(accounts.length).toBe(2)
|
||||||
expect(accounts.map((a) => a.email).sort()).toEqual(["a@example.com", "b@example.com"])
|
expect(accounts.map((a) => a.email).sort()).toEqual(["a@example.com", "b@example.com"])
|
||||||
}),
|
}),
|
||||||
@@ -170,9 +170,9 @@ it.live("remove deletes an account", () =>
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
yield* AccountRepo.Service.use((r) => r.remove(id))
|
yield* AccountRepo.use.remove(id)
|
||||||
|
|
||||||
const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
|
const row = yield* AccountRepo.use.getRow(id)
|
||||||
expect(Option.isNone(row)).toBe(true)
|
expect(Option.isNone(row)).toBe(true)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -207,12 +207,12 @@ it.live("use stores the selected org and marks the account active", () =>
|
|||||||
)
|
)
|
||||||
|
|
||||||
yield* AccountRepo.Service.use((r) => r.use(id1, Option.some(OrgID.make("org-99"))))
|
yield* AccountRepo.Service.use((r) => r.use(id1, Option.some(OrgID.make("org-99"))))
|
||||||
const active1 = yield* AccountRepo.Service.use((r) => r.active())
|
const active1 = yield* AccountRepo.use.active()
|
||||||
expect(Option.getOrThrow(active1).id).toBe(id1)
|
expect(Option.getOrThrow(active1).id).toBe(id1)
|
||||||
expect(Option.getOrThrow(active1).active_org_id).toBe(OrgID.make("org-99"))
|
expect(Option.getOrThrow(active1).active_org_id).toBe(OrgID.make("org-99"))
|
||||||
|
|
||||||
yield* AccountRepo.Service.use((r) => r.use(id1, Option.none()))
|
yield* AccountRepo.Service.use((r) => r.use(id1, Option.none()))
|
||||||
const active2 = yield* AccountRepo.Service.use((r) => r.active())
|
const active2 = yield* AccountRepo.use.active()
|
||||||
expect(Option.getOrThrow(active2).active_org_id).toBeNull()
|
expect(Option.getOrThrow(active2).active_org_id).toBeNull()
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -243,7 +243,7 @@ it.live("persistToken updates token fields", () =>
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
|
const row = yield* AccountRepo.use.getRow(id)
|
||||||
const value = Option.getOrThrow(row)
|
const value = Option.getOrThrow(row)
|
||||||
expect(value.access_token).toBe(AccessToken.make("new_token"))
|
expect(value.access_token).toBe(AccessToken.make("new_token"))
|
||||||
expect(value.refresh_token).toBe(RefreshToken.make("new_refresh"))
|
expect(value.refresh_token).toBe(RefreshToken.make("new_refresh"))
|
||||||
@@ -276,7 +276,7 @@ it.live("persistToken with no expiry sets token_expiry to null", () =>
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
|
const row = yield* AccountRepo.use.getRow(id)
|
||||||
expect(Option.getOrThrow(row).token_expiry).toBeNull()
|
expect(Option.getOrThrow(row).token_expiry).toBeNull()
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -309,14 +309,14 @@ it.live("persistAccount upserts on conflict", () =>
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
const accounts = yield* AccountRepo.Service.use((r) => r.list())
|
const accounts = yield* AccountRepo.use.list()
|
||||||
expect(accounts.length).toBe(1)
|
expect(accounts.length).toBe(1)
|
||||||
|
|
||||||
const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
|
const row = yield* AccountRepo.use.getRow(id)
|
||||||
const value = Option.getOrThrow(row)
|
const value = Option.getOrThrow(row)
|
||||||
expect(value.access_token).toBe(AccessToken.make("at_v2"))
|
expect(value.access_token).toBe(AccessToken.make("at_v2"))
|
||||||
|
|
||||||
const active = yield* AccountRepo.Service.use((r) => r.active())
|
const active = yield* AccountRepo.use.active()
|
||||||
expect(Option.getOrThrow(active).active_org_id).toBe(OrgID.make("org-2"))
|
expect(Option.getOrThrow(active).active_org_id).toBe(OrgID.make("org-2"))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -337,9 +337,9 @@ it.live("remove clears active state when deleting the active account", () =>
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
yield* AccountRepo.Service.use((r) => r.remove(id))
|
yield* AccountRepo.use.remove(id)
|
||||||
|
|
||||||
const active = yield* AccountRepo.Service.use((r) => r.active())
|
const active = yield* AccountRepo.use.active()
|
||||||
expect(Option.isNone(active)).toBe(true)
|
expect(Option.isNone(active)).toBe(true)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ it.live("login normalizes trailing slashes in the provided server URL", () =>
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
const result = yield* Account.Service.use((s) => s.login("https://one.example.com/")).pipe(
|
const result = yield* Account.use.login("https://one.example.com/").pipe(
|
||||||
Effect.provide(live(client)),
|
Effect.provide(live(client)),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ it.live("login maps transport failures to account transport errors", () =>
|
|||||||
)
|
)
|
||||||
|
|
||||||
const error = yield* Effect.flip(
|
const error = yield* Effect.flip(
|
||||||
Account.Service.use((s) => s.login("https://one.example.com")).pipe(Effect.provide(live(client))),
|
Account.use.login("https://one.example.com").pipe(Effect.provide(live(client))),
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(error).toBeInstanceOf(AccountTransportError)
|
expect(error).toBeInstanceOf(AccountTransportError)
|
||||||
@@ -163,7 +163,7 @@ it.live("orgsByAccount groups orgs per account", () =>
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
const rows = yield* Account.Service.use((s) => s.orgsByAccount()).pipe(Effect.provide(live(client)))
|
const rows = yield* Account.use.orgsByAccount().pipe(Effect.provide(live(client)))
|
||||||
|
|
||||||
expect(rows.map((row) => [row.account.id, row.orgs.map((org) => org.id)]).map(([id, orgs]) => [id, orgs])).toEqual([
|
expect(rows.map((row) => [row.account.id, row.orgs.map((org) => org.id)]).map(([id, orgs]) => [id, orgs])).toEqual([
|
||||||
[AccountID.make("user-1"), [OrgID.make("org-1")]],
|
[AccountID.make("user-1"), [OrgID.make("org-1")]],
|
||||||
@@ -201,12 +201,12 @@ it.live("token refresh persists the new token", () =>
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
const token = yield* Account.Service.use((s) => s.token(id)).pipe(Effect.provide(live(client)))
|
const token = yield* Account.use.token(id).pipe(Effect.provide(live(client)))
|
||||||
|
|
||||||
expect(Option.getOrThrow(token)).toBeDefined()
|
expect(Option.getOrThrow(token)).toBeDefined()
|
||||||
expect(String(Option.getOrThrow(token))).toBe("at_new")
|
expect(String(Option.getOrThrow(token))).toBe("at_new")
|
||||||
|
|
||||||
const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
|
const row = yield* AccountRepo.use.getRow(id)
|
||||||
const value = Option.getOrThrow(row)
|
const value = Option.getOrThrow(row)
|
||||||
expect(value.access_token).toBe(AccessToken.make("at_new"))
|
expect(value.access_token).toBe(AccessToken.make("at_new"))
|
||||||
expect(value.refresh_token).toBe(RefreshToken.make("rt_new"))
|
expect(value.refresh_token).toBe(RefreshToken.make("rt_new"))
|
||||||
@@ -246,12 +246,12 @@ it.live("token refreshes before expiry when inside the eager refresh window", ()
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
const token = yield* Account.Service.use((s) => s.token(id)).pipe(Effect.provide(live(client)))
|
const token = yield* Account.use.token(id).pipe(Effect.provide(live(client)))
|
||||||
|
|
||||||
expect(String(Option.getOrThrow(token))).toBe("at_new")
|
expect(String(Option.getOrThrow(token))).toBe("at_new")
|
||||||
expect(refreshCalls).toBe(1)
|
expect(refreshCalls).toBe(1)
|
||||||
|
|
||||||
const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
|
const row = yield* AccountRepo.use.getRow(id)
|
||||||
const value = Option.getOrThrow(row)
|
const value = Option.getOrThrow(row)
|
||||||
expect(value.access_token).toBe(AccessToken.make("at_new"))
|
expect(value.access_token).toBe(AccessToken.make("at_new"))
|
||||||
expect(value.refresh_token).toBe(RefreshToken.make("rt_new"))
|
expect(value.refresh_token).toBe(RefreshToken.make("rt_new"))
|
||||||
@@ -315,7 +315,7 @@ it.live("concurrent config and token requests coalesce token refresh", () =>
|
|||||||
expect(String(Option.getOrThrow(token))).toBe("at_new")
|
expect(String(Option.getOrThrow(token))).toBe("at_new")
|
||||||
expect(refreshCalls).toBe(1)
|
expect(refreshCalls).toBe(1)
|
||||||
|
|
||||||
const row = yield* AccountRepo.Service.use((r) => r.getRow(id))
|
const row = yield* AccountRepo.use.getRow(id)
|
||||||
const value = Option.getOrThrow(row)
|
const value = Option.getOrThrow(row)
|
||||||
expect(value.access_token).toBe(AccessToken.make("at_new"))
|
expect(value.access_token).toBe(AccessToken.make("at_new"))
|
||||||
expect(value.refresh_token).toBe(RefreshToken.make("rt_new"))
|
expect(value.refresh_token).toBe(RefreshToken.make("rt_new"))
|
||||||
@@ -388,7 +388,7 @@ it.live("poll stores the account and first org on success", () =>
|
|||||||
expect(res.email).toBe("user@example.com")
|
expect(res.email).toBe("user@example.com")
|
||||||
}
|
}
|
||||||
|
|
||||||
const active = yield* AccountRepo.Service.use((r) => r.active())
|
const active = yield* AccountRepo.use.active()
|
||||||
expect(Option.getOrThrow(active)).toEqual(
|
expect(Option.getOrThrow(active)).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: "user-1",
|
id: "user-1",
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ function testAgent(input: {
|
|||||||
|
|
||||||
it.instance("[#26514] subagent spawned from plan mode inherits read-only restriction (edit denied)", () =>
|
it.instance("[#26514] subagent spawned from plan mode inherits read-only restriction (edit denied)", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const planAgent = yield* Agent.Service.use((svc) => svc.get("plan"))
|
const planAgent = yield* Agent.use.get("plan")
|
||||||
const generalAgent = yield* Agent.Service.use((svc) => svc.get("general"))
|
const generalAgent = yield* Agent.use.get("general")
|
||||||
|
|
||||||
expect(planAgent).toBeDefined()
|
expect(planAgent).toBeDefined()
|
||||||
expect(generalAgent).toBeDefined()
|
expect(generalAgent).toBeDefined()
|
||||||
@@ -83,8 +83,8 @@ it.instance("[#26514] explore subagent launched from plan mode also stays read-o
|
|||||||
// should propagate the parent **agent** permissions, not just deny edit
|
// should propagate the parent **agent** permissions, not just deny edit
|
||||||
// when the subagent happens to already deny it.
|
// when the subagent happens to already deny it.
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const planAgent = yield* Agent.Service.use((svc) => svc.get("plan"))
|
const planAgent = yield* Agent.use.get("plan")
|
||||||
const explore = yield* Agent.Service.use((svc) => svc.get("explore"))
|
const explore = yield* Agent.use.get("explore")
|
||||||
expect(planAgent).toBeDefined()
|
expect(planAgent).toBeDefined()
|
||||||
expect(explore).toBeDefined()
|
expect(explore).toBeDefined()
|
||||||
|
|
||||||
@@ -108,8 +108,8 @@ it.instance(
|
|||||||
// be able to edit when the parent agent is `plan`.
|
// be able to edit when the parent agent is `plan`.
|
||||||
() =>
|
() =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const planAgent = yield* Agent.Service.use((svc) => svc.get("plan"))
|
const planAgent = yield* Agent.use.get("plan")
|
||||||
const my = yield* Agent.Service.use((svc) => svc.get("my_subagent"))
|
const my = yield* Agent.use.get("my_subagent")
|
||||||
expect(planAgent).toBeDefined()
|
expect(planAgent).toBeDefined()
|
||||||
expect(my).toBeDefined()
|
expect(my).toBeDefined()
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ it.instance(
|
|||||||
() =>
|
() =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
yield* Plugin.Service.use((p) => p.init())
|
yield* Plugin.Service.use((p) => p.init())
|
||||||
const agents = yield* Agent.Service.use((svc) => svc.list())
|
const agents = yield* Agent.use.list()
|
||||||
const added = agents.find((agent) => agent.name === PLUGIN_AGENT.name)
|
const added = agents.find((agent) => agent.name === PLUGIN_AGENT.name)
|
||||||
expect(added?.description).toBe(PLUGIN_AGENT.description)
|
expect(added?.description).toBe(PLUGIN_AGENT.description)
|
||||||
expect(added?.mode).toBe(PLUGIN_AGENT.mode)
|
expect(added?.mode).toBe(PLUGIN_AGENT.mode)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ it.instance(
|
|||||||
"agent color parsed from project config",
|
"agent color parsed from project config",
|
||||||
() =>
|
() =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const cfg = yield* Config.Service.use((svc) => svc.get())
|
const cfg = yield* Config.use.get()
|
||||||
expect(cfg.agent?.["build"]?.color).toBe("#FFA500")
|
expect(cfg.agent?.["build"]?.color).toBe("#FFA500")
|
||||||
expect(cfg.agent?.["plan"]?.color).toBe("primary")
|
expect(cfg.agent?.["plan"]?.color).toBe("primary")
|
||||||
}),
|
}),
|
||||||
@@ -30,9 +30,9 @@ it.instance(
|
|||||||
"Agent.get includes color from config",
|
"Agent.get includes color from config",
|
||||||
() =>
|
() =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const plan = yield* AgentSvc.Service.use((svc) => svc.get("plan"))
|
const plan = yield* AgentSvc.use.get("plan")
|
||||||
expect(plan?.color).toBe("#A855F7")
|
expect(plan?.color).toBe("#A855F7")
|
||||||
const build = yield* AgentSvc.Service.use((svc) => svc.get("build"))
|
const build = yield* AgentSvc.use.get("build")
|
||||||
expect(build?.color).toBe("accent")
|
expect(build?.color).toBe("accent")
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -70,14 +70,14 @@ const load = (ctx: InstanceContext) =>
|
|||||||
)
|
)
|
||||||
const saveGlobal = (config: Config.Info) =>
|
const saveGlobal = (config: Config.Info) =>
|
||||||
Effect.runPromise(
|
Effect.runPromise(
|
||||||
Config.Service.use((svc) => svc.updateGlobal(config)).pipe(
|
Config.use.updateGlobal(config).pipe(
|
||||||
Effect.map((result) => result.info),
|
Effect.map((result) => result.info),
|
||||||
Effect.scoped,
|
Effect.scoped,
|
||||||
Effect.provide(layer),
|
Effect.provide(layer),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
const clear = async (wait = false) => {
|
const clear = async (wait = false) => {
|
||||||
await Effect.runPromise(Config.Service.use((svc) => svc.invalidate()).pipe(Effect.scoped, Effect.provide(layer)))
|
await Effect.runPromise(Config.use.invalidate().pipe(Effect.scoped, Effect.provide(layer)))
|
||||||
if (wait) await InstanceRuntime.disposeAllInstances()
|
if (wait) await InstanceRuntime.disposeAllInstances()
|
||||||
}
|
}
|
||||||
const listDirs = (ctx: InstanceContext) =>
|
const listDirs = (ctx: InstanceContext) =>
|
||||||
@@ -162,7 +162,7 @@ async function check(map: (dir: string) => string) {
|
|||||||
|
|
||||||
it.instance("loads config with defaults when no files exist", () =>
|
it.instance("loads config with defaults when no files exist", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.username).toBeDefined()
|
expect(config.username).toBeDefined()
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -218,7 +218,7 @@ test("does not create global config when OPENCODE_CONFIG_DIR is set", async () =
|
|||||||
it.instance(
|
it.instance(
|
||||||
"loads JSON config file",
|
"loads JSON config file",
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.model).toBe("test/model")
|
expect(config.model).toBe("test/model")
|
||||||
expect(config.username).toBe("testuser")
|
expect(config.username).toBe("testuser")
|
||||||
}),
|
}),
|
||||||
@@ -228,7 +228,7 @@ it.instance(
|
|||||||
it.instance(
|
it.instance(
|
||||||
"loads shell config field",
|
"loads shell config field",
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.shell).toBe("bash")
|
expect(config.shell).toBe("bash")
|
||||||
}),
|
}),
|
||||||
{ config: { shell: "bash" } },
|
{ config: { shell: "bash" } },
|
||||||
@@ -313,7 +313,7 @@ test("updates global config and omits empty shell key in jsonc", async () => {
|
|||||||
it.instance(
|
it.instance(
|
||||||
"loads formatter boolean config",
|
"loads formatter boolean config",
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.formatter).toBe(true)
|
expect(config.formatter).toBe(true)
|
||||||
}),
|
}),
|
||||||
{ config: { formatter: true } },
|
{ config: { formatter: true } },
|
||||||
@@ -322,7 +322,7 @@ it.instance(
|
|||||||
it.instance(
|
it.instance(
|
||||||
"loads lsp boolean config",
|
"loads lsp boolean config",
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.lsp).toBe(true)
|
expect(config.lsp).toBe(true)
|
||||||
}),
|
}),
|
||||||
{ config: { lsp: true } },
|
{ config: { lsp: true } },
|
||||||
@@ -355,7 +355,7 @@ it.instance("ignores legacy tui keys in opencode config", () =>
|
|||||||
tui: { scroll_speed: 4 },
|
tui: { scroll_speed: 4 },
|
||||||
})
|
})
|
||||||
|
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.model).toBe("test/model")
|
expect(config.model).toBe("test/model")
|
||||||
expect((config as Record<string, unknown>).theme).toBeUndefined()
|
expect((config as Record<string, unknown>).theme).toBeUndefined()
|
||||||
expect((config as Record<string, unknown>).tui).toBeUndefined()
|
expect((config as Record<string, unknown>).tui).toBeUndefined()
|
||||||
@@ -376,7 +376,7 @@ it.instance("loads JSONC config file", () =>
|
|||||||
}`,
|
}`,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.model).toBe("test/model")
|
expect(config.model).toBe("test/model")
|
||||||
expect(config.username).toBe("testuser")
|
expect(config.username).toBe("testuser")
|
||||||
}),
|
}),
|
||||||
@@ -398,7 +398,7 @@ it.instance("jsonc overrides json in the same directory", () =>
|
|||||||
$schema: "https://opencode.ai/config.json",
|
$schema: "https://opencode.ai/config.json",
|
||||||
model: "override",
|
model: "override",
|
||||||
})
|
})
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.model).toBe("base")
|
expect(config.model).toBe("base")
|
||||||
expect(config.username).toBe("base")
|
expect(config.username).toBe("base")
|
||||||
}),
|
}),
|
||||||
@@ -414,7 +414,7 @@ it.instance("handles environment variable substitution", () =>
|
|||||||
$schema: "https://opencode.ai/config.json",
|
$schema: "https://opencode.ai/config.json",
|
||||||
username: "{env:TEST_VAR}",
|
username: "{env:TEST_VAR}",
|
||||||
})
|
})
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.username).toBe("test-user")
|
expect(config.username).toBe("test-user")
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -435,7 +435,7 @@ it.instance("preserves env variables when adding $schema to config", () =>
|
|||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.username).toBe("secret_value")
|
expect(config.username).toBe("secret_value")
|
||||||
|
|
||||||
// Read the file to verify the env variable was preserved
|
// Read the file to verify the env variable was preserved
|
||||||
@@ -455,7 +455,7 @@ it.instance("handles file inclusion substitution", () =>
|
|||||||
$schema: "https://opencode.ai/config.json",
|
$schema: "https://opencode.ai/config.json",
|
||||||
username: "{file:included.txt}",
|
username: "{file:included.txt}",
|
||||||
})
|
})
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.username).toBe("test-user")
|
expect(config.username).toBe("test-user")
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -470,7 +470,7 @@ it.instance("handles file inclusion with replacement tokens", () =>
|
|||||||
$schema: "https://opencode.ai/config.json",
|
$schema: "https://opencode.ai/config.json",
|
||||||
username: "{file:included.md}",
|
username: "{file:included.md}",
|
||||||
})
|
})
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.username).toBe("const out = await Bun.$`echo hi`")
|
expect(config.username).toBe("const out = await Bun.$`echo hi`")
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -547,7 +547,7 @@ it.instance("validates config schema and throws on invalid fields", () =>
|
|||||||
$schema: "https://opencode.ai/config.json",
|
$schema: "https://opencode.ai/config.json",
|
||||||
invalid_field: "should cause error",
|
invalid_field: "should cause error",
|
||||||
})
|
})
|
||||||
const exit = yield* Config.Service.use((svc) => svc.get()).pipe(Effect.exit)
|
const exit = yield* Config.use.get().pipe(Effect.exit)
|
||||||
expect(Exit.isFailure(exit)).toBe(true)
|
expect(Exit.isFailure(exit)).toBe(true)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -556,7 +556,7 @@ it.instance("throws error for invalid JSON", () =>
|
|||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const test = yield* TestInstance
|
const test = yield* TestInstance
|
||||||
yield* Effect.promise(() => Filesystem.write(path.join(test.directory, "opencode.json"), "{ invalid json }"))
|
yield* Effect.promise(() => Filesystem.write(path.join(test.directory, "opencode.json"), "{ invalid json }"))
|
||||||
const exit = yield* Config.Service.use((svc) => svc.get()).pipe(Effect.exit)
|
const exit = yield* Config.use.get().pipe(Effect.exit)
|
||||||
expect(Exit.isFailure(exit)).toBe(true)
|
expect(Exit.isFailure(exit)).toBe(true)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -574,7 +574,7 @@ it.instance("handles agent configuration", () =>
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.agent?.["test_agent"]).toEqual(
|
expect(config.agent?.["test_agent"]).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
model: "test/model",
|
model: "test/model",
|
||||||
@@ -598,7 +598,7 @@ it.instance("treats agent variant as model-scoped setting (not provider option)"
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
const agent = config.agent?.["test_agent"]
|
const agent = config.agent?.["test_agent"]
|
||||||
|
|
||||||
expect(agent?.variant).toBe("xhigh")
|
expect(agent?.variant).toBe("xhigh")
|
||||||
@@ -622,7 +622,7 @@ it.instance("handles command configuration", () =>
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.command?.["test_command"]).toEqual({
|
expect(config.command?.["test_command"]).toEqual({
|
||||||
template: "test template",
|
template: "test template",
|
||||||
description: "test command",
|
description: "test command",
|
||||||
@@ -638,7 +638,7 @@ it.instance("migrates autoshare to share field", () =>
|
|||||||
$schema: "https://opencode.ai/config.json",
|
$schema: "https://opencode.ai/config.json",
|
||||||
autoshare: true,
|
autoshare: true,
|
||||||
})
|
})
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.share).toBe("auto")
|
expect(config.share).toBe("auto")
|
||||||
expect(config.autoshare).toBe(true)
|
expect(config.autoshare).toBe(true)
|
||||||
}),
|
}),
|
||||||
@@ -656,7 +656,7 @@ it.instance("migrates mode field to agent field", () =>
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.agent?.["test_mode"]).toEqual({
|
expect(config.agent?.["test_mode"]).toEqual({
|
||||||
model: "test/model",
|
model: "test/model",
|
||||||
temperature: 0.5,
|
temperature: 0.5,
|
||||||
@@ -679,7 +679,7 @@ model: test/model
|
|||||||
Test agent prompt`,
|
Test agent prompt`,
|
||||||
)
|
)
|
||||||
|
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.agent?.["test"]).toEqual(
|
expect(config.agent?.["test"]).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
name: "test",
|
name: "test",
|
||||||
@@ -705,7 +705,7 @@ permission:
|
|||||||
Ordered permissions`,
|
Ordered permissions`,
|
||||||
)
|
)
|
||||||
|
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(Object.keys(config.agent?.ordered?.permission ?? {})).toEqual(["bash", "*", "edit"])
|
expect(Object.keys(config.agent?.ordered?.permission ?? {})).toEqual(["bash", "*", "edit"])
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -732,7 +732,7 @@ mode: subagent
|
|||||||
Nested agent prompt`,
|
Nested agent prompt`,
|
||||||
)
|
)
|
||||||
|
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
|
|
||||||
expect(config.agent?.["helper"]).toMatchObject({
|
expect(config.agent?.["helper"]).toMatchObject({
|
||||||
name: "helper",
|
name: "helper",
|
||||||
@@ -770,7 +770,7 @@ description: Nested command
|
|||||||
Nested command template`,
|
Nested command template`,
|
||||||
)
|
)
|
||||||
|
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
|
|
||||||
expect(config.command?.["hello"]).toEqual({
|
expect(config.command?.["hello"]).toEqual({
|
||||||
description: "Test command",
|
description: "Test command",
|
||||||
@@ -804,7 +804,7 @@ description: Nested command
|
|||||||
Nested command template`,
|
Nested command template`,
|
||||||
)
|
)
|
||||||
|
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
|
|
||||||
expect(config.command?.["hello"]).toEqual({
|
expect(config.command?.["hello"]).toEqual({
|
||||||
description: "Test command",
|
description: "Test command",
|
||||||
@@ -834,7 +834,7 @@ it.instance("updates config and writes to file", () =>
|
|||||||
|
|
||||||
it.instance("gets config directories", () =>
|
it.instance("gets config directories", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const dirs = yield* Config.Service.use((svc) => svc.directories())
|
const dirs = yield* Config.use.directories()
|
||||||
expect(dirs.length).toBeGreaterThanOrEqual(1)
|
expect(dirs.length).toBeGreaterThanOrEqual(1)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -950,7 +950,7 @@ it.instance("resolves scoped npm plugins in config", () =>
|
|||||||
yield* writeTextEffect(path.join(pluginDir, "index.js"), "export default {}\n")
|
yield* writeTextEffect(path.join(pluginDir, "index.js"), "export default {}\n")
|
||||||
yield* writeConfigEffect(test.directory, { plugin: ["@scope/plugin"] })
|
yield* writeConfigEffect(test.directory, { plugin: ["@scope/plugin"] })
|
||||||
|
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.plugin ?? []).toContain("@scope/plugin")
|
expect(config.plugin ?? []).toContain("@scope/plugin")
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -1014,7 +1014,7 @@ mode: subagent
|
|||||||
Helper subagent prompt`,
|
Helper subagent prompt`,
|
||||||
)
|
)
|
||||||
|
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.agent?.["helper"]).toMatchObject({
|
expect(config.agent?.["helper"]).toMatchObject({
|
||||||
name: "helper",
|
name: "helper",
|
||||||
model: "test/model",
|
model: "test/model",
|
||||||
@@ -1212,7 +1212,7 @@ it.instance("migrates legacy tools config to permissions - allow", () =>
|
|||||||
agent: { test: { tools: { bash: true, read: true } } },
|
agent: { test: { tools: { bash: true, read: true } } },
|
||||||
})
|
})
|
||||||
|
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.agent?.["test"]?.permission).toEqual({
|
expect(config.agent?.["test"]?.permission).toEqual({
|
||||||
bash: "allow",
|
bash: "allow",
|
||||||
read: "allow",
|
read: "allow",
|
||||||
@@ -1228,7 +1228,7 @@ it.instance("migrates legacy tools config to permissions - deny", () =>
|
|||||||
agent: { test: { tools: { bash: false, webfetch: false } } },
|
agent: { test: { tools: { bash: false, webfetch: false } } },
|
||||||
})
|
})
|
||||||
|
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.agent?.["test"]?.permission).toEqual({
|
expect(config.agent?.["test"]?.permission).toEqual({
|
||||||
bash: "deny",
|
bash: "deny",
|
||||||
webfetch: "deny",
|
webfetch: "deny",
|
||||||
@@ -1244,7 +1244,7 @@ it.instance("migrates legacy write tool to edit permission", () =>
|
|||||||
agent: { test: { tools: { write: true } } },
|
agent: { test: { tools: { write: true } } },
|
||||||
})
|
})
|
||||||
|
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.agent?.["test"]?.permission).toEqual({ edit: "allow" })
|
expect(config.agent?.["test"]?.permission).toEqual({ edit: "allow" })
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -1261,7 +1261,7 @@ it.instance(
|
|||||||
share: "disabled",
|
share: "disabled",
|
||||||
})
|
})
|
||||||
|
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.model).toBe("managed/model")
|
expect(config.model).toBe("managed/model")
|
||||||
expect(config.share).toBe("disabled")
|
expect(config.share).toBe("disabled")
|
||||||
expect(config.username).toBe("testuser")
|
expect(config.username).toBe("testuser")
|
||||||
@@ -1278,7 +1278,7 @@ it.instance(
|
|||||||
disabled_providers: ["openai"],
|
disabled_providers: ["openai"],
|
||||||
})
|
})
|
||||||
|
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.autoupdate).toBe(false)
|
expect(config.autoupdate).toBe(false)
|
||||||
expect(config.disabled_providers).toEqual(["openai"])
|
expect(config.disabled_providers).toEqual(["openai"])
|
||||||
}),
|
}),
|
||||||
@@ -1288,7 +1288,7 @@ it.instance(
|
|||||||
it.instance(
|
it.instance(
|
||||||
"missing managed settings file is not an error",
|
"missing managed settings file is not an error",
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.model).toBe("user/model")
|
expect(config.model).toBe("user/model")
|
||||||
}),
|
}),
|
||||||
{ config: { model: "user/model" } },
|
{ config: { model: "user/model" } },
|
||||||
@@ -1302,7 +1302,7 @@ it.instance("migrates legacy edit tool to edit permission", () =>
|
|||||||
agent: { test: { tools: { edit: false } } },
|
agent: { test: { tools: { edit: false } } },
|
||||||
})
|
})
|
||||||
|
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.agent?.["test"]?.permission).toEqual({ edit: "deny" })
|
expect(config.agent?.["test"]?.permission).toEqual({ edit: "deny" })
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -1315,7 +1315,7 @@ it.instance("migrates legacy patch tool to edit permission", () =>
|
|||||||
agent: { test: { tools: { patch: true } } },
|
agent: { test: { tools: { patch: true } } },
|
||||||
})
|
})
|
||||||
|
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.agent?.["test"]?.permission).toEqual({ edit: "allow" })
|
expect(config.agent?.["test"]?.permission).toEqual({ edit: "allow" })
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -1328,7 +1328,7 @@ it.instance("migrates mixed legacy tools config", () =>
|
|||||||
agent: { test: { tools: { bash: true, write: true, read: false, webfetch: true } } },
|
agent: { test: { tools: { bash: true, write: true, read: false, webfetch: true } } },
|
||||||
})
|
})
|
||||||
|
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.agent?.["test"]?.permission).toEqual({
|
expect(config.agent?.["test"]?.permission).toEqual({
|
||||||
bash: "allow",
|
bash: "allow",
|
||||||
edit: "allow",
|
edit: "allow",
|
||||||
@@ -1346,7 +1346,7 @@ it.instance("merges legacy tools with existing permission config", () =>
|
|||||||
agent: { test: { permission: { glob: "allow" }, tools: { bash: true } } },
|
agent: { test: { permission: { glob: "allow" }, tools: { bash: true } } },
|
||||||
})
|
})
|
||||||
|
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.agent?.["test"]?.permission).toEqual({
|
expect(config.agent?.["test"]?.permission).toEqual({
|
||||||
glob: "allow",
|
glob: "allow",
|
||||||
bash: "allow",
|
bash: "allow",
|
||||||
@@ -1375,7 +1375,7 @@ it.instance("permission config preserves user key order", () =>
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(Object.keys(config.permission!)).toEqual([
|
expect(Object.keys(config.permission!)).toEqual([
|
||||||
"*",
|
"*",
|
||||||
"edit",
|
"edit",
|
||||||
@@ -1451,7 +1451,7 @@ it.instance("project config can override MCP server enabled status", () =>
|
|||||||
"opencode.jsonc",
|
"opencode.jsonc",
|
||||||
)
|
)
|
||||||
|
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.mcp?.jira).toEqual({
|
expect(config.mcp?.jira).toEqual({
|
||||||
type: "remote",
|
type: "remote",
|
||||||
url: "https://jira.example.com/mcp",
|
url: "https://jira.example.com/mcp",
|
||||||
@@ -1496,7 +1496,7 @@ it.instance("MCP config deep merges preserving base config properties", () =>
|
|||||||
"opencode.jsonc",
|
"opencode.jsonc",
|
||||||
)
|
)
|
||||||
|
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.mcp?.myserver).toEqual({
|
expect(config.mcp?.myserver).toEqual({
|
||||||
type: "remote",
|
type: "remote",
|
||||||
url: "https://myserver.example.com/mcp",
|
url: "https://myserver.example.com/mcp",
|
||||||
@@ -1537,7 +1537,7 @@ it.instance("local .opencode config can override MCP from project config", () =>
|
|||||||
"opencode.json",
|
"opencode.json",
|
||||||
)
|
)
|
||||||
|
|
||||||
const config = yield* Config.Service.use((svc) => svc.get())
|
const config = yield* Config.use.get()
|
||||||
expect(config.mcp?.docs?.enabled).toBe(true)
|
expect(config.mcp?.docs?.enabled).toBe(true)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ it.instance("keeps server and tui plugin merge semantics aligned", () =>
|
|||||||
plugin: [["shared-plugin@2.0.0", { source: "local" }], "local-only@1.0.0"],
|
plugin: [["shared-plugin@2.0.0", { source: "local" }], "local-only@1.0.0"],
|
||||||
})
|
})
|
||||||
|
|
||||||
const server = yield* Config.Service.use((svc) => svc.get())
|
const server = yield* Config.use.get()
|
||||||
const tui = yield* getTuiConfig(test.directory)
|
const tui = yield* getTuiConfig(test.directory)
|
||||||
const serverPlugins = (server.plugin ?? []).map((item) => ConfigPlugin.pluginSpecifier(item))
|
const serverPlugins = (server.plugin ?? []).map((item) => ConfigPlugin.pluginSpecifier(item))
|
||||||
const tuiPlugins = (tui.plugin ?? []).map((item) => ConfigPlugin.pluginSpecifier(item))
|
const tuiPlugins = (tui.plugin ?? []).map((item) => ConfigPlugin.pluginSpecifier(item))
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -36,7 +36,7 @@ describe("file fsmonitor", () => {
|
|||||||
const before = yield* Effect.promise(() => $`git fsmonitor--daemon status`.cwd(directory).quiet().nothrow())
|
const before = yield* Effect.promise(() => $`git fsmonitor--daemon status`.cwd(directory).quiet().nothrow())
|
||||||
expect(before.exitCode).not.toBe(0)
|
expect(before.exitCode).not.toBe(0)
|
||||||
|
|
||||||
yield* File.Service.use((svc) => svc.status())
|
yield* File.use.status()
|
||||||
|
|
||||||
const after = yield* Effect.promise(() => $`git fsmonitor--daemon status`.cwd(directory).quiet().nothrow())
|
const after = yield* Effect.promise(() => $`git fsmonitor--daemon status`.cwd(directory).quiet().nothrow())
|
||||||
expect(after.exitCode).not.toBe(0)
|
expect(after.exitCode).not.toBe(0)
|
||||||
@@ -63,7 +63,7 @@ describe("file fsmonitor", () => {
|
|||||||
const before = yield* Effect.promise(() => $`git fsmonitor--daemon status`.cwd(directory).quiet().nothrow())
|
const before = yield* Effect.promise(() => $`git fsmonitor--daemon status`.cwd(directory).quiet().nothrow())
|
||||||
expect(before.exitCode).not.toBe(0)
|
expect(before.exitCode).not.toBe(0)
|
||||||
|
|
||||||
yield* File.Service.use((svc) => svc.read("tracked.txt"))
|
yield* File.use.read("tracked.txt")
|
||||||
|
|
||||||
const after = yield* Effect.promise(() => $`git fsmonitor--daemon status`.cwd(directory).quiet().nothrow())
|
const after = yield* Effect.promise(() => $`git fsmonitor--daemon status`.cwd(directory).quiet().nothrow())
|
||||||
expect(after.exitCode).not.toBe(0)
|
expect(after.exitCode).not.toBe(0)
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import { TestInstance } from "../fixture/fixture"
|
|||||||
import { testEffect } from "../lib/effect"
|
import { testEffect } from "../lib/effect"
|
||||||
|
|
||||||
const it = testEffect(File.defaultLayer)
|
const it = testEffect(File.defaultLayer)
|
||||||
const read = (file: string) => File.Service.use((svc) => svc.read(file))
|
const read = (file: string) => File.use.read(file)
|
||||||
const list = (dir?: string) => File.Service.use((svc) => svc.list(dir))
|
const list = (dir?: string) => File.use.list(dir)
|
||||||
const expectAccessDenied = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
|
const expectAccessDenied = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const exit = yield* effect.pipe(Effect.exit)
|
const exit = yield* effect.pipe(Effect.exit)
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ describe("file.ripgrep", () => {
|
|||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const dir = yield* tmpdir((dir) => write(path.join(dir, "match.ts"), "const value = 'other'\n"))
|
const dir = yield* tmpdir((dir) => write(path.join(dir, "match.ts"), "const value = 'other'\n"))
|
||||||
|
|
||||||
const result = yield* Ripgrep.Service.use((rg) => rg.search({ cwd: dir, pattern: "needle" }))
|
const result = yield* Ripgrep.use.search({ cwd: dir, pattern: "needle" })
|
||||||
expect(result.partial).toBe(false)
|
expect(result.partial).toBe(false)
|
||||||
expect(result.items).toEqual([])
|
expect(result.items).toEqual([])
|
||||||
}),
|
}),
|
||||||
@@ -100,7 +100,7 @@ describe("file.ripgrep", () => {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
const result = yield* Ripgrep.Service.use((rg) => rg.search({ cwd: dir, pattern: "needle" }))
|
const result = yield* Ripgrep.use.search({ cwd: dir, pattern: "needle" })
|
||||||
expect(result.partial).toBe(false)
|
expect(result.partial).toBe(false)
|
||||||
expect(result.items).toHaveLength(1)
|
expect(result.items).toHaveLength(1)
|
||||||
expect(result.items[0]?.path.text).toBe(path.join("src", "match.ts"))
|
expect(result.items[0]?.path.text).toBe(path.join("src", "match.ts"))
|
||||||
@@ -118,7 +118,7 @@ describe("file.ripgrep", () => {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
const result = yield* Ripgrep.Service.use((rg) => rg.search({ cwd: dir, pattern: "needle", glob: ["*.ts"] }))
|
const result = yield* Ripgrep.use.search({ cwd: dir, pattern: "needle", glob: ["*.ts"] })
|
||||||
expect(result.partial).toBe(false)
|
expect(result.partial).toBe(false)
|
||||||
expect(result.items).toHaveLength(1)
|
expect(result.items).toHaveLength(1)
|
||||||
expect(result.items[0]?.path.text).toContain("match.ts")
|
expect(result.items[0]?.path.text).toContain("match.ts")
|
||||||
@@ -136,7 +136,7 @@ describe("file.ripgrep", () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const file = path.join(dir, "match.ts")
|
const file = path.join(dir, "match.ts")
|
||||||
const result = yield* Ripgrep.Service.use((rg) => rg.search({ cwd: dir, pattern: "needle", file: [file] }))
|
const result = yield* Ripgrep.use.search({ cwd: dir, pattern: "needle", file: [file] })
|
||||||
expect(result.partial).toBe(false)
|
expect(result.partial).toBe(false)
|
||||||
expect(result.items).toHaveLength(1)
|
expect(result.items).toHaveLength(1)
|
||||||
expect(result.items[0]?.path.text).toBe(file)
|
expect(result.items[0]?.path.text).toBe(file)
|
||||||
@@ -200,7 +200,7 @@ describe("file.ripgrep", () => {
|
|||||||
|
|
||||||
const result = yield* withRipgrepConfig(
|
const result = yield* withRipgrepConfig(
|
||||||
path.join(dir, "missing-ripgreprc"),
|
path.join(dir, "missing-ripgreprc"),
|
||||||
Ripgrep.Service.use((rg) => rg.search({ cwd: dir, pattern: "needle" })),
|
Ripgrep.use.search({ cwd: dir, pattern: "needle" }),
|
||||||
)
|
)
|
||||||
expect(result.items).toHaveLength(1)
|
expect(result.items).toHaveLength(1)
|
||||||
}),
|
}),
|
||||||
@@ -212,7 +212,7 @@ describe("file.ripgrep", () => {
|
|||||||
|
|
||||||
const result = yield* withRipgrepConfig(
|
const result = yield* withRipgrepConfig(
|
||||||
path.join(dir, "missing-ripgreprc"),
|
path.join(dir, "missing-ripgreprc"),
|
||||||
Ripgrep.Service.use((rg) => rg.search({ cwd: dir, pattern: "needle" })),
|
Ripgrep.use.search({ cwd: dir, pattern: "needle" }),
|
||||||
)
|
)
|
||||||
expect(result.items).toHaveLength(1)
|
expect(result.items).toHaveLength(1)
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ describe("Format", () => {
|
|||||||
const file = `${dir}/test.txt`
|
const file = `${dir}/test.txt`
|
||||||
yield* Effect.promise(() => Bun.write(file, "x"))
|
yield* Effect.promise(() => Bun.write(file, "x"))
|
||||||
|
|
||||||
const formatted = yield* Format.Service.use((fmt) => fmt.file(file))
|
const formatted = yield* Format.use.file(file)
|
||||||
expect(formatted).toBe(false)
|
expect(formatted).toBe(false)
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
@@ -146,10 +146,10 @@ describe("Format", () => {
|
|||||||
|
|
||||||
it.live("status() initializes formatter state per directory", () =>
|
it.live("status() initializes formatter state per directory", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const a = yield* provideTmpdirInstance(() => Format.Service.use((fmt) => fmt.status()), {
|
const a = yield* provideTmpdirInstance(() => Format.use.status(), {
|
||||||
config: { formatter: false },
|
config: { formatter: false },
|
||||||
})
|
})
|
||||||
const b = yield* provideTmpdirInstance(() => Format.Service.use((fmt) => fmt.status()), {
|
const b = yield* provideTmpdirInstance(() => Format.use.status(), {
|
||||||
config: {
|
config: {
|
||||||
formatter: true,
|
formatter: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ describe("installation", () => {
|
|||||||
"reads release version from GitHub releases",
|
"reads release version from GitHub releases",
|
||||||
() =>
|
() =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const result = yield* Installation.Service.use((svc) => svc.latest("unknown"))
|
const result = yield* Installation.use.latest("unknown")
|
||||||
expect(result).toBe("1.2.3")
|
expect(result).toBe("1.2.3")
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -67,7 +67,7 @@ describe("installation", () => {
|
|||||||
"strips v prefix from GitHub release tag",
|
"strips v prefix from GitHub release tag",
|
||||||
() =>
|
() =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const result = yield* Installation.Service.use((svc) => svc.latest("curl"))
|
const result = yield* Installation.use.latest("curl")
|
||||||
expect(result).toBe("4.0.0-beta.1")
|
expect(result).toBe("4.0.0-beta.1")
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -80,7 +80,7 @@ describe("installation", () => {
|
|||||||
}),
|
}),
|
||||||
).effect("reads npm versions via registry", () =>
|
).effect("reads npm versions via registry", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const result = yield* Installation.Service.use((svc) => svc.latest("npm"))
|
const result = yield* Installation.use.latest("npm")
|
||||||
expect(result).toBe("1.5.0")
|
expect(result).toBe("1.5.0")
|
||||||
expect(npmCalls).toContain(`https://registry.npmjs.org/opencode-ai/${InstallationChannel}`)
|
expect(npmCalls).toContain(`https://registry.npmjs.org/opencode-ai/${InstallationChannel}`)
|
||||||
}),
|
}),
|
||||||
@@ -94,7 +94,7 @@ describe("installation", () => {
|
|||||||
}),
|
}),
|
||||||
).effect("reads bun versions via registry", () =>
|
).effect("reads bun versions via registry", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const result = yield* Installation.Service.use((svc) => svc.latest("bun"))
|
const result = yield* Installation.use.latest("bun")
|
||||||
expect(result).toBe("1.6.0")
|
expect(result).toBe("1.6.0")
|
||||||
expect(bunCalls).toContain(`https://registry.npmjs.org/opencode-ai/${InstallationChannel}`)
|
expect(bunCalls).toContain(`https://registry.npmjs.org/opencode-ai/${InstallationChannel}`)
|
||||||
}),
|
}),
|
||||||
@@ -108,7 +108,7 @@ describe("installation", () => {
|
|||||||
}),
|
}),
|
||||||
).effect("reads pnpm versions via registry", () =>
|
).effect("reads pnpm versions via registry", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const result = yield* Installation.Service.use((svc) => svc.latest("pnpm"))
|
const result = yield* Installation.use.latest("pnpm")
|
||||||
expect(result).toBe("1.7.0")
|
expect(result).toBe("1.7.0")
|
||||||
expect(pnpmCalls).toContain(`https://registry.npmjs.org/opencode-ai/${InstallationChannel}`)
|
expect(pnpmCalls).toContain(`https://registry.npmjs.org/opencode-ai/${InstallationChannel}`)
|
||||||
}),
|
}),
|
||||||
@@ -116,7 +116,7 @@ describe("installation", () => {
|
|||||||
|
|
||||||
testEffect(testLayer(() => jsonResponse({ version: "2.3.4" }))).effect("reads scoop manifest versions", () =>
|
testEffect(testLayer(() => jsonResponse({ version: "2.3.4" }))).effect("reads scoop manifest versions", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const result = yield* Installation.Service.use((svc) => svc.latest("scoop"))
|
const result = yield* Installation.use.latest("scoop")
|
||||||
expect(result).toBe("2.3.4")
|
expect(result).toBe("2.3.4")
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -125,7 +125,7 @@ describe("installation", () => {
|
|||||||
"reads chocolatey feed versions",
|
"reads chocolatey feed versions",
|
||||||
() =>
|
() =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const result = yield* Installation.Service.use((svc) => svc.latest("choco"))
|
const result = yield* Installation.use.latest("choco")
|
||||||
expect(result).toBe("3.4.5")
|
expect(result).toBe("3.4.5")
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -142,7 +142,7 @@ describe("installation", () => {
|
|||||||
),
|
),
|
||||||
).effect("reads brew formulae API versions", () =>
|
).effect("reads brew formulae API versions", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const result = yield* Installation.Service.use((svc) => svc.latest("brew"))
|
const result = yield* Installation.use.latest("brew")
|
||||||
expect(result).toBe("2.0.0")
|
expect(result).toBe("2.0.0")
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -161,7 +161,7 @@ describe("installation", () => {
|
|||||||
),
|
),
|
||||||
).effect("reads brew tap info JSON via CLI", () =>
|
).effect("reads brew tap info JSON via CLI", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const result = yield* Installation.Service.use((svc) => svc.latest("brew"))
|
const result = yield* Installation.use.latest("brew")
|
||||||
expect(result).toBe("2.1.0")
|
expect(result).toBe("2.1.0")
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ mcpTest.instance("state() generates a new state when none is saved", () =>
|
|||||||
auth,
|
auth,
|
||||||
)
|
)
|
||||||
|
|
||||||
const entryBefore = yield* McpAuth.Service.use((auth) => auth.get("test-state-gen"))
|
const entryBefore = yield* McpAuth.use.get("test-state-gen")
|
||||||
expect(entryBefore?.oauthState).toBeUndefined()
|
expect(entryBefore?.oauthState).toBeUndefined()
|
||||||
|
|
||||||
// state() should generate and return a new state, not throw
|
// state() should generate and return a new state, not throw
|
||||||
@@ -184,7 +184,7 @@ mcpTest.instance("state() generates a new state when none is saved", () =>
|
|||||||
expect(state.length).toBe(64) // 32 bytes as hex
|
expect(state.length).toBe(64) // 32 bytes as hex
|
||||||
|
|
||||||
// The generated state should be persisted
|
// The generated state should be persisted
|
||||||
const entryAfter = yield* McpAuth.Service.use((auth) => auth.get("test-state-gen"))
|
const entryAfter = yield* McpAuth.use.get("test-state-gen")
|
||||||
expect(entryAfter?.oauthState).toBe(state)
|
expect(entryAfter?.oauthState).toBe(state)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -202,7 +202,7 @@ mcpTest.instance("state() returns existing state when one is saved", () =>
|
|||||||
|
|
||||||
// Pre-save a state
|
// Pre-save a state
|
||||||
const existingState = "pre-saved-state-value"
|
const existingState = "pre-saved-state-value"
|
||||||
yield* McpAuth.Service.use((auth) => auth.updateOAuthState("test-state-existing", existingState))
|
yield* McpAuth.use.updateOAuthState("test-state-existing", existingState)
|
||||||
|
|
||||||
// state() should return the existing state
|
// state() should return the existing state
|
||||||
const state = yield* Effect.promise(() => provider.state())
|
const state = yield* Effect.promise(() => provider.state())
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { testEffect } from "./lib/effect"
|
|||||||
|
|
||||||
const it = testEffect(Config.defaultLayer)
|
const it = testEffect(Config.defaultLayer)
|
||||||
|
|
||||||
const load = Config.Service.use((svc) => svc.get())
|
const load = Config.use.get()
|
||||||
|
|
||||||
describe("Permission.evaluate for permission.task", () => {
|
describe("Permission.evaluate for permission.task", () => {
|
||||||
const createRuleset = (rules: Record<string, "allow" | "deny" | "ask">): Permission.Ruleset =>
|
const createRuleset = (rules: Record<string, "allow" | "deny" | "ask">): Permission.Ruleset =>
|
||||||
|
|||||||
@@ -72,10 +72,10 @@ describe("plugin.auth-override", () => {
|
|||||||
|
|
||||||
const plain = yield* tmpdirScoped({ git: true })
|
const plain = yield* tmpdirScoped({ git: true })
|
||||||
const plugin = pathToFileURL(path.join(pluginDir, "custom-copilot-auth.ts")).href
|
const plugin = pathToFileURL(path.join(pluginDir, "custom-copilot-auth.ts")).href
|
||||||
const methods = yield* ProviderAuth.Service.use((svc) => svc.methods()).pipe(
|
const methods = yield* ProviderAuth.use.methods().pipe(
|
||||||
Effect.provide(layer(tmp.directory, [plugin])),
|
Effect.provide(layer(tmp.directory, [plugin])),
|
||||||
)
|
)
|
||||||
const plainMethods = yield* ProviderAuth.Service.use((svc) => svc.methods()).pipe(
|
const plainMethods = yield* ProviderAuth.use.methods().pipe(
|
||||||
Effect.provide(layer(plain, [])),
|
Effect.provide(layer(plain, [])),
|
||||||
provideInstance(plain),
|
provideInstance(plain),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const set = (k: string, v: string) =>
|
|||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
if (!originalEnv.has(k)) originalEnv.set(k, process.env[k])
|
if (!originalEnv.has(k)) originalEnv.set(k, process.env[k])
|
||||||
process.env[k] = v
|
process.env[k] = v
|
||||||
yield* Env.Service.use((svc) => svc.set(k, v))
|
yield* Env.use.set(k, v)
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
@@ -30,7 +30,7 @@ afterEach(async () => {
|
|||||||
await disposeAllInstances()
|
await disposeAllInstances()
|
||||||
})
|
})
|
||||||
|
|
||||||
const list = Provider.Service.use((svc) => svc.list())
|
const list = Provider.use.list()
|
||||||
|
|
||||||
const withAuthJson = (contents: string) =>
|
const withAuthJson = (contents: string) =>
|
||||||
Effect.acquireRelease(
|
Effect.acquireRelease(
|
||||||
|
|||||||
@@ -35,14 +35,14 @@ const set = (k: string, v: string) =>
|
|||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
rememberEnv(k)
|
rememberEnv(k)
|
||||||
process.env[k] = v
|
process.env[k] = v
|
||||||
yield* Env.Service.use((svc) => svc.set(k, v))
|
yield* Env.use.set(k, v)
|
||||||
})
|
})
|
||||||
|
|
||||||
const remove = (k: string) =>
|
const remove = (k: string) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
rememberEnv(k)
|
rememberEnv(k)
|
||||||
delete process.env[k]
|
delete process.env[k]
|
||||||
yield* Env.Service.use((svc) => svc.remove(k))
|
yield* Env.use.remove(k)
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
@@ -332,7 +332,7 @@ test("parseModel handles model IDs with slashes", () => {
|
|||||||
it.instance("defaultModel returns first available model when no config set", () =>
|
it.instance("defaultModel returns first available model when no config set", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
yield* setProcessEnv("ANTHROPIC_API_KEY", "test-api-key")
|
yield* setProcessEnv("ANTHROPIC_API_KEY", "test-api-key")
|
||||||
const model = yield* Provider.Service.use((provider) => provider.defaultModel())
|
const model = yield* Provider.use.defaultModel()
|
||||||
expect(model.providerID).toBeDefined()
|
expect(model.providerID).toBeDefined()
|
||||||
expect(model.modelID).toBeDefined()
|
expect(model.modelID).toBeDefined()
|
||||||
}),
|
}),
|
||||||
@@ -342,7 +342,7 @@ it.instance(
|
|||||||
"defaultModel respects config model setting",
|
"defaultModel respects config model setting",
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
yield* setProcessEnv("ANTHROPIC_API_KEY", "test-api-key")
|
yield* setProcessEnv("ANTHROPIC_API_KEY", "test-api-key")
|
||||||
const model = yield* Provider.Service.use((provider) => provider.defaultModel())
|
const model = yield* Provider.use.defaultModel()
|
||||||
expect(String(model.providerID)).toBe("anthropic")
|
expect(String(model.providerID)).toBe("anthropic")
|
||||||
expect(String(model.modelID)).toBe("claude-sonnet-4-20250514")
|
expect(String(model.modelID)).toBe("claude-sonnet-4-20250514")
|
||||||
}),
|
}),
|
||||||
@@ -1032,7 +1032,7 @@ it.instance("getProvider returns undefined for nonexistent provider", () =>
|
|||||||
it.instance("getProvider returns provider info", () =>
|
it.instance("getProvider returns provider info", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
yield* set("ANTHROPIC_API_KEY", "test-api-key")
|
yield* set("ANTHROPIC_API_KEY", "test-api-key")
|
||||||
const provider = yield* Provider.Service.use((svc) => svc.getProvider(ProviderID.anthropic))
|
const provider = yield* Provider.use.getProvider(ProviderID.anthropic)
|
||||||
expect(provider).toBeDefined()
|
expect(provider).toBeDefined()
|
||||||
expect(String(provider?.id)).toBe("anthropic")
|
expect(String(provider?.id)).toBe("anthropic")
|
||||||
}),
|
}),
|
||||||
@@ -1680,7 +1680,7 @@ it.effect("opencode loader keeps paid models when config apiKey is present", ()
|
|||||||
})
|
})
|
||||||
|
|
||||||
const listIn = (directory: string) =>
|
const listIn = (directory: string) =>
|
||||||
Provider.Service.use((svc) => svc.list())
|
Provider.use.list()
|
||||||
.pipe(provideInstanceEffect(directory))
|
.pipe(provideInstanceEffect(directory))
|
||||||
.pipe(Effect.provide(InstanceLayer.layer), Effect.provide(CrossSpawnSpawner.defaultLayer))
|
.pipe(Effect.provide(InstanceLayer.layer), Effect.provide(CrossSpawnSpawner.defaultLayer))
|
||||||
|
|
||||||
@@ -1698,7 +1698,7 @@ it.effect("opencode loader keeps paid models when auth exists", () =>
|
|||||||
const keyedDir = yield* tmpdirScoped()
|
const keyedDir = yield* tmpdirScoped()
|
||||||
|
|
||||||
const listIn = (directory: string) =>
|
const listIn = (directory: string) =>
|
||||||
Provider.Service.use((svc) => svc.list())
|
Provider.use.list()
|
||||||
.pipe(provideInstanceEffect(directory))
|
.pipe(provideInstanceEffect(directory))
|
||||||
.pipe(Effect.provide(InstanceLayer.layer), Effect.provide(CrossSpawnSpawner.defaultLayer))
|
.pipe(Effect.provide(InstanceLayer.layer), Effect.provide(CrossSpawnSpawner.defaultLayer))
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const it = testEffect(Layer.mergeAll(SessionNs.defaultLayer, Project.defaultLaye
|
|||||||
|
|
||||||
const withSession = (input?: Parameters<SessionNs.Interface["create"]>[0]) =>
|
const withSession = (input?: Parameters<SessionNs.Interface["create"]>[0]) =>
|
||||||
Effect.acquireRelease(
|
Effect.acquireRelease(
|
||||||
SessionNs.Service.use((session) => session.create(input)),
|
SessionNs.use.create(input),
|
||||||
(created) => SessionNs.Service.use((session) => session.remove(created.id).pipe(Effect.ignore)),
|
(created) => SessionNs.Service.use((session) => session.remove(created.id).pipe(Effect.ignore)),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -34,8 +34,8 @@ describe("session.listGlobal", () => {
|
|||||||
expect(ids).toContain(firstSession.id)
|
expect(ids).toContain(firstSession.id)
|
||||||
expect(ids).toContain(secondSession.id)
|
expect(ids).toContain(secondSession.id)
|
||||||
|
|
||||||
const firstProject = yield* Project.Service.use((project) => project.get(firstSession.projectID))
|
const firstProject = yield* Project.use.get(firstSession.projectID)
|
||||||
const secondProject = yield* Project.Service.use((project) => project.get(secondSession.projectID))
|
const secondProject = yield* Project.use.get(secondSession.projectID)
|
||||||
|
|
||||||
const firstItem = sessions.find((session) => session.id === firstSession.id)
|
const firstItem = sessions.find((session) => session.id === firstSession.id)
|
||||||
const secondItem = sessions.find((session) => session.id === secondSession.id)
|
const secondItem = sessions.find((session) => session.id === secondSession.id)
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ function request(path: string, directory: string, init: RequestInit = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createSession(input?: Session.CreateInput) {
|
function createSession(input?: Session.CreateInput) {
|
||||||
return Session.Service.use((svc) => svc.create(input))
|
return Session.use.create(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
function json<T>(response: Response) {
|
function json<T>(response: Response) {
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ const createLocalWorkspace = (input: { projectID: Project.Info["id"]; type: stri
|
|||||||
projectID: input.projectID,
|
projectID: input.projectID,
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
(info) => Workspace.Service.use((workspace) => workspace.remove(info.id)).pipe(Effect.ignore),
|
(info) => Workspace.use.remove(info.id).pipe(Effect.ignore),
|
||||||
)
|
)
|
||||||
|
|
||||||
const probeInstanceContext = Effect.gen(function* () {
|
const probeInstanceContext = Effect.gen(function* () {
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ function pathFor(path: string, params: Record<string, string>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createSession(input?: Session.CreateInput) {
|
function createSession(input?: Session.CreateInput) {
|
||||||
return Session.Service.use((svc) => svc.create(input))
|
return Session.use.create(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
function createTextMessage(sessionID: SessionIDType, text: string) {
|
function createTextMessage(sessionID: SessionIDType, text: string) {
|
||||||
@@ -101,7 +101,7 @@ const createLocalWorkspace = (input: { projectID: Project.Info["id"]; type: stri
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
(info) => Workspace.Service.use((svc) => svc.remove(info.id)).pipe(Effect.ignore),
|
(info) => Workspace.use.remove(info.id).pipe(Effect.ignore),
|
||||||
)
|
)
|
||||||
|
|
||||||
const insertLegacyAssistantMessage = (sessionID: SessionIDType, time = 1) =>
|
const insertLegacyAssistantMessage = (sessionID: SessionIDType, time = 1) =>
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ describe("sync HttpApi", () => {
|
|||||||
const tmp = yield* TestInstance
|
const tmp = yield* TestInstance
|
||||||
const headers = { "x-opencode-directory": tmp.directory, "content-type": "application/json" }
|
const headers = { "x-opencode-directory": tmp.directory, "content-type": "application/json" }
|
||||||
const info = spyOn(Log.create({ service: "server.sync" }), "info")
|
const info = spyOn(Log.create({ service: "server.sync" }), "info")
|
||||||
const session = yield* Session.Service.use((svc) => svc.create({ title: "sync" }))
|
const session = yield* Session.use.create({ title: "sync" })
|
||||||
|
|
||||||
const started = yield* Effect.promise(() =>
|
const started = yield* Effect.promise(() =>
|
||||||
Promise.resolve(app().request(SyncPaths.start, { method: "POST", headers })),
|
Promise.resolve(app().request(SyncPaths.start, { method: "POST", headers })),
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ const createWorkspace = (input: { projectID: Project.Info["id"]; type: string; a
|
|||||||
projectID: input.projectID,
|
projectID: input.projectID,
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
(info) => Workspace.Service.use((workspace) => workspace.remove(info.id)).pipe(Effect.ignore),
|
(info) => Workspace.use.remove(info.id).pipe(Effect.ignore),
|
||||||
)
|
)
|
||||||
|
|
||||||
const createRemoteWorkspace = (input: {
|
const createRemoteWorkspace = (input: {
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ describe("workspace HttpApi", () => {
|
|||||||
const workspace = (yield* Effect.promise(() => created.json())) as Workspace.Info
|
const workspace = (yield* Effect.promise(() => created.json())) as Workspace.Info
|
||||||
expect(workspace).toMatchObject({ type: "local-test", name: "local-test" })
|
expect(workspace).toMatchObject({ type: "local-test", name: "local-test" })
|
||||||
|
|
||||||
const session = yield* Session.Service.use((svc) => svc.create({})).pipe(provideInstance(dir))
|
const session = yield* Session.use.create({}).pipe(provideInstance(dir))
|
||||||
const warped = yield* request(WorkspacePaths.warp, dir, {
|
const warped = yield* request(WorkspacePaths.warp, dir, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "content-type": "application/json" },
|
headers: { "content-type": "application/json" },
|
||||||
@@ -424,7 +424,7 @@ describe("workspace HttpApi", () => {
|
|||||||
body: JSON.stringify({ type: "remote-session-target", branch: null }),
|
body: JSON.stringify({ type: "remote-session-target", branch: null }),
|
||||||
})
|
})
|
||||||
const workspace = (yield* Effect.promise(() => created.json())) as Workspace.Info
|
const workspace = (yield* Effect.promise(() => created.json())) as Workspace.Info
|
||||||
const session = yield* Session.Service.use((svc) => svc.create()).pipe(
|
const session = yield* Session.use.create().pipe(
|
||||||
Effect.provideService(WorkspaceRef, workspace.id),
|
Effect.provideService(WorkspaceRef, workspace.id),
|
||||||
provideInstance(dir),
|
provideInstance(dir),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ describe("project.initGit endpoint", () => {
|
|||||||
worktree: tmp.directory,
|
worktree: tmp.directory,
|
||||||
})
|
})
|
||||||
|
|
||||||
const ctx = yield* InstanceStore.Service.use((store) => store.reload({ directory: tmp.directory }))
|
const ctx = yield* InstanceStore.use.reload({ directory: tmp.directory })
|
||||||
const tracked = yield* Snapshot.Service.use((snapshot) => snapshot.track()).pipe(
|
const tracked = yield* Snapshot.Service.use((snapshot) => snapshot.track()).pipe(
|
||||||
Effect.provideService(InstanceRef, ctx),
|
Effect.provideService(InstanceRef, ctx),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ describe("session action routes", () => {
|
|||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const test = yield* TestInstance
|
const test = yield* TestInstance
|
||||||
const session = yield* Effect.acquireRelease(
|
const session = yield* Effect.acquireRelease(
|
||||||
SessionNs.Service.use((svc) => svc.create({})),
|
SessionNs.use.create({}),
|
||||||
(created) => SessionNs.Service.use((svc) => svc.remove(created.id)).pipe(Effect.ignore),
|
(created) => SessionNs.use.remove(created.id).pipe(Effect.ignore),
|
||||||
)
|
)
|
||||||
|
|
||||||
const res = yield* Effect.promise(() =>
|
const res = yield* Effect.promise(() =>
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ function pathFor(template: string, params: Record<string, string>) {
|
|||||||
|
|
||||||
const withSession = (input?: Parameters<Session.Interface["create"]>[0]) =>
|
const withSession = (input?: Parameters<Session.Interface["create"]>[0]) =>
|
||||||
Effect.acquireRelease(
|
Effect.acquireRelease(
|
||||||
Session.Service.use((session) => session.create(input)),
|
Session.use.create(input),
|
||||||
(created) => Session.Service.use((session) => session.remove(created.id)).pipe(Effect.ignore),
|
(created) => Session.use.remove(created.id).pipe(Effect.ignore),
|
||||||
)
|
)
|
||||||
|
|
||||||
describe("session diff with missing patch (#26574)", () => {
|
describe("session diff with missing patch (#26574)", () => {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const it = testEffect(
|
|||||||
|
|
||||||
const withSession = (input?: Parameters<SessionNs.Interface["create"]>[0]) =>
|
const withSession = (input?: Parameters<SessionNs.Interface["create"]>[0]) =>
|
||||||
Effect.acquireRelease(
|
Effect.acquireRelease(
|
||||||
SessionNs.Service.use((session) => session.create(input)),
|
SessionNs.use.create(input),
|
||||||
(created) => SessionNs.Service.use((session) => session.remove(created.id).pipe(Effect.ignore)),
|
(created) => SessionNs.Service.use((session) => session.remove(created.id).pipe(Effect.ignore)),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ describe("session.list", () => {
|
|||||||
provideInstance(path.join(test.directory, "packages", "app")),
|
provideInstance(path.join(test.directory, "packages", "app")),
|
||||||
)
|
)
|
||||||
|
|
||||||
const ids = (yield* SessionNs.Service.use((session) => session.list())).map((session) => session.id)
|
const ids = (yield* SessionNs.use.list()).map((session) => session.id)
|
||||||
expect(ids).toContain(root.id)
|
expect(ids).toContain(root.id)
|
||||||
expect(ids).toContain(parent.id)
|
expect(ids).toContain(parent.id)
|
||||||
expect(ids).toContain(current.id)
|
expect(ids).toContain(current.id)
|
||||||
@@ -179,7 +179,7 @@ describe("session.list", () => {
|
|||||||
const root = yield* withSession({ title: "root-session" })
|
const root = yield* withSession({ title: "root-session" })
|
||||||
const child = yield* withSession({ title: "child-session", parentID: root.id })
|
const child = yield* withSession({ title: "child-session", parentID: root.id })
|
||||||
|
|
||||||
const sessions = yield* SessionNs.Service.use((session) => session.list({ roots: true }))
|
const sessions = yield* SessionNs.use.list({ roots: true })
|
||||||
const ids = sessions.map((session) => session.id)
|
const ids = sessions.map((session) => session.id)
|
||||||
|
|
||||||
expect(ids).toContain(root.id)
|
expect(ids).toContain(root.id)
|
||||||
@@ -206,7 +206,7 @@ describe("session.list", () => {
|
|||||||
yield* withSession({ title: "unique-search-term-abc" })
|
yield* withSession({ title: "unique-search-term-abc" })
|
||||||
yield* withSession({ title: "other-session-xyz" })
|
yield* withSession({ title: "other-session-xyz" })
|
||||||
|
|
||||||
const sessions = yield* SessionNs.Service.use((session) => session.list({ search: "unique-search" }))
|
const sessions = yield* SessionNs.use.list({ search: "unique-search" })
|
||||||
const titles = sessions.map((session) => session.title)
|
const titles = sessions.map((session) => session.title)
|
||||||
|
|
||||||
expect(titles).toContain("unique-search-term-abc")
|
expect(titles).toContain("unique-search-term-abc")
|
||||||
@@ -223,7 +223,7 @@ describe("session.list", () => {
|
|||||||
yield* withSession({ title: "session-2" })
|
yield* withSession({ title: "session-2" })
|
||||||
yield* withSession({ title: "session-3" })
|
yield* withSession({ title: "session-3" })
|
||||||
|
|
||||||
const sessions = yield* SessionNs.Service.use((session) => session.list({ limit: 2 }))
|
const sessions = yield* SessionNs.use.list({ limit: 2 })
|
||||||
expect(sessions.length).toBe(2)
|
expect(sessions.length).toBe(2)
|
||||||
}),
|
}),
|
||||||
{ git: true },
|
{ git: true },
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ const withoutWatcher = <A, E, R>(effect: Effect.Effect<A, E, R>) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const sessionScoped = Effect.acquireRelease(
|
const sessionScoped = Effect.acquireRelease(
|
||||||
SessionNs.Service.use((svc) => svc.create({})),
|
SessionNs.use.create({}),
|
||||||
(session) => SessionNs.Service.use((svc) => svc.remove(session.id)).pipe(Effect.ignore),
|
(session) => SessionNs.use.remove(session.id).pipe(Effect.ignore),
|
||||||
)
|
)
|
||||||
|
|
||||||
const fill = Effect.fn("SessionMessagesTest.fill")(function* (
|
const fill = Effect.fn("SessionMessagesTest.fill")(function* (
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ describe("tui.selectSession endpoint", () => {
|
|||||||
() =>
|
() =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const tmp = yield* TestInstance
|
const tmp = yield* TestInstance
|
||||||
const session = yield* Session.Service.use((svc) => svc.create({}))
|
const session = yield* Session.use.create({})
|
||||||
|
|
||||||
const app = Server.Default().app
|
const app = Server.Default().app
|
||||||
const response = yield* Effect.promise(() =>
|
const response = yield* Effect.promise(() =>
|
||||||
|
|||||||
@@ -292,7 +292,7 @@ function createSummaryCompaction(sessionID: SessionID) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function readCompactionPart(sessionID: SessionID) {
|
function readCompactionPart(sessionID: SessionID) {
|
||||||
return SessionNs.Service.use((ssn) => ssn.messages({ sessionID })).pipe(
|
return SessionNs.use.messages({ sessionID }).pipe(
|
||||||
Effect.map((messages) =>
|
Effect.map((messages) =>
|
||||||
messages.at(-2)?.parts.find((item): item is MessageV2.CompactionPart => item.type === "compaction"),
|
messages.at(-2)?.parts.find((item): item is MessageV2.CompactionPart => item.type === "compaction"),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ const awaitDeferred = <T>(deferred: Deferred.Deferred<T>, message: string) =>
|
|||||||
Effect.sleep("2 seconds").pipe(Effect.flatMap(() => Effect.fail(new Error(message)))),
|
Effect.sleep("2 seconds").pipe(Effect.flatMap(() => Effect.fail(new Error(message)))),
|
||||||
)
|
)
|
||||||
|
|
||||||
const remove = (id: SessionID) => SessionNs.Service.use((svc) => svc.remove(id))
|
const remove = (id: SessionID) => SessionNs.use.remove(id)
|
||||||
|
|
||||||
const subscribeGlobal = (type: string, callback: (event: NonNullable<GlobalEvent["payload"]>) => void) => {
|
const subscribeGlobal = (type: string, callback: (event: NonNullable<GlobalEvent["payload"]>) => void) => {
|
||||||
const listener = (event: GlobalEvent) => {
|
const listener = (event: GlobalEvent) => {
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ describe("ShareNext", () => {
|
|||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
yield* seed("https://control.example.com", "org-1")
|
yield* seed("https://control.example.com", "org-1")
|
||||||
|
|
||||||
const req = yield* ShareNext.Service.use((svc) => svc.request()).pipe(Effect.provide(live(none)))
|
const req = yield* ShareNext.use.request().pipe(Effect.provide(live(none)))
|
||||||
|
|
||||||
expect(req.api.create).toBe("/api/shares")
|
expect(req.api.create).toBe("/api/shares")
|
||||||
expect(req.api.sync("shr_123")).toBe("/api/shares/shr_123/sync")
|
expect(req.api.sync("shr_123")).toBe("/api/shares/shr_123/sync")
|
||||||
@@ -147,7 +147,7 @@ describe("ShareNext", () => {
|
|||||||
provideTmpdirInstance(
|
provideTmpdirInstance(
|
||||||
() =>
|
() =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const session = yield* Session.Service.use((svc) => svc.create({ title: "test" }))
|
const session = yield* Session.use.create({ title: "test" })
|
||||||
const seen: HttpClientRequest.HttpClientRequest[] = []
|
const seen: HttpClientRequest.HttpClientRequest[] = []
|
||||||
const client = HttpClient.make((req) => {
|
const client = HttpClient.make((req) => {
|
||||||
seen.push(req)
|
seen.push(req)
|
||||||
@@ -163,7 +163,7 @@ describe("ShareNext", () => {
|
|||||||
return Effect.succeed(json(req, { ok: true }))
|
return Effect.succeed(json(req, { ok: true }))
|
||||||
})
|
})
|
||||||
|
|
||||||
const result = yield* ShareNext.Service.use((svc) => svc.create(session.id)).pipe(
|
const result = yield* ShareNext.use.create(session.id).pipe(
|
||||||
Effect.provide(live(client)),
|
Effect.provide(live(client)),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -188,7 +188,7 @@ describe("ShareNext", () => {
|
|||||||
provideTmpdirInstance(
|
provideTmpdirInstance(
|
||||||
() =>
|
() =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const session = yield* Session.Service.use((svc) => svc.create({ title: "test" }))
|
const session = yield* Session.use.create({ title: "test" })
|
||||||
const seen: HttpClientRequest.HttpClientRequest[] = []
|
const seen: HttpClientRequest.HttpClientRequest[] = []
|
||||||
const client = HttpClient.make((req) => {
|
const client = HttpClient.make((req) => {
|
||||||
seen.push(req)
|
seen.push(req)
|
||||||
@@ -205,8 +205,8 @@ describe("ShareNext", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
yield* Effect.gen(function* () {
|
yield* Effect.gen(function* () {
|
||||||
yield* ShareNext.Service.use((svc) => svc.create(session.id))
|
yield* ShareNext.use.create(session.id)
|
||||||
yield* ShareNext.Service.use((svc) => svc.remove(session.id))
|
yield* ShareNext.use.remove(session.id)
|
||||||
}).pipe(Effect.provide(live(client)))
|
}).pipe(Effect.provide(live(client)))
|
||||||
|
|
||||||
expect(share(session.id)).toBeUndefined()
|
expect(share(session.id)).toBeUndefined()
|
||||||
@@ -222,7 +222,7 @@ describe("ShareNext", () => {
|
|||||||
it.live("create fails on a non-ok response and does not persist a share", () =>
|
it.live("create fails on a non-ok response and does not persist a share", () =>
|
||||||
provideTmpdirInstance(() =>
|
provideTmpdirInstance(() =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const session = yield* Session.Service.use((svc) => svc.create({ title: "test" }))
|
const session = yield* Session.use.create({ title: "test" })
|
||||||
const client = HttpClient.make((req) => Effect.succeed(json(req, { error: "bad" }, 500)))
|
const client = HttpClient.make((req) => Effect.succeed(json(req, { error: "bad" }, 500)))
|
||||||
|
|
||||||
const exit = yield* ShareNext.Service.use((svc) => Effect.exit(svc.create(session.id))).pipe(
|
const exit = yield* ShareNext.Service.use((svc) => Effect.exit(svc.create(session.id))).pipe(
|
||||||
|
|||||||
Reference in New Issue
Block a user