chore(observability): merge v2
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.17.14",
|
||||
"version": "1.17.15",
|
||||
"name": "opencode",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -84,6 +84,7 @@ export const AttachCommand = cmd({
|
||||
const { createLegacyTuiPluginHost } = await import("@/plugin/tui/runtime")
|
||||
await Effect.runPromise(
|
||||
run({
|
||||
// @ts-expect-error V1 does not consume the V2-only server input.
|
||||
client: createOpencodeClient({ baseUrl: args.url, headers, directory }),
|
||||
api: OpenCode.make({ baseUrl: args.url, headers }),
|
||||
config,
|
||||
|
||||
@@ -112,6 +112,7 @@ export const RunCommand = effectCmd({
|
||||
file: args.file ?? [],
|
||||
title: args.title,
|
||||
server: args.server ?? args.attach,
|
||||
// @ts-expect-error V1 does not consume the V2-only resolved server input.
|
||||
password: args.password ?? process.env.OPENCODE_PASSWORD ?? process.env.OPENCODE_SERVER_PASSWORD,
|
||||
username: args.username ?? process.env.OPENCODE_SERVER_USERNAME,
|
||||
directory: args.dir,
|
||||
|
||||
@@ -159,6 +159,7 @@ export const TuiThreadCommand = cmd({
|
||||
const { createLegacyTuiPluginHost } = await import("@/plugin/tui/runtime")
|
||||
await Effect.runPromise(
|
||||
run({
|
||||
// @ts-expect-error V1 does not consume the V2-only server input.
|
||||
client: createOpencodeClient({ baseUrl: url, headers, directory: cwd }),
|
||||
api: OpenCode.make({ baseUrl: url, headers }),
|
||||
async onSnapshot() {
|
||||
|
||||
@@ -293,6 +293,7 @@ const layer = Layer.effect(
|
||||
})
|
||||
|
||||
const ensureGitignore = Effect.fn("Config.ensureGitignore")(function* (dir: string) {
|
||||
yield* fs.ensureDir(dir)
|
||||
const gitignore = path.join(dir, ".gitignore")
|
||||
const hasIgnore = yield* fs.existsSafe(gitignore)
|
||||
if (!hasIgnore) {
|
||||
|
||||
@@ -10,7 +10,7 @@ function ok<T>(data: T) {
|
||||
}
|
||||
|
||||
function form(id: string, sessionID: string): FormInfo {
|
||||
return { id, sessionID, mode: "form", fields: [] }
|
||||
return { id, sessionID, title: "Input requested", mode: "form", fields: [] }
|
||||
}
|
||||
|
||||
function formCreated(info: FormInfo): V2Event {
|
||||
@@ -21,7 +21,7 @@ function prompted(inputID: string): V2Event {
|
||||
return {
|
||||
id: "evt_prompted",
|
||||
created: 0,
|
||||
type: "session.prompt.promoted",
|
||||
type: "session.input.promoted",
|
||||
durable: { aggregateID: "ses_1", seq: 0, version: 1 },
|
||||
data: { sessionID: "ses_1", inputID },
|
||||
}
|
||||
@@ -84,7 +84,7 @@ async function run(input: { turn: (inputID: string) => V2Event[]; pendingForms?:
|
||||
files: [],
|
||||
thinking: false,
|
||||
format: "default",
|
||||
dangerouslySkipPermissions: false,
|
||||
auto: false,
|
||||
attached: input.attached ?? false,
|
||||
renderTool: () => Promise.resolve(),
|
||||
renderToolError: () => Promise.resolve(),
|
||||
|
||||
@@ -63,6 +63,23 @@ function durable(sessionID: string, seq = 0, version: 1 | 2 = 1) {
|
||||
return { aggregateID: sessionID, seq, version }
|
||||
}
|
||||
|
||||
function promptAdmission(input: Parameters<OpenCodeClient["session"]["prompt"]>[0], sessionID = "ses_1") {
|
||||
return {
|
||||
admittedSeq: 1,
|
||||
id: input.id ?? "msg_prompt",
|
||||
sessionID,
|
||||
type: "user" as const,
|
||||
data: {
|
||||
text: input.text,
|
||||
files: input.files,
|
||||
agents: input.agents,
|
||||
metadata: input.metadata,
|
||||
},
|
||||
delivery: input.delivery ?? ("steer" as const),
|
||||
timeCreated: 2,
|
||||
}
|
||||
}
|
||||
|
||||
function footer() {
|
||||
const commits: StreamCommit[] = []
|
||||
const events: FooterEvent[] = []
|
||||
@@ -170,19 +187,8 @@ describe("V2 mini transport", () => {
|
||||
|
||||
let admitted = false
|
||||
spyOn(client.session, "prompt").mockImplementation((request) => {
|
||||
const messageID = request.id ?? "msg_prompt"
|
||||
const prompt = request.prompt ?? { text: "" }
|
||||
admitted = true
|
||||
return ok({
|
||||
data: {
|
||||
admittedSeq: 1,
|
||||
id: messageID,
|
||||
sessionID: "ses_1",
|
||||
prompt,
|
||||
delivery: "steer" as const,
|
||||
timeCreated: 2,
|
||||
},
|
||||
}) as never
|
||||
return ok({ data: promptAdmission(request) }) as never
|
||||
})
|
||||
|
||||
const turn = transport.runPromptTurn({
|
||||
@@ -197,7 +203,7 @@ describe("V2 mini transport", () => {
|
||||
events.push({
|
||||
id: "evt_prompted",
|
||||
created: 0,
|
||||
type: "session.prompt.promoted",
|
||||
type: "session.input.promoted",
|
||||
durable: durable("ses_1"),
|
||||
data: {
|
||||
sessionID: "ses_1",
|
||||
@@ -255,7 +261,7 @@ describe("V2 mini transport", () => {
|
||||
events.push({
|
||||
id: "evt_prompted",
|
||||
created: 0,
|
||||
type: "session.prompt.promoted",
|
||||
type: "session.input.promoted",
|
||||
durable: durable("ses_1"),
|
||||
data: {
|
||||
sessionID: "ses_1",
|
||||
@@ -270,16 +276,7 @@ describe("V2 mini transport", () => {
|
||||
data: { sessionID: "ses_1" },
|
||||
})
|
||||
})
|
||||
return ok({
|
||||
data: {
|
||||
admittedSeq: 1,
|
||||
id: input.id ?? "msg_prompt",
|
||||
sessionID: "ses_1",
|
||||
prompt: input.prompt ?? { text: "" },
|
||||
delivery: "steer" as const,
|
||||
timeCreated: 2,
|
||||
},
|
||||
}) as never
|
||||
return ok({ data: promptAdmission(input) }) as never
|
||||
})
|
||||
|
||||
await transport.runPromptTurn({
|
||||
@@ -310,8 +307,8 @@ describe("V2 mini transport", () => {
|
||||
includeFiles: true,
|
||||
})
|
||||
|
||||
expect(request?.prompt?.text).toBe("Review @note.ts and @docs")
|
||||
expect(request?.prompt?.files).toEqual([
|
||||
expect(request?.text).toBe("Review @note.ts and @docs")
|
||||
expect(request?.files).toEqual([
|
||||
{
|
||||
uri: pathToFileURL(filePath).href,
|
||||
name: "note.ts",
|
||||
@@ -350,7 +347,7 @@ describe("V2 mini transport", () => {
|
||||
events.push({
|
||||
id: "evt_prompted",
|
||||
created: 0,
|
||||
type: "session.prompt.promoted",
|
||||
type: "session.input.promoted",
|
||||
durable: durable("ses_1"),
|
||||
data: {
|
||||
sessionID: "ses_1",
|
||||
@@ -365,16 +362,7 @@ describe("V2 mini transport", () => {
|
||||
data: { sessionID: "ses_1" },
|
||||
})
|
||||
})
|
||||
return ok({
|
||||
data: {
|
||||
admittedSeq: 1,
|
||||
id: input.id ?? "msg_prompt",
|
||||
sessionID: "ses_1",
|
||||
prompt: input.prompt ?? { text: "" },
|
||||
delivery: "steer" as const,
|
||||
timeCreated: 2,
|
||||
},
|
||||
})
|
||||
return ok({ data: promptAdmission(input) })
|
||||
})
|
||||
|
||||
await transport.runPromptTurn({
|
||||
@@ -407,8 +395,8 @@ describe("V2 mini transport", () => {
|
||||
|
||||
expect(remoteRead).not.toHaveBeenCalled()
|
||||
expect(remoteList).not.toHaveBeenCalled()
|
||||
expect(request?.prompt?.text).toBe("Review @note.ts and @docs")
|
||||
expect(request?.prompt?.files).toEqual([
|
||||
expect(request?.text).toBe("Review @note.ts and @docs")
|
||||
expect(request?.files).toEqual([
|
||||
{
|
||||
uri: "file:///remote/project/note.ts",
|
||||
name: "note.ts",
|
||||
@@ -448,7 +436,7 @@ describe("V2 mini transport", () => {
|
||||
events.push({
|
||||
id: "evt_prompted",
|
||||
created: 0,
|
||||
type: "session.prompt.promoted",
|
||||
type: "session.input.promoted",
|
||||
durable: durable("ses_1"),
|
||||
data: {
|
||||
sessionID: "ses_1",
|
||||
@@ -463,16 +451,7 @@ describe("V2 mini transport", () => {
|
||||
data: { sessionID: "ses_1" },
|
||||
})
|
||||
})
|
||||
return ok({
|
||||
data: {
|
||||
admittedSeq: 1,
|
||||
id: input.id ?? "msg_prompt",
|
||||
sessionID: "ses_1",
|
||||
prompt: input.prompt ?? { text: "" },
|
||||
delivery: "steer" as const,
|
||||
timeCreated: 2,
|
||||
},
|
||||
})
|
||||
return ok({ data: promptAdmission(input) })
|
||||
})
|
||||
|
||||
await transport.runPromptTurn({
|
||||
@@ -496,8 +475,8 @@ describe("V2 mini transport", () => {
|
||||
includeFiles: true,
|
||||
})
|
||||
|
||||
expect(request?.prompt?.text).toBe("Review @diagram.png")
|
||||
expect(request?.prompt?.files).toEqual([
|
||||
expect(request?.text).toBe("Review @diagram.png")
|
||||
expect(request?.files).toEqual([
|
||||
{
|
||||
name: "diagram.png",
|
||||
uri: pathToFileURL(filePath).href,
|
||||
@@ -589,12 +568,8 @@ describe("V2 mini transport", () => {
|
||||
// The generated method has conditional return types for throwOnError; this mock represents the successful branch.
|
||||
// @ts-expect-error successful SDK response is valid for both modes at runtime
|
||||
spyOn(client.session, "prompt").mockImplementation((request) => {
|
||||
const messageID = request.id ?? "msg_prompt"
|
||||
const prompt = request.prompt ?? { text: "" }
|
||||
admitted = true
|
||||
return ok({
|
||||
data: { admittedSeq: 1, id: messageID, sessionID: "ses_1", prompt, delivery: "steer" as const, timeCreated: 2 },
|
||||
})
|
||||
return ok({ data: promptAdmission(request) })
|
||||
})
|
||||
|
||||
const turn = transport.runPromptTurn({
|
||||
@@ -661,12 +636,8 @@ describe("V2 mini transport", () => {
|
||||
// The generated method has conditional return types for throwOnError; this mock represents the successful branch.
|
||||
// @ts-expect-error successful SDK response is valid for both modes at runtime
|
||||
spyOn(client.session, "prompt").mockImplementation((request) => {
|
||||
const messageID = request.id ?? "msg_prompt"
|
||||
const prompt = request.prompt ?? { text: "" }
|
||||
admitted = true
|
||||
return ok({
|
||||
data: { admittedSeq: 1, id: messageID, sessionID: "ses_1", prompt, delivery: "steer" as const, timeCreated: 2 },
|
||||
})
|
||||
return ok({ data: promptAdmission(request) })
|
||||
})
|
||||
|
||||
const turn = transport.runPromptTurn({
|
||||
@@ -899,12 +870,8 @@ describe("V2 mini transport", () => {
|
||||
// The generated method has conditional return types for throwOnError; this mock represents the successful branch.
|
||||
// @ts-expect-error successful SDK response is valid for both modes at runtime
|
||||
spyOn(client.session, "prompt").mockImplementation((request) => {
|
||||
const messageID = request.id ?? "msg_prompt"
|
||||
const prompt = request.prompt ?? { text: "" }
|
||||
admitted = true
|
||||
return ok({
|
||||
data: { admittedSeq: 1, id: messageID, sessionID: "ses_1", prompt, delivery: "steer" as const, timeCreated: 2 },
|
||||
})
|
||||
return ok({ data: promptAdmission(request) })
|
||||
})
|
||||
const interrupted = spyOn(client.session, "interrupt").mockImplementation(() => ok(undefined))
|
||||
|
||||
@@ -956,12 +923,8 @@ describe("V2 mini transport", () => {
|
||||
// The generated method has conditional return types for throwOnError; this mock represents the successful branch.
|
||||
// @ts-expect-error successful SDK response is valid for both modes at runtime
|
||||
spyOn(client.session, "prompt").mockImplementation((request) => {
|
||||
const messageID = request.id ?? "msg_prompt"
|
||||
const prompt = request.prompt ?? { text: "" }
|
||||
admitted = true
|
||||
return ok({
|
||||
data: { admittedSeq: 1, id: messageID, sessionID: "ses_1", prompt, delivery: "steer" as const, timeCreated: 2 },
|
||||
})
|
||||
return ok({ data: promptAdmission(request) })
|
||||
})
|
||||
|
||||
const turn = transport.runPromptTurn({
|
||||
@@ -976,7 +939,7 @@ describe("V2 mini transport", () => {
|
||||
events.push({
|
||||
id: "evt_prompted",
|
||||
created: 0,
|
||||
type: "session.prompt.promoted",
|
||||
type: "session.input.promoted",
|
||||
durable: durable("ses_1"),
|
||||
data: {
|
||||
sessionID: "ses_1",
|
||||
@@ -1015,12 +978,8 @@ describe("V2 mini transport", () => {
|
||||
// The generated method has conditional return types for throwOnError; this mock represents the successful branch.
|
||||
// @ts-expect-error successful SDK response is valid for both modes at runtime
|
||||
spyOn(client.session, "prompt").mockImplementation((request) => {
|
||||
const messageID = request.id ?? "msg_prompt"
|
||||
const prompt = request.prompt ?? { text: "" }
|
||||
admitted = true
|
||||
return ok({
|
||||
data: { admittedSeq: 1, id: messageID, sessionID: "ses_1", prompt, delivery: "steer" as const, timeCreated: 2 },
|
||||
})
|
||||
return ok({ data: promptAdmission(request) })
|
||||
})
|
||||
const interrupted = spyOn(client.session, "interrupt").mockImplementation(() => ok(undefined))
|
||||
const controller = new AbortController()
|
||||
@@ -1037,7 +996,7 @@ describe("V2 mini transport", () => {
|
||||
events.push({
|
||||
id: "evt_prompted",
|
||||
created: 0,
|
||||
type: "session.prompt.promoted",
|
||||
type: "session.input.promoted",
|
||||
durable: durable("ses_1"),
|
||||
data: {
|
||||
sessionID: "ses_1",
|
||||
@@ -1464,7 +1423,7 @@ describe("V2 mini transport", () => {
|
||||
events.push({
|
||||
id: "evt_prompted",
|
||||
created: 0,
|
||||
type: "session.prompt.promoted",
|
||||
type: "session.input.promoted",
|
||||
durable: durable("ses_1"),
|
||||
data: {
|
||||
sessionID: "ses_1",
|
||||
@@ -1483,7 +1442,8 @@ describe("V2 mini transport", () => {
|
||||
admittedSeq: 1,
|
||||
id: input.id ?? "msg_cmd",
|
||||
sessionID: "ses_1",
|
||||
prompt: { text: "evaluated template" },
|
||||
type: "user" as const,
|
||||
data: { text: "evaluated template" },
|
||||
delivery: "steer" as const,
|
||||
timeCreated: 2,
|
||||
})
|
||||
@@ -1862,13 +1822,12 @@ describe("V2 mini transport", () => {
|
||||
events.push({
|
||||
id: "evt_child_admitted",
|
||||
created: 1,
|
||||
type: "session.prompt.admitted",
|
||||
type: "session.input.admitted",
|
||||
durable: durable("ses_child"),
|
||||
data: {
|
||||
sessionID: "ses_child",
|
||||
inputID: "msg_child_prompt",
|
||||
prompt: { text: "actual child prompt" },
|
||||
delivery: "steer",
|
||||
input: { type: "user", data: { text: "actual child prompt" }, delivery: "steer" },
|
||||
},
|
||||
})
|
||||
await Bun.sleep(0)
|
||||
@@ -1881,7 +1840,7 @@ describe("V2 mini transport", () => {
|
||||
events.push({
|
||||
id: "evt_child_promoted",
|
||||
created: 2,
|
||||
type: "session.prompt.promoted",
|
||||
type: "session.input.promoted",
|
||||
durable: durable("ses_child", 1),
|
||||
data: { sessionID: "ses_child", inputID: "msg_child_prompt" },
|
||||
})
|
||||
@@ -1928,13 +1887,12 @@ describe("V2 mini transport", () => {
|
||||
events.push({
|
||||
id: "evt_child_admitted_race",
|
||||
created: 1,
|
||||
type: "session.prompt.admitted",
|
||||
type: "session.input.admitted",
|
||||
durable: durable("ses_child"),
|
||||
data: {
|
||||
sessionID: "ses_child",
|
||||
inputID: "msg_child_race",
|
||||
prompt: { text: "prompt admitted before hydration" },
|
||||
delivery: "steer",
|
||||
input: { type: "user", data: { text: "prompt admitted before hydration" }, delivery: "steer" },
|
||||
},
|
||||
})
|
||||
await Bun.sleep(0)
|
||||
@@ -1943,7 +1901,7 @@ describe("V2 mini transport", () => {
|
||||
events.push({
|
||||
id: "evt_child_promoted_race",
|
||||
created: 2,
|
||||
type: "session.prompt.promoted",
|
||||
type: "session.input.promoted",
|
||||
durable: durable("ses_child", 1),
|
||||
data: { sessionID: "ses_child", inputID: "msg_child_race" },
|
||||
})
|
||||
|
||||
@@ -923,6 +923,31 @@ it.effect("does not try to install dependencies in read-only OPENCODE_CONFIG_DIR
|
||||
}).pipe(Effect.provide(testInstanceStoreLayer), Effect.provide(LayerNode.compile(CrossSpawnSpawner.node))),
|
||||
)
|
||||
|
||||
it.effect("ignores an inaccessible OPENCODE_CONFIG_DIR", () =>
|
||||
Effect.gen(function* () {
|
||||
if (process.platform === "win32") return
|
||||
|
||||
const dir = yield* tmpdirScoped()
|
||||
const configDir = path.join(dir, "inaccessible")
|
||||
yield* FSUtil.use.ensureDir(configDir)
|
||||
yield* FSUtil.use.chmod(configDir, 0o000)
|
||||
yield* Effect.addFinalizer(() => FSUtil.use.chmod(configDir, 0o755).pipe(Effect.ignore))
|
||||
|
||||
yield* withProcessEnv("OPENCODE_CONFIG_DIR", configDir, Config.use.get().pipe(provideInstanceEffect(dir)))
|
||||
}).pipe(Effect.provide(testInstanceStoreLayer), Effect.provide(LayerNode.compile(CrossSpawnSpawner.node))),
|
||||
)
|
||||
|
||||
it.effect("creates a missing OPENCODE_CONFIG_DIR", () =>
|
||||
Effect.gen(function* () {
|
||||
const dir = yield* tmpdirScoped()
|
||||
const configDir = path.join(dir, "configdir")
|
||||
|
||||
yield* withProcessEnv("OPENCODE_CONFIG_DIR", configDir, Config.use.get().pipe(provideInstanceEffect(dir)))
|
||||
|
||||
expect(yield* FSUtil.use.readFileString(path.join(configDir, ".gitignore"))).toContain("node_modules")
|
||||
}).pipe(Effect.provide(testInstanceStoreLayer), Effect.provide(LayerNode.compile(CrossSpawnSpawner.node))),
|
||||
)
|
||||
|
||||
it.effect("installs dependencies in writable OPENCODE_CONFIG_DIR", () =>
|
||||
Effect.gen(function* () {
|
||||
const dir = yield* tmpdirScoped()
|
||||
|
||||
Reference in New Issue
Block a user