chore: generate
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
|
||||
This is a plugin to simulate a remote environment locally. Add this to `.opencode/opencode.jsonc`:
|
||||
|
||||
```json
|
||||
@@ -15,6 +14,6 @@ With the plugin install, you can now run OpenCode and create a `debug` workspace
|
||||
|
||||
How this works:
|
||||
|
||||
* The workspace server needs to know the workspace id and port to run. It waits for this information to be written to a file and starts the server when the data is written.
|
||||
* The debug plugin writes this information in the `create` call to the workspace. So create a `debug` workspace will always kick off a new external server.
|
||||
* The server script watches for file changes, so whenver you create a new `debug` workspace it will restart with the new information. This means that there is only ever one working `debug` workspace at a time; when you create a new one all previous sessions will show that it can't connect because previous debug workspaces do not exist.
|
||||
- The workspace server needs to know the workspace id and port to run. It waits for this information to be written to a file and starts the server when the data is written.
|
||||
- The debug plugin writes this information in the `create` call to the workspace. So create a `debug` workspace will always kick off a new external server.
|
||||
- The server script watches for file changes, so whenver you create a new `debug` workspace it will restart with the new information. This means that there is only ever one working `debug` workspace at a time; when you create a new one all previous sessions will show that it can't connect because previous debug workspaces do not exist.
|
||||
|
||||
@@ -101,10 +101,13 @@ export class SyncHttpError extends Schema.TaggedErrorClass<SyncHttpError>()("Wor
|
||||
body: Schema.optional(Schema.String),
|
||||
}) {}
|
||||
|
||||
export class WorkspaceNotFoundError extends Schema.TaggedErrorClass<WorkspaceNotFoundError>()("WorkspaceNotFoundError", {
|
||||
export class WorkspaceNotFoundError extends Schema.TaggedErrorClass<WorkspaceNotFoundError>()(
|
||||
"WorkspaceNotFoundError",
|
||||
{
|
||||
message: Schema.String,
|
||||
workspaceID: WorkspaceID,
|
||||
}) {}
|
||||
},
|
||||
) {}
|
||||
|
||||
export class SessionEventsNotFoundError extends Schema.TaggedErrorClass<SessionEventsNotFoundError>()(
|
||||
"WorkspaceSessionEventsNotFoundError",
|
||||
|
||||
@@ -170,7 +170,11 @@ function recordedAdaptor(input: {
|
||||
return input.configure?.(info) ?? info
|
||||
},
|
||||
async create(info, env, from) {
|
||||
calls.create.push({ info: structuredClone(info), env: { ...env }, from: from ? structuredClone(from) : undefined })
|
||||
calls.create.push({
|
||||
info: structuredClone(info),
|
||||
env: { ...env },
|
||||
from: from ? structuredClone(from) : undefined,
|
||||
})
|
||||
await input.create?.(info, env, from)
|
||||
},
|
||||
async remove(info) {
|
||||
@@ -279,12 +283,18 @@ function insertProject(id: ProjectID, worktree: string) {
|
||||
}
|
||||
|
||||
function attachSessionToWorkspace(sessionID: SessionID, workspaceID: WorkspaceID) {
|
||||
Database.use((db) => db.update(SessionTable).set({ workspace_id: workspaceID }).where(eq(SessionTable.id, sessionID)).run())
|
||||
Database.use((db) =>
|
||||
db.update(SessionTable).set({ workspace_id: workspaceID }).where(eq(SessionTable.id, sessionID)).run(),
|
||||
)
|
||||
}
|
||||
|
||||
function sessionSequence(sessionID: SessionID) {
|
||||
return Database.use((db) =>
|
||||
db.select({ seq: EventSequenceTable.seq }).from(EventSequenceTable).where(eq(EventSequenceTable.aggregate_id, sessionID)).get(),
|
||||
db
|
||||
.select({ seq: EventSequenceTable.seq })
|
||||
.from(EventSequenceTable)
|
||||
.where(eq(EventSequenceTable.aggregate_id, sessionID))
|
||||
.get(),
|
||||
)?.seq
|
||||
}
|
||||
|
||||
@@ -308,7 +318,9 @@ function replaceSessionEvents(sessionID: SessionID, count: number) {
|
||||
db.delete(EventSequenceTable).where(eq(EventSequenceTable.aggregate_id, sessionID)).run()
|
||||
if (count === 0) return
|
||||
|
||||
db.insert(EventSequenceTable).values({ aggregate_id: sessionID, seq: count - 1 }).run()
|
||||
db.insert(EventSequenceTable)
|
||||
.values({ aggregate_id: sessionID, seq: count - 1 })
|
||||
.run()
|
||||
db.insert(EventTable)
|
||||
.values(
|
||||
Array.from({ length: count }, (_, i) => ({
|
||||
@@ -522,7 +534,8 @@ describe("workspace-old CRUD", () => {
|
||||
it.live("remote create connects to routed event and history endpoints", () => {
|
||||
const calls: FetchCall[] = []
|
||||
return Effect.gen(function* () {
|
||||
yield* HttpServer.serveEffect()(Effect.gen(function* () {
|
||||
yield* HttpServer.serveEffect()(
|
||||
Effect.gen(function* () {
|
||||
const req = yield* HttpServerRequest.HttpServerRequest
|
||||
const bodyText = yield* req.text
|
||||
const call = {
|
||||
@@ -533,12 +546,15 @@ describe("workspace-old CRUD", () => {
|
||||
json: bodyText ? JSON.parse(bodyText) : undefined,
|
||||
}
|
||||
calls.push(call)
|
||||
if (call.url.pathname === "/base/global/event") return HttpServerResponse.fromWeb(eventStreamResponse([], false))
|
||||
if (call.url.pathname === "/base/global/event")
|
||||
return HttpServerResponse.fromWeb(eventStreamResponse([], false))
|
||||
if (call.url.pathname === "/base/sync/history") return yield* HttpServerResponse.json([])
|
||||
return HttpServerResponse.text("unexpected", { status: 500 })
|
||||
}))
|
||||
}),
|
||||
)
|
||||
const url = yield* serverUrl()
|
||||
yield* provideTmpdirInstance((dir) =>
|
||||
yield* provideTmpdirInstance(
|
||||
(dir) =>
|
||||
Effect.gen(function* () {
|
||||
const workspace = yield* WorkspaceOld.Service
|
||||
const type = unique("remote-create")
|
||||
@@ -547,10 +563,9 @@ describe("workspace-old CRUD", () => {
|
||||
|
||||
const info = yield* workspace.create({ type, branch: null, projectID: Instance.project.id, extra: null })
|
||||
|
||||
expect(calls.map((call) => `${call.method} ${call.url.pathname}${call.url.search}${call.url.hash}`)).toEqual([
|
||||
"GET /base/global/event",
|
||||
"POST /base/sync/history",
|
||||
])
|
||||
expect(
|
||||
calls.map((call) => `${call.method} ${call.url.pathname}${call.url.search}${call.url.hash}`),
|
||||
).toEqual(["GET /base/global/event", "POST /base/sync/history"])
|
||||
expect(calls[1].json).toEqual({})
|
||||
expect((yield* workspace.status()).find((item) => item.workspaceID === info.id)?.status).toBe("connected")
|
||||
expect(yield* workspace.isSyncing(info.id)).toBe(true)
|
||||
@@ -651,11 +666,16 @@ describe("workspace-old sync state", () => {
|
||||
insertWorkspace(withoutSession)
|
||||
registerAdaptor(Instance.project.id, withSessionType, localAdaptor(withSessionDir).adaptor)
|
||||
registerAdaptor(Instance.project.id, withoutSessionType, localAdaptor(withoutSessionDir).adaptor)
|
||||
attachSessionToWorkspace((await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.create({})))).id, withSession.id)
|
||||
attachSessionToWorkspace(
|
||||
(await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.create({})))).id,
|
||||
withSession.id,
|
||||
)
|
||||
|
||||
WorkspaceOld.startWorkspaceSyncing(Instance.project.id)
|
||||
|
||||
await eventually(() => expect(WorkspaceOld.status().find((item) => item.workspaceID === withSession.id)?.status).toBe("connected"))
|
||||
await eventually(() =>
|
||||
expect(WorkspaceOld.status().find((item) => item.workspaceID === withSession.id)?.status).toBe("connected"),
|
||||
)
|
||||
expect(WorkspaceOld.status().find((item) => item.workspaceID === withoutSession.id)?.status).toBeUndefined()
|
||||
await WorkspaceOld.remove(withSession.id)
|
||||
await WorkspaceOld.remove(withoutSession.id)
|
||||
@@ -667,12 +687,21 @@ describe("workspace-old sync state", () => {
|
||||
const type = unique("missing-local")
|
||||
const info = workspaceInfo(Instance.project.id, type)
|
||||
insertWorkspace(info)
|
||||
registerAdaptor(Instance.project.id, type, localAdaptor(path.join(dir, "missing-target"), { createDir: false }).adaptor)
|
||||
attachSessionToWorkspace((await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.create({})))).id, info.id)
|
||||
registerAdaptor(
|
||||
Instance.project.id,
|
||||
type,
|
||||
localAdaptor(path.join(dir, "missing-target"), { createDir: false }).adaptor,
|
||||
)
|
||||
attachSessionToWorkspace(
|
||||
(await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.create({})))).id,
|
||||
info.id,
|
||||
)
|
||||
|
||||
WorkspaceOld.startWorkspaceSyncing(Instance.project.id)
|
||||
|
||||
await eventually(() => expect(WorkspaceOld.status().find((item) => item.workspaceID === info.id)?.status).toBe("error"))
|
||||
await eventually(() =>
|
||||
expect(WorkspaceOld.status().find((item) => item.workspaceID === info.id)?.status).toBe("error"),
|
||||
)
|
||||
expect(await WorkspaceOld.isSyncing(info.id)).toBe(false)
|
||||
await WorkspaceOld.remove(info.id)
|
||||
})
|
||||
@@ -688,12 +717,17 @@ describe("workspace-old sync state", () => {
|
||||
await fs.mkdir(target, { recursive: true })
|
||||
insertWorkspace(info)
|
||||
registerAdaptor(Instance.project.id, type, localAdaptor(target).adaptor)
|
||||
attachSessionToWorkspace((await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.create({})))).id, info.id)
|
||||
attachSessionToWorkspace(
|
||||
(await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.create({})))).id,
|
||||
info.id,
|
||||
)
|
||||
|
||||
WorkspaceOld.startWorkspaceSyncing(Instance.project.id)
|
||||
WorkspaceOld.startWorkspaceSyncing(Instance.project.id)
|
||||
|
||||
await eventually(() => expect(WorkspaceOld.status().find((item) => item.workspaceID === info.id)?.status).toBe("connected"))
|
||||
await eventually(() =>
|
||||
expect(WorkspaceOld.status().find((item) => item.workspaceID === info.id)?.status).toBe("connected"),
|
||||
)
|
||||
expect(
|
||||
captured.events.filter(
|
||||
(event) => event.workspace === info.id && event.payload.type === WorkspaceOld.Event.Status.type,
|
||||
@@ -709,7 +743,8 @@ describe("workspace-old sync state", () => {
|
||||
it.live("remote start emits disconnected, connecting, and connected then refuses duplicate listeners", () => {
|
||||
const calls: FetchCall[] = []
|
||||
return Effect.gen(function* () {
|
||||
yield* HttpServer.serveEffect()(Effect.gen(function* () {
|
||||
yield* HttpServer.serveEffect()(
|
||||
Effect.gen(function* () {
|
||||
const req = yield* HttpServerRequest.HttpServerRequest
|
||||
const bodyText = yield* req.text
|
||||
const call = {
|
||||
@@ -723,9 +758,11 @@ describe("workspace-old sync state", () => {
|
||||
if (call.url.pathname === "/sync/global/event") return HttpServerResponse.fromWeb(eventStreamResponse())
|
||||
if (call.url.pathname === "/sync/sync/history") return HttpServerResponse.fromWeb(Response.json([]))
|
||||
return HttpServerResponse.text("unexpected", { status: 500 })
|
||||
}))
|
||||
}),
|
||||
)
|
||||
const url = yield* serverUrl()
|
||||
yield* provideTmpdirInstance(() =>
|
||||
yield* provideTmpdirInstance(
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const workspace = yield* WorkspaceOld.Service
|
||||
const sessionSvc = yield* SessionNs.Service
|
||||
@@ -738,15 +775,21 @@ describe("workspace-old sync state", () => {
|
||||
attachSessionToWorkspace((yield* sessionSvc.create({})).id, info.id)
|
||||
|
||||
yield* workspace.startWorkspaceSyncing(Instance.project.id)
|
||||
yield* eventuallyEffect(Effect.gen(function* () {
|
||||
expect((yield* workspace.status()).find((item) => item.workspaceID === info.id)?.status).toBe("connected")
|
||||
}))
|
||||
yield* eventuallyEffect(
|
||||
Effect.gen(function* () {
|
||||
expect((yield* workspace.status()).find((item) => item.workspaceID === info.id)?.status).toBe(
|
||||
"connected",
|
||||
)
|
||||
}),
|
||||
)
|
||||
yield* workspace.startWorkspaceSyncing(Instance.project.id)
|
||||
yield* Effect.sleep("25 millis")
|
||||
|
||||
expect(
|
||||
captured.events
|
||||
.filter((event) => event.workspace === info.id && event.payload.type === WorkspaceOld.Event.Status.type)
|
||||
.filter(
|
||||
(event) => event.workspace === info.id && event.payload.type === WorkspaceOld.Event.Status.type,
|
||||
)
|
||||
.map((event) => event.payload.properties.status),
|
||||
).toEqual(["disconnected", "connecting", "connected"])
|
||||
expect(calls.filter((call) => call.url.pathname === "/sync/global/event")).toHaveLength(1)
|
||||
@@ -766,14 +809,17 @@ describe("workspace-old sync state", () => {
|
||||
|
||||
it.live("remote connection HTTP failures set error and clear syncing", () =>
|
||||
Effect.gen(function* () {
|
||||
yield* HttpServer.serveEffect()(Effect.gen(function* () {
|
||||
yield* HttpServer.serveEffect()(
|
||||
Effect.gen(function* () {
|
||||
const req = yield* HttpServerRequest.HttpServerRequest
|
||||
if (new URL(req.url, "http://localhost").pathname === "/failed/global/event")
|
||||
return HttpServerResponse.text("nope", { status: 503 })
|
||||
return HttpServerResponse.fromWeb(Response.json([]))
|
||||
}))
|
||||
}),
|
||||
)
|
||||
const url = yield* serverUrl()
|
||||
yield* provideTmpdirInstance(() =>
|
||||
yield* provideTmpdirInstance(
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const workspace = yield* WorkspaceOld.Service
|
||||
const sessionSvc = yield* SessionNs.Service
|
||||
@@ -785,9 +831,11 @@ describe("workspace-old sync state", () => {
|
||||
|
||||
yield* workspace.startWorkspaceSyncing(Instance.project.id)
|
||||
|
||||
yield* eventuallyEffect(Effect.gen(function* () {
|
||||
yield* eventuallyEffect(
|
||||
Effect.gen(function* () {
|
||||
expect((yield* workspace.status()).find((item) => item.workspaceID === info.id)?.status).toBe("error")
|
||||
}))
|
||||
}),
|
||||
)
|
||||
expect(yield* workspace.isSyncing(info.id)).toBe(false)
|
||||
yield* workspace.remove(info.id)
|
||||
}),
|
||||
@@ -798,15 +846,20 @@ describe("workspace-old sync state", () => {
|
||||
|
||||
it.live("remote history HTTP failures set error", () =>
|
||||
Effect.gen(function* () {
|
||||
yield* HttpServer.serveEffect()(Effect.gen(function* () {
|
||||
yield* HttpServer.serveEffect()(
|
||||
Effect.gen(function* () {
|
||||
const req = yield* HttpServerRequest.HttpServerRequest
|
||||
const url = new URL(req.url, "http://localhost")
|
||||
if (url.pathname === "/history-failed/global/event") return HttpServerResponse.fromWeb(eventStreamResponse([], false))
|
||||
if (url.pathname === "/history-failed/sync/history") return HttpServerResponse.text("history failed", { status: 500 })
|
||||
if (url.pathname === "/history-failed/global/event")
|
||||
return HttpServerResponse.fromWeb(eventStreamResponse([], false))
|
||||
if (url.pathname === "/history-failed/sync/history")
|
||||
return HttpServerResponse.text("history failed", { status: 500 })
|
||||
return HttpServerResponse.fromWeb(Response.json([]))
|
||||
}))
|
||||
}),
|
||||
)
|
||||
const url = yield* serverUrl()
|
||||
yield* provideTmpdirInstance(() =>
|
||||
yield* provideTmpdirInstance(
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const workspace = yield* WorkspaceOld.Service
|
||||
const sessionSvc = yield* SessionNs.Service
|
||||
@@ -818,9 +871,11 @@ describe("workspace-old sync state", () => {
|
||||
|
||||
yield* workspace.startWorkspaceSyncing(Instance.project.id)
|
||||
|
||||
yield* eventuallyEffect(Effect.gen(function* () {
|
||||
yield* eventuallyEffect(
|
||||
Effect.gen(function* () {
|
||||
expect((yield* workspace.status()).find((item) => item.workspaceID === info.id)?.status).toBe("error")
|
||||
}))
|
||||
}),
|
||||
)
|
||||
expect(yield* workspace.isSyncing(info.id)).toBe(false)
|
||||
yield* workspace.remove(info.id)
|
||||
}),
|
||||
@@ -834,7 +889,8 @@ describe("workspace-old sync state", () => {
|
||||
let historySessionID: SessionID | undefined
|
||||
let historyNextSeq = 0
|
||||
return Effect.gen(function* () {
|
||||
yield* HttpServer.serveEffect()(Effect.gen(function* () {
|
||||
yield* HttpServer.serveEffect()(
|
||||
Effect.gen(function* () {
|
||||
const req = yield* HttpServerRequest.HttpServerRequest
|
||||
const bodyText = yield* req.text
|
||||
const url = new URL(req.url, "http://localhost")
|
||||
@@ -854,9 +910,11 @@ describe("workspace-old sync state", () => {
|
||||
)
|
||||
}
|
||||
return HttpServerResponse.text("unexpected", { status: 500 })
|
||||
}))
|
||||
}),
|
||||
)
|
||||
const url = yield* serverUrl()
|
||||
yield* provideTmpdirInstance(() =>
|
||||
yield* provideTmpdirInstance(
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const workspace = yield* WorkspaceOld.Service
|
||||
const sessionSvc = yield* SessionNs.Service
|
||||
@@ -873,14 +931,18 @@ describe("workspace-old sync state", () => {
|
||||
|
||||
yield* workspace.startWorkspaceSyncing(Instance.project.id)
|
||||
|
||||
yield* eventuallyEffect(Effect.gen(function* () {
|
||||
yield* eventuallyEffect(
|
||||
Effect.gen(function* () {
|
||||
expect((yield* sessionSvc.get(session.id)).title).toBe("from history")
|
||||
}))
|
||||
}),
|
||||
)
|
||||
expect(historyBodies).toEqual([{ [session.id]: historyNextSeq - 1 }])
|
||||
expect(
|
||||
captured.events.some(
|
||||
(event) =>
|
||||
event.workspace === info.id && event.payload.type === "sync" && event.payload.syncEvent.seq === historyNextSeq,
|
||||
event.workspace === info.id &&
|
||||
event.payload.type === "sync" &&
|
||||
event.payload.syncEvent.seq === historyNextSeq,
|
||||
),
|
||||
).toBe(true)
|
||||
yield* workspace.remove(info.id)
|
||||
@@ -895,7 +957,8 @@ describe("workspace-old sync state", () => {
|
||||
|
||||
it.live("SSE forwards non-heartbeat events and ignores heartbeats", () =>
|
||||
Effect.gen(function* () {
|
||||
yield* HttpServer.serveEffect()(Effect.gen(function* () {
|
||||
yield* HttpServer.serveEffect()(
|
||||
Effect.gen(function* () {
|
||||
const req = yield* HttpServerRequest.HttpServerRequest
|
||||
const url = new URL(req.url, "http://localhost")
|
||||
if (url.pathname === "/sse-forward/global/event")
|
||||
@@ -914,9 +977,11 @@ describe("workspace-old sync state", () => {
|
||||
)
|
||||
if (url.pathname === "/sse-forward/sync/history") return HttpServerResponse.fromWeb(Response.json([]))
|
||||
return HttpServerResponse.text("unexpected", { status: 500 })
|
||||
}))
|
||||
}),
|
||||
)
|
||||
const url = yield* serverUrl()
|
||||
yield* provideTmpdirInstance(() =>
|
||||
yield* provideTmpdirInstance(
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const workspace = yield* WorkspaceOld.Service
|
||||
const sessionSvc = yield* SessionNs.Service
|
||||
@@ -930,16 +995,27 @@ describe("workspace-old sync state", () => {
|
||||
|
||||
yield* workspace.startWorkspaceSyncing(Instance.project.id)
|
||||
|
||||
yield* eventuallyEffect(Effect.sync(() =>
|
||||
expect(captured.events.some((event) => event.workspace === info.id && event.payload.type === "custom.remote"))
|
||||
.toBe(true),
|
||||
))
|
||||
expect(captured.events.some((event) => event.workspace === info.id && event.payload.type === "server.heartbeat")).toBe(
|
||||
false,
|
||||
yield* eventuallyEffect(
|
||||
Effect.sync(() =>
|
||||
expect(
|
||||
captured.events.some(
|
||||
(event) => event.workspace === info.id && event.payload.type === "custom.remote",
|
||||
),
|
||||
).toBe(true),
|
||||
),
|
||||
)
|
||||
expect(
|
||||
captured.events.some(
|
||||
(event) => event.workspace === info.id && event.payload.type === "server.heartbeat",
|
||||
),
|
||||
).toBe(false)
|
||||
expect(
|
||||
captured.events.find((event) => event.workspace === info.id && event.payload.type === "custom.remote"),
|
||||
).toMatchObject({ directory: "remote-dir", project: "remote-project", payload: { properties: { ok: true } } })
|
||||
).toMatchObject({
|
||||
directory: "remote-dir",
|
||||
project: "remote-project",
|
||||
payload: { properties: { ok: true } },
|
||||
})
|
||||
yield* workspace.remove(info.id)
|
||||
} finally {
|
||||
captured.dispose()
|
||||
@@ -954,7 +1030,8 @@ describe("workspace-old sync state", () => {
|
||||
let sseSessionID: SessionID | undefined
|
||||
let sseNextSeq = 0
|
||||
return Effect.gen(function* () {
|
||||
yield* HttpServer.serveEffect()(Effect.gen(function* () {
|
||||
yield* HttpServer.serveEffect()(
|
||||
Effect.gen(function* () {
|
||||
const req = yield* HttpServerRequest.HttpServerRequest
|
||||
const url = new URL(req.url, "http://localhost")
|
||||
if (url.pathname === "/sse-sync/global/event")
|
||||
@@ -981,9 +1058,11 @@ describe("workspace-old sync state", () => {
|
||||
)
|
||||
if (url.pathname === "/sse-sync/sync/history") return HttpServerResponse.fromWeb(Response.json([]))
|
||||
return HttpServerResponse.text("unexpected", { status: 500 })
|
||||
}))
|
||||
}),
|
||||
)
|
||||
const url = yield* serverUrl()
|
||||
yield* provideTmpdirInstance(() =>
|
||||
yield* provideTmpdirInstance(
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const workspace = yield* WorkspaceOld.Service
|
||||
const sessionSvc = yield* SessionNs.Service
|
||||
@@ -1000,12 +1079,17 @@ describe("workspace-old sync state", () => {
|
||||
|
||||
yield* workspace.startWorkspaceSyncing(Instance.project.id)
|
||||
|
||||
yield* eventuallyEffect(Effect.gen(function* () {
|
||||
yield* eventuallyEffect(
|
||||
Effect.gen(function* () {
|
||||
expect((yield* sessionSvc.get(session.id)).title).toBe("from sse")
|
||||
}))
|
||||
}),
|
||||
)
|
||||
expect(
|
||||
captured.events.some(
|
||||
(event) => event.workspace === info.id && event.payload.type === "sync" && event.payload.syncEvent.seq === sseNextSeq,
|
||||
(event) =>
|
||||
event.workspace === info.id &&
|
||||
event.payload.type === "sync" &&
|
||||
event.payload.syncEvent.seq === sseNextSeq,
|
||||
),
|
||||
).toBe(true)
|
||||
yield* workspace.remove(info.id)
|
||||
@@ -1031,8 +1115,12 @@ describe("workspace-old waitForSync", () => {
|
||||
const sessionID = SessionID.descending("ses_wait_done")
|
||||
Database.use((db) => db.insert(EventSequenceTable).values({ aggregate_id: sessionID, seq: 4 }).run())
|
||||
|
||||
await expect(WorkspaceOld.waitForSync(WorkspaceID.ascending("wrk_wait_done"), { [sessionID]: 4 })).resolves.toBeUndefined()
|
||||
await expect(WorkspaceOld.waitForSync(WorkspaceID.ascending("wrk_wait_done_2"), { [sessionID]: 3 })).resolves.toBeUndefined()
|
||||
await expect(
|
||||
WorkspaceOld.waitForSync(WorkspaceID.ascending("wrk_wait_done"), { [sessionID]: 4 }),
|
||||
).resolves.toBeUndefined()
|
||||
await expect(
|
||||
WorkspaceOld.waitForSync(WorkspaceID.ascending("wrk_wait_done_2"), { [sessionID]: 3 }),
|
||||
).resolves.toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1084,13 +1172,15 @@ describe("workspace-old waitForSync", () => {
|
||||
)
|
||||
abort.abort(reason)
|
||||
|
||||
await expect(waited).rejects.toMatchObject({ _tag: "WorkspaceSyncAbortedError", message: reason.message, cause: reason })
|
||||
await expect(waited).rejects.toMatchObject({
|
||||
_tag: "WorkspaceSyncAbortedError",
|
||||
message: reason.message,
|
||||
cause: reason,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test(
|
||||
"times out with the requested fence in the error message",
|
||||
async () => {
|
||||
test("times out with the requested fence in the error message", async () => {
|
||||
await withInstance(async () => {
|
||||
const sessionID = SessionID.descending("ses_wait_timeout")
|
||||
|
||||
@@ -1098,9 +1188,7 @@ describe("workspace-old waitForSync", () => {
|
||||
WorkspaceOld.waitForSync(WorkspaceID.ascending("wrk_wait_timeout"), { [sessionID]: 1 }),
|
||||
).rejects.toThrow(`Timed out waiting for sync fence: {"${sessionID}":1}`)
|
||||
})
|
||||
},
|
||||
7000,
|
||||
)
|
||||
}, 7000)
|
||||
})
|
||||
|
||||
describe("workspace-old sessionRestore", () => {
|
||||
@@ -1132,7 +1220,8 @@ describe("workspace-old sessionRestore", () => {
|
||||
it.live("posts remote replay batches of 10, emits progress, and includes the workspace update event", () => {
|
||||
const replay: FetchCall[] = []
|
||||
return Effect.gen(function* () {
|
||||
yield* HttpServer.serveEffect()(Effect.gen(function* () {
|
||||
yield* HttpServer.serveEffect()(
|
||||
Effect.gen(function* () {
|
||||
const req = yield* HttpServerRequest.HttpServerRequest
|
||||
const bodyText = yield* req.text
|
||||
const call = {
|
||||
@@ -1147,9 +1236,11 @@ describe("workspace-old sessionRestore", () => {
|
||||
return HttpServerResponse.fromWeb(Response.json({ ok: true }))
|
||||
}
|
||||
return HttpServerResponse.text("unexpected", { status: 500 })
|
||||
}))
|
||||
}),
|
||||
)
|
||||
const url = yield* serverUrl()
|
||||
yield* provideTmpdirInstance((dir) =>
|
||||
yield* provideTmpdirInstance(
|
||||
(dir) =>
|
||||
Effect.gen(function* () {
|
||||
const workspace = yield* WorkspaceOld.Service
|
||||
const sessionSvc = yield* SessionNs.Service
|
||||
@@ -1183,9 +1274,13 @@ describe("workspace-old sessionRestore", () => {
|
||||
expect(replay.map((call) => (call.json as { events: unknown[] }).events.length)).toEqual([10, 10, 5])
|
||||
expect(replay.map((call) => (call.json as { directory: string }).directory)).toEqual([dir, dir, dir])
|
||||
expect(
|
||||
replay.flatMap((call) => (call.json as { events: Array<{ seq: number }> }).events.map((event) => event.seq)),
|
||||
replay.flatMap((call) =>
|
||||
(call.json as { events: Array<{ seq: number }> }).events.map((event) => event.seq),
|
||||
),
|
||||
).toEqual(Array.from({ length: 25 }, (_, i) => i))
|
||||
expect((replay[2].json as { events: Array<{ seq: number; type: string; data: unknown }> }).events.at(-1)).toMatchObject({
|
||||
expect(
|
||||
(replay[2].json as { events: Array<{ seq: number; type: string; data: unknown }> }).events.at(-1),
|
||||
).toMatchObject({
|
||||
seq: 24,
|
||||
type: sessionUpdatedType(),
|
||||
data: { sessionID: session.id, info: { workspaceID: info.id } },
|
||||
@@ -1193,7 +1288,9 @@ describe("workspace-old sessionRestore", () => {
|
||||
expect((yield* sessionSvc.get(session.id)).workspaceID).toBe(info.id)
|
||||
expect(
|
||||
captured.events
|
||||
.filter((event) => event.workspace === info.id && event.payload.type === WorkspaceOld.Event.Restore.type)
|
||||
.filter(
|
||||
(event) => event.workspace === info.id && event.payload.type === WorkspaceOld.Event.Restore.type,
|
||||
)
|
||||
.map((event) => event.payload.properties.step),
|
||||
).toEqual([0, 1, 2, 3])
|
||||
yield* workspace.remove(info.id)
|
||||
@@ -1209,7 +1306,8 @@ describe("workspace-old sessionRestore", () => {
|
||||
it.live("remote restore sends an empty directory string when the workspace directory is null", () => {
|
||||
const replay: FetchCall[] = []
|
||||
return Effect.gen(function* () {
|
||||
yield* HttpServer.serveEffect()(Effect.gen(function* () {
|
||||
yield* HttpServer.serveEffect()(
|
||||
Effect.gen(function* () {
|
||||
const req = yield* HttpServerRequest.HttpServerRequest
|
||||
const bodyText = yield* req.text
|
||||
replay.push({
|
||||
@@ -1220,9 +1318,11 @@ describe("workspace-old sessionRestore", () => {
|
||||
json: bodyText ? JSON.parse(bodyText) : undefined,
|
||||
})
|
||||
return HttpServerResponse.fromWeb(Response.json({ ok: true }))
|
||||
}))
|
||||
}),
|
||||
)
|
||||
const url = yield* serverUrl()
|
||||
yield* provideTmpdirInstance(() =>
|
||||
yield* provideTmpdirInstance(
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const workspace = yield* WorkspaceOld.Service
|
||||
const sessionSvc = yield* SessionNs.Service
|
||||
@@ -1233,7 +1333,9 @@ describe("workspace-old sessionRestore", () => {
|
||||
const session = yield* sessionSvc.create({ title: "null dir" })
|
||||
replaceSessionEvents(session.id, 0)
|
||||
|
||||
expect(yield* workspace.sessionRestore({ workspaceID: info.id, sessionID: session.id })).toEqual({ total: 1 })
|
||||
expect(yield* workspace.sessionRestore({ workspaceID: info.id, sessionID: session.id })).toEqual({
|
||||
total: 1,
|
||||
})
|
||||
expect((replay[0].json as { directory: string }).directory).toBe("")
|
||||
expect((replay[0].json as { events: unknown[] }).events).toHaveLength(1)
|
||||
yield* workspace.remove(info.id)
|
||||
@@ -1246,7 +1348,8 @@ describe("workspace-old sessionRestore", () => {
|
||||
it.live("remote restore failures include status and body and do not emit completed batch progress", () => {
|
||||
const replay: FetchCall[] = []
|
||||
return Effect.gen(function* () {
|
||||
yield* HttpServer.serveEffect()(Effect.gen(function* () {
|
||||
yield* HttpServer.serveEffect()(
|
||||
Effect.gen(function* () {
|
||||
const req = yield* HttpServerRequest.HttpServerRequest
|
||||
const bodyText = yield* req.text
|
||||
replay.push({
|
||||
@@ -1257,9 +1360,11 @@ describe("workspace-old sessionRestore", () => {
|
||||
json: bodyText ? JSON.parse(bodyText) : undefined,
|
||||
})
|
||||
return HttpServerResponse.text("replay failed", { status: 503 })
|
||||
}))
|
||||
}),
|
||||
)
|
||||
const url = yield* serverUrl()
|
||||
yield* provideTmpdirInstance((dir) =>
|
||||
yield* provideTmpdirInstance(
|
||||
(dir) =>
|
||||
Effect.gen(function* () {
|
||||
const workspace = yield* WorkspaceOld.Service
|
||||
const sessionSvc = yield* SessionNs.Service
|
||||
@@ -1272,7 +1377,9 @@ describe("workspace-old sessionRestore", () => {
|
||||
const session = yield* sessionSvc.create({ title: "restore fail" })
|
||||
replaceSessionEvents(session.id, 11)
|
||||
|
||||
const error = yield* Effect.flip(workspace.sessionRestore({ workspaceID: info.id, sessionID: session.id }))
|
||||
const error = yield* Effect.flip(
|
||||
workspace.sessionRestore({ workspaceID: info.id, sessionID: session.id }),
|
||||
)
|
||||
expect((error as Error).message).toContain(
|
||||
`Failed to replay session ${session.id} into workspace ${info.id}: HTTP 503 replay failed`,
|
||||
)
|
||||
@@ -1280,7 +1387,9 @@ describe("workspace-old sessionRestore", () => {
|
||||
expect(replay).toHaveLength(1)
|
||||
expect(
|
||||
captured.events
|
||||
.filter((event) => event.workspace === info.id && event.payload.type === WorkspaceOld.Event.Restore.type)
|
||||
.filter(
|
||||
(event) => event.workspace === info.id && event.payload.type === WorkspaceOld.Event.Restore.type,
|
||||
)
|
||||
.map((event) => event.payload.properties.step),
|
||||
).toEqual([0])
|
||||
yield* workspace.remove(info.id)
|
||||
@@ -1310,7 +1419,9 @@ describe("workspace-old sessionRestore", () => {
|
||||
const info = workspaceInfo(Instance.project.id, type, { directory: dir })
|
||||
insertWorkspace(info)
|
||||
registerAdaptor(Instance.project.id, type, localAdaptor(dir).adaptor)
|
||||
const session = await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.create({ title: "restore local" })))
|
||||
const session = await AppRuntime.runPromise(
|
||||
SessionNs.Service.use((svc) => svc.create({ title: "restore local" })),
|
||||
)
|
||||
replaceSessionEvents(session.id, 20)
|
||||
|
||||
expect(await WorkspaceOld.sessionRestore({ workspaceID: info.id, sessionID: session.id })).toEqual({ total: 3 })
|
||||
@@ -1318,7 +1429,9 @@ describe("workspace-old sessionRestore", () => {
|
||||
expect(fetchCallCount).toBe(0)
|
||||
expect(replayAll).toHaveBeenCalledTimes(3)
|
||||
expect(replayAll.mock.calls.map((call) => call[0].length)).toEqual([10, 10, 1])
|
||||
expect((await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.get(session.id)))).workspaceID).toBe(info.id)
|
||||
expect((await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.get(session.id)))).workspaceID).toBe(
|
||||
info.id,
|
||||
)
|
||||
expect(eventRows(session.id).map((row) => row.seq)).toEqual(Array.from({ length: 21 }, (_, i) => i))
|
||||
expect(
|
||||
captured.events
|
||||
@@ -1335,7 +1448,8 @@ describe("workspace-old sessionRestore", () => {
|
||||
it.live("session restore includes real message and part events in sequence order", () => {
|
||||
const replay: FetchCall[] = []
|
||||
return Effect.gen(function* () {
|
||||
yield* HttpServer.serveEffect()(Effect.gen(function* () {
|
||||
yield* HttpServer.serveEffect()(
|
||||
Effect.gen(function* () {
|
||||
const req = yield* HttpServerRequest.HttpServerRequest
|
||||
const bodyText = yield* req.text
|
||||
replay.push({
|
||||
@@ -1346,9 +1460,11 @@ describe("workspace-old sessionRestore", () => {
|
||||
json: bodyText ? JSON.parse(bodyText) : undefined,
|
||||
})
|
||||
return HttpServerResponse.fromWeb(Response.json({ ok: true }))
|
||||
}))
|
||||
}),
|
||||
)
|
||||
const url = yield* serverUrl()
|
||||
yield* provideTmpdirInstance((dir) =>
|
||||
yield* provideTmpdirInstance(
|
||||
(dir) =>
|
||||
Effect.gen(function* () {
|
||||
const workspace = yield* WorkspaceOld.Service
|
||||
const sessionSvc = yield* SessionNs.Service
|
||||
@@ -1376,7 +1492,9 @@ describe("workspace-old sessionRestore", () => {
|
||||
}
|
||||
const before = eventRows(session.id)
|
||||
|
||||
expect(yield* workspace.sessionRestore({ workspaceID: info.id, sessionID: session.id })).toEqual({ total: 1 })
|
||||
expect(yield* workspace.sessionRestore({ workspaceID: info.id, sessionID: session.id })).toEqual({
|
||||
total: 1,
|
||||
})
|
||||
|
||||
const posted = (replay[0].json as { events: Array<{ seq: number; type: string }> }).events
|
||||
expect(posted.map((event) => event.seq)).toEqual([...before.map((row) => row.seq), before.at(-1)!.seq + 1])
|
||||
|
||||
@@ -452,6 +452,38 @@ export type EventVcsBranchUpdated = {
|
||||
}
|
||||
}
|
||||
|
||||
export type EventWorkspaceReady = {
|
||||
type: "workspace.ready"
|
||||
properties: {
|
||||
name: string
|
||||
}
|
||||
}
|
||||
|
||||
export type EventWorkspaceFailed = {
|
||||
type: "workspace.failed"
|
||||
properties: {
|
||||
message: string
|
||||
}
|
||||
}
|
||||
|
||||
export type EventWorkspaceRestore = {
|
||||
type: "workspace.restore"
|
||||
properties: {
|
||||
workspaceID: string
|
||||
sessionID: string
|
||||
total: number
|
||||
step: number
|
||||
}
|
||||
}
|
||||
|
||||
export type EventWorkspaceStatus = {
|
||||
type: "workspace.status"
|
||||
properties: {
|
||||
workspaceID: string
|
||||
status: "connected" | "connecting" | "disconnected" | "error"
|
||||
}
|
||||
}
|
||||
|
||||
export type EventWorktreeReady = {
|
||||
type: "worktree.ready"
|
||||
properties: {
|
||||
@@ -506,38 +538,6 @@ export type EventPtyDeleted = {
|
||||
}
|
||||
}
|
||||
|
||||
export type EventWorkspaceReady = {
|
||||
type: "workspace.ready"
|
||||
properties: {
|
||||
name: string
|
||||
}
|
||||
}
|
||||
|
||||
export type EventWorkspaceFailed = {
|
||||
type: "workspace.failed"
|
||||
properties: {
|
||||
message: string
|
||||
}
|
||||
}
|
||||
|
||||
export type EventWorkspaceRestore = {
|
||||
type: "workspace.restore"
|
||||
properties: {
|
||||
workspaceID: string
|
||||
sessionID: string
|
||||
total: number
|
||||
step: number
|
||||
}
|
||||
}
|
||||
|
||||
export type EventWorkspaceStatus = {
|
||||
type: "workspace.status"
|
||||
properties: {
|
||||
workspaceID: string
|
||||
status: "connected" | "connecting" | "disconnected" | "error"
|
||||
}
|
||||
}
|
||||
|
||||
export type OutputFormatText = {
|
||||
type: "text"
|
||||
}
|
||||
@@ -1141,16 +1141,16 @@ export type GlobalEvent = {
|
||||
| EventMcpBrowserOpenFailed
|
||||
| EventCommandExecuted
|
||||
| EventVcsBranchUpdated
|
||||
| EventWorkspaceReady
|
||||
| EventWorkspaceFailed
|
||||
| EventWorkspaceRestore
|
||||
| EventWorkspaceStatus
|
||||
| EventWorktreeReady
|
||||
| EventWorktreeFailed
|
||||
| EventPtyCreated
|
||||
| EventPtyUpdated
|
||||
| EventPtyExited
|
||||
| EventPtyDeleted
|
||||
| EventWorkspaceReady
|
||||
| EventWorkspaceFailed
|
||||
| EventWorkspaceRestore
|
||||
| EventWorkspaceStatus
|
||||
| EventMessageUpdated
|
||||
| EventMessageRemoved
|
||||
| EventMessagePartUpdated
|
||||
@@ -2084,16 +2084,16 @@ export type Event =
|
||||
| EventMcpBrowserOpenFailed
|
||||
| EventCommandExecuted
|
||||
| EventVcsBranchUpdated
|
||||
| EventWorkspaceReady
|
||||
| EventWorkspaceFailed
|
||||
| EventWorkspaceRestore
|
||||
| EventWorkspaceStatus
|
||||
| EventWorktreeReady
|
||||
| EventWorktreeFailed
|
||||
| EventPtyCreated
|
||||
| EventPtyUpdated
|
||||
| EventPtyExited
|
||||
| EventPtyDeleted
|
||||
| EventWorkspaceReady
|
||||
| EventWorkspaceFailed
|
||||
| EventWorkspaceRestore
|
||||
| EventWorkspaceStatus
|
||||
| EventMessageUpdated
|
||||
| EventMessageRemoved
|
||||
| EventMessagePartUpdated
|
||||
|
||||
+120
-120
@@ -8743,6 +8743,102 @@
|
||||
},
|
||||
"required": ["type", "properties"]
|
||||
},
|
||||
"Event.workspace.ready": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"const": "workspace.ready"
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["name"]
|
||||
}
|
||||
},
|
||||
"required": ["type", "properties"]
|
||||
},
|
||||
"Event.workspace.failed": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"const": "workspace.failed"
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["message"]
|
||||
}
|
||||
},
|
||||
"required": ["type", "properties"]
|
||||
},
|
||||
"Event.workspace.restore": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"const": "workspace.restore"
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"workspaceID": {
|
||||
"type": "string",
|
||||
"pattern": "^wrk.*"
|
||||
},
|
||||
"sessionID": {
|
||||
"type": "string",
|
||||
"pattern": "^ses.*"
|
||||
},
|
||||
"total": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
},
|
||||
"step": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": ["workspaceID", "sessionID", "total", "step"]
|
||||
}
|
||||
},
|
||||
"required": ["type", "properties"]
|
||||
},
|
||||
"Event.workspace.status": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"const": "workspace.status"
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"workspaceID": {
|
||||
"type": "string",
|
||||
"pattern": "^wrk.*"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["connected", "connecting", "disconnected", "error"]
|
||||
}
|
||||
},
|
||||
"required": ["workspaceID", "status"]
|
||||
}
|
||||
},
|
||||
"required": ["type", "properties"]
|
||||
},
|
||||
"Event.worktree.ready": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -8901,102 +8997,6 @@
|
||||
},
|
||||
"required": ["type", "properties"]
|
||||
},
|
||||
"Event.workspace.ready": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"const": "workspace.ready"
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["name"]
|
||||
}
|
||||
},
|
||||
"required": ["type", "properties"]
|
||||
},
|
||||
"Event.workspace.failed": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"const": "workspace.failed"
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["message"]
|
||||
}
|
||||
},
|
||||
"required": ["type", "properties"]
|
||||
},
|
||||
"Event.workspace.restore": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"const": "workspace.restore"
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"workspaceID": {
|
||||
"type": "string",
|
||||
"pattern": "^wrk.*"
|
||||
},
|
||||
"sessionID": {
|
||||
"type": "string",
|
||||
"pattern": "^ses.*"
|
||||
},
|
||||
"total": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
},
|
||||
"step": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": ["workspaceID", "sessionID", "total", "step"]
|
||||
}
|
||||
},
|
||||
"required": ["type", "properties"]
|
||||
},
|
||||
"Event.workspace.status": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"const": "workspace.status"
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"workspaceID": {
|
||||
"type": "string",
|
||||
"pattern": "^wrk.*"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["connected", "connecting", "disconnected", "error"]
|
||||
}
|
||||
},
|
||||
"required": ["workspaceID", "status"]
|
||||
}
|
||||
},
|
||||
"required": ["type", "properties"]
|
||||
},
|
||||
"OutputFormatText": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -11047,6 +11047,18 @@
|
||||
{
|
||||
"$ref": "#/components/schemas/Event.vcs.branch.updated"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Event.workspace.ready"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Event.workspace.failed"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Event.workspace.restore"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Event.workspace.status"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Event.worktree.ready"
|
||||
},
|
||||
@@ -11065,18 +11077,6 @@
|
||||
{
|
||||
"$ref": "#/components/schemas/Event.pty.deleted"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Event.workspace.ready"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Event.workspace.failed"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Event.workspace.restore"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Event.workspace.status"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Event.message.updated"
|
||||
},
|
||||
@@ -13340,6 +13340,18 @@
|
||||
{
|
||||
"$ref": "#/components/schemas/Event.vcs.branch.updated"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Event.workspace.ready"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Event.workspace.failed"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Event.workspace.restore"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Event.workspace.status"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Event.worktree.ready"
|
||||
},
|
||||
@@ -13358,18 +13370,6 @@
|
||||
{
|
||||
"$ref": "#/components/schemas/Event.pty.deleted"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Event.workspace.ready"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Event.workspace.failed"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Event.workspace.restore"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Event.workspace.status"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/Event.message.updated"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user