backeer-opencode 0.3.2 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,1863 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __esm = (fn, res, err) => function __init() {
4
+ if (err) throw err[0];
5
+ try {
6
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
7
+ } catch (e) {
8
+ throw err = [e], e;
9
+ }
10
+ };
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+
16
+ // node_modules/@opencode/client/dist/chunks/service-timing-9rqn6x4v.js
17
+ function __exportSetter(name, newValue) {
18
+ this[name] = __returnValue.bind(null, newValue);
19
+ }
20
+ var __defProp2, __returnValue, __export2;
21
+ var init_service_timing_9rqn6x4v = __esm({
22
+ "node_modules/@opencode/client/dist/chunks/service-timing-9rqn6x4v.js"() {
23
+ __defProp2 = Object.defineProperty;
24
+ __returnValue = (v) => v;
25
+ __export2 = (target, all) => {
26
+ for (var name in all)
27
+ __defProp2(target, name, {
28
+ get: all[name],
29
+ enumerable: true,
30
+ configurable: true,
31
+ set: __exportSetter.bind(all, name)
32
+ });
33
+ };
34
+ }
35
+ });
36
+
37
+ // node_modules/@opencode/client/dist/chunks/service-timing-frbwqjmf.js
38
+ function make7(connect) {
39
+ let current;
40
+ const capacity = 4096;
41
+ function stop2(connection) {
42
+ connection.connected = void 0;
43
+ connection.controller.abort();
44
+ if (current === connection)
45
+ current = void 0;
46
+ }
47
+ async function run(connection) {
48
+ let iterator;
49
+ let completion = {};
50
+ try {
51
+ if (connection.controller.signal.aborted)
52
+ return;
53
+ iterator = connect(connection.controller.signal, () => {
54
+ connection.subscribers.forEach((subscriber) => subscriber.activity?.());
55
+ })[Symbol.asyncIterator]();
56
+ while (!connection.controller.signal.aborted) {
57
+ const item = await iterator.next();
58
+ if (item.done || connection.controller.signal.aborted)
59
+ break;
60
+ if (item.value.type === "server.connected")
61
+ connection.connected = item.value;
62
+ connection.subscribers.forEach((subscriber) => subscriber.push(item.value));
63
+ }
64
+ } catch (error) {
65
+ completion = { error };
66
+ } finally {
67
+ stop2(connection);
68
+ try {
69
+ await iterator?.return?.();
70
+ } catch (error) {
71
+ if (!("error" in completion))
72
+ completion = { error };
73
+ }
74
+ connection.subscribers.forEach((subscriber) => subscriber.finish(completion));
75
+ }
76
+ }
77
+ return {
78
+ subscribe(options) {
79
+ return {
80
+ [Symbol.asyncIterator]() {
81
+ const pending = [];
82
+ const queued = [];
83
+ let started = false;
84
+ let completion;
85
+ let connection;
86
+ function finish(result, discard = true) {
87
+ completion = result;
88
+ if (discard)
89
+ queued.length = 0;
90
+ options?.signal?.removeEventListener("abort", abort);
91
+ if (connection?.subscribers.delete(subscriber) && !connection.subscribers.size)
92
+ stop2(connection);
93
+ pending.splice(0).forEach((request) => {
94
+ if ("error" in result)
95
+ request.reject(result.error);
96
+ else
97
+ request.resolve({ done: true, value: void 0 });
98
+ });
99
+ }
100
+ function abort() {
101
+ finish({});
102
+ }
103
+ const subscriber = {
104
+ activity: options?.onActivity,
105
+ finish(result) {
106
+ finish(result, false);
107
+ },
108
+ push(value) {
109
+ if (completion)
110
+ return;
111
+ const request = pending.shift();
112
+ if (request) {
113
+ request.resolve({ done: false, value });
114
+ return;
115
+ }
116
+ if (queued.length === capacity) {
117
+ finish({ error: new Error(`Event subscriber exceeded its ${capacity}-event capacity`) });
118
+ return;
119
+ }
120
+ queued.push(value);
121
+ }
122
+ };
123
+ function start() {
124
+ if (completion)
125
+ return;
126
+ const fresh = !current;
127
+ connection = current ?? {
128
+ controller: new AbortController(),
129
+ subscribers: /* @__PURE__ */ new Set()
130
+ };
131
+ current = connection;
132
+ connection.subscribers.add(subscriber);
133
+ if (connection.connected)
134
+ subscriber.push(connection.connected);
135
+ if (fresh)
136
+ run(connection);
137
+ }
138
+ return {
139
+ next() {
140
+ const value = queued.shift();
141
+ if (value)
142
+ return Promise.resolve({ done: false, value });
143
+ if (completion) {
144
+ if ("error" in completion)
145
+ return Promise.reject(completion.error);
146
+ return Promise.resolve({ done: true, value: void 0 });
147
+ }
148
+ if (options?.signal?.aborted) {
149
+ abort();
150
+ return Promise.resolve({ done: true, value: void 0 });
151
+ }
152
+ const request = Promise.withResolvers();
153
+ pending.push(request);
154
+ if (!started) {
155
+ started = true;
156
+ options?.signal?.addEventListener("abort", abort, { once: true });
157
+ start();
158
+ }
159
+ return request.promise;
160
+ },
161
+ return() {
162
+ finish({});
163
+ return Promise.resolve({ done: true, value: void 0 });
164
+ }
165
+ };
166
+ }
167
+ };
168
+ }
169
+ };
170
+ }
171
+ var exports_shared_events;
172
+ var init_service_timing_frbwqjmf = __esm({
173
+ "node_modules/@opencode/client/dist/chunks/service-timing-frbwqjmf.js"() {
174
+ init_service_timing_9rqn6x4v();
175
+ exports_shared_events = {};
176
+ __export2(exports_shared_events, {
177
+ SharedEvents: () => exports_shared_events,
178
+ make: () => make7
179
+ });
180
+ }
181
+ });
182
+
183
+ // node_modules/@opencode/client/dist/chunks/service-timing-js54wxgp.js
184
+ var ClientError2;
185
+ var init_service_timing_js54wxgp = __esm({
186
+ "node_modules/@opencode/client/dist/chunks/service-timing-js54wxgp.js"() {
187
+ ClientError2 = class extends Error {
188
+ reason;
189
+ name = "ClientError";
190
+ constructor(reason, options) {
191
+ const detail = options?.detail ?? (options?.cause instanceof Error ? options.cause.message : void 0);
192
+ super(detail ? `${reason}: ${detail}` : reason, options);
193
+ this.reason = reason;
194
+ }
195
+ };
196
+ }
197
+ });
198
+
199
+ // node_modules/@opencode/client/dist/chunks/service-timing-tbh3h57v.js
200
+ function make3(options) {
201
+ const fetch2 = options.fetch ?? globalThis.fetch;
202
+ const prepare = (descriptor, requestOptions) => {
203
+ const baseUrl = new URL(options.baseUrl);
204
+ if (!baseUrl.pathname.endsWith("/"))
205
+ baseUrl.pathname += "/";
206
+ const url = new URL(descriptor.path.slice(1), baseUrl);
207
+ for (const [key, value] of Object.entries(descriptor.query ?? {}))
208
+ appendQuery(url.searchParams, key, value);
209
+ const headers = new Headers(options.headers);
210
+ for (const [key, value] of Object.entries(descriptor.headers ?? {})) {
211
+ if (value !== void 0 && value !== null)
212
+ headers.set(key, String(value));
213
+ }
214
+ for (const [key, value] of new Headers(requestOptions?.headers))
215
+ headers.set(key, value);
216
+ if (descriptor.body !== void 0 && !headers.has("content-type"))
217
+ headers.set("content-type", descriptor.binaryBody ? "application/octet-stream" : "application/json");
218
+ return {
219
+ url,
220
+ init: {
221
+ method: descriptor.method,
222
+ signal: requestOptions?.signal,
223
+ headers,
224
+ body: descriptor.body === void 0 ? void 0 : descriptor.binaryBody ? descriptor.body : JSON.stringify(descriptor.body)
225
+ }
226
+ };
227
+ };
228
+ const execute = async (descriptor, requestOptions) => {
229
+ try {
230
+ const prepared = prepare(descriptor, requestOptions);
231
+ return await fetch2(prepared.url, prepared.init);
232
+ } catch (cause) {
233
+ throw new ClientError2("Transport", { cause });
234
+ }
235
+ };
236
+ const responseError = async (response, descriptor) => {
237
+ if (descriptor.declaredStatuses.includes(response.status))
238
+ throw declared(await json2(response));
239
+ try {
240
+ await response.body?.cancel();
241
+ } catch {
242
+ }
243
+ throw new ClientError2("UnexpectedStatus", { cause: { status: response.status }, detail: String(response.status) });
244
+ };
245
+ const request = async (descriptor, requestOptions) => {
246
+ const response = await execute(descriptor, requestOptions);
247
+ if (response.status !== descriptor.successStatus)
248
+ return responseError(response, descriptor);
249
+ if (descriptor.binary)
250
+ return new Uint8Array(await response.arrayBuffer());
251
+ if (descriptor.empty) {
252
+ try {
253
+ await response.body?.cancel();
254
+ } catch {
255
+ }
256
+ return;
257
+ }
258
+ return await json2(response);
259
+ };
260
+ const sse = (descriptor, requestOptions) => ({
261
+ async *[Symbol.asyncIterator]() {
262
+ const response = await execute(descriptor, requestOptions);
263
+ if (response.status !== descriptor.successStatus)
264
+ await responseError(response, descriptor);
265
+ if (!isContentType(response, "text/event-stream")) {
266
+ try {
267
+ await response.body?.cancel();
268
+ } catch {
269
+ }
270
+ throw new ClientError2("UnsupportedContentType", { detail: response.headers.get("content-type") });
271
+ }
272
+ if (response.body === null)
273
+ throw new ClientError2("MalformedResponse");
274
+ const reader = response.body.getReader();
275
+ const decoder = new TextDecoder();
276
+ let buffer = "";
277
+ try {
278
+ while (true) {
279
+ let next;
280
+ try {
281
+ next = await reader.read();
282
+ } catch (cause) {
283
+ throw new ClientError2("Transport", { cause });
284
+ }
285
+ if (!next.done)
286
+ requestOptions?.onActivity?.();
287
+ buffer += decoder.decode(next.value, { stream: !next.done });
288
+ if (buffer.length > maxSseEventBytes)
289
+ throw new ClientError2("SseEventTooLarge");
290
+ const trailingCarriageReturn = !next.done && buffer.endsWith("\r");
291
+ if (trailingCarriageReturn)
292
+ buffer = buffer.slice(0, -1);
293
+ buffer = buffer.replaceAll(`\r
294
+ `, `
295
+ `).replaceAll("\r", `
296
+ `);
297
+ if (trailingCarriageReturn)
298
+ buffer += "\r";
299
+ if (next.done && buffer !== "")
300
+ buffer += `
301
+
302
+ `;
303
+ let boundary = buffer.indexOf(`
304
+
305
+ `);
306
+ while (boundary >= 0) {
307
+ const block = buffer.slice(0, boundary);
308
+ buffer = buffer.slice(boundary + 2);
309
+ const data = block.split(`
310
+ `).flatMap((line) => line.startsWith("data:") ? [line.slice(5).trimStart()] : []).join(`
311
+ `);
312
+ if (data !== "") {
313
+ try {
314
+ yield JSON.parse(data);
315
+ } catch (cause) {
316
+ throw new ClientError2("MalformedResponse", { cause });
317
+ }
318
+ }
319
+ boundary = buffer.indexOf(`
320
+
321
+ `);
322
+ }
323
+ if (next.done)
324
+ return;
325
+ }
326
+ } finally {
327
+ try {
328
+ await reader.cancel();
329
+ } catch {
330
+ }
331
+ reader.releaseLock();
332
+ }
333
+ }
334
+ });
335
+ return {
336
+ server: {
337
+ info: (requestOptions) => request({ method: "GET", path: `/api/info`, successStatus: 200, declaredStatuses: [400, 401], empty: false }, requestOptions),
338
+ pair: (requestOptions) => request({ method: "POST", path: `/api/pair`, successStatus: 200, declaredStatuses: [400, 401], empty: false }, requestOptions),
339
+ connect: (input, requestOptions) => request({
340
+ method: "GET",
341
+ path: `/auth/connect/${encodeURIComponent(input.code)}`,
342
+ successStatus: 200,
343
+ declaredStatuses: [400, 401],
344
+ empty: false
345
+ }, requestOptions)
346
+ },
347
+ location: {
348
+ get: (input, requestOptions) => request({
349
+ method: "GET",
350
+ path: `/api/location`,
351
+ query: { location: input?.["location"] },
352
+ successStatus: 200,
353
+ declaredStatuses: [400, 401, 404],
354
+ empty: false
355
+ }, requestOptions),
356
+ reload: (requestOptions) => request({
357
+ method: "POST",
358
+ path: `/api/location/reload`,
359
+ successStatus: 204,
360
+ declaredStatuses: [400, 401, 503],
361
+ empty: true
362
+ }, requestOptions)
363
+ },
364
+ agent: {
365
+ list: (input, requestOptions) => request({
366
+ method: "GET",
367
+ path: `/api/agent`,
368
+ query: { location: input?.["location"] },
369
+ successStatus: 200,
370
+ declaredStatuses: [400, 401, 404],
371
+ empty: false
372
+ }, requestOptions),
373
+ get: (input, requestOptions) => request({
374
+ method: "GET",
375
+ path: `/api/agent/${encodeURIComponent(input.agentID)}`,
376
+ query: { location: input["location"] },
377
+ successStatus: 200,
378
+ declaredStatuses: [400, 401, 404],
379
+ empty: false
380
+ }, requestOptions)
381
+ },
382
+ plugin: {
383
+ list: (input, requestOptions) => request({
384
+ method: "GET",
385
+ path: `/api/plugin`,
386
+ query: { location: input?.["location"] },
387
+ successStatus: 200,
388
+ declaredStatuses: [400, 401, 404],
389
+ empty: false
390
+ }, requestOptions),
391
+ check: (input, requestOptions) => request({
392
+ method: "POST",
393
+ path: `/api/plugin/check`,
394
+ query: { location: input?.["location"] },
395
+ body: { target: input?.["target"] },
396
+ successStatus: 200,
397
+ declaredStatuses: [400, 401, 404],
398
+ empty: false
399
+ }, requestOptions),
400
+ update: (input, requestOptions) => request({
401
+ method: "POST",
402
+ path: `/api/plugin/update`,
403
+ query: { location: input["location"] },
404
+ body: { targets: input["targets"] },
405
+ successStatus: 204,
406
+ declaredStatuses: [400, 401, 404, 503],
407
+ empty: true
408
+ }, requestOptions)
409
+ },
410
+ session: {
411
+ list: (input, requestOptions) => request({
412
+ method: "GET",
413
+ path: `/api/session`,
414
+ query: {
415
+ limit: input?.["limit"],
416
+ order: input?.["order"],
417
+ search: input?.["search"],
418
+ parentID: input?.["parentID"],
419
+ directory: input?.["directory"],
420
+ project: input?.["project"],
421
+ subpath: input?.["subpath"],
422
+ cursor: input?.["cursor"]
423
+ },
424
+ successStatus: 200,
425
+ declaredStatuses: [400, 401],
426
+ empty: false
427
+ }, requestOptions),
428
+ stats: (input, requestOptions) => request({
429
+ method: "GET",
430
+ path: `/api/experimental/session/stats`,
431
+ query: {
432
+ from: input?.["from"],
433
+ to: input?.["to"],
434
+ project: input?.["project"],
435
+ timezone: input?.["timezone"],
436
+ tools: input?.["tools"]
437
+ },
438
+ successStatus: 200,
439
+ declaredStatuses: [400, 401],
440
+ empty: false
441
+ }, requestOptions).then((value) => value.data),
442
+ create: (input, requestOptions) => request({
443
+ method: "POST",
444
+ path: `/api/session`,
445
+ body: {
446
+ id: input?.["id"],
447
+ parentID: input?.["parentID"],
448
+ title: input?.["title"],
449
+ agent: input?.["agent"],
450
+ model: input?.["model"],
451
+ location: input?.["location"],
452
+ metadata: input?.["metadata"],
453
+ permissions: input?.["permissions"]
454
+ },
455
+ successStatus: 200,
456
+ declaredStatuses: [400, 401, 404],
457
+ empty: false
458
+ }, requestOptions).then((value) => value.data),
459
+ import: (input, requestOptions) => request({
460
+ method: "POST",
461
+ path: `/api/experimental/session/import`,
462
+ body: { info: input["info"], messages: input["messages"], location: input["location"] },
463
+ successStatus: 200,
464
+ declaredStatuses: [400, 401, 404, 409],
465
+ empty: false
466
+ }, requestOptions).then((value) => value.data),
467
+ export: (input, requestOptions) => request({
468
+ method: "GET",
469
+ path: `/api/experimental/session/${encodeURIComponent(input.sessionID)}/export`,
470
+ query: { sanitize: input["sanitize"] },
471
+ successStatus: 200,
472
+ declaredStatuses: [400, 401, 404, 500],
473
+ empty: false
474
+ }, requestOptions).then((value) => value.data),
475
+ active: (requestOptions) => request({
476
+ method: "GET",
477
+ path: `/api/session/active`,
478
+ successStatus: 200,
479
+ declaredStatuses: [400, 401],
480
+ empty: false
481
+ }, requestOptions).then((value) => value.data),
482
+ get: (input, requestOptions) => request({
483
+ method: "GET",
484
+ path: `/api/session/${encodeURIComponent(input.sessionID)}`,
485
+ successStatus: 200,
486
+ declaredStatuses: [400, 401, 404],
487
+ empty: false
488
+ }, requestOptions).then((value) => value.data),
489
+ remove: (input, requestOptions) => request({
490
+ method: "DELETE",
491
+ path: `/api/session/${encodeURIComponent(input.sessionID)}`,
492
+ successStatus: 204,
493
+ declaredStatuses: [400, 401, 404],
494
+ empty: true
495
+ }, requestOptions),
496
+ fork: (input, requestOptions) => request({
497
+ method: "POST",
498
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/fork`,
499
+ body: { before: input["before"] },
500
+ successStatus: 200,
501
+ declaredStatuses: [400, 401, 404],
502
+ empty: false
503
+ }, requestOptions).then((value) => value.data),
504
+ switchAgent: (input, requestOptions) => request({
505
+ method: "POST",
506
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/agent`,
507
+ body: { agent: input["agent"] },
508
+ successStatus: 204,
509
+ declaredStatuses: [400, 401, 404],
510
+ empty: true
511
+ }, requestOptions),
512
+ switchModel: (input, requestOptions) => request({
513
+ method: "POST",
514
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/model`,
515
+ body: { model: input["model"] },
516
+ successStatus: 204,
517
+ declaredStatuses: [400, 401, 404],
518
+ empty: true
519
+ }, requestOptions),
520
+ update: (input, requestOptions) => request({
521
+ method: "PATCH",
522
+ path: `/api/session/${encodeURIComponent(input.sessionID)}`,
523
+ body: { title: input["title"], metadata: input["metadata"], permissions: input["permissions"] },
524
+ successStatus: 204,
525
+ declaredStatuses: [400, 401, 404],
526
+ empty: true
527
+ }, requestOptions),
528
+ move: (input, requestOptions) => request({
529
+ method: "POST",
530
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/move`,
531
+ body: { directory: input["directory"], delivery: input["delivery"] },
532
+ successStatus: 204,
533
+ declaredStatuses: [400, 401, 404],
534
+ empty: true
535
+ }, requestOptions),
536
+ prompt: (input, requestOptions) => request({
537
+ method: "POST",
538
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/prompt`,
539
+ body: {
540
+ id: input["id"],
541
+ text: input["text"],
542
+ files: input["files"],
543
+ agents: input["agents"],
544
+ skills: input["skills"],
545
+ metadata: input["metadata"],
546
+ delivery: input["delivery"],
547
+ resume: input["resume"]
548
+ },
549
+ successStatus: 200,
550
+ declaredStatuses: [400, 401, 404, 409],
551
+ empty: false
552
+ }, requestOptions).then((value) => value.data),
553
+ command: (input, requestOptions) => request({
554
+ method: "POST",
555
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/command`,
556
+ body: {
557
+ name: input["name"],
558
+ text: input["text"],
559
+ files: input["files"],
560
+ agents: input["agents"],
561
+ skills: input["skills"],
562
+ delivery: input["delivery"]
563
+ },
564
+ successStatus: 204,
565
+ declaredStatuses: [400, 401, 404, 500],
566
+ empty: true
567
+ }, requestOptions),
568
+ skill: (input, requestOptions) => request({
569
+ method: "POST",
570
+ path: `/api/experimental/session/${encodeURIComponent(input.sessionID)}/skill`,
571
+ body: { id: input["id"], resume: input["resume"] },
572
+ successStatus: 204,
573
+ declaredStatuses: [400, 401, 404],
574
+ empty: true
575
+ }, requestOptions),
576
+ synthetic: (input, requestOptions) => request({
577
+ method: "POST",
578
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/synthetic`,
579
+ body: {
580
+ id: input["id"],
581
+ text: input["text"],
582
+ description: input["description"],
583
+ metadata: input["metadata"],
584
+ delivery: input["delivery"],
585
+ resume: input["resume"]
586
+ },
587
+ successStatus: 200,
588
+ declaredStatuses: [400, 401, 404, 409],
589
+ empty: false
590
+ }, requestOptions).then((value) => value.data),
591
+ shell: (input, requestOptions) => request({
592
+ method: "POST",
593
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/shell`,
594
+ body: { id: input["id"], command: input["command"] },
595
+ successStatus: 204,
596
+ declaredStatuses: [400, 401, 404],
597
+ empty: true
598
+ }, requestOptions),
599
+ compact: (input, requestOptions) => request({
600
+ method: "POST",
601
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/compact`,
602
+ body: { id: input["id"], delivery: input["delivery"] },
603
+ successStatus: 200,
604
+ declaredStatuses: [400, 401, 404, 409],
605
+ empty: false
606
+ }, requestOptions).then((value) => value.data),
607
+ wait: (input, requestOptions) => request({
608
+ method: "POST",
609
+ path: `/api/experimental/session/${encodeURIComponent(input.sessionID)}/wait`,
610
+ successStatus: 204,
611
+ declaredStatuses: [400, 401, 404, 503],
612
+ empty: true
613
+ }, requestOptions),
614
+ revert: {
615
+ stage: (input, requestOptions) => request({
616
+ method: "POST",
617
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/revert/stage`,
618
+ body: { messageID: input["messageID"], files: input["files"] },
619
+ successStatus: 200,
620
+ declaredStatuses: [400, 401, 404, 409, 500],
621
+ empty: false
622
+ }, requestOptions).then((value) => value.data),
623
+ clear: (input, requestOptions) => request({
624
+ method: "DELETE",
625
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/revert`,
626
+ successStatus: 204,
627
+ declaredStatuses: [400, 401, 404, 409, 500],
628
+ empty: true
629
+ }, requestOptions),
630
+ commit: (input, requestOptions) => request({
631
+ method: "POST",
632
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/revert/commit`,
633
+ successStatus: 204,
634
+ declaredStatuses: [400, 401, 404, 409],
635
+ empty: true
636
+ }, requestOptions)
637
+ },
638
+ context: (input, requestOptions) => request({
639
+ method: "GET",
640
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/context`,
641
+ successStatus: 200,
642
+ declaredStatuses: [400, 401, 404, 500],
643
+ empty: false
644
+ }, requestOptions).then((value) => value.data),
645
+ diff: (input, requestOptions) => request({
646
+ method: "GET",
647
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/diff`,
648
+ query: { from: input["from"], to: input["to"], context: input["context"] },
649
+ successStatus: 200,
650
+ declaredStatuses: [400, 401, 404, 500],
651
+ empty: false
652
+ }, requestOptions).then((value) => value.data),
653
+ inbox: {
654
+ list: (input, requestOptions) => request({
655
+ method: "GET",
656
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/inbox`,
657
+ successStatus: 200,
658
+ declaredStatuses: [400, 401, 404],
659
+ empty: false
660
+ }, requestOptions).then((value) => value.data),
661
+ cancel: (input, requestOptions) => request({
662
+ method: "DELETE",
663
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/inbox/${encodeURIComponent(input.inboxID)}`,
664
+ successStatus: 204,
665
+ declaredStatuses: [400, 401, 404],
666
+ empty: true
667
+ }, requestOptions),
668
+ update: (input, requestOptions) => request({
669
+ method: "PATCH",
670
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/inbox/${encodeURIComponent(input.inboxID)}`,
671
+ body: { delivery: input["delivery"] },
672
+ successStatus: 204,
673
+ declaredStatuses: [400, 401, 404, 409],
674
+ empty: true
675
+ }, requestOptions)
676
+ },
677
+ instructions: {
678
+ entry: {
679
+ list: (input, requestOptions) => request({
680
+ method: "GET",
681
+ path: `/api/experimental/session/${encodeURIComponent(input.sessionID)}/instructions/entries`,
682
+ successStatus: 200,
683
+ declaredStatuses: [400, 401, 404],
684
+ empty: false
685
+ }, requestOptions).then((value) => value.data),
686
+ put: (input, requestOptions) => request({
687
+ method: "PUT",
688
+ path: `/api/experimental/session/${encodeURIComponent(input.sessionID)}/instructions/entries/${encodeURIComponent(input.key)}`,
689
+ body: { value: input["value"] },
690
+ successStatus: 204,
691
+ declaredStatuses: [400, 401, 404, 413],
692
+ empty: true
693
+ }, requestOptions),
694
+ remove: (input, requestOptions) => request({
695
+ method: "DELETE",
696
+ path: `/api/experimental/session/${encodeURIComponent(input.sessionID)}/instructions/entries/${encodeURIComponent(input.key)}`,
697
+ successStatus: 204,
698
+ declaredStatuses: [400, 401, 404],
699
+ empty: true
700
+ }, requestOptions)
701
+ }
702
+ },
703
+ generate: (input, requestOptions) => request({
704
+ method: "POST",
705
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/generate`,
706
+ body: { prompt: input["prompt"] },
707
+ successStatus: 200,
708
+ declaredStatuses: [400, 401, 404, 503],
709
+ empty: false
710
+ }, requestOptions).then((value) => value.data),
711
+ log: (input, requestOptions) => sse({
712
+ method: "GET",
713
+ path: `/api/experimental/session/${encodeURIComponent(input.sessionID)}/log`,
714
+ query: { after: input["after"], follow: input["follow"] },
715
+ successStatus: 200,
716
+ declaredStatuses: [400, 401, 404],
717
+ empty: false
718
+ }, requestOptions),
719
+ interrupt: (input, requestOptions) => request({
720
+ method: "POST",
721
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/interrupt`,
722
+ query: { resume: input["resume"] },
723
+ successStatus: 200,
724
+ declaredStatuses: [400, 401, 404],
725
+ empty: false
726
+ }, requestOptions),
727
+ background: (input, requestOptions) => request({
728
+ method: "POST",
729
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/background`,
730
+ successStatus: 204,
731
+ declaredStatuses: [400, 401, 404],
732
+ empty: true
733
+ }, requestOptions),
734
+ message: {
735
+ get: (input, requestOptions) => request({
736
+ method: "GET",
737
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/message/${encodeURIComponent(input.messageID)}`,
738
+ successStatus: 200,
739
+ declaredStatuses: [400, 401, 404],
740
+ empty: false
741
+ }, requestOptions).then((value) => value.data)
742
+ },
743
+ form: {
744
+ list: (input, requestOptions) => request({
745
+ method: "GET",
746
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/form`,
747
+ successStatus: 200,
748
+ declaredStatuses: [400, 401, 404],
749
+ empty: false
750
+ }, requestOptions).then((value) => value.data),
751
+ create: (input, requestOptions) => request({
752
+ method: "POST",
753
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/form`,
754
+ body: { id: input["id"], title: input["title"], metadata: input["metadata"], fields: input["fields"] },
755
+ successStatus: 200,
756
+ declaredStatuses: [400, 401, 404, 409],
757
+ empty: false
758
+ }, requestOptions).then((value) => value.data),
759
+ get: (input, requestOptions) => request({
760
+ method: "GET",
761
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/form/${encodeURIComponent(input.formID)}`,
762
+ successStatus: 200,
763
+ declaredStatuses: [400, 401, 404],
764
+ empty: false
765
+ }, requestOptions).then((value) => value.data),
766
+ reply: (input, requestOptions) => request({
767
+ method: "POST",
768
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/form/${encodeURIComponent(input.formID)}/reply`,
769
+ body: { answer: input["answer"] },
770
+ successStatus: 204,
771
+ declaredStatuses: [400, 401, 404, 409],
772
+ empty: true
773
+ }, requestOptions),
774
+ cancel: (input, requestOptions) => request({
775
+ method: "DELETE",
776
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/form/${encodeURIComponent(input.formID)}`,
777
+ query: { message: input["message"] },
778
+ successStatus: 204,
779
+ declaredStatuses: [400, 401, 404, 409],
780
+ empty: true
781
+ }, requestOptions)
782
+ },
783
+ environment: (input, requestOptions) => request({
784
+ method: "PUT",
785
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/environment`,
786
+ body: { variables: input["variables"] },
787
+ successStatus: 204,
788
+ declaredStatuses: [400, 401, 404],
789
+ empty: true
790
+ }, requestOptions),
791
+ view: (input, requestOptions) => request({
792
+ method: "POST",
793
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/view`,
794
+ body: { idle: input["idle"] },
795
+ successStatus: 204,
796
+ declaredStatuses: [400, 401, 404],
797
+ empty: true
798
+ }, requestOptions)
799
+ },
800
+ message: {
801
+ list: (input, requestOptions) => request({
802
+ method: "GET",
803
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/message`,
804
+ query: { limit: input["limit"], order: input["order"], cursor: input["cursor"], type: input["type"] },
805
+ successStatus: 200,
806
+ declaredStatuses: [400, 401, 404, 500],
807
+ empty: false
808
+ }, requestOptions)
809
+ },
810
+ model: {
811
+ list: (input, requestOptions) => request({
812
+ method: "GET",
813
+ path: `/api/model`,
814
+ query: { location: input?.["location"] },
815
+ successStatus: 200,
816
+ declaredStatuses: [400, 401, 404, 503],
817
+ empty: false
818
+ }, requestOptions),
819
+ default: (input, requestOptions) => request({
820
+ method: "GET",
821
+ path: `/api/model/default`,
822
+ query: { location: input?.["location"] },
823
+ successStatus: 200,
824
+ declaredStatuses: [400, 401, 404, 503],
825
+ empty: false
826
+ }, requestOptions)
827
+ },
828
+ generate: {
829
+ text: (input, requestOptions) => request({
830
+ method: "POST",
831
+ path: `/api/experimental/generate`,
832
+ body: { prompt: input["prompt"], model: input["model"] },
833
+ successStatus: 200,
834
+ declaredStatuses: [400, 401, 404, 503],
835
+ empty: false
836
+ }, requestOptions).then((value) => value.data)
837
+ },
838
+ provider: {
839
+ list: (input, requestOptions) => request({
840
+ method: "GET",
841
+ path: `/api/provider`,
842
+ query: { location: input?.["location"] },
843
+ successStatus: 200,
844
+ declaredStatuses: [400, 401, 404, 503],
845
+ empty: false
846
+ }, requestOptions),
847
+ get: (input, requestOptions) => request({
848
+ method: "GET",
849
+ path: `/api/provider/${encodeURIComponent(input.providerID)}`,
850
+ query: { location: input["location"] },
851
+ successStatus: 200,
852
+ declaredStatuses: [400, 401, 404, 503],
853
+ empty: false
854
+ }, requestOptions)
855
+ },
856
+ integration: {
857
+ list: (input, requestOptions) => request({
858
+ method: "GET",
859
+ path: `/api/integration`,
860
+ query: { location: input?.["location"] },
861
+ successStatus: 200,
862
+ declaredStatuses: [400, 401, 404],
863
+ empty: false
864
+ }, requestOptions),
865
+ get: (input, requestOptions) => request({
866
+ method: "GET",
867
+ path: `/api/integration/${encodeURIComponent(input.integrationID)}`,
868
+ query: { location: input["location"] },
869
+ successStatus: 200,
870
+ declaredStatuses: [400, 401, 404],
871
+ empty: false
872
+ }, requestOptions),
873
+ wellknown: {
874
+ add: (input, requestOptions) => request({
875
+ method: "POST",
876
+ path: `/api/experimental/integration/wellknown`,
877
+ query: { location: input["location"] },
878
+ body: { url: input["url"] },
879
+ successStatus: 204,
880
+ declaredStatuses: [400, 401, 404],
881
+ empty: true
882
+ }, requestOptions)
883
+ },
884
+ connect: {
885
+ key: (input, requestOptions) => request({
886
+ method: "POST",
887
+ path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/key`,
888
+ query: { location: input["location"] },
889
+ body: { key: input["key"], answer: input["answer"], label: input["label"] },
890
+ successStatus: 204,
891
+ declaredStatuses: [400, 401, 404],
892
+ empty: true
893
+ }, requestOptions),
894
+ external: (input, requestOptions) => request({
895
+ method: "POST",
896
+ path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/external`,
897
+ query: { location: input["location"] },
898
+ body: { methodID: input["methodID"], answer: input["answer"], label: input["label"] },
899
+ successStatus: 204,
900
+ declaredStatuses: [400, 401, 404],
901
+ empty: true
902
+ }, requestOptions)
903
+ },
904
+ oauth: {
905
+ connect: (input, requestOptions) => request({
906
+ method: "POST",
907
+ path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/oauth`,
908
+ query: { location: input["location"] },
909
+ body: { methodID: input["methodID"], answer: input["answer"], label: input["label"] },
910
+ successStatus: 200,
911
+ declaredStatuses: [400, 401, 404],
912
+ empty: false
913
+ }, requestOptions),
914
+ status: (input, requestOptions) => request({
915
+ method: "GET",
916
+ path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/oauth/${encodeURIComponent(input.attemptID)}`,
917
+ query: { location: input["location"] },
918
+ successStatus: 200,
919
+ declaredStatuses: [400, 401, 404],
920
+ empty: false
921
+ }, requestOptions),
922
+ complete: (input, requestOptions) => request({
923
+ method: "POST",
924
+ path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/oauth/${encodeURIComponent(input.attemptID)}/complete`,
925
+ query: { location: input["location"] },
926
+ body: { code: input["code"] },
927
+ successStatus: 204,
928
+ declaredStatuses: [400, 401, 404],
929
+ empty: true
930
+ }, requestOptions),
931
+ cancel: (input, requestOptions) => request({
932
+ method: "DELETE",
933
+ path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/oauth/${encodeURIComponent(input.attemptID)}`,
934
+ query: { location: input["location"] },
935
+ successStatus: 204,
936
+ declaredStatuses: [400, 401, 404],
937
+ empty: true
938
+ }, requestOptions)
939
+ },
940
+ command: {
941
+ connect: (input, requestOptions) => request({
942
+ method: "POST",
943
+ path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/command`,
944
+ query: { location: input["location"] },
945
+ body: { methodID: input["methodID"], label: input["label"] },
946
+ successStatus: 200,
947
+ declaredStatuses: [400, 401, 404],
948
+ empty: false
949
+ }, requestOptions),
950
+ status: (input, requestOptions) => request({
951
+ method: "GET",
952
+ path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/command/${encodeURIComponent(input.attemptID)}`,
953
+ query: { location: input["location"] },
954
+ successStatus: 200,
955
+ declaredStatuses: [400, 401, 404],
956
+ empty: false
957
+ }, requestOptions),
958
+ cancel: (input, requestOptions) => request({
959
+ method: "DELETE",
960
+ path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/command/${encodeURIComponent(input.attemptID)}`,
961
+ query: { location: input["location"] },
962
+ successStatus: 204,
963
+ declaredStatuses: [400, 401, 404],
964
+ empty: true
965
+ }, requestOptions)
966
+ }
967
+ },
968
+ mcp: {
969
+ list: (input, requestOptions) => request({
970
+ method: "GET",
971
+ path: `/api/mcp`,
972
+ query: { location: input?.["location"] },
973
+ successStatus: 200,
974
+ declaredStatuses: [400, 401, 404],
975
+ empty: false
976
+ }, requestOptions),
977
+ add: (input, requestOptions) => request({
978
+ method: "PUT",
979
+ path: `/api/experimental/mcp/${encodeURIComponent(input.server)}`,
980
+ query: { location: input["location"] },
981
+ body: { config: input["config"] },
982
+ successStatus: 204,
983
+ declaredStatuses: [400, 401, 404],
984
+ empty: true
985
+ }, requestOptions),
986
+ remove: (input, requestOptions) => request({
987
+ method: "DELETE",
988
+ path: `/api/experimental/mcp/${encodeURIComponent(input.server)}`,
989
+ query: { location: input["location"] },
990
+ successStatus: 204,
991
+ declaredStatuses: [400, 401, 404],
992
+ empty: true
993
+ }, requestOptions),
994
+ connect: (input, requestOptions) => request({
995
+ method: "POST",
996
+ path: `/api/experimental/mcp/${encodeURIComponent(input.server)}/connect`,
997
+ query: { location: input["location"] },
998
+ successStatus: 204,
999
+ declaredStatuses: [400, 401, 404],
1000
+ empty: true
1001
+ }, requestOptions),
1002
+ disconnect: (input, requestOptions) => request({
1003
+ method: "POST",
1004
+ path: `/api/experimental/mcp/${encodeURIComponent(input.server)}/disconnect`,
1005
+ query: { location: input["location"] },
1006
+ successStatus: 204,
1007
+ declaredStatuses: [400, 401, 404],
1008
+ empty: true
1009
+ }, requestOptions),
1010
+ resource: {
1011
+ catalog: (input, requestOptions) => request({
1012
+ method: "GET",
1013
+ path: `/api/mcp/resource`,
1014
+ query: { location: input?.["location"] },
1015
+ successStatus: 200,
1016
+ declaredStatuses: [400, 401, 404],
1017
+ empty: false
1018
+ }, requestOptions)
1019
+ }
1020
+ },
1021
+ credential: {
1022
+ list: (requestOptions) => request({ method: "GET", path: `/api/credential`, successStatus: 200, declaredStatuses: [400, 401], empty: false }, requestOptions).then((value) => value.data),
1023
+ create: (input, requestOptions) => request({
1024
+ method: "POST",
1025
+ path: `/api/credential`,
1026
+ body: {
1027
+ id: input["id"],
1028
+ integrationID: input["integrationID"],
1029
+ label: input["label"],
1030
+ value: input["value"],
1031
+ activate: input["activate"]
1032
+ },
1033
+ successStatus: 200,
1034
+ declaredStatuses: [400, 401, 409],
1035
+ empty: false
1036
+ }, requestOptions).then((value) => value.data),
1037
+ update: (input, requestOptions) => request({
1038
+ method: "PATCH",
1039
+ path: `/api/credential/${encodeURIComponent(input.credentialID)}`,
1040
+ body: { label: input["label"] },
1041
+ successStatus: 204,
1042
+ declaredStatuses: [400, 401],
1043
+ empty: true
1044
+ }, requestOptions),
1045
+ activate: (input, requestOptions) => request({
1046
+ method: "POST",
1047
+ path: `/api/credential/${encodeURIComponent(input.credentialID)}/activate`,
1048
+ successStatus: 204,
1049
+ declaredStatuses: [400, 401],
1050
+ empty: true
1051
+ }, requestOptions),
1052
+ remove: (input, requestOptions) => request({
1053
+ method: "DELETE",
1054
+ path: `/api/credential/${encodeURIComponent(input.credentialID)}`,
1055
+ successStatus: 204,
1056
+ declaredStatuses: [400, 401],
1057
+ empty: true
1058
+ }, requestOptions)
1059
+ },
1060
+ project: {
1061
+ list: (requestOptions) => request({ method: "GET", path: `/api/project`, successStatus: 200, declaredStatuses: [400, 401, 404], empty: false }, requestOptions),
1062
+ update: (input, requestOptions) => request({
1063
+ method: "PATCH",
1064
+ path: `/api/project/${encodeURIComponent(input.projectID)}`,
1065
+ body: {
1066
+ canonical: input["canonical"],
1067
+ name: input["name"],
1068
+ icon: input["icon"],
1069
+ commands: input["commands"]
1070
+ },
1071
+ successStatus: 200,
1072
+ declaredStatuses: [400, 401, 404],
1073
+ empty: false
1074
+ }, requestOptions)
1075
+ },
1076
+ form: {
1077
+ list: (input, requestOptions) => request({
1078
+ method: "GET",
1079
+ path: `/api/form`,
1080
+ query: { location: input?.["location"] },
1081
+ successStatus: 200,
1082
+ declaredStatuses: [400, 401, 404],
1083
+ empty: false
1084
+ }, requestOptions)
1085
+ },
1086
+ permission: {
1087
+ request: {
1088
+ list: (input, requestOptions) => request({
1089
+ method: "GET",
1090
+ path: `/api/permission/request`,
1091
+ query: { location: input?.["location"] },
1092
+ successStatus: 200,
1093
+ declaredStatuses: [400, 401, 404],
1094
+ empty: false
1095
+ }, requestOptions)
1096
+ },
1097
+ saved: {
1098
+ list: (input, requestOptions) => request({
1099
+ method: "GET",
1100
+ path: `/api/permission/saved`,
1101
+ query: { projectID: input?.["projectID"] },
1102
+ successStatus: 200,
1103
+ declaredStatuses: [400, 401, 404],
1104
+ empty: false
1105
+ }, requestOptions).then((value) => value.data),
1106
+ remove: (input, requestOptions) => request({
1107
+ method: "DELETE",
1108
+ path: `/api/permission/saved/${encodeURIComponent(input.id)}`,
1109
+ successStatus: 204,
1110
+ declaredStatuses: [400, 401, 404],
1111
+ empty: true
1112
+ }, requestOptions)
1113
+ },
1114
+ create: (input, requestOptions) => request({
1115
+ method: "POST",
1116
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/permission`,
1117
+ body: {
1118
+ id: input["id"],
1119
+ action: input["action"],
1120
+ resources: input["resources"],
1121
+ save: input["save"],
1122
+ metadata: input["metadata"],
1123
+ source: input["source"],
1124
+ agent: input["agent"]
1125
+ },
1126
+ successStatus: 200,
1127
+ declaredStatuses: [400, 401, 404],
1128
+ empty: false
1129
+ }, requestOptions).then((value) => value.data),
1130
+ list: (input, requestOptions) => request({
1131
+ method: "GET",
1132
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/permission`,
1133
+ successStatus: 200,
1134
+ declaredStatuses: [400, 401, 404],
1135
+ empty: false
1136
+ }, requestOptions).then((value) => value.data),
1137
+ get: (input, requestOptions) => request({
1138
+ method: "GET",
1139
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/permission/${encodeURIComponent(input.requestID)}`,
1140
+ successStatus: 200,
1141
+ declaredStatuses: [400, 401, 404],
1142
+ empty: false
1143
+ }, requestOptions).then((value) => value.data),
1144
+ reply: (input, requestOptions) => request({
1145
+ method: "POST",
1146
+ path: `/api/session/${encodeURIComponent(input.sessionID)}/permission/${encodeURIComponent(input.requestID)}/reply`,
1147
+ body: { decision: input["decision"], message: input["message"] },
1148
+ successStatus: 204,
1149
+ declaredStatuses: [400, 401, 404],
1150
+ empty: true
1151
+ }, requestOptions)
1152
+ },
1153
+ file: {
1154
+ read: (input, requestOptions) => request({
1155
+ method: "GET",
1156
+ path: `/api/fs/read/${encodePath(input.path)}`,
1157
+ query: { location: input["location"] },
1158
+ successStatus: 200,
1159
+ declaredStatuses: [400, 401, 404],
1160
+ empty: false,
1161
+ binary: true
1162
+ }, requestOptions),
1163
+ list: (input, requestOptions) => request({
1164
+ method: "GET",
1165
+ path: `/api/fs/list`,
1166
+ query: { location: input?.["location"], path: input?.["path"] },
1167
+ successStatus: 200,
1168
+ declaredStatuses: [400, 401, 404],
1169
+ empty: false
1170
+ }, requestOptions),
1171
+ find: (input, requestOptions) => request({
1172
+ method: "GET",
1173
+ path: `/api/fs/find`,
1174
+ query: { location: input["location"], query: input["query"], type: input["type"], limit: input["limit"] },
1175
+ successStatus: 200,
1176
+ declaredStatuses: [400, 401, 404],
1177
+ empty: false
1178
+ }, requestOptions),
1179
+ write: (input, requestOptions) => request({
1180
+ method: "POST",
1181
+ path: `/api/experimental/fs/write`,
1182
+ query: { location: input["location"], path: input["path"] },
1183
+ body: input["payload"],
1184
+ successStatus: 200,
1185
+ declaredStatuses: [400, 401, 404],
1186
+ empty: false,
1187
+ binaryBody: true
1188
+ }, requestOptions)
1189
+ },
1190
+ command: {
1191
+ list: (input, requestOptions) => request({
1192
+ method: "GET",
1193
+ path: `/api/command`,
1194
+ query: { location: input?.["location"] },
1195
+ successStatus: 200,
1196
+ declaredStatuses: [400, 401, 404],
1197
+ empty: false
1198
+ }, requestOptions)
1199
+ },
1200
+ skill: {
1201
+ list: (input, requestOptions) => request({
1202
+ method: "GET",
1203
+ path: `/api/skill`,
1204
+ query: { location: input?.["location"] },
1205
+ successStatus: 200,
1206
+ declaredStatuses: [400, 401, 404],
1207
+ empty: false
1208
+ }, requestOptions)
1209
+ },
1210
+ rpc: {
1211
+ call: (input, requestOptions) => request({
1212
+ method: "POST",
1213
+ path: `/api/rpc/${encodeURIComponent(input.rpcID)}/${encodeURIComponent(input.method)}`,
1214
+ query: { location: input["location"] },
1215
+ body: { input: input["input"] },
1216
+ successStatus: 200,
1217
+ declaredStatuses: [400, 401, 404, 500],
1218
+ empty: false
1219
+ }, requestOptions)
1220
+ },
1221
+ event: {
1222
+ subscribe: (requestOptions) => sse({ method: "GET", path: `/api/event`, successStatus: 200, declaredStatuses: [400, 401], empty: false }, requestOptions)
1223
+ },
1224
+ pty: {
1225
+ list: (input, requestOptions) => request({
1226
+ method: "GET",
1227
+ path: `/api/pty`,
1228
+ query: { location: input?.["location"] },
1229
+ successStatus: 200,
1230
+ declaredStatuses: [400, 401, 404],
1231
+ empty: false
1232
+ }, requestOptions),
1233
+ create: (input, requestOptions) => request({
1234
+ method: "POST",
1235
+ path: `/api/pty`,
1236
+ query: { location: input?.["location"] },
1237
+ body: {
1238
+ command: input?.["command"],
1239
+ args: input?.["args"],
1240
+ cwd: input?.["cwd"],
1241
+ title: input?.["title"],
1242
+ env: input?.["env"]
1243
+ },
1244
+ successStatus: 200,
1245
+ declaredStatuses: [400, 401, 404],
1246
+ empty: false
1247
+ }, requestOptions),
1248
+ get: (input, requestOptions) => request({
1249
+ method: "GET",
1250
+ path: `/api/pty/${encodeURIComponent(input.ptyID)}`,
1251
+ query: { location: input["location"] },
1252
+ successStatus: 200,
1253
+ declaredStatuses: [400, 401, 404],
1254
+ empty: false
1255
+ }, requestOptions),
1256
+ update: (input, requestOptions) => request({
1257
+ method: "PUT",
1258
+ path: `/api/pty/${encodeURIComponent(input.ptyID)}`,
1259
+ query: { location: input["location"] },
1260
+ body: { title: input["title"], size: input["size"] },
1261
+ successStatus: 200,
1262
+ declaredStatuses: [400, 401, 404],
1263
+ empty: false
1264
+ }, requestOptions),
1265
+ remove: (input, requestOptions) => request({
1266
+ method: "DELETE",
1267
+ path: `/api/pty/${encodeURIComponent(input.ptyID)}`,
1268
+ query: { location: input["location"] },
1269
+ successStatus: 204,
1270
+ declaredStatuses: [400, 401, 404],
1271
+ empty: true
1272
+ }, requestOptions),
1273
+ connect: {
1274
+ token: (input, requestOptions) => request({
1275
+ method: "POST",
1276
+ path: `/api/pty/${encodeURIComponent(input.ptyID)}/connect-token`,
1277
+ query: { location: input["location"] },
1278
+ headers: { "x-opencode-ticket": input["x-opencode-ticket"] },
1279
+ successStatus: 200,
1280
+ declaredStatuses: [400, 401, 403, 404],
1281
+ empty: false
1282
+ }, requestOptions)
1283
+ }
1284
+ },
1285
+ experimental: {
1286
+ persistentPty: {
1287
+ read: (input, requestOptions) => request({
1288
+ method: "GET",
1289
+ path: `/api/experimental/session/${encodeURIComponent(input.sessionID)}/terminal/read`,
1290
+ query: { lines: input["lines"] },
1291
+ successStatus: 200,
1292
+ declaredStatuses: [400, 401, 503],
1293
+ empty: false
1294
+ }, requestOptions).then((value) => value.data),
1295
+ list: (input, requestOptions) => request({
1296
+ method: "GET",
1297
+ path: `/api/experimental/session/${encodeURIComponent(input.sessionID)}/terminal`,
1298
+ successStatus: 200,
1299
+ declaredStatuses: [400, 401, 503],
1300
+ empty: false
1301
+ }, requestOptions).then((value) => value.data),
1302
+ create: (input, requestOptions) => request({
1303
+ method: "POST",
1304
+ path: `/api/experimental/session/${encodeURIComponent(input.sessionID)}/terminal`,
1305
+ body: {
1306
+ command: input["command"],
1307
+ args: input["args"],
1308
+ cwd: input["cwd"],
1309
+ title: input["title"],
1310
+ env: input["env"],
1311
+ size: input["size"]
1312
+ },
1313
+ successStatus: 200,
1314
+ declaredStatuses: [400, 401, 503],
1315
+ empty: false
1316
+ }, requestOptions).then((value) => value.data),
1317
+ shutdown: (requestOptions) => request({
1318
+ method: "POST",
1319
+ path: `/api/experimental/persistent-pty/shutdown`,
1320
+ successStatus: 204,
1321
+ declaredStatuses: [400, 401, 503],
1322
+ empty: true
1323
+ }, requestOptions),
1324
+ handoff: (requestOptions) => request({
1325
+ method: "POST",
1326
+ path: `/api/experimental/persistent-pty/handoff`,
1327
+ successStatus: 200,
1328
+ declaredStatuses: [400, 401, 503],
1329
+ empty: false
1330
+ }, requestOptions),
1331
+ get: (input, requestOptions) => request({
1332
+ method: "GET",
1333
+ path: `/api/experimental/persistent-pty/${encodeURIComponent(input.ptyID)}`,
1334
+ successStatus: 200,
1335
+ declaredStatuses: [400, 401, 404, 503],
1336
+ empty: false
1337
+ }, requestOptions).then((value) => value.data),
1338
+ update: (input, requestOptions) => request({
1339
+ method: "PUT",
1340
+ path: `/api/experimental/persistent-pty/${encodeURIComponent(input.ptyID)}`,
1341
+ body: { attachmentID: input["attachmentID"], size: input["size"] },
1342
+ successStatus: 200,
1343
+ declaredStatuses: [400, 401, 404, 503],
1344
+ empty: false
1345
+ }, requestOptions).then((value) => value.data),
1346
+ snapshot: (input, requestOptions) => request({
1347
+ method: "GET",
1348
+ path: `/api/experimental/persistent-pty/${encodeURIComponent(input.ptyID)}/snapshot`,
1349
+ successStatus: 200,
1350
+ declaredStatuses: [400, 401, 404, 503],
1351
+ empty: false
1352
+ }, requestOptions).then((value) => value.data),
1353
+ remove: (input, requestOptions) => request({
1354
+ method: "DELETE",
1355
+ path: `/api/experimental/persistent-pty/${encodeURIComponent(input.ptyID)}`,
1356
+ successStatus: 204,
1357
+ declaredStatuses: [400, 401, 404, 503],
1358
+ empty: true
1359
+ }, requestOptions),
1360
+ connectToken: (input, requestOptions) => request({
1361
+ method: "POST",
1362
+ path: `/api/experimental/persistent-pty/${encodeURIComponent(input.ptyID)}/connect-token`,
1363
+ headers: { "x-opencode-ticket": input["x-opencode-ticket"] },
1364
+ successStatus: 200,
1365
+ declaredStatuses: [400, 401, 403, 404, 503],
1366
+ empty: false
1367
+ }, requestOptions).then((value) => value.data)
1368
+ }
1369
+ },
1370
+ shell: {
1371
+ list: (input, requestOptions) => request({
1372
+ method: "GET",
1373
+ path: `/api/shell`,
1374
+ query: { location: input?.["location"] },
1375
+ successStatus: 200,
1376
+ declaredStatuses: [400, 401, 404],
1377
+ empty: false
1378
+ }, requestOptions),
1379
+ create: (input, requestOptions) => request({
1380
+ method: "POST",
1381
+ path: `/api/shell`,
1382
+ query: { location: input["location"] },
1383
+ body: {
1384
+ command: input["command"],
1385
+ cwd: input["cwd"],
1386
+ timeout: input["timeout"],
1387
+ metadata: input["metadata"]
1388
+ },
1389
+ successStatus: 200,
1390
+ declaredStatuses: [400, 401, 404],
1391
+ empty: false
1392
+ }, requestOptions),
1393
+ get: (input, requestOptions) => request({
1394
+ method: "GET",
1395
+ path: `/api/shell/${encodeURIComponent(input.id)}`,
1396
+ query: { location: input["location"] },
1397
+ successStatus: 200,
1398
+ declaredStatuses: [400, 401, 404],
1399
+ empty: false
1400
+ }, requestOptions),
1401
+ output: (input, requestOptions) => request({
1402
+ method: "GET",
1403
+ path: `/api/shell/${encodeURIComponent(input.id)}/output`,
1404
+ query: { location: input["location"], cursor: input["cursor"], limit: input["limit"] },
1405
+ successStatus: 200,
1406
+ declaredStatuses: [400, 401, 404],
1407
+ empty: false
1408
+ }, requestOptions),
1409
+ remove: (input, requestOptions) => request({
1410
+ method: "DELETE",
1411
+ path: `/api/shell/${encodeURIComponent(input.id)}`,
1412
+ query: { location: input["location"] },
1413
+ successStatus: 204,
1414
+ declaredStatuses: [400, 401, 404],
1415
+ empty: true
1416
+ }, requestOptions)
1417
+ },
1418
+ reference: {
1419
+ list: (input, requestOptions) => request({
1420
+ method: "GET",
1421
+ path: `/api/reference`,
1422
+ query: { location: input?.["location"] },
1423
+ successStatus: 200,
1424
+ declaredStatuses: [400, 401, 404],
1425
+ empty: false
1426
+ }, requestOptions)
1427
+ },
1428
+ worktree: {
1429
+ list: (input, requestOptions) => request({
1430
+ method: "GET",
1431
+ path: `/api/worktree`,
1432
+ query: { projectID: input["projectID"] },
1433
+ successStatus: 200,
1434
+ declaredStatuses: [400, 401, 404],
1435
+ empty: false
1436
+ }, requestOptions),
1437
+ create: (input, requestOptions) => request({
1438
+ method: "POST",
1439
+ path: `/api/worktree`,
1440
+ body: {
1441
+ projectID: input["projectID"],
1442
+ from: input["from"],
1443
+ branch: input["branch"],
1444
+ directory: input["directory"],
1445
+ name: input["name"]
1446
+ },
1447
+ successStatus: 200,
1448
+ declaredStatuses: [400, 401, 404],
1449
+ empty: false
1450
+ }, requestOptions),
1451
+ remove: (input, requestOptions) => request({
1452
+ method: "DELETE",
1453
+ path: `/api/worktree`,
1454
+ body: { projectID: input["projectID"], directory: input["directory"], force: input["force"] },
1455
+ successStatus: 204,
1456
+ declaredStatuses: [400, 401, 404],
1457
+ empty: true
1458
+ }, requestOptions),
1459
+ refresh: (input, requestOptions) => request({
1460
+ method: "POST",
1461
+ path: `/api/worktree/refresh`,
1462
+ body: { projectID: input["projectID"] },
1463
+ successStatus: 204,
1464
+ declaredStatuses: [400, 401, 404],
1465
+ empty: true
1466
+ }, requestOptions)
1467
+ },
1468
+ vcs: {
1469
+ init: (input, requestOptions) => request({
1470
+ method: "POST",
1471
+ path: `/api/vcs/init`,
1472
+ query: { location: input?.["location"], provider: input?.["provider"] },
1473
+ successStatus: 204,
1474
+ declaredStatuses: [400, 401, 404, 409, 501, 503],
1475
+ empty: true
1476
+ }, requestOptions),
1477
+ get: (input, requestOptions) => request({
1478
+ method: "GET",
1479
+ path: `/api/vcs`,
1480
+ query: { location: input?.["location"] },
1481
+ successStatus: 200,
1482
+ declaredStatuses: [400, 401, 404],
1483
+ empty: false
1484
+ }, requestOptions),
1485
+ base: (input, requestOptions) => request({
1486
+ method: "GET",
1487
+ path: `/api/vcs/base`,
1488
+ query: { location: input?.["location"] },
1489
+ successStatus: 200,
1490
+ declaredStatuses: [400, 401, 404, 503],
1491
+ empty: false
1492
+ }, requestOptions),
1493
+ status: (input, requestOptions) => request({
1494
+ method: "GET",
1495
+ path: `/api/vcs/status`,
1496
+ query: { location: input?.["location"] },
1497
+ successStatus: 200,
1498
+ declaredStatuses: [400, 401, 404],
1499
+ empty: false
1500
+ }, requestOptions),
1501
+ branch: {
1502
+ list: (input, requestOptions) => request({
1503
+ method: "GET",
1504
+ path: `/api/vcs/branch`,
1505
+ query: { location: input?.["location"], search: input?.["search"], limit: input?.["limit"] },
1506
+ successStatus: 200,
1507
+ declaredStatuses: [400, 401, 404],
1508
+ empty: false
1509
+ }, requestOptions)
1510
+ },
1511
+ diff: (input, requestOptions) => request({
1512
+ method: "GET",
1513
+ path: `/api/vcs/diff`,
1514
+ query: { location: input["location"], mode: input["mode"], base: input["base"], context: input["context"] },
1515
+ successStatus: 200,
1516
+ declaredStatuses: [400, 401, 404, 503],
1517
+ empty: false
1518
+ }, requestOptions)
1519
+ },
1520
+ debug: {
1521
+ location: {
1522
+ list: (requestOptions) => request({
1523
+ method: "GET",
1524
+ path: `/api/debug/location`,
1525
+ successStatus: 200,
1526
+ declaredStatuses: [400, 401],
1527
+ empty: false
1528
+ }, requestOptions),
1529
+ evict: (input, requestOptions) => request({
1530
+ method: "DELETE",
1531
+ path: `/api/debug/location`,
1532
+ query: { location: input?.["location"] },
1533
+ successStatus: 204,
1534
+ declaredStatuses: [400, 401],
1535
+ empty: true
1536
+ }, requestOptions)
1537
+ }
1538
+ },
1539
+ migration: {
1540
+ v1: {
1541
+ status: (requestOptions) => request({
1542
+ method: "GET",
1543
+ path: `/api/experimental/migration/v1`,
1544
+ successStatus: 200,
1545
+ declaredStatuses: [400, 401],
1546
+ empty: false
1547
+ }, requestOptions)
1548
+ }
1549
+ },
1550
+ websearch: {
1551
+ providers: (input, requestOptions) => request({
1552
+ method: "GET",
1553
+ path: `/api/websearch/provider`,
1554
+ query: { location: input?.["location"] },
1555
+ successStatus: 200,
1556
+ declaredStatuses: [400, 401, 404, 503],
1557
+ empty: false
1558
+ }, requestOptions),
1559
+ query: (input, requestOptions) => request({
1560
+ method: "POST",
1561
+ path: `/api/websearch`,
1562
+ query: { location: input["location"] },
1563
+ body: { query: input["query"], providerID: input["providerID"] },
1564
+ successStatus: 200,
1565
+ declaredStatuses: [400, 401, 404, 503],
1566
+ empty: false
1567
+ }, requestOptions)
1568
+ },
1569
+ config: {
1570
+ get: (input, requestOptions) => request({
1571
+ method: "GET",
1572
+ path: `/api/config`,
1573
+ query: { location: input?.["location"] },
1574
+ successStatus: 200,
1575
+ declaredStatuses: [400, 401, 404],
1576
+ empty: false
1577
+ }, requestOptions),
1578
+ shells: (requestOptions) => request({
1579
+ method: "GET",
1580
+ path: `/api/config/shell`,
1581
+ successStatus: 200,
1582
+ declaredStatuses: [400, 401, 404],
1583
+ empty: false
1584
+ }, requestOptions),
1585
+ update: (input, requestOptions) => request({
1586
+ method: "PATCH",
1587
+ path: `/api/experimental/config`,
1588
+ body: { shell: input["shell"] },
1589
+ successStatus: 204,
1590
+ declaredStatuses: [400, 401, 404],
1591
+ empty: true
1592
+ }, requestOptions)
1593
+ }
1594
+ };
1595
+ }
1596
+ function encodePath(value) {
1597
+ return value.split("/").map(encodeURIComponent).join("/");
1598
+ }
1599
+ function appendQuery(params, key, value) {
1600
+ if (value === void 0)
1601
+ return;
1602
+ if (value === null) {
1603
+ params.append(key, "null");
1604
+ return;
1605
+ }
1606
+ if (Array.isArray(value)) {
1607
+ for (const item of value)
1608
+ appendQuery(params, key, item);
1609
+ return;
1610
+ }
1611
+ if (typeof value === "object") {
1612
+ for (const [child, item] of Object.entries(value))
1613
+ appendQuery(params, `${key}[${child}]`, item);
1614
+ return;
1615
+ }
1616
+ params.append(key, String(value));
1617
+ }
1618
+ async function json2(response) {
1619
+ if (!isContentType(response, "application/json") && !response.headers.get("content-type")?.includes("+json")) {
1620
+ try {
1621
+ await response.body?.cancel();
1622
+ } catch {
1623
+ }
1624
+ throw new ClientError2("UnsupportedContentType", { detail: response.headers.get("content-type") });
1625
+ }
1626
+ let text;
1627
+ try {
1628
+ text = await response.text();
1629
+ } catch (cause) {
1630
+ throw new ClientError2("Transport", { cause });
1631
+ }
1632
+ if (text === "")
1633
+ throw new ClientError2("MalformedResponse");
1634
+ try {
1635
+ return JSON.parse(text);
1636
+ } catch (cause) {
1637
+ throw new ClientError2("MalformedResponse", { cause });
1638
+ }
1639
+ }
1640
+ function declared(body) {
1641
+ const error = Object.assign(new Error(body.message ?? body.data?.message), body);
1642
+ if (body._tag)
1643
+ error.name = body._tag;
1644
+ return error;
1645
+ }
1646
+ function isContentType(response, expected) {
1647
+ return response.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase() === expected;
1648
+ }
1649
+ var exports_client2, maxSseEventBytes;
1650
+ var init_service_timing_tbh3h57v = __esm({
1651
+ "node_modules/@opencode/client/dist/chunks/service-timing-tbh3h57v.js"() {
1652
+ init_service_timing_js54wxgp();
1653
+ init_service_timing_9rqn6x4v();
1654
+ exports_client2 = {};
1655
+ __export2(exports_client2, {
1656
+ make: () => make3
1657
+ });
1658
+ maxSseEventBytes = 16 * 1024 * 1024;
1659
+ }
1660
+ });
1661
+
1662
+ // node_modules/@opencode/client/dist/chunks/service-timing-vt9jbch8.js
1663
+ var isInvalidRequestError2, isUnauthorizedError2, isLocationNotFoundError2, isServiceUnavailableError2, isAgentNotFoundError2, isInvalidCursorError2, isSessionNotFoundError2, isConflictError2, isUnknownError2, isMessageNotFoundError2, isCommandNotFoundError2, isCommandExecutionError2, isSkillNotFoundError2, isSessionBusyError2, isInstructionEntryValueTooLargeError2, isFormNotFoundError2, isFormInvalidAnswerError2, isFormAlreadySettledError2, isProviderNotFoundError2, isIntegrationNotFoundError2, isIntegrationAttemptNotFoundError2, isIntegrationMethodNotFoundError2, isMcpServerNotFoundError2, isProjectNotFoundError2, isPermissionNotFoundError2, isFileNotFoundError2, isRpcError2, isRpcInternalError2, isPtyNotFoundError2, isForbiddenError2, isShellNotFoundError2, isWorktreeError2, isVcsInitNotSupportedError2;
1664
+ var init_service_timing_vt9jbch8 = __esm({
1665
+ "node_modules/@opencode/client/dist/chunks/service-timing-vt9jbch8.js"() {
1666
+ isInvalidRequestError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "InvalidRequestError";
1667
+ isUnauthorizedError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "UnauthorizedError";
1668
+ isLocationNotFoundError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "LocationNotFoundError";
1669
+ isServiceUnavailableError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "ServiceUnavailableError";
1670
+ isAgentNotFoundError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "AgentNotFoundError";
1671
+ isInvalidCursorError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "InvalidCursorError";
1672
+ isSessionNotFoundError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "SessionNotFoundError";
1673
+ isConflictError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "ConflictError";
1674
+ isUnknownError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "UnknownError";
1675
+ isMessageNotFoundError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "MessageNotFoundError";
1676
+ isCommandNotFoundError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "CommandNotFoundError";
1677
+ isCommandExecutionError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "CommandExecutionError";
1678
+ isSkillNotFoundError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "SkillNotFoundError";
1679
+ isSessionBusyError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "SessionBusyError";
1680
+ isInstructionEntryValueTooLargeError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "InstructionEntryValueTooLargeError";
1681
+ isFormNotFoundError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "FormNotFoundError";
1682
+ isFormInvalidAnswerError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "FormInvalidAnswerError";
1683
+ isFormAlreadySettledError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "FormAlreadySettledError";
1684
+ isProviderNotFoundError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "ProviderNotFoundError";
1685
+ isIntegrationNotFoundError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "IntegrationNotFoundError";
1686
+ isIntegrationAttemptNotFoundError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "IntegrationAttemptNotFoundError";
1687
+ isIntegrationMethodNotFoundError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "IntegrationMethodNotFoundError";
1688
+ isMcpServerNotFoundError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "McpServerNotFoundError";
1689
+ isProjectNotFoundError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "ProjectNotFoundError";
1690
+ isPermissionNotFoundError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "PermissionNotFoundError";
1691
+ isFileNotFoundError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "FileNotFoundError";
1692
+ isRpcError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "RpcError";
1693
+ isRpcInternalError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "RpcInternalError";
1694
+ isPtyNotFoundError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "PtyNotFoundError";
1695
+ isForbiddenError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "ForbiddenError";
1696
+ isShellNotFoundError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "ShellNotFoundError";
1697
+ isWorktreeError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "WorktreeError";
1698
+ isVcsInitNotSupportedError2 = (value) => typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "VcsInitNotSupportedError";
1699
+ }
1700
+ });
1701
+
1702
+ // node_modules/@opencode/client/dist/chunks/service-timing-1y4a697q.js
1703
+ function makeRpc2(raw, events) {
1704
+ return (definition) => {
1705
+ const subscribe = (name, options) => {
1706
+ if (!Object.hasOwn(definition.events, name))
1707
+ throw new Error(`Unknown RPC event: ${definition.id}.${name}`);
1708
+ const type = eventType(definition, name);
1709
+ return {
1710
+ [Symbol.asyncIterator]() {
1711
+ const controller = new AbortController();
1712
+ const signal2 = options?.signal ? AbortSignal.any([controller.signal, options.signal]) : controller.signal;
1713
+ const iterator = (async function* () {
1714
+ try {
1715
+ for await (const published of events.subscribe({ signal: signal2 })) {
1716
+ if (signal2.aborted)
1717
+ return;
1718
+ if (published.type !== type)
1719
+ continue;
1720
+ yield published;
1721
+ }
1722
+ } catch (error) {
1723
+ if (!signal2.aborted)
1724
+ throw error;
1725
+ } finally {
1726
+ controller.abort();
1727
+ }
1728
+ })();
1729
+ return {
1730
+ next: () => iterator.next(),
1731
+ return: () => {
1732
+ controller.abort();
1733
+ return iterator.return();
1734
+ }
1735
+ };
1736
+ }
1737
+ };
1738
+ };
1739
+ return Object.assign(Object.fromEntries(Object.keys(definition.methods).map((name) => [
1740
+ name,
1741
+ async (input, options) => {
1742
+ try {
1743
+ const result = await raw.rpc.call({
1744
+ rpcID: definition.id,
1745
+ method: name,
1746
+ input,
1747
+ location: options?.location
1748
+ }, { signal: options?.signal, headers: options?.headers });
1749
+ return result.output;
1750
+ } catch (error) {
1751
+ if (!isRpcError2(error) && !isRpcInternalError2(error))
1752
+ throw error;
1753
+ throw error.data === void 0 ? { type: error.type, message: error.message } : { type: error.type, message: error.message, data: error.data };
1754
+ }
1755
+ }
1756
+ ])), {
1757
+ events: {
1758
+ subscribe,
1759
+ on: (name, handler, options) => {
1760
+ const controller = new AbortController();
1761
+ const signal2 = options?.signal ? AbortSignal.any([controller.signal, options.signal]) : controller.signal;
1762
+ const source = subscribe(name, { signal: signal2 });
1763
+ (async () => {
1764
+ for await (const event of source)
1765
+ await handler(event);
1766
+ })().catch((error) => console.error(error));
1767
+ return () => controller.abort();
1768
+ }
1769
+ }
1770
+ });
1771
+ };
1772
+ }
1773
+ function eventType(definition, name) {
1774
+ return `rpc.${definition.id}.${name}`;
1775
+ }
1776
+ var init_service_timing_1y4a697q = __esm({
1777
+ "node_modules/@opencode/client/dist/chunks/service-timing-1y4a697q.js"() {
1778
+ init_service_timing_vt9jbch8();
1779
+ }
1780
+ });
1781
+
1782
+ // node_modules/@opencode/client/dist/chunks/service-timing-e4sc9kq6.js
1783
+ function make2(options) {
1784
+ const raw = make3(options);
1785
+ const events = make7((signal2, onActivity) => raw.event.subscribe({ signal: signal2, onActivity }));
1786
+ return {
1787
+ ...raw,
1788
+ rpc: Object.assign(makeRpc2(raw, events), raw.rpc),
1789
+ event: events
1790
+ };
1791
+ }
1792
+ var exports_client;
1793
+ var init_service_timing_e4sc9kq6 = __esm({
1794
+ "node_modules/@opencode/client/dist/chunks/service-timing-e4sc9kq6.js"() {
1795
+ init_service_timing_frbwqjmf();
1796
+ init_service_timing_tbh3h57v();
1797
+ init_service_timing_1y4a697q();
1798
+ init_service_timing_9rqn6x4v();
1799
+ exports_client = {};
1800
+ __export2(exports_client, {
1801
+ OpenCode: () => exports_client,
1802
+ make: () => make2
1803
+ });
1804
+ }
1805
+ });
1806
+
1807
+ // node_modules/@opencode/client/dist/promise/index.js
1808
+ var promise_exports = {};
1809
+ __export(promise_exports, {
1810
+ ClientError: () => ClientError2,
1811
+ OpenCode: () => exports_client,
1812
+ isAgentNotFoundError: () => isAgentNotFoundError2,
1813
+ isCommandExecutionError: () => isCommandExecutionError2,
1814
+ isCommandNotFoundError: () => isCommandNotFoundError2,
1815
+ isConflictError: () => isConflictError2,
1816
+ isFileNotFoundError: () => isFileNotFoundError2,
1817
+ isForbiddenError: () => isForbiddenError2,
1818
+ isFormAlreadySettledError: () => isFormAlreadySettledError2,
1819
+ isFormInvalidAnswerError: () => isFormInvalidAnswerError2,
1820
+ isFormNotFoundError: () => isFormNotFoundError2,
1821
+ isInstructionEntryValueTooLargeError: () => isInstructionEntryValueTooLargeError2,
1822
+ isIntegrationAttemptNotFoundError: () => isIntegrationAttemptNotFoundError2,
1823
+ isIntegrationMethodNotFoundError: () => isIntegrationMethodNotFoundError2,
1824
+ isIntegrationNotFoundError: () => isIntegrationNotFoundError2,
1825
+ isInvalidCursorError: () => isInvalidCursorError2,
1826
+ isInvalidRequestError: () => isInvalidRequestError2,
1827
+ isLocationNotFoundError: () => isLocationNotFoundError2,
1828
+ isMcpServerNotFoundError: () => isMcpServerNotFoundError2,
1829
+ isMessageNotFoundError: () => isMessageNotFoundError2,
1830
+ isPermissionNotFoundError: () => isPermissionNotFoundError2,
1831
+ isProjectNotFoundError: () => isProjectNotFoundError2,
1832
+ isProviderNotFoundError: () => isProviderNotFoundError2,
1833
+ isPtyNotFoundError: () => isPtyNotFoundError2,
1834
+ isRpcError: () => isRpcError2,
1835
+ isRpcInternalError: () => isRpcInternalError2,
1836
+ isServiceUnavailableError: () => isServiceUnavailableError2,
1837
+ isSessionBusyError: () => isSessionBusyError2,
1838
+ isSessionNotFoundError: () => isSessionNotFoundError2,
1839
+ isShellNotFoundError: () => isShellNotFoundError2,
1840
+ isSkillNotFoundError: () => isSkillNotFoundError2,
1841
+ isUnauthorizedError: () => isUnauthorizedError2,
1842
+ isUnknownError: () => isUnknownError2,
1843
+ isVcsInitNotSupportedError: () => isVcsInitNotSupportedError2,
1844
+ isWorktreeError: () => isWorktreeError2
1845
+ });
1846
+ var init_promise = __esm({
1847
+ "node_modules/@opencode/client/dist/promise/index.js"() {
1848
+ init_service_timing_e4sc9kq6();
1849
+ init_service_timing_tbh3h57v();
1850
+ init_service_timing_js54wxgp();
1851
+ init_service_timing_1y4a697q();
1852
+ init_service_timing_vt9jbch8();
1853
+ init_service_timing_frbwqjmf();
1854
+ }
1855
+ });
1856
+
1
1857
  // src/config.js
2
- import { readFileSync } from "node:fs";
1858
+ import { readFileSync as readFileSync2 } from "node:fs";
3
1859
  import { homedir } from "node:os";
4
- import { join } from "node:path";
1860
+ import { join as join2 } from "node:path";
5
1861
 
6
1862
  // src/utilities.js
7
1863
  var MAX_TRACKED_ITEMS = 500;
@@ -74,6 +1930,48 @@ function rememberRecent(map, key, value) {
74
1930
  while (map.size > 100) map.delete(map.keys().next().value);
75
1931
  }
76
1932
 
1933
+ // src/live-bridge-config.js
1934
+ import { constants, closeSync, fstatSync, openSync, readFileSync } from "node:fs";
1935
+ import { isAbsolute, join } from "node:path";
1936
+ function readCredential(file) {
1937
+ if (typeof file !== "string" || !isAbsolute(file)) {
1938
+ throw new Error("Live control requires an absolute credential-file path");
1939
+ }
1940
+ let descriptor;
1941
+ try {
1942
+ descriptor = openSync(file, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0) | (constants.O_NONBLOCK ?? 0));
1943
+ const stat = fstatSync(descriptor);
1944
+ if (!stat.isFile() || stat.size > 4096 || process.platform !== "win32" && ((stat.mode & 63) !== 0 || stat.uid !== process.getuid())) {
1945
+ throw new Error("invalid_file");
1946
+ }
1947
+ const token = readFileSync(descriptor, "utf8").trim();
1948
+ if (!/^[A-Za-z0-9_-]{32,256}$/.test(token)) throw new Error("invalid_token");
1949
+ return token;
1950
+ } catch {
1951
+ throw new Error("Live control credential must be an owner-only regular file containing a 32\u2013256 character token");
1952
+ } finally {
1953
+ if (descriptor !== void 0) closeSync(descriptor);
1954
+ }
1955
+ }
1956
+ function loadLiveBridgeConfig(option, { configDirectory, ingestToken } = {}) {
1957
+ const values = option && typeof option === "object" ? option : {};
1958
+ const enabled2 = (typeof option === "boolean" ? option : values.enabled) ?? process.env.BACKEER_LIVE_ENABLED;
1959
+ if (enabled2 !== true && enabled2 !== "true" && enabled2 !== "1") return { enabled: false };
1960
+ try {
1961
+ const rawPort = values.port ?? process.env.BACKEER_LIVE_PORT ?? 43871;
1962
+ const port = Number(rawPort);
1963
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
1964
+ throw new Error("Live control port must be an integer between 1 and 65535");
1965
+ }
1966
+ const tokenFile = values.tokenFile ?? process.env.BACKEER_LIVE_TOKEN_FILE ?? join(configDirectory, "secrets", "backeer-live-token");
1967
+ const token = readCredential(tokenFile);
1968
+ if (token === ingestToken) throw new Error("Live control requires a credential distinct from the Backeer ingest token");
1969
+ return { enabled: true, token, tokenFile, port };
1970
+ } catch (error) {
1971
+ return { enabled: false, error: error.message };
1972
+ }
1973
+ }
1974
+
77
1975
  // src/config.js
78
1976
  var DEFAULT_ORIGIN = "https://external-gateway.backeer.com";
79
1977
  function integer(value, fallback, { minimum = 0, maximum = 86400 } = {}) {
@@ -115,13 +2013,13 @@ function loadLifecycleConfig(option) {
115
2013
  }
116
2014
  function defaultConfigDirectory() {
117
2015
  const base = process.env.XDG_CONFIG_HOME?.trim();
118
- return base ? join(base, "opencode") : join(homedir(), ".config", "opencode");
2016
+ return base ? join2(base, "opencode") : join2(homedir(), ".config", "opencode");
119
2017
  }
120
2018
  function loadToken(tokenFile) {
121
2019
  const environmentToken = process.env.BACKEER_INGEST_TOKEN?.trim();
122
2020
  if (environmentToken) return { token: environmentToken, source: "environment" };
123
2021
  try {
124
- const token = readFileSync(tokenFile, "utf8").trim();
2022
+ const token = readFileSync2(tokenFile, "utf8").trim();
125
2023
  return { token, source: token ? "file" : "missing" };
126
2024
  } catch {
127
2025
  return { token: "", source: "missing" };
@@ -134,7 +2032,7 @@ function loadConfig(options = {}) {
134
2032
  if (origin.protocol !== "https:") {
135
2033
  throw new Error("Backeer origin must use HTTPS");
136
2034
  }
137
- const tokenFile = options.tokenFile ?? process.env.BACKEER_TOKEN_FILE ?? join(defaultConfigDirectory(), "secrets", "backeer-ingest-token");
2035
+ const tokenFile = options.tokenFile ?? process.env.BACKEER_TOKEN_FILE ?? join2(defaultConfigDirectory(), "secrets", "backeer-ingest-token");
138
2036
  const loadedToken = loadToken(tokenFile);
139
2037
  return {
140
2038
  origin,
@@ -144,6 +2042,10 @@ function loadConfig(options = {}) {
144
2042
  token: loadedToken.token,
145
2043
  tokenSource: loadedToken.source,
146
2044
  tokenFile,
2045
+ liveBridge: loadLiveBridgeConfig(options.liveBridge, {
2046
+ configDirectory: defaultConfigDirectory(),
2047
+ ingestToken: loadedToken.token
2048
+ }),
147
2049
  permissions: enabled(
148
2050
  options.permissions ?? process.env.BACKEER_NOTIFY_PERMISSIONS
149
2051
  ),
@@ -238,19 +2140,19 @@ function isRetryable(status) {
238
2140
  function isAbortError(error) {
239
2141
  return error instanceof Error && error.name === "AbortError";
240
2142
  }
241
- function sleep(milliseconds, signal) {
2143
+ function sleep(milliseconds, signal2) {
242
2144
  return new Promise((resolve2) => {
243
- if (signal?.aborted) {
2145
+ if (signal2?.aborted) {
244
2146
  resolve2();
245
2147
  return;
246
2148
  }
247
2149
  const timeout = setTimeout(done, milliseconds);
248
2150
  function done() {
249
2151
  clearTimeout(timeout);
250
- signal?.removeEventListener("abort", done);
2152
+ signal2?.removeEventListener("abort", done);
251
2153
  resolve2();
252
2154
  }
253
- signal?.addEventListener("abort", done, { once: true });
2155
+ signal2?.addEventListener("abort", done, { once: true });
254
2156
  });
255
2157
  }
256
2158
  async function postJsonWithRetry(config, url, body, options = {}) {
@@ -481,7 +2383,7 @@ async function publishTodoCompletion(config, reporter, request, eventID, client,
481
2383
  import {
482
2384
  chmodSync,
483
2385
  mkdirSync,
484
- readFileSync as readFileSync2,
2386
+ readFileSync as readFileSync3,
485
2387
  renameSync,
486
2388
  statSync as statSync2,
487
2389
  writeFileSync
@@ -493,7 +2395,7 @@ import { randomUUID } from "node:crypto";
493
2395
  import { statSync } from "node:fs";
494
2396
 
495
2397
  // src/updates.js
496
- var PLUGIN_VERSION = "0.3.2";
2398
+ var PLUGIN_VERSION = "0.5.1";
497
2399
  var PACKAGE_NAME = "backeer-opencode";
498
2400
  var NPM_REGISTRY_URL = "https://registry.npmjs.org";
499
2401
  var CHECK_TIMEOUT_MS = 3e3;
@@ -524,7 +2426,8 @@ function isVersionNewer(latestValue, currentValue) {
524
2426
  }
525
2427
  function createUpdateChecker({
526
2428
  fetchImplementation = globalThis.fetch,
527
- now = () => Date.now()
2429
+ now = () => Date.now(),
2430
+ v2 = false
528
2431
  } = {}) {
529
2432
  let checkedAt = 0;
530
2433
  let cachedResult;
@@ -554,7 +2457,7 @@ function createUpdateChecker({
554
2457
  state: "available",
555
2458
  currentVersion: PLUGIN_VERSION,
556
2459
  latestVersion,
557
- updateCommand: `opencode plugin ${PACKAGE_NAME}@${latestVersion} -g --force`
2460
+ updateCommand: v2 ? `opencode plugin add ${PACKAGE_NAME}@${latestVersion}` : `opencode plugin ${PACKAGE_NAME}@${latestVersion} -g --force`
558
2461
  };
559
2462
  } catch {
560
2463
  return { state: "unavailable", currentVersion: PLUGIN_VERSION };
@@ -983,7 +2886,7 @@ function rotateTokenFromFile(config, sourcePathValue) {
983
2886
  if ((sourceStats.mode & 63) !== 0) {
984
2887
  throw new Error("The token source must not be readable by group or other users");
985
2888
  }
986
- const token = readFileSync2(sourcePath, "utf8").trim();
2889
+ const token = readFileSync3(sourcePath, "utf8").trim();
987
2890
  if (!token || /\s/.test(token)) {
988
2891
  throw new Error("The token source must contain exactly one non-empty token");
989
2892
  }
@@ -1510,35 +3413,546 @@ function createLifecycleController({
1510
3413
  };
1511
3414
  }
1512
3415
 
1513
- // src/permission-command.js
1514
- var ALLOWED_DECISIONS = /* @__PURE__ */ new Set(["once", "always", "reject"]);
1515
- var DECISION_LABELS = {
1516
- once: "Allow once",
1517
- always: "Always allow",
1518
- reject: "Reject"
1519
- };
1520
- function validatePermissionCommand(command, now = Date.now()) {
1521
- if (!ALLOWED_DECISIONS.has(command.decision)) {
1522
- return {
1523
- status: "rejected",
1524
- decision: command.decision,
1525
- errorCode: "invalid_decision"
1526
- };
1527
- }
1528
- const expiresAt = Date.parse(command.expiresAt);
1529
- if (!Number.isFinite(expiresAt) || expiresAt <= now) {
1530
- return { status: "expired", decision: command.decision };
1531
- }
1532
- if (command.decision === "always" && command.confirmed !== true) {
1533
- return {
1534
- status: "rejected",
1535
- decision: command.decision,
1536
- errorCode: "confirmation_required"
1537
- };
3416
+ // src/live-bridge.js
3417
+ import { createServer } from "node:http";
3418
+ import { randomUUID as randomUUID3, timingSafeEqual } from "node:crypto";
3419
+
3420
+ // src/live-bridge-client.js
3421
+ function createV2LiveClient(ctx) {
3422
+ const known = /* @__PURE__ */ new Map();
3423
+ let disposed = false;
3424
+ async function get(id) {
3425
+ const info = await ctx.session.get({ sessionID: id });
3426
+ if (disposed) return info;
3427
+ known.set(id, info);
3428
+ if (known.size > 100) known.delete(known.keys().next().value);
3429
+ return info;
1538
3430
  }
3431
+ return {
3432
+ async list() {
3433
+ if (typeof ctx.session.list === "function") {
3434
+ return ctx.session.list({ directory: ctx.location.directory, limit: 100, order: "desc" });
3435
+ }
3436
+ const refreshed = [];
3437
+ for (const id of known.keys()) {
3438
+ try {
3439
+ refreshed.push(await get(id));
3440
+ } catch {
3441
+ known.delete(id);
3442
+ }
3443
+ }
3444
+ return refreshed;
3445
+ },
3446
+ get,
3447
+ prompt: (id, text) => ctx.session.prompt({ sessionID: id, text, delivery: "queue" }),
3448
+ dispose: () => {
3449
+ disposed = true;
3450
+ known.clear();
3451
+ }
3452
+ };
1539
3453
  }
1540
- function failureAcknowledgement(command, error) {
1541
- const stale = error instanceof HttpError && error.status === 404;
3454
+ function unwrap(value) {
3455
+ if (value?.error) throw new Error("OpenCode request failed");
3456
+ return value?.data ?? value;
3457
+ }
3458
+ function createV1LiveClient(client, directory) {
3459
+ return {
3460
+ list: async () => unwrap(await client.session.list({ query: { directory }, throwOnError: true })),
3461
+ get: async (id) => unwrap(await client.session.get({ path: { id }, query: { directory }, throwOnError: true })),
3462
+ async prompt(id, text) {
3463
+ if (typeof client.session.promptAsync !== "function") {
3464
+ throw Object.assign(new Error("OpenCode asynchronous prompts are unavailable"), { status: 501 });
3465
+ }
3466
+ return unwrap(await client.session.promptAsync({
3467
+ path: { id },
3468
+ query: { directory },
3469
+ body: { parts: [{ type: "text", text }] },
3470
+ throwOnError: true
3471
+ }));
3472
+ }
3473
+ };
3474
+ }
3475
+
3476
+ // src/live-connection.js
3477
+ import { randomUUID as randomUUID2 } from "node:crypto";
3478
+ var LIVE_RECONNECT_MS = 3e3;
3479
+ var LIVE_HEARTBEAT_MS = 15e3;
3480
+ function createLiveConnectionNotifier(config, reporter) {
3481
+ const epoch = randomUUID2();
3482
+ const controller = new AbortController();
3483
+ let last, sequence = 0, pending, task;
3484
+ async function drain() {
3485
+ while (pending && !controller.signal.aborted) {
3486
+ const event = pending;
3487
+ pending = void 0;
3488
+ try {
3489
+ await postJsonWithRetry(config, config.eventsEndpoint, event, { signal: controller.signal });
3490
+ } catch {
3491
+ if (!controller.signal.aborted) reporter?.error("Live connection notification could not be sent");
3492
+ }
3493
+ }
3494
+ }
3495
+ return {
3496
+ update(connected) {
3497
+ if (controller.signal.aborted || connected === last) return;
3498
+ const initial = last === void 0;
3499
+ last = connected;
3500
+ if (initial || !config.token || !config.eventsEndpoint) return;
3501
+ pending = {
3502
+ type: "application",
3503
+ severity: connected ? "info" : "warning",
3504
+ title: connected ? "OpenCode ba\u011Flant\u0131s\u0131 yeniden kuruldu" : "OpenCode ba\u011Flant\u0131s\u0131 kesildi",
3505
+ body: connected ? "Canl\u0131 takip yeniden ba\u011Fland\u0131. Sunucudaki g\xF6rev otomatik olarak yeniden ba\u015Flat\u0131lmad\u0131." : "Canl\u0131 takip ba\u011Flant\u0131s\u0131 kesildi. Yeniden ba\u011Flanma her 3 saniyede denenecek; sunucudaki g\xF6rev durdurulmad\u0131.",
3506
+ payload: [{ key: "Connection", value: connected ? "reconnected" : "disconnected" }],
3507
+ externalId: `opencode:live:${epoch}:${++sequence}`,
3508
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3509
+ notify: true,
3510
+ direction: "in"
3511
+ };
3512
+ task ??= drain().finally(() => {
3513
+ task = void 0;
3514
+ });
3515
+ },
3516
+ async dispose() {
3517
+ controller.abort();
3518
+ pending = void 0;
3519
+ await task;
3520
+ }
3521
+ };
3522
+ }
3523
+
3524
+ // src/live-bridge-events.js
3525
+ var ID = /^[A-Za-z0-9_-]{1,160}$/;
3526
+ var validBridgeID = (value) => typeof value === "string" && ID.test(value);
3527
+ function createBridgeSanitizer(secrets = []) {
3528
+ const known = secrets.filter((value) => typeof value === "string" && value.length > 0);
3529
+ const assistants = /* @__PURE__ */ new Map();
3530
+ function clipped(value, max = 12e3) {
3531
+ if (typeof value !== "string") return { value: "", truncated: false };
3532
+ let result = value.slice(0, 64e3);
3533
+ for (const secret of known) result = result.split(secret).join("[REDACTED]");
3534
+ result = result.replace(/-----BEGIN [^-]*PRIVATE KEY-----[\s\S]*?(?:-----END [^-]*PRIVATE KEY-----|$)/g, "[REDACTED PRIVATE KEY]").replace(/\bBearer\s+[^\s"'<>]+/gi, "Bearer [REDACTED]").replace(/\b(?:sk|bgi)_[A-Za-z0-9_-]{12,}|\bsk-[A-Za-z0-9_-]{12,}/g, "[REDACTED]").replace(/((?:api[_-]?key|access[_-]?token|secret|password|authorization)\s*[=:]\s*)[^\s,;]+/gi, "$1[REDACTED]");
3535
+ return { value: result.slice(0, max), truncated: value.length > 64e3 || result.length > max };
3536
+ }
3537
+ function text(value, max) {
3538
+ return clipped(value, max).value;
3539
+ }
3540
+ function content(key, value) {
3541
+ const result = clipped(value);
3542
+ return { [key]: result.value, ...result.truncated ? { truncated: true } : {} };
3543
+ }
3544
+ function sanitize(event) {
3545
+ const p = event?.properties ?? event?.data ?? {};
3546
+ const sessionID = p.sessionID ?? p.info?.sessionID ?? p.part?.sessionID;
3547
+ if (!validBridgeID(sessionID) || sessionID === "global") return void 0;
3548
+ const base = { type: event.type, sessionID, data: {} };
3549
+ if (event.type === "message.updated") {
3550
+ const info = p.info;
3551
+ if (!validBridgeID(info?.id)) return void 0;
3552
+ if (info.role === "assistant") {
3553
+ assistants.set(info.id, sessionID);
3554
+ if (assistants.size > 256) assistants.delete(assistants.keys().next().value);
3555
+ }
3556
+ return info.role === "assistant" ? { ...base, data: {
3557
+ messageID: info.id,
3558
+ completed: !!info.time?.completed,
3559
+ failed: !!info.error
3560
+ } } : void 0;
3561
+ }
3562
+ if (event.type === "message.part.updated") {
3563
+ const part = p.part;
3564
+ if (assistants.get(part?.messageID) !== sessionID) return void 0;
3565
+ if (part.type === "text") return { ...base, type: "assistant.text", data: {
3566
+ messageID: part.messageID,
3567
+ ...content("text", part.text),
3568
+ replace: true
3569
+ } };
3570
+ if (part.type !== "tool") return void 0;
3571
+ const state = part.state ?? {};
3572
+ return { ...base, type: "tool.status", data: {
3573
+ tool: text(part.tool, 80),
3574
+ status: text(state.status, 40),
3575
+ ...state.status === "completed" ? content("output", state.output) : {},
3576
+ ...state.status === "error" ? { failed: true } : {}
3577
+ } };
3578
+ }
3579
+ if (event.type === "session.text.ended") return { ...base, type: "assistant.text", data: {
3580
+ messageID: validBridgeID(p.assistantMessageID) ? p.assistantMessageID : void 0,
3581
+ ...content("text", p.text),
3582
+ replace: true
3583
+ } };
3584
+ if (["session.tool.called", "session.tool.success", "session.tool.failed"].includes(event.type)) {
3585
+ return { ...base, type: "tool.status", data: {
3586
+ status: event.type.split(".").at(-1),
3587
+ ...event.type === "session.tool.success" ? content(
3588
+ "output",
3589
+ (p.content ?? []).filter((item) => item?.type === "text").map((item) => item.text).join("\n")
3590
+ ) : {}
3591
+ } };
3592
+ }
3593
+ if (["permission.asked", "question.asked", "question.v2.asked"].includes(event.type)) {
3594
+ return { ...base, type: "approval.waiting", data: { kind: event.type.split(".")[0] } };
3595
+ }
3596
+ if (["permission.replied", "question.replied", "question.v2.replied", "question.rejected", "question.v2.rejected"].includes(event.type)) {
3597
+ return { ...base, type: "approval.settled", data: { kind: event.type.split(".")[0] } };
3598
+ }
3599
+ if (event.type === "session.status") return { ...base, data: {
3600
+ status: ["idle", "busy", "retry"].includes(p.status?.type) ? p.status.type : "unknown"
3601
+ } };
3602
+ if (["session.idle", "session.error", "session.deleted"].includes(event.type)) return base;
3603
+ return void 0;
3604
+ }
3605
+ return { sanitize, text, clear: () => assistants.clear() };
3606
+ }
3607
+
3608
+ // src/live-bridge.js
3609
+ var LIMITS = { events: 256, bytes: 2e6, clients: 8, commands: 1024, commandTTL: 864e5, body: 32768, requests: 16 };
3610
+ function failure(status, code) {
3611
+ return Object.assign(new Error(code), { status, code });
3612
+ }
3613
+ function json(response, status, value) {
3614
+ if (!response.destroyed && !response.headersSent) {
3615
+ response.writeHead(status, { "content-type": "application/json", "cache-control": "no-store" });
3616
+ response.end(JSON.stringify(value));
3617
+ }
3618
+ }
3619
+ async function readBody(request) {
3620
+ const chunks = [];
3621
+ let bytes = 0;
3622
+ for await (const chunk of request) {
3623
+ bytes += chunk.length;
3624
+ if (bytes > LIMITS.body) throw failure(413, "body_too_large");
3625
+ chunks.push(chunk);
3626
+ }
3627
+ try {
3628
+ return JSON.parse(Buffer.concat(chunks).toString("utf8"));
3629
+ } catch {
3630
+ throw failure(400, "invalid_json");
3631
+ }
3632
+ }
3633
+ async function createLiveBridge({ config, client, directory, reporter }) {
3634
+ const options = config.liveBridge;
3635
+ if (!options?.enabled) return { handle() {
3636
+ }, async dispose() {
3637
+ }, address: void 0 };
3638
+ const token = options.token;
3639
+ if (typeof token !== "string" || token.length < 32 || token === config.token) {
3640
+ throw new Error("Live bridge requires a separate credential of at least 32 characters");
3641
+ }
3642
+ const api = client.liveBridge ?? createV1LiveClient(client, directory);
3643
+ const connectionNotifications = createLiveConnectionNotifier(config, reporter);
3644
+ const sanitizer = createBridgeSanitizer([token, config.token]);
3645
+ const epoch = randomUUID3();
3646
+ const events = [], streams = /* @__PURE__ */ new Set(), commands = /* @__PURE__ */ new Map(), states = /* @__PURE__ */ new Map();
3647
+ let sequence = 0, bytes = 0, active = 0, disposed = false, disposeTask;
3648
+ let lookups = 0;
3649
+ const lookupWaiters = /* @__PURE__ */ new Set();
3650
+ const cursor = () => `${epoch}:${sequence}`;
3651
+ function lookup(id) {
3652
+ if (disposed) return Promise.reject(failure(503, "bridge_closed"));
3653
+ if (lookups >= 16) return Promise.reject(failure(429, "session_lookup_busy"));
3654
+ lookups++;
3655
+ return new Promise((resolve2, reject) => {
3656
+ let finished = false;
3657
+ const finish = (complete, value) => {
3658
+ if (finished) return;
3659
+ finished = true;
3660
+ clearTimeout(timer);
3661
+ lookupWaiters.delete(cancel);
3662
+ complete(value);
3663
+ };
3664
+ const cancel = () => finish(reject, failure(503, "bridge_closed"));
3665
+ const timer = setTimeout(() => finish(reject, failure(504, "session_lookup_timed_out")), 1e3);
3666
+ lookupWaiters.add(cancel);
3667
+ Promise.resolve().then(() => api.get(id)).then(
3668
+ (value) => {
3669
+ lookups--;
3670
+ finish(resolve2, value);
3671
+ },
3672
+ (error) => {
3673
+ lookups--;
3674
+ finish(reject, error);
3675
+ }
3676
+ );
3677
+ });
3678
+ }
3679
+ async function session(id) {
3680
+ if (!validBridgeID(id) || id === "global") throw failure(400, "invalid_session");
3681
+ let info;
3682
+ try {
3683
+ info = await lookup(id);
3684
+ } catch (error) {
3685
+ if (["session_lookup_busy", "session_lookup_timed_out", "bridge_closed"].includes(error.code)) throw error;
3686
+ throw failure(404, "session_not_found");
3687
+ }
3688
+ if (info?.id !== id || (info.location?.directory ?? info.directory) !== directory) {
3689
+ throw failure(404, "session_not_found");
3690
+ }
3691
+ return {
3692
+ id,
3693
+ title: sanitizer.text(info.title, 240),
3694
+ ...validBridgeID(info.parentID) ? { parentID: info.parentID } : {},
3695
+ status: states.get(id) ?? "unknown"
3696
+ };
3697
+ }
3698
+ function removeStream(stream) {
3699
+ const removed = streams.delete(stream);
3700
+ clearInterval(stream.heartbeat);
3701
+ stream.queue.length = 0;
3702
+ stream.queuedBytes = 0;
3703
+ if (removed && !streams.size && !disposed) connectionNotifications.update(false);
3704
+ }
3705
+ function write(stream, value) {
3706
+ if (!streams.has(stream)) return;
3707
+ if (stream.blocked) {
3708
+ stream.queue.push(value);
3709
+ stream.queuedBytes += Buffer.byteLength(value);
3710
+ if (stream.queuedBytes > LIMITS.bytes) {
3711
+ removeStream(stream);
3712
+ stream.response.destroy();
3713
+ }
3714
+ return;
3715
+ }
3716
+ if (!stream.response.write(value)) {
3717
+ stream.blocked = true;
3718
+ }
3719
+ }
3720
+ async function handle(event) {
3721
+ if (disposed) return;
3722
+ const safe = sanitizer.sanitize(event);
3723
+ if (!safe) return;
3724
+ try {
3725
+ await session(safe.sessionID);
3726
+ } catch {
3727
+ return;
3728
+ }
3729
+ if (disposed) return;
3730
+ if (safe.type === "session.status") states.set(safe.sessionID, safe.data.status);
3731
+ if (safe.type === "session.idle") states.set(safe.sessionID, "idle");
3732
+ if (safe.type === "session.deleted") states.delete(safe.sessionID);
3733
+ if (states.size > 256) states.delete(states.keys().next().value);
3734
+ const record = { id: `${epoch}:${++sequence}`, ...safe, timestamp: Date.now() };
3735
+ let frame = `id: ${record.id}
3736
+ event: backeer.event
3737
+ data: ${JSON.stringify(record)}
3738
+
3739
+ `;
3740
+ if (Buffer.byteLength(frame) > 32768) {
3741
+ record.data = { truncated: true };
3742
+ frame = `id: ${record.id}
3743
+ event: backeer.event
3744
+ data: ${JSON.stringify(record)}
3745
+
3746
+ `;
3747
+ }
3748
+ const size = Buffer.byteLength(frame);
3749
+ events.push({ sequence, sessionID: record.sessionID, frame, size });
3750
+ bytes += size;
3751
+ while (events.length > LIMITS.events || bytes > LIMITS.bytes) bytes -= events.shift().size;
3752
+ for (const stream of streams) if (!stream.sessionID || stream.sessionID === record.sessionID) write(stream, frame);
3753
+ }
3754
+ function authorized(request) {
3755
+ if (request.headers.origin || request.headers["sec-fetch-site"]) return false;
3756
+ const expected = Buffer.from(`Bearer ${token}`);
3757
+ const actual = Buffer.from(request.headers.authorization ?? "");
3758
+ return actual.length === expected.length && timingSafeEqual(actual, expected);
3759
+ }
3760
+ function parseCursor(value) {
3761
+ if (!value) return { after: 0 };
3762
+ const match = /^([a-f0-9-]{36}):(\d{1,16})$/.exec(value);
3763
+ if (!match) throw failure(400, "invalid_cursor");
3764
+ if (match[1] !== epoch) return { gap: "stream_restarted" };
3765
+ const after = Number(match[2]);
3766
+ if (!Number.isSafeInteger(after) || after > sequence) throw failure(400, "invalid_cursor");
3767
+ if (events.length && after < events[0].sequence - 1) return { gap: "cursor_expired" };
3768
+ return { after };
3769
+ }
3770
+ async function route(request, response) {
3771
+ if (!authorized(request)) return json(response, 401, { error: "unauthorized" });
3772
+ if (active >= LIMITS.requests) return json(response, 429, { error: "bridge_busy" });
3773
+ active++;
3774
+ const timeout = setTimeout(() => json(response, 504, { error: "request_timed_out" }), 15e3);
3775
+ timeout.unref();
3776
+ try {
3777
+ const url = new URL(request.url, "http://127.0.0.1");
3778
+ if (url.username || url.password) throw failure(400, "invalid_url");
3779
+ if (request.method === "GET" && url.pathname === "/v1/sessions") {
3780
+ const result = await api.list();
3781
+ const values = Array.isArray(result) ? result : result?.data;
3782
+ if (!Array.isArray(values)) throw failure(502, "invalid_session_list");
3783
+ const own = values.slice(0, 100).filter((info) => validBridgeID(info?.id) && (info.location?.directory ?? info.directory) === directory);
3784
+ return json(response, 200, { sessions: own.map((info) => ({
3785
+ id: info.id,
3786
+ title: sanitizer.text(info.title, 240),
3787
+ status: states.get(info.id) ?? "unknown",
3788
+ ...validBridgeID(info.parentID) ? { parentID: info.parentID } : {}
3789
+ })), cursor: cursor() });
3790
+ }
3791
+ if (request.method === "GET" && url.pathname === "/v1/events") {
3792
+ const selected = url.searchParams.get("sessionID") || void 0;
3793
+ if (selected) await session(selected);
3794
+ const parsed = parseCursor(url.searchParams.get("after") ?? request.headers["last-event-id"]);
3795
+ if (response.headersSent) return;
3796
+ if (streams.size >= LIMITS.clients) throw failure(429, "too_many_streams");
3797
+ clearTimeout(timeout);
3798
+ response.writeHead(200, {
3799
+ "content-type": "text/event-stream",
3800
+ "cache-control": "no-cache, no-transform",
3801
+ "x-accel-buffering": "no",
3802
+ connection: "keep-alive"
3803
+ });
3804
+ if (parsed.gap) {
3805
+ response.end(`event: backeer.gap
3806
+ data: ${JSON.stringify({ reason: parsed.gap, cursor: cursor() })}
3807
+
3808
+ `);
3809
+ return;
3810
+ }
3811
+ const stream = {
3812
+ response,
3813
+ sessionID: selected,
3814
+ heartbeat: void 0,
3815
+ blocked: false,
3816
+ queue: [],
3817
+ queuedBytes: 0
3818
+ };
3819
+ streams.add(stream);
3820
+ connectionNotifications.update(true);
3821
+ request.socket.setTimeout(0);
3822
+ request.socket.setKeepAlive(true, LIVE_HEARTBEAT_MS);
3823
+ response.on("close", () => removeStream(stream));
3824
+ response.on("drain", () => {
3825
+ stream.blocked = false;
3826
+ while (streams.has(stream) && !stream.blocked && stream.queue.length) {
3827
+ const next = stream.queue.shift();
3828
+ stream.queuedBytes -= Buffer.byteLength(next);
3829
+ write(stream, next);
3830
+ }
3831
+ });
3832
+ write(stream, `: connected
3833
+ retry: ${LIVE_RECONNECT_MS}
3834
+
3835
+ `);
3836
+ for (const item of events) {
3837
+ if (!streams.has(stream)) break;
3838
+ if (item.sequence > parsed.after && (!selected || selected === item.sessionID)) write(stream, item.frame);
3839
+ }
3840
+ if (streams.has(stream)) {
3841
+ stream.heartbeat = setInterval(() => write(stream, ": heartbeat\n\n"), LIVE_HEARTBEAT_MS);
3842
+ stream.heartbeat.unref();
3843
+ }
3844
+ return;
3845
+ }
3846
+ const match = /^\/v1\/sessions\/([A-Za-z0-9_-]{1,160})(\/instructions)?$/.exec(url.pathname);
3847
+ if (!match) throw failure(404, "route_not_found");
3848
+ if (request.method === "GET" && !match[2]) return json(response, 200, await session(match[1]));
3849
+ if (request.method !== "POST" || !match[2]) throw failure(405, "method_not_allowed");
3850
+ if (!/^application\/json(?:;|$)/i.test(request.headers["content-type"] ?? "")) throw failure(415, "json_required");
3851
+ const body = await readBody(request);
3852
+ if (!body || !validBridgeID(body.requestId) || typeof body.prompt !== "string" || !body.prompt.trim() || body.prompt.length > 16e3 || Object.keys(body).some((key) => !["requestId", "prompt"].includes(key))) throw failure(400, "invalid_instruction");
3853
+ await session(match[1]);
3854
+ if (response.headersSent || disposed) return;
3855
+ for (const [id, command2] of commands) {
3856
+ if (command2.settled && Date.now() - command2.created >= LIMITS.commandTTL) commands.delete(id);
3857
+ }
3858
+ const existing = commands.get(body.requestId);
3859
+ if (existing) {
3860
+ if (existing.sessionID !== match[1] || existing.prompt !== body.prompt) throw failure(409, "request_id_conflict");
3861
+ await existing.task;
3862
+ return json(response, 200, existing.result);
3863
+ }
3864
+ if (commands.size >= LIMITS.commands) throw failure(429, "command_window_full");
3865
+ const command = {
3866
+ sessionID: match[1],
3867
+ prompt: body.prompt,
3868
+ created: Date.now(),
3869
+ settled: false,
3870
+ result: { requestId: body.requestId, sessionID: match[1], status: "accepted" },
3871
+ task: void 0
3872
+ };
3873
+ commands.set(body.requestId, command);
3874
+ command.task = Promise.resolve().then(() => api.prompt(match[1], body.prompt)).finally(() => {
3875
+ command.settled = true;
3876
+ });
3877
+ await command.task;
3878
+ return json(response, 202, command.result);
3879
+ } catch (error) {
3880
+ json(response, error.status ?? 502, { error: error.code ?? "opencode_request_failed" });
3881
+ } finally {
3882
+ active--;
3883
+ clearTimeout(timeout);
3884
+ }
3885
+ }
3886
+ const server = createServer((request, response) => {
3887
+ void route(request, response);
3888
+ });
3889
+ server.requestTimeout = 15e3;
3890
+ server.headersTimeout = 1e4;
3891
+ server.keepAliveTimeout = 5e3;
3892
+ await new Promise((resolve2, reject) => {
3893
+ server.once("error", reject);
3894
+ server.listen(options.port ?? 43871, "127.0.0.1", () => {
3895
+ server.off("error", reject);
3896
+ resolve2();
3897
+ });
3898
+ });
3899
+ server.on("error", () => {
3900
+ });
3901
+ return {
3902
+ handle,
3903
+ address: server.address(),
3904
+ dispose() {
3905
+ disposeTask ??= new Promise((resolve2) => {
3906
+ disposed = true;
3907
+ for (const cancel of lookupWaiters) cancel();
3908
+ for (const stream of streams) {
3909
+ removeStream(stream);
3910
+ stream.response.destroy();
3911
+ }
3912
+ server.close(() => {
3913
+ void connectionNotifications.dispose().then(resolve2);
3914
+ });
3915
+ server.closeAllConnections?.();
3916
+ events.length = 0;
3917
+ states.clear();
3918
+ commands.clear();
3919
+ sanitizer.clear();
3920
+ api.dispose?.();
3921
+ });
3922
+ return disposeTask;
3923
+ }
3924
+ };
3925
+ }
3926
+
3927
+ // src/permission-command.js
3928
+ var ALLOWED_DECISIONS = /* @__PURE__ */ new Set(["once", "always", "reject"]);
3929
+ var DECISION_LABELS = {
3930
+ once: "Allow once",
3931
+ always: "Always allow",
3932
+ reject: "Reject"
3933
+ };
3934
+ function validatePermissionCommand(command, now = Date.now()) {
3935
+ if (!ALLOWED_DECISIONS.has(command.decision)) {
3936
+ return {
3937
+ status: "rejected",
3938
+ decision: command.decision,
3939
+ errorCode: "invalid_decision"
3940
+ };
3941
+ }
3942
+ const expiresAt = Date.parse(command.expiresAt);
3943
+ if (!Number.isFinite(expiresAt) || expiresAt <= now) {
3944
+ return { status: "expired", decision: command.decision };
3945
+ }
3946
+ if (command.decision === "always" && command.confirmed !== true) {
3947
+ return {
3948
+ status: "rejected",
3949
+ decision: command.decision,
3950
+ errorCode: "confirmation_required"
3951
+ };
3952
+ }
3953
+ }
3954
+ function failureAcknowledgement(command, error) {
3955
+ const stale = error instanceof HttpError && error.status === 404;
1542
3956
  return {
1543
3957
  status: stale ? "stale" : "failed",
1544
3958
  decision: command.decision,
@@ -1651,13 +4065,13 @@ function nextSseFrame(buffer) {
1651
4065
  }
1652
4066
  return { frame: buffer.slice(0, lfIndex), rest: buffer.slice(lfIndex + 2) };
1653
4067
  }
1654
- async function consumeSse(response, state, onCommand, signal, reporter) {
4068
+ async function consumeSse(response, state, onCommand, signal2, reporter) {
1655
4069
  if (!response.body) throw new Error("Backeer command stream has no body");
1656
4070
  const reader = response.body.getReader();
1657
4071
  const decoder = new TextDecoder();
1658
4072
  let buffer = "";
1659
4073
  try {
1660
- while (!signal.aborted) {
4074
+ while (!signal2.aborted) {
1661
4075
  const item = await reader.read();
1662
4076
  if (item.done) break;
1663
4077
  buffer += decoder.decode(item.value, { stream: true });
@@ -1686,7 +4100,7 @@ async function consumeSse(response, state, onCommand, signal, reporter) {
1686
4100
  });
1687
4101
  }
1688
4102
  }
1689
- async function openCommandStream(config, state, signal, fetchImplementation) {
4103
+ async function openCommandStream(config, state, signal2, fetchImplementation) {
1690
4104
  const headers = {
1691
4105
  Accept: "text/event-stream",
1692
4106
  Authorization: `Bearer ${config.token}`
@@ -1694,7 +4108,7 @@ async function openCommandStream(config, state, signal, fetchImplementation) {
1694
4108
  if (state.lastEventID) headers["Last-Event-ID"] = state.lastEventID;
1695
4109
  const response = await fetchImplementation(config.commandStreamEndpoint, {
1696
4110
  headers,
1697
- signal
4111
+ signal: signal2
1698
4112
  });
1699
4113
  if (!response.ok) {
1700
4114
  const responseBody = limit(await response.text(), 500);
@@ -1713,7 +4127,7 @@ async function consumeConnection({
1713
4127
  response,
1714
4128
  state,
1715
4129
  onCommand,
1716
- signal,
4130
+ signal: signal2,
1717
4131
  reporter,
1718
4132
  now,
1719
4133
  stableConnectionMs,
@@ -1722,7 +4136,7 @@ async function consumeConnection({
1722
4136
  const connectedAt = now();
1723
4137
  const framesBeforeConnection = state.framesReceived;
1724
4138
  try {
1725
- await consumeSse(response, state, onCommand, signal, reporter);
4139
+ await consumeSse(response, state, onCommand, signal2, reporter);
1726
4140
  } finally {
1727
4141
  const receivedFrame = state.framesReceived > framesBeforeConnection;
1728
4142
  if (receivedFrame || now() - connectedAt >= stableConnectionMs) onStable();
@@ -1743,7 +4157,7 @@ function streamErrorState(error, lastLoggedError, reconnectDelay, reporter) {
1743
4157
  };
1744
4158
  }
1745
4159
  async function runCommandStream(config, reporter, {
1746
- signal,
4160
+ signal: signal2,
1747
4161
  hasPending,
1748
4162
  onCommand,
1749
4163
  fetchImplementation = fetch,
@@ -1754,19 +4168,19 @@ async function runCommandStream(config, reporter, {
1754
4168
  const state = { lastEventID: "", framesReceived: 0 };
1755
4169
  let reconnectDelay = 1e3;
1756
4170
  let lastLoggedError = "";
1757
- while (!signal.aborted && hasPending()) {
4171
+ while (!signal2.aborted && hasPending()) {
1758
4172
  try {
1759
4173
  const response = await openCommandStream(
1760
4174
  config,
1761
4175
  state,
1762
- signal,
4176
+ signal2,
1763
4177
  fetchImplementation
1764
4178
  );
1765
4179
  await consumeConnection({
1766
4180
  response,
1767
4181
  state,
1768
4182
  onCommand,
1769
- signal,
4183
+ signal: signal2,
1770
4184
  reporter,
1771
4185
  now,
1772
4186
  stableConnectionMs,
@@ -1776,13 +4190,13 @@ async function runCommandStream(config, reporter, {
1776
4190
  }
1777
4191
  });
1778
4192
  } catch (error) {
1779
- if (signal.aborted || isAbortError(error)) return;
4193
+ if (signal2.aborted || isAbortError(error)) return;
1780
4194
  const next = streamErrorState(error, lastLoggedError, reconnectDelay, reporter);
1781
4195
  reconnectDelay = next.reconnectDelay;
1782
4196
  lastLoggedError = next.lastLoggedError;
1783
4197
  }
1784
- if (!signal.aborted && hasPending()) {
1785
- await sleepImplementation(reconnectDelay, signal);
4198
+ if (!signal2.aborted && hasPending()) {
4199
+ await sleepImplementation(reconnectDelay, signal2);
1786
4200
  reconnectDelay = Math.min(reconnectDelay * 2, 6e4);
1787
4201
  }
1788
4202
  }
@@ -2070,7 +4484,7 @@ function createPermissionController({
2070
4484
  }
2071
4485
 
2072
4486
  // src/plugin.js
2073
- async function BackeerPlugin({ client, directory }, options = {}) {
4487
+ async function BackeerPlugin({ client, directory, feedback, v2 = false }, options = {}) {
2074
4488
  const bootstrapReporter = createOpenCodeReporter(
2075
4489
  client,
2076
4490
  options.logLevel ?? process.env.BACKEER_LOG_LEVEL
@@ -2083,7 +4497,14 @@ async function BackeerPlugin({ client, directory }, options = {}) {
2083
4497
  return {};
2084
4498
  }
2085
4499
  const reporter = createOpenCodeReporter(client, config.logLevel);
2086
- const conversation = createConversationFeedback({ client, reporter });
4500
+ const conversation = feedback ?? createConversationFeedback({ client, reporter });
4501
+ if (config.liveBridge.error) reporter.error("Live control disabled", config.liveBridge.error);
4502
+ const liveBridge = await createLiveBridge({ config, client, directory, reporter }).catch((error) => {
4503
+ reporter.error("Live control unavailable", error);
4504
+ return { async handle() {
4505
+ }, async dispose() {
4506
+ } };
4507
+ });
2087
4508
  const permissionController = createPermissionController({
2088
4509
  config,
2089
4510
  reporter,
@@ -2101,7 +4522,7 @@ async function BackeerPlugin({ client, directory }, options = {}) {
2101
4522
  const permissionPublications = createDeliveryTracker();
2102
4523
  const questionPublications = createDeliveryTracker();
2103
4524
  const todoCompletions = createCompletionTracker();
2104
- const updateChecker = createUpdateChecker();
4525
+ const updateChecker = createUpdateChecker({ v2 });
2105
4526
  const handleBackeerCommand = createBackeerCommandHandler({
2106
4527
  client,
2107
4528
  config,
@@ -2191,6 +4612,38 @@ async function BackeerPlugin({ client, directory }, options = {}) {
2191
4612
  conversation.error(identity.sessionID, "Question could not be sent");
2192
4613
  });
2193
4614
  }
4615
+ async function handleNotificationEvent(event) {
4616
+ if (!config.token) return;
4617
+ conversation.handle(event);
4618
+ lifecycleController.handle(event);
4619
+ if (config.permissions && event.type === "permission.asked") {
4620
+ await handlePermissionAsked(event);
4621
+ return;
4622
+ }
4623
+ if (config.permissions && event.type === "permission.replied") {
4624
+ permissionController.resolveLocally(
4625
+ event.properties.requestID ?? event.properties.id
4626
+ );
4627
+ return;
4628
+ }
4629
+ if (config.todos && event.type === "todo.updated") {
4630
+ await handleTodoUpdated(event);
4631
+ return;
4632
+ }
4633
+ if (config.questions && (event.type === "question.asked" || event.type === "question.v2.asked")) {
4634
+ await handleQuestionAsked(event);
4635
+ return;
4636
+ }
4637
+ if (config.questions && ["question.replied", "question.v2.replied", "question.rejected", "question.v2.rejected"].includes(event.type)) {
4638
+ const requestID = event.properties.requestID ?? event.properties.id;
4639
+ if (typeof requestID === "string") {
4640
+ permissionController.resolveQuestionLocally(
4641
+ requestID,
4642
+ event.type.endsWith("rejected") ? "rejected" : "answered"
4643
+ );
4644
+ }
4645
+ }
4646
+ }
2194
4647
  return {
2195
4648
  config: async (opencodeConfig) => {
2196
4649
  registerBackeerCommand(opencodeConfig);
@@ -2198,38 +4651,10 @@ async function BackeerPlugin({ client, directory }, options = {}) {
2198
4651
  "command.execute.before": handleBackeerCommand,
2199
4652
  "chat.message": handleUpdateNotice,
2200
4653
  event: async ({ event }) => {
2201
- if (!config.token) return;
2202
- conversation.handle(event);
2203
- lifecycleController.handle(event);
2204
- if (config.permissions && event.type === "permission.asked") {
2205
- await handlePermissionAsked(event);
2206
- return;
2207
- }
2208
- if (config.permissions && event.type === "permission.replied") {
2209
- permissionController.resolveLocally(
2210
- event.properties.requestID ?? event.properties.id
2211
- );
2212
- return;
2213
- }
2214
- if (config.todos && event.type === "todo.updated") {
2215
- await handleTodoUpdated(event);
2216
- return;
2217
- }
2218
- if (config.questions && (event.type === "question.asked" || event.type === "question.v2.asked")) {
2219
- await handleQuestionAsked(event);
2220
- return;
2221
- }
2222
- if (config.questions && ["question.replied", "question.v2.replied", "question.rejected", "question.v2.rejected"].includes(event.type)) {
2223
- const requestID = event.properties.requestID ?? event.properties.id;
2224
- if (typeof requestID === "string") {
2225
- permissionController.resolveQuestionLocally(
2226
- requestID,
2227
- event.type.endsWith("rejected") ? "rejected" : "answered"
2228
- );
2229
- }
2230
- }
4654
+ await Promise.all([handleNotificationEvent(event), liveBridge.handle(event)]);
2231
4655
  },
2232
4656
  dispose: async () => {
4657
+ await liveBridge.dispose();
2233
4658
  await permissionController.dispose();
2234
4659
  lifecycleController.dispose();
2235
4660
  permissionPublications.clear();
@@ -2239,6 +4664,744 @@ async function BackeerPlugin({ client, directory }, options = {}) {
2239
4664
  }
2240
4665
  };
2241
4666
  }
4667
+
4668
+ // src/opencode-v2-forms.js
4669
+ init_promise();
4670
+
4671
+ // node_modules/@opencode/client/dist/chunks/service-timing-ve4aws36.js
4672
+ import { spawn } from "node:child_process";
4673
+ var stderrLimit = 8 * 1024;
4674
+ function spawnServiceContender2(command, args, env) {
4675
+ const child = spawn(command, args, {
4676
+ detached: true,
4677
+ windowsHide: true,
4678
+ stdio: ["ignore", "ignore", "pipe"],
4679
+ env: { ...process.env, ...env }
4680
+ });
4681
+ let error;
4682
+ let closed = false;
4683
+ let stderr = Buffer.alloc(0);
4684
+ const onStderr = (chunk) => {
4685
+ const tail = chunk.subarray(-stderrLimit);
4686
+ stderr = tail.length === stderrLimit ? Buffer.from(tail) : Buffer.concat([stderr.subarray(-(stderrLimit - tail.length)), tail]);
4687
+ };
4688
+ child.stderr?.on("data", onStderr);
4689
+ if (child.stderr !== null && "unref" in child.stderr && typeof child.stderr.unref === "function")
4690
+ child.stderr.unref();
4691
+ child.once("error", (cause) => {
4692
+ error = new Error("Failed to start server", { cause });
4693
+ });
4694
+ child.once("close", () => {
4695
+ closed = true;
4696
+ });
4697
+ child.unref();
4698
+ return {
4699
+ child,
4700
+ error: () => error,
4701
+ closed: () => closed,
4702
+ stderr: () => stderr.toString("utf8").trim(),
4703
+ release: () => {
4704
+ child.stderr?.off("data", onStderr);
4705
+ child.stderr?.resume();
4706
+ stderr = Buffer.alloc(0);
4707
+ }
4708
+ };
4709
+ }
4710
+ function contenderPool2(timing) {
4711
+ const contenders = /* @__PURE__ */ new Set();
4712
+ let failure2;
4713
+ let spawnDelay = timing.spawnDelay;
4714
+ let lastSpawn = 0;
4715
+ return {
4716
+ failure: () => failure2,
4717
+ serviceAnswered() {
4718
+ spawnDelay = timing.spawnDelay;
4719
+ },
4720
+ evict(pid) {
4721
+ for (const item of contenders) {
4722
+ if (item.child.pid === pid || contenderFinished2(item)) {
4723
+ item.release();
4724
+ contenders.delete(item);
4725
+ }
4726
+ }
4727
+ failure2 = void 0;
4728
+ lastSpawn = 0;
4729
+ },
4730
+ recruitNow() {
4731
+ lastSpawn = Date.now() - spawnDelay;
4732
+ },
4733
+ reap() {
4734
+ const finished = [...contenders].filter(contenderFinished2);
4735
+ failure2 ??= finished.map(contenderFailure).find((error) => error !== void 0);
4736
+ if (finished.some((item) => item.child.exitCode === 0))
4737
+ spawnDelay = Math.min(spawnDelay * 2, timing.maxSpawnDelay);
4738
+ finished.forEach((item) => contenders.delete(item));
4739
+ return contenders.size === 0 ? failure2 : void 0;
4740
+ },
4741
+ shouldRecruit(registered2) {
4742
+ if (lastSpawn === 0 && registered2)
4743
+ lastSpawn = Date.now();
4744
+ return failure2 === void 0 && contenders.size < 2 && Date.now() - lastSpawn >= spawnDelay;
4745
+ },
4746
+ add(contender) {
4747
+ contenders.add(contender);
4748
+ lastSpawn = Date.now();
4749
+ },
4750
+ releaseAll() {
4751
+ contenders.forEach((contender) => contender.release());
4752
+ }
4753
+ };
4754
+ }
4755
+ function contenderFailure(contender) {
4756
+ const error = contender.error();
4757
+ if (error !== void 0)
4758
+ return error;
4759
+ if (contender.child.exitCode !== null && contender.child.exitCode !== 0)
4760
+ return startupError(`Server process exited with code ${contender.child.exitCode}`, contender.stderr());
4761
+ if (contender.child.signalCode !== null)
4762
+ return startupError(`Server process terminated by ${contender.child.signalCode}`, contender.stderr());
4763
+ return;
4764
+ }
4765
+ function contenderFinished2(contender) {
4766
+ return contender.error() !== void 0 || contender.closed();
4767
+ }
4768
+ function startupError(message, stderr) {
4769
+ return new Error(stderr ? `${message}
4770
+ ${stderr}` : message);
4771
+ }
4772
+
4773
+ // node_modules/@opencode/client/dist/chunks/service-timing-xgnj2w4v.js
4774
+ var timings = /* @__PURE__ */ new WeakMap();
4775
+ var defaultEnsureTiming2 = {
4776
+ pollInterval: 25,
4777
+ requestTimeout: 2e3,
4778
+ spawnDelay: 5e3,
4779
+ maxSpawnDelay: 3e4,
4780
+ promiseTimeout: 12e4,
4781
+ stopPollInterval: 50,
4782
+ stopPollAttempts: 100
4783
+ };
4784
+ function ensureTiming2(options) {
4785
+ return timings.get(options) ?? defaultEnsureTiming2;
4786
+ }
4787
+
4788
+ // node_modules/@opencode/client/dist/chunks/service-timing-sxt88q69.js
4789
+ function matchesVersion2(version, options) {
4790
+ if (options.version === void 0)
4791
+ return true;
4792
+ if (version === void 0)
4793
+ return false;
4794
+ if (typeof options.version === "function")
4795
+ return options.version(version);
4796
+ return version === options.version;
4797
+ }
4798
+
4799
+ // node_modules/@opencode/client/dist/chunks/service-timing-0rcx1gca.js
4800
+ init_service_timing_9rqn6x4v();
4801
+ import { readFile, rename, rm, writeFile } from "node:fs/promises";
4802
+ var exports_pty_handoff = {};
4803
+ __export2(exports_pty_handoff, {
4804
+ PtyHandoff: () => exports_pty_handoff,
4805
+ clear: () => clear2,
4806
+ complete: () => complete2,
4807
+ environment: () => environment2,
4808
+ prepare: () => prepare2
4809
+ });
4810
+ async function prepare2(file, info, timeout) {
4811
+ const existing = await read(file);
4812
+ if (existing !== void 0 && existing.expiresAt > Date.now() && same(existing.source, info))
4813
+ return;
4814
+ const { OpenCode } = await Promise.resolve().then(() => (init_promise(), promise_exports));
4815
+ const client = OpenCode.make({
4816
+ baseUrl: info.url,
4817
+ headers: info.password === void 0 ? void 0 : { authorization: "Basic " + Buffer.from(`opencode:${info.password}`).toString("base64") }
4818
+ });
4819
+ const handoff = await client.experimental.persistentPty.handoff({ signal: AbortSignal.timeout(timeout) }).then(parse).catch(async (cause) => {
4820
+ const concurrent = await read(file);
4821
+ if (concurrent !== void 0 && concurrent.expiresAt > Date.now() && same(concurrent.source, info))
4822
+ return;
4823
+ console.warn("Background service cannot hand off persistent terminals; shutting them down before replacement", cause);
4824
+ await client.experimental.persistentPty.shutdown({ signal: AbortSignal.timeout(timeout) }).catch(() => {
4825
+ });
4826
+ return null;
4827
+ });
4828
+ if (handoff === void 0)
4829
+ return;
4830
+ await publish2(file, info, handoff).catch((cause) => console.warn("Failed to publish persistent terminal handoff", cause));
4831
+ }
4832
+ function parse(body) {
4833
+ if (typeof body !== "object" || body === null || !("handoff" in body))
4834
+ throw new Error("Invalid persistent terminal handoff response");
4835
+ if (body.handoff === null)
4836
+ return null;
4837
+ if (!isHandoff(body.handoff) || body.handoff.expiresAt <= Date.now())
4838
+ throw new Error("Invalid or expired persistent terminal handoff");
4839
+ return body.handoff;
4840
+ }
4841
+ async function publish2(file, info, handoff) {
4842
+ const temporary = `${file}.pty-handoff.${crypto.randomUUID()}.tmp`;
4843
+ await writeFile(temporary, JSON.stringify({
4844
+ source: { id: info.id, pid: info.pid, url: info.url },
4845
+ handoff,
4846
+ expiresAt: handoff?.expiresAt ?? Date.now() + 3e4
4847
+ }), { mode: 384, flag: "wx" });
4848
+ await rename(temporary, file + ".pty-handoff").finally(() => rm(temporary, { force: true }));
4849
+ }
4850
+ async function environment2(file, env) {
4851
+ const record = await read(file);
4852
+ const current = await readFile(file, "utf8").then((text) => JSON.parse(text)).catch(() => {
4853
+ return;
4854
+ });
4855
+ const handoff = record !== void 0 && record.expiresAt > Date.now() && (current === void 0 || same(record.source, current)) ? record.handoff : void 0;
4856
+ return { ...env, OPENCODE_PTY_HANDOFF: handoff == null ? void 0 : JSON.stringify(handoff) };
4857
+ }
4858
+ async function complete2(file, info) {
4859
+ const record = await read(file);
4860
+ if (record !== void 0 && !same(record.source, info))
4861
+ await clear2(file);
4862
+ }
4863
+ async function clear2(file) {
4864
+ await rm(file + ".pty-handoff", { force: true });
4865
+ }
4866
+ async function read(file) {
4867
+ const value = await readFile(file + ".pty-handoff", "utf8").then((text) => JSON.parse(text)).catch(() => {
4868
+ return;
4869
+ });
4870
+ if (typeof value !== "object" || value === null || !("source" in value) || !("handoff" in value))
4871
+ return;
4872
+ if (typeof value.source !== "object" || value.source === null)
4873
+ return;
4874
+ if (!("pid" in value.source) || typeof value.source.pid !== "number")
4875
+ return;
4876
+ if (!("url" in value.source) || typeof value.source.url !== "string")
4877
+ return;
4878
+ if ("id" in value.source && typeof value.source.id !== "string")
4879
+ return;
4880
+ if (value.handoff !== null && !isHandoff(value.handoff))
4881
+ return;
4882
+ if (!("expiresAt" in value) || typeof value.expiresAt !== "number" || !Number.isFinite(value.expiresAt))
4883
+ return;
4884
+ return {
4885
+ source: {
4886
+ id: "id" in value.source && typeof value.source.id === "string" ? value.source.id : void 0,
4887
+ pid: value.source.pid,
4888
+ url: value.source.url
4889
+ },
4890
+ handoff: value.handoff,
4891
+ expiresAt: value.expiresAt
4892
+ };
4893
+ }
4894
+ function same(left, right) {
4895
+ return left.id === right.id && left.pid === right.pid && left.url === right.url;
4896
+ }
4897
+ function isHandoff(value) {
4898
+ return typeof value === "object" && value !== null && "directory" in value && typeof value.directory === "string" && "instanceID" in value && typeof value.instanceID === "string" && "ticket" in value && typeof value.ticket === "string" && "expiresAt" in value && typeof value.expiresAt === "number" && Number.isFinite(value.expiresAt);
4899
+ }
4900
+
4901
+ // node_modules/@opencode/client/dist/chunks/service-timing-8ye1ye6f.js
4902
+ import { homedir as homedir2 } from "node:os";
4903
+ import { join as join3 } from "node:path";
4904
+ function decide2(service, options) {
4905
+ if (!matchesVersion2(service.version, options))
4906
+ return { _tag: "replace", pty: service.state === "ready" ? "handoff" : "clear" };
4907
+ if (!service.compatible)
4908
+ return fail("Background service uses an incompatible health protocol. Update this client or explicitly restart the service.");
4909
+ if (service.state === "ready")
4910
+ return { _tag: "reuse" };
4911
+ if (service.state === "failed")
4912
+ return fail("Background service failed to start");
4913
+ return { _tag: "wait" };
4914
+ }
4915
+ function fail(message) {
4916
+ return { _tag: "fail", error: new Error(message) };
4917
+ }
4918
+ function fallback2() {
4919
+ return join3(process.env["XDG_STATE_HOME"] ?? join3(homedir2(), ".local", "state"), "opencode", "service.json");
4920
+ }
4921
+ function same2(left, right) {
4922
+ return left.id === right.id && left.version === right.version && left.url === right.url && left.pid === right.pid;
4923
+ }
4924
+ async function probeResult2(info, timeout = defaultEnsureTiming2.requestTimeout) {
4925
+ const endpoint = {
4926
+ url: info.url,
4927
+ auth: info.password === void 0 ? void 0 : { type: "basic", username: "opencode", password: info.password }
4928
+ };
4929
+ const signal2 = AbortSignal.timeout(timeout);
4930
+ const result = await fetch(new URL("/api/info", info.url), { headers: headers2(endpoint), signal: signal2 }).then(async (response2) => ({
4931
+ response: response2,
4932
+ body: response2.status === 404 ? void 0 : await response2.json()
4933
+ })).catch(() => {
4934
+ return;
4935
+ });
4936
+ if (result === void 0)
4937
+ return { service: void 0, timedOut: signal2.aborted };
4938
+ const response = result.response;
4939
+ if (response.status === 404)
4940
+ return {
4941
+ service: {
4942
+ info,
4943
+ endpoint,
4944
+ version: info.version,
4945
+ state: "ready",
4946
+ compatible: false
4947
+ },
4948
+ timedOut: false
4949
+ };
4950
+ const serverInfo = decodeInfo(result.body);
4951
+ if (serverInfo === void 0)
4952
+ return { service: void 0, timedOut: false };
4953
+ if (serverInfo.pid !== info.pid)
4954
+ return { service: void 0, timedOut: false };
4955
+ if (info.version !== void 0 && serverInfo.version !== info.version)
4956
+ return { service: void 0, timedOut: false };
4957
+ return {
4958
+ service: {
4959
+ info,
4960
+ endpoint,
4961
+ version: serverInfo.version,
4962
+ state: response.ok ? "ready" : response.status === 500 ? "failed" : "waiting",
4963
+ compatible: true
4964
+ },
4965
+ timedOut: false
4966
+ };
4967
+ }
4968
+ function headers2(endpoint) {
4969
+ if (endpoint.auth === void 0)
4970
+ return;
4971
+ return {
4972
+ authorization: "Basic " + Buffer.from(endpoint.auth.username + ":" + endpoint.auth.password).toString("base64")
4973
+ };
4974
+ }
4975
+ function decodeInfo(input) {
4976
+ if (typeof input !== "object" || input === null)
4977
+ return;
4978
+ if (!("version" in input) || typeof input.version !== "string")
4979
+ return;
4980
+ if (!("pid" in input) || typeof input.pid !== "number" || !Number.isInteger(input.pid) || input.pid < 0)
4981
+ return;
4982
+ return { version: input.version, pid: input.pid };
4983
+ }
4984
+
4985
+ // node_modules/@opencode/client/dist/promise/service.js
4986
+ import { readFile as readFile2, rm as rm2 } from "node:fs/promises";
4987
+ async function discover(options = {}) {
4988
+ const found = (await registered(options.file)).service;
4989
+ if (found?.state !== "ready")
4990
+ return;
4991
+ if (!found.compatible)
4992
+ return;
4993
+ if (!matchesVersion2(found.version, options))
4994
+ return;
4995
+ return found.endpoint;
4996
+ }
4997
+ async function ensure(options = {}) {
4998
+ const timing = ensureTiming2(options);
4999
+ const deadline = Date.now() + timing.promiseTimeout;
5000
+ const pool = contenderPool2(timing);
5001
+ let timeouts;
5002
+ let announced = false;
5003
+ const announce = (reason, previousVersion) => {
5004
+ if (announced)
5005
+ return;
5006
+ announced = true;
5007
+ options.onStart?.(reason, previousVersion);
5008
+ };
5009
+ const spawnContender = async () => {
5010
+ const [command, ...args] = options.command ?? ["opencode", "serve", "--service"];
5011
+ if (command === void 0)
5012
+ throw new Error("Missing service command");
5013
+ try {
5014
+ return spawnServiceContender2(command, args, await environment2(options.file ?? fallback2(), options.env));
5015
+ } catch (cause) {
5016
+ throw new Error("Failed to start server", { cause });
5017
+ }
5018
+ };
5019
+ try {
5020
+ while (true) {
5021
+ if (Date.now() >= deadline)
5022
+ throw pool.failure() ?? new Error("Timed out waiting for the background service to start");
5023
+ const registration = await registered(options.file, timing.requestTimeout);
5024
+ if (registration.timedOut && registration.info !== void 0) {
5025
+ timeouts = {
5026
+ info: registration.info,
5027
+ count: timeouts !== void 0 && same2(timeouts.info, registration.info) ? timeouts.count + 1 : 1
5028
+ };
5029
+ if (timeouts.count >= 3) {
5030
+ announce("missing");
5031
+ console.warn("Background service is unresponsive; recovery cannot preserve persistent terminals");
5032
+ await clear2(options.file ?? fallback2());
5033
+ await terminate(registration.info, options, timing);
5034
+ pool.evict(registration.info.pid);
5035
+ pool.recruitNow();
5036
+ timeouts = void 0;
5037
+ }
5038
+ } else
5039
+ timeouts = void 0;
5040
+ if (registration.service !== void 0) {
5041
+ pool.serviceAnswered();
5042
+ const service2 = registration.service;
5043
+ const decision = decide2(service2, options);
5044
+ if (decision._tag === "fail")
5045
+ throw decision.error;
5046
+ if (decision._tag === "reuse") {
5047
+ await complete2(options.file ?? fallback2(), service2.info);
5048
+ return service2.endpoint;
5049
+ }
5050
+ if (decision._tag === "replace") {
5051
+ announce("version-mismatch", service2.version);
5052
+ if (service2.state !== "ready")
5053
+ console.warn("Background service is not ready; replacement cannot preserve persistent terminals");
5054
+ await stop({ file: options.file, pty: decision.pty }).catch(() => {
5055
+ return;
5056
+ });
5057
+ pool.evict(service2.info.pid);
5058
+ }
5059
+ } else {
5060
+ const failed = pool.reap();
5061
+ if (failed !== void 0)
5062
+ throw failed;
5063
+ if (pool.shouldRecruit(registration.info !== void 0)) {
5064
+ announce("missing");
5065
+ pool.add(await spawnContender());
5066
+ }
5067
+ }
5068
+ await delay(timing.pollInterval);
5069
+ }
5070
+ } finally {
5071
+ pool.releaseAll();
5072
+ }
5073
+ }
5074
+ async function stop(options = {}) {
5075
+ const info = await read2(options.file);
5076
+ await (options.pty === "handoff" && info !== void 0 ? prepare2(options.file ?? fallback2(), info, defaultEnsureTiming2.requestTimeout) : clear2(options.file ?? fallback2())).catch((cause) => console.warn("Failed to prepare persistent terminals for replacement", cause));
5077
+ if (info !== void 0)
5078
+ await terminate(info, options, ensureTiming2(options));
5079
+ }
5080
+ async function read2(file) {
5081
+ const text = await readFile2(file ?? fallback2(), "utf8").catch(() => {
5082
+ return;
5083
+ });
5084
+ if (text === void 0)
5085
+ return;
5086
+ try {
5087
+ return JSON.parse(text);
5088
+ } catch {
5089
+ return;
5090
+ }
5091
+ }
5092
+ async function registered(file, timeout) {
5093
+ const info = await read2(file);
5094
+ if (info === void 0)
5095
+ return { info: void 0, service: void 0, timedOut: false };
5096
+ return { info, ...await probeResult2(info, timeout) };
5097
+ }
5098
+ function signal(pid, name) {
5099
+ try {
5100
+ process.kill(pid, name);
5101
+ } catch {
5102
+ }
5103
+ }
5104
+ function stopped(pid) {
5105
+ try {
5106
+ process.kill(pid, 0);
5107
+ return false;
5108
+ } catch {
5109
+ return true;
5110
+ }
5111
+ }
5112
+ async function waitUntilStopped(pid, timing) {
5113
+ for (let attempt = 0; attempt <= timing.stopPollAttempts; attempt++) {
5114
+ if (stopped(pid))
5115
+ return true;
5116
+ if (attempt < timing.stopPollAttempts)
5117
+ await delay(timing.stopPollInterval);
5118
+ }
5119
+ return false;
5120
+ }
5121
+ async function terminate(info, options, timing) {
5122
+ const current = await read2(options.file);
5123
+ if (current === void 0 || !same2(current, info))
5124
+ return;
5125
+ signal(info.pid, "SIGTERM");
5126
+ if (!await waitUntilStopped(info.pid, timing)) {
5127
+ signal(info.pid, "SIGKILL");
5128
+ if (!await waitUntilStopped(info.pid, timing))
5129
+ throw new Error(`Server process ${info.pid} is still running`);
5130
+ }
5131
+ const latest = await read2(options.file);
5132
+ if (latest === void 0 || !same2(latest, info))
5133
+ return;
5134
+ await rm2(options.file ?? fallback2(), { force: true });
5135
+ }
5136
+ function delay(milliseconds) {
5137
+ return new Promise((resolve2) => setTimeout(resolve2, milliseconds));
5138
+ }
5139
+ var Service = { discover, ensure, stop, headers: headers2 };
5140
+
5141
+ // src/opencode-v2-forms.js
5142
+ async function getFormApi(ctx, sessionID, { makeClient = exports_client.make } = {}) {
5143
+ const endpoint = await Service.discover({ version: ctx.app.version });
5144
+ if (!endpoint) {
5145
+ throw new HttpError("Remote question replies require the OpenCode V2 shared service", 501);
5146
+ }
5147
+ const client = makeClient({ baseUrl: endpoint.url, headers: Service.headers(endpoint) });
5148
+ let session;
5149
+ try {
5150
+ session = await client.session.get({ sessionID });
5151
+ } catch {
5152
+ throw new HttpError("The question session is not owned by the shared OpenCode service", 501);
5153
+ }
5154
+ if (session.location.directory !== ctx.location.directory) {
5155
+ throw new HttpError("The question session belongs to another OpenCode location", 403);
5156
+ }
5157
+ return client.session.form;
5158
+ }
5159
+
5160
+ // src/opencode-v2-client.js
5161
+ async function nativeReply(operation) {
5162
+ try {
5163
+ return await operation();
5164
+ } catch (error) {
5165
+ const statuses = {
5166
+ PermissionNotFoundError: 404,
5167
+ SessionNotFoundError: 404,
5168
+ FormNotFoundError: 404,
5169
+ FormAlreadySettledError: 409,
5170
+ FormInvalidAnswerError: 422
5171
+ };
5172
+ const status = statuses[error?._tag];
5173
+ if (status) throw new HttpError(error.message || error._tag, status);
5174
+ throw error;
5175
+ }
5176
+ }
5177
+ function questionForm(form) {
5178
+ return form?.metadata?.kind === "question" && typeof form.sessionID === "string" && form.sessionID !== "global" && Array.isArray(form.fields) && form.fields.length > 0 && form.fields.every(
5179
+ (field) => ["string", "multiselect"].includes(field.type) && !field.hidden && !field.when?.length
5180
+ );
5181
+ }
5182
+ function normalizeV2Event(event) {
5183
+ const properties = event.data ?? {};
5184
+ const normalized = { ...event, properties };
5185
+ if (event.type === "form.created") {
5186
+ const form = properties.form;
5187
+ if (!questionForm(form)) return [];
5188
+ return [{ ...normalized, type: "question.v2.asked", properties: {
5189
+ id: form.id,
5190
+ sessionID: form.sessionID,
5191
+ questions: form.fields.map((field) => ({
5192
+ header: field.title ?? form.title,
5193
+ question: field.description ?? field.title ?? form.title,
5194
+ options: (field.options ?? []).map((option) => ({
5195
+ label: option.label,
5196
+ description: option.description ?? ""
5197
+ })),
5198
+ multiple: field.type === "multiselect",
5199
+ custom: field.custom === true || !field.options?.length
5200
+ }))
5201
+ } }];
5202
+ }
5203
+ if (event.type === "form.replied" || event.type === "form.cancelled") {
5204
+ return [{ ...normalized, type: event.type === "form.replied" ? "question.v2.replied" : "question.v2.rejected" }];
5205
+ }
5206
+ if (event.type === "session.execution.started") {
5207
+ return [{ ...normalized, type: "session.status", properties: {
5208
+ ...properties,
5209
+ status: { type: "busy" }
5210
+ } }];
5211
+ }
5212
+ if (["session.execution.succeeded", "session.execution.interrupted"].includes(event.type)) {
5213
+ return [
5214
+ { ...normalized, type: "message.updated", properties: { info: {
5215
+ role: "assistant",
5216
+ sessionID: properties.sessionID,
5217
+ id: event.id,
5218
+ time: { completed: event.created },
5219
+ error: event.type === "session.execution.interrupted"
5220
+ } } },
5221
+ { ...normalized, type: "session.idle" }
5222
+ ];
5223
+ }
5224
+ if (event.type === "session.execution.failed") {
5225
+ return [{ ...normalized, type: "session.error" }, { ...normalized, type: "session.idle" }];
5226
+ }
5227
+ if (event.type === "session.idle" || event.type === "session.status" && properties.status?.type === "idle") {
5228
+ return [];
5229
+ }
5230
+ return [normalized];
5231
+ }
5232
+ function createV2Client(ctx, resolveFormApi = (sessionID) => getFormApi(ctx, sessionID)) {
5233
+ return {
5234
+ liveBridge: createV2LiveClient(ctx),
5235
+ app: {
5236
+ async log({ body }) {
5237
+ console.error(JSON.stringify(body));
5238
+ }
5239
+ },
5240
+ session: {
5241
+ get: ({ path }) => ctx.session.get({ sessionID: path.id }),
5242
+ prompt: ({ path, body }) => ctx.session.synthetic({
5243
+ sessionID: path.id,
5244
+ text: body.parts.map((part) => part.text).join("\n"),
5245
+ resume: false
5246
+ })
5247
+ },
5248
+ postSessionIdPermissionsPermissionId: ({ path, body }) => nativeReply(() => ctx.permission.reply({
5249
+ sessionID: path.id,
5250
+ requestID: path.permissionID,
5251
+ decision: body.response
5252
+ })),
5253
+ v2: { session: { question: {
5254
+ async reply({ sessionID, requestID, questionV2Reply }) {
5255
+ const api = await resolveFormApi(sessionID);
5256
+ const form = await nativeReply(() => api.get({ sessionID, formID: requestID }));
5257
+ if (!questionForm(form)) throw new HttpError("Unsupported OpenCode question form", 422);
5258
+ const answers = questionV2Reply.answers;
5259
+ if (!Array.isArray(answers) || answers.length !== form.fields.length) {
5260
+ throw new HttpError("Question answer count does not match the form", 422);
5261
+ }
5262
+ const answer = {};
5263
+ form.fields.forEach((field, index) => {
5264
+ const selections = answers[index];
5265
+ if (!Array.isArray(selections) || selections.some((value) => typeof value !== "string")) {
5266
+ throw new HttpError("Invalid question answer", 422);
5267
+ }
5268
+ const values = selections.map(
5269
+ (label) => field.options?.find((option) => option.label === label)?.value ?? label
5270
+ );
5271
+ if (field.type === "multiselect") answer[field.key] = values;
5272
+ else if (values.length === 1) answer[field.key] = values[0];
5273
+ else if (values.length > 1) throw new HttpError("Single-choice question has multiple answers", 422);
5274
+ });
5275
+ return nativeReply(() => api.reply({ sessionID, formID: requestID, answer }));
5276
+ },
5277
+ async reject({ sessionID, requestID }) {
5278
+ const api = await resolveFormApi(sessionID);
5279
+ const form = await nativeReply(() => api.get({ sessionID, formID: requestID }));
5280
+ if (!questionForm(form)) throw new HttpError("Unsupported OpenCode question form", 422);
5281
+ return nativeReply(() => api.cancel({ sessionID, formID: requestID }));
5282
+ }
5283
+ } } },
5284
+ async ownsEvent(event) {
5285
+ if (event.location?.directory) return event.location.directory === ctx.location.directory;
5286
+ const sessionID = event.data?.sessionID ?? event.data?.form?.sessionID;
5287
+ if (!sessionID) return event.type === "installation.update-available";
5288
+ if (sessionID === "global") return false;
5289
+ try {
5290
+ const session = await ctx.session.get({ sessionID });
5291
+ return session.location?.directory === ctx.location.directory;
5292
+ } catch {
5293
+ return false;
5294
+ }
5295
+ }
5296
+ };
5297
+ }
5298
+
5299
+ // src/opencode-v2.js
5300
+ function waitForReconnect(signal2, delay2) {
5301
+ return new Promise((resolve2) => {
5302
+ const finish = () => {
5303
+ clearTimeout(timer);
5304
+ signal2.removeEventListener("abort", finish);
5305
+ resolve2();
5306
+ };
5307
+ const timer = setTimeout(finish, delay2);
5308
+ signal2.addEventListener("abort", finish, { once: true });
5309
+ if (signal2.aborted) finish();
5310
+ });
5311
+ }
5312
+ async function setup(ctx) {
5313
+ const client = createV2Client(ctx);
5314
+ const reporter = createOpenCodeReporter(client, ctx.options?.logLevel ?? process.env.BACKEER_LOG_LEVEL);
5315
+ const feedback = Object.fromEntries(
5316
+ ["info", "success", "applied", "warning", "error"].map((kind) => [
5317
+ kind,
5318
+ () => {
5319
+ }
5320
+ ])
5321
+ );
5322
+ feedback.handle = () => {
5323
+ };
5324
+ feedback.dispose = () => {
5325
+ };
5326
+ const hooks = await BackeerPlugin({
5327
+ client,
5328
+ directory: ctx.location.directory,
5329
+ feedback,
5330
+ v2: true
5331
+ }, { ...ctx.options, todos: false });
5332
+ if (!hooks.event) return;
5333
+ const controller = new AbortController();
5334
+ let streamTask;
5335
+ let cleanupTask;
5336
+ const registrations = [];
5337
+ function cleanup() {
5338
+ cleanupTask ??= (async () => {
5339
+ controller.abort();
5340
+ await streamTask?.catch(() => {
5341
+ });
5342
+ await hooks.dispose();
5343
+ await Promise.all(registrations.map((registration) => registration.dispose()));
5344
+ })();
5345
+ return cleanupTask;
5346
+ }
5347
+ try {
5348
+ await ctx.command.transform((editor) => {
5349
+ editor.add({
5350
+ name: "backeer",
5351
+ description: "Check, test, update, or manage Backeer",
5352
+ async execute({ sessionID, prompt, delivery }) {
5353
+ const output = { parts: [] };
5354
+ await hooks["command.execute.before"]({
5355
+ command: "backeer",
5356
+ sessionID,
5357
+ arguments: prompt.text
5358
+ }, output);
5359
+ if (output.parts.length) {
5360
+ await ctx.session.prompt({
5361
+ sessionID,
5362
+ text: output.parts.map((part) => part.text).join("\n"),
5363
+ delivery
5364
+ });
5365
+ }
5366
+ }
5367
+ });
5368
+ }).then((registration) => registrations.push(registration));
5369
+ registrations.push(await ctx.session.hook("prompt", async (event) => {
5370
+ const output = { message: { id: event.messageID }, parts: [] };
5371
+ await hooks["chat.message"]({ sessionID: event.sessionID }, output);
5372
+ if (output.parts.length) {
5373
+ event.prompt.text += `
5374
+
5375
+ ${output.parts.map((part) => part.text).join("\n")}`;
5376
+ }
5377
+ }));
5378
+ streamTask = (async () => {
5379
+ while (!controller.signal.aborted) {
5380
+ try {
5381
+ for await (const event of ctx.event.subscribe({ signal: controller.signal })) {
5382
+ if (controller.signal.aborted) break;
5383
+ if (!await client.ownsEvent(event)) continue;
5384
+ for (const normalized of normalizeV2Event(event)) {
5385
+ await hooks.event({ event: normalized });
5386
+ }
5387
+ }
5388
+ } catch (error) {
5389
+ if (!controller.signal.aborted) reporter.error("OpenCode event stream disconnected", error);
5390
+ }
5391
+ if (!controller.signal.aborted) {
5392
+ await waitForReconnect(controller.signal, LIVE_RECONNECT_MS);
5393
+ }
5394
+ }
5395
+ })();
5396
+ return cleanup;
5397
+ } catch (error) {
5398
+ await cleanup();
5399
+ throw error;
5400
+ }
5401
+ }
5402
+
5403
+ // src/index.js
5404
+ var index_default = { id: "backeer-opencode", setup, server: BackeerPlugin };
2242
5405
  export {
2243
- BackeerPlugin as default
5406
+ index_default as default
2244
5407
  };