@glrs-dev/cli 2.3.0 → 2.4.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.
- package/CHANGELOG.md +8 -0
- package/dist/{chunk-EM4MJBOD.js → chunk-2AZKRWC6.js} +4 -4
- package/dist/{chunk-UXBOTMDY.js → chunk-2P3ETOT2.js} +2 -2
- package/dist/chunk-2VMFXAJH.js +795 -0
- package/dist/chunk-5ZVUFNCP.js +140 -0
- package/dist/{chunk-W37UX3U2.js → chunk-6Y27RQQL.js} +2 -2
- package/dist/{chunk-RZWOWTKF.js → chunk-EKNRKZWR.js} +4 -4
- package/dist/{chunk-YGNDPKIW.js → chunk-HQUCVJ4G.js} +3 -1
- package/dist/{chunk-OABVEBWW.js → chunk-MBEVC327.js} +1 -1
- package/dist/{chunk-MIWZLETC.js → chunk-MCM47HH4.js} +1 -1
- package/dist/{chunk-F3AFRUT2.js → chunk-PTIO556V.js} +2 -2
- package/dist/{chunk-E2UNZIZT.js → chunk-R2WXQ54P.js} +1 -1
- package/dist/{chunk-I2KUXY3I.js → chunk-SMDIOB5B.js} +2 -2
- package/dist/{chunk-SPULDN7P.js → chunk-YY7EWHMA.js} +5 -3
- package/dist/cli.js +31 -20
- package/dist/commands/autopilot-interactive.d.ts +89 -0
- package/dist/commands/autopilot-interactive.js +248 -0
- package/dist/commands/autopilot-raw.d.ts +1 -0
- package/dist/commands/autopilot-raw.js +368 -0
- package/dist/commands/autopilot-tui.d.ts +7 -0
- package/dist/commands/autopilot-tui.js +7 -0
- package/dist/commands/autopilot.d.ts +39 -0
- package/dist/commands/autopilot.js +395 -0
- package/dist/commands/cleanup.js +3 -3
- package/dist/commands/create.js +4 -4
- package/dist/commands/dashboard.d.ts +3 -0
- package/dist/commands/dashboard.js +1549 -0
- package/dist/commands/debrief.d.ts +57 -0
- package/dist/commands/debrief.js +9 -0
- package/dist/commands/delete.js +3 -3
- package/dist/commands/go.js +2 -2
- package/dist/commands/list.js +3 -3
- package/dist/commands/loop.d.ts +42 -0
- package/dist/commands/loop.js +133 -0
- package/dist/commands/plan-picker.d.ts +15 -0
- package/dist/commands/plan-picker.js +76 -0
- package/dist/commands/scoper.d.ts +54 -0
- package/dist/{vendor/harness-opencode/dist/scoper-S77SOK7X.js → commands/scoper.js} +30 -15
- package/dist/commands/switch.js +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/lib/auto-update.js +1 -1
- package/dist/lib/config.d.ts +3 -2
- package/dist/lib/config.js +1 -1
- package/dist/lib/registry.d.ts +2 -0
- package/dist/lib/registry.js +1 -1
- package/dist/lib/worktree.js +3 -3
- package/dist/node_modules/@glrs-dev/adapter-opencode/dist/index.d.ts +261 -0
- package/dist/node_modules/@glrs-dev/adapter-opencode/dist/index.js +488 -0
- package/dist/node_modules/@glrs-dev/adapter-opencode/package.json +8 -0
- package/dist/node_modules/@glrs-dev/autopilot/dist/auto-ship-LCT6LIH7.js +7 -0
- package/dist/node_modules/@glrs-dev/autopilot/dist/changeset-generator-DG3MVWVV.js +15 -0
- package/dist/node_modules/@glrs-dev/autopilot/dist/chunk-7OSEI5TF.js +249 -0
- package/dist/node_modules/@glrs-dev/autopilot/dist/chunk-E7PWTRFO.js +91 -0
- package/dist/node_modules/@glrs-dev/autopilot/dist/chunk-M2ZVBPWL.js +101 -0
- package/dist/node_modules/@glrs-dev/autopilot/dist/chunk-Q4ULU6ER.js +68 -0
- package/dist/node_modules/@glrs-dev/autopilot/dist/chunk-VITL2Z45.js +2772 -0
- package/dist/node_modules/@glrs-dev/autopilot/dist/chunk-ZNJWARTM.js +449 -0
- package/dist/node_modules/@glrs-dev/autopilot/dist/index.d.ts +1765 -0
- package/dist/node_modules/@glrs-dev/autopilot/dist/index.js +688 -0
- package/dist/node_modules/@glrs-dev/autopilot/dist/logger-UITJGIZE.js +8 -0
- package/dist/node_modules/@glrs-dev/autopilot/dist/loop-session-XKL3NHUA.js +8 -0
- package/dist/node_modules/@glrs-dev/autopilot/dist/plan-enrichment-D3RPJR2J.js +14 -0
- package/dist/node_modules/@glrs-dev/autopilot/package.json +8 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/plan.md +7 -0
- package/dist/vendor/harness-opencode/dist/chunk-GILWWWMB.js +66 -0
- package/dist/vendor/harness-opencode/dist/cli.js +335 -639
- package/dist/vendor/harness-opencode/dist/index.js +35 -8
- package/dist/vendor/harness-opencode/dist/plugin-check-GJRD2OK6.js +14 -0
- package/dist/vendor/harness-opencode/package.json +1 -1
- package/package.json +14 -6
- package/dist/vendor/harness-opencode/dist/autopilot/prompt-template.md +0 -104
- package/dist/vendor/harness-opencode/dist/chunk-GCWHRUOK.js +0 -259
- package/dist/vendor/harness-opencode/dist/chunk-MJSMBY2Y.js +0 -87
- package/dist/vendor/harness-opencode/dist/chunk-NIFAVPNN.js +0 -544
- package/dist/vendor/harness-opencode/dist/loop-session-J35NILUZ.js +0 -30
- package/dist/vendor/harness-opencode/dist/opencode-server-KPCDFYAX.js +0 -22
- package/dist/vendor/harness-opencode/dist/plan-parser-TMHEKT22.js +0 -6
- package/dist/vendor/harness-opencode/dist/plan-session-7VS32P52.js +0 -117
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
// src/opencode-adapter.ts
|
|
2
|
+
import { execFile } from "child_process";
|
|
3
|
+
import { promisify } from "util";
|
|
4
|
+
import * as fs from "fs";
|
|
5
|
+
import {
|
|
6
|
+
createOpencode
|
|
7
|
+
} from "@opencode-ai/sdk";
|
|
8
|
+
var execFileP = promisify(execFile);
|
|
9
|
+
var DEFAULT_STARTUP_TIMEOUT_MS = 3e4;
|
|
10
|
+
async function ensureOpencodeOnPath() {
|
|
11
|
+
try {
|
|
12
|
+
await execFileP("opencode", ["--version"]);
|
|
13
|
+
} catch {
|
|
14
|
+
throw new Error(
|
|
15
|
+
"opencode CLI not found on PATH.\n Install: https://opencode.ai\n Or: bunx opencode upgrade"
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async function startServer(opts) {
|
|
20
|
+
await ensureOpencodeOnPath();
|
|
21
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_STARTUP_TIMEOUT_MS;
|
|
22
|
+
const port = opts.port ?? 0;
|
|
23
|
+
const { client, server } = await createOpencode({
|
|
24
|
+
port,
|
|
25
|
+
timeout: timeoutMs,
|
|
26
|
+
hostname: "127.0.0.1"
|
|
27
|
+
});
|
|
28
|
+
let shutdownCalled = false;
|
|
29
|
+
const shutdown = async () => {
|
|
30
|
+
if (shutdownCalled) return;
|
|
31
|
+
shutdownCalled = true;
|
|
32
|
+
try {
|
|
33
|
+
server.close();
|
|
34
|
+
} catch {
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
return { url: server.url, client, shutdown };
|
|
38
|
+
}
|
|
39
|
+
async function selfTest(client) {
|
|
40
|
+
try {
|
|
41
|
+
await client.session.list();
|
|
42
|
+
} catch (err) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`OpenCode server self-test failed \u2014 the server started but isn't responding to API calls.
|
|
45
|
+
Error: ${err instanceof Error ? err.message : String(err)}
|
|
46
|
+
Run \`opencode --version\` to verify your installation.`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async function createSession(client, opts) {
|
|
51
|
+
const result = await client.session.create({
|
|
52
|
+
query: { directory: opts.cwd },
|
|
53
|
+
body: {}
|
|
54
|
+
});
|
|
55
|
+
if (!result.data) {
|
|
56
|
+
throw new Error("session.create returned no data");
|
|
57
|
+
}
|
|
58
|
+
return result.data.id;
|
|
59
|
+
}
|
|
60
|
+
async function sendAndWait(client, opts) {
|
|
61
|
+
const stallMs = opts.stallMs ?? 60 * 60 * 1e3;
|
|
62
|
+
const idlePromise = waitForIdle(client, {
|
|
63
|
+
sessionId: opts.sessionId,
|
|
64
|
+
stallMs,
|
|
65
|
+
abortSignal: opts.abortSignal,
|
|
66
|
+
onToolCall: opts.onToolCall,
|
|
67
|
+
onTextDelta: opts.onTextDelta,
|
|
68
|
+
onCostUpdate: opts.onCostUpdate,
|
|
69
|
+
autoRejectPermissions: opts.autoRejectPermissions,
|
|
70
|
+
serverUrl: opts.serverUrl,
|
|
71
|
+
onPermissionRejected: opts.onPermissionRejected
|
|
72
|
+
});
|
|
73
|
+
await client.session.prompt({
|
|
74
|
+
path: { id: opts.sessionId },
|
|
75
|
+
body: {
|
|
76
|
+
parts: [{ type: "text", text: opts.message }],
|
|
77
|
+
...opts.agentName ? { agent: opts.agentName } : {}
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
return idlePromise;
|
|
81
|
+
}
|
|
82
|
+
async function waitForIdle(client, opts) {
|
|
83
|
+
const stallMs = opts.stallMs ?? 60 * 60 * 1e3;
|
|
84
|
+
const sse = await client.event.subscribe();
|
|
85
|
+
const reportedToolCalls = /* @__PURE__ */ new Set();
|
|
86
|
+
let sseFd = null;
|
|
87
|
+
if (process.env["GLRS_DEBUG_SSE"] === "1") {
|
|
88
|
+
try {
|
|
89
|
+
sseFd = fs.openSync("/tmp/glrs-sse.log", "a");
|
|
90
|
+
} catch {
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const sseLog = (msg) => {
|
|
94
|
+
if (sseFd !== null) {
|
|
95
|
+
try {
|
|
96
|
+
fs.writeSync(sseFd, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
97
|
+
`);
|
|
98
|
+
} catch {
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
return new Promise((resolve) => {
|
|
103
|
+
let stallTimer = null;
|
|
104
|
+
let settled = false;
|
|
105
|
+
let pollerRunning = true;
|
|
106
|
+
const settle = (result) => {
|
|
107
|
+
if (settled) return;
|
|
108
|
+
settled = true;
|
|
109
|
+
pollerRunning = false;
|
|
110
|
+
if (stallTimer) clearTimeout(stallTimer);
|
|
111
|
+
if (sseFd !== null) {
|
|
112
|
+
try {
|
|
113
|
+
fs.closeSync(sseFd);
|
|
114
|
+
} catch {
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
resolve(result);
|
|
118
|
+
};
|
|
119
|
+
const resetStall = () => {
|
|
120
|
+
if (stallTimer) clearTimeout(stallTimer);
|
|
121
|
+
stallTimer = setTimeout(() => settle({ kind: "stall", stallMs }), stallMs);
|
|
122
|
+
};
|
|
123
|
+
if (opts.abortSignal) {
|
|
124
|
+
if (opts.abortSignal.aborted) {
|
|
125
|
+
settle({ kind: "abort" });
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
opts.abortSignal.addEventListener("abort", () => settle({ kind: "abort" }), { once: true });
|
|
129
|
+
}
|
|
130
|
+
resetStall();
|
|
131
|
+
if (opts.onToolCall) {
|
|
132
|
+
(async () => {
|
|
133
|
+
while (pollerRunning && !settled) {
|
|
134
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
135
|
+
if (!pollerRunning || settled) break;
|
|
136
|
+
try {
|
|
137
|
+
const msgs = await client.session.messages({ path: { id: opts.sessionId } });
|
|
138
|
+
const messages = msgs?.data ?? msgs;
|
|
139
|
+
if (!Array.isArray(messages)) continue;
|
|
140
|
+
for (const msg of messages) {
|
|
141
|
+
if (msg?.info?.role !== "assistant") continue;
|
|
142
|
+
for (const part of msg?.parts ?? []) {
|
|
143
|
+
if (part?.type !== "tool") continue;
|
|
144
|
+
const callID = part.callID ?? part.id ?? "";
|
|
145
|
+
const status = part.state?.status ?? "";
|
|
146
|
+
const key = `${callID}:${status}`;
|
|
147
|
+
if (reportedToolCalls.has(key)) continue;
|
|
148
|
+
if (status !== "running" && status !== "completed" && status !== "error") continue;
|
|
149
|
+
reportedToolCalls.add(key);
|
|
150
|
+
resetStall();
|
|
151
|
+
const input = part.state?.input;
|
|
152
|
+
let firstArg;
|
|
153
|
+
if (input && typeof input === "object") {
|
|
154
|
+
for (const k of ["filePath", "file_path", "path", "command", "pattern", "query"]) {
|
|
155
|
+
const val = input[k];
|
|
156
|
+
if (typeof val === "string" && val.length > 0) {
|
|
157
|
+
firstArg = val.length > 80 ? val.slice(0, 77) + "..." : val;
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
opts.onToolCall(part.tool ?? "unknown", firstArg);
|
|
164
|
+
} catch {
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (opts.onCostUpdate && msg?.info?.cost > 0) {
|
|
168
|
+
const costKey = `cost:${msg.info.id}`;
|
|
169
|
+
if (!reportedToolCalls.has(costKey)) {
|
|
170
|
+
reportedToolCalls.add(costKey);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (opts.onCostUpdate) {
|
|
175
|
+
let totalCost = 0;
|
|
176
|
+
let totalIn = 0;
|
|
177
|
+
let totalOut = 0;
|
|
178
|
+
for (const m of messages) {
|
|
179
|
+
if (m?.info?.role === "assistant" && typeof m.info.cost === "number") {
|
|
180
|
+
totalCost += m.info.cost;
|
|
181
|
+
const t = m.info.tokens ?? {};
|
|
182
|
+
totalIn += t.input ?? 0;
|
|
183
|
+
totalOut += t.output ?? 0;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (totalCost > 0) {
|
|
187
|
+
const costKey = `cumcost:${totalCost.toFixed(6)}`;
|
|
188
|
+
if (!reportedToolCalls.has(costKey)) {
|
|
189
|
+
reportedToolCalls.add(costKey);
|
|
190
|
+
try {
|
|
191
|
+
opts.onCostUpdate(totalCost, { input: totalIn, output: totalOut });
|
|
192
|
+
} catch {
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
} catch {
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
})();
|
|
201
|
+
}
|
|
202
|
+
(async () => {
|
|
203
|
+
try {
|
|
204
|
+
for await (const event of sse.stream) {
|
|
205
|
+
if (settled) break;
|
|
206
|
+
const ev = event;
|
|
207
|
+
const props = ev.properties ?? {};
|
|
208
|
+
const type = ev.type ?? "";
|
|
209
|
+
sseLog(`[SSE] type=${type}`);
|
|
210
|
+
if (opts.onCostUpdate && type === "message.updated") {
|
|
211
|
+
const info = props["info"];
|
|
212
|
+
if (info && info.role === "assistant" && typeof info.cost === "number") {
|
|
213
|
+
resetStall();
|
|
214
|
+
try {
|
|
215
|
+
opts.onCostUpdate(
|
|
216
|
+
info.cost,
|
|
217
|
+
{
|
|
218
|
+
input: info.tokens?.input ?? 0,
|
|
219
|
+
output: info.tokens?.output ?? 0
|
|
220
|
+
}
|
|
221
|
+
);
|
|
222
|
+
} catch {
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (opts.onTextDelta && (type === "message.part.delta" || type === "message.part.updated")) {
|
|
227
|
+
const delta = props["delta"];
|
|
228
|
+
if (typeof delta === "string" && delta.length > 0) {
|
|
229
|
+
resetStall();
|
|
230
|
+
try {
|
|
231
|
+
opts.onTextDelta(delta.length);
|
|
232
|
+
} catch {
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (opts.onToolCall && (type === "message.part.updated" || type === "message.part.delta")) {
|
|
237
|
+
const part = props["part"];
|
|
238
|
+
if (part && part.type === "tool" && part.sessionID === opts.sessionId) {
|
|
239
|
+
const status = part.state?.status;
|
|
240
|
+
const hasCallId = !!part.callID;
|
|
241
|
+
const hasTool = !!part.tool;
|
|
242
|
+
const shouldFire = hasCallId && hasTool && !reportedToolCalls.has(part.callID) && (status === "completed" || // A: tool finished
|
|
243
|
+
status === "running" || // B: tool started
|
|
244
|
+
status === void 0);
|
|
245
|
+
sseLog(`[TOOL] type=${part.type} status=${status} tool=${part.tool} sessionMatch=${part.sessionID === opts.sessionId} callID=${part.callID} shouldFire=${shouldFire}`);
|
|
246
|
+
if (shouldFire) {
|
|
247
|
+
reportedToolCalls.add(part.callID);
|
|
248
|
+
resetStall();
|
|
249
|
+
let firstArg;
|
|
250
|
+
const input = part.state?.input;
|
|
251
|
+
if (input) {
|
|
252
|
+
const argKeys = ["filePath", "file_path", "path", "command", "pattern", "query"];
|
|
253
|
+
for (const key of argKeys) {
|
|
254
|
+
const val = input[key];
|
|
255
|
+
if (typeof val === "string" && val.length > 0) {
|
|
256
|
+
firstArg = val.length > 80 ? val.slice(0, 77) + "..." : val;
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
try {
|
|
262
|
+
opts.onToolCall(part.tool ?? "unknown", firstArg);
|
|
263
|
+
} catch {
|
|
264
|
+
}
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
const eventSessionId = props["sessionID"];
|
|
270
|
+
if (opts.autoRejectPermissions && (type === "permission.updated" || type === "question.asked")) {
|
|
271
|
+
const permissionId = props["id"];
|
|
272
|
+
const permissionType = type === "question.asked" ? "question" : props["type"] ?? "unknown";
|
|
273
|
+
const permissionTitle = props["title"] ?? "";
|
|
274
|
+
if (opts.onPermissionRejected) {
|
|
275
|
+
try {
|
|
276
|
+
opts.onPermissionRejected({
|
|
277
|
+
id: permissionId ?? "unknown",
|
|
278
|
+
type: permissionType,
|
|
279
|
+
title: permissionTitle
|
|
280
|
+
});
|
|
281
|
+
} catch {
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (type === "permission.updated" && permissionId) {
|
|
285
|
+
(async () => {
|
|
286
|
+
try {
|
|
287
|
+
await client.postSessionIdPermissionsPermissionId({
|
|
288
|
+
path: { id: opts.sessionId, permissionID: permissionId },
|
|
289
|
+
body: { response: "reject" }
|
|
290
|
+
});
|
|
291
|
+
} catch {
|
|
292
|
+
}
|
|
293
|
+
})();
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
if (type === "question.asked") {
|
|
297
|
+
if (opts.serverUrl && permissionId) {
|
|
298
|
+
(async () => {
|
|
299
|
+
try {
|
|
300
|
+
await fetch(`${opts.serverUrl}/question/${permissionId}/reject`, {
|
|
301
|
+
method: "POST"
|
|
302
|
+
});
|
|
303
|
+
} catch {
|
|
304
|
+
}
|
|
305
|
+
})();
|
|
306
|
+
}
|
|
307
|
+
settle({
|
|
308
|
+
kind: "question_rejected",
|
|
309
|
+
title: permissionTitle
|
|
310
|
+
});
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (eventSessionId !== opts.sessionId) continue;
|
|
315
|
+
resetStall();
|
|
316
|
+
if (type === "session.idle") {
|
|
317
|
+
settle({ kind: "idle" });
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
320
|
+
if (type === "session.error") {
|
|
321
|
+
const msg = props["message"] ?? "session error";
|
|
322
|
+
sseLog(`[ERROR] session.error: ${msg} props=${JSON.stringify(props)}`);
|
|
323
|
+
settle({ kind: "error", message: msg });
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
} catch (err) {
|
|
328
|
+
if (!settled) {
|
|
329
|
+
settle({ kind: "error", message: err instanceof Error ? err.message : String(err) });
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
})();
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
async function getSessionCost(client, sessionId) {
|
|
336
|
+
const stats = await getSessionStats(client, sessionId);
|
|
337
|
+
return stats.cost;
|
|
338
|
+
}
|
|
339
|
+
async function getSessionStats(client, sessionId) {
|
|
340
|
+
try {
|
|
341
|
+
const result = await client.session.messages({ path: { id: sessionId } });
|
|
342
|
+
if (!result.data) {
|
|
343
|
+
return { cost: 0, tokensIn: 0, tokensOut: 0 };
|
|
344
|
+
}
|
|
345
|
+
const messages = result.data;
|
|
346
|
+
let cost = 0;
|
|
347
|
+
let tokensIn = 0;
|
|
348
|
+
let tokensOut = 0;
|
|
349
|
+
for (const m of messages) {
|
|
350
|
+
if (m.info.role === "assistant") {
|
|
351
|
+
if (typeof m.info.cost === "number") cost += m.info.cost;
|
|
352
|
+
if (m.info.tokens) {
|
|
353
|
+
tokensIn += m.info.tokens.input ?? 0;
|
|
354
|
+
tokensOut += m.info.tokens.output ?? 0;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return { cost, tokensIn, tokensOut };
|
|
359
|
+
} catch {
|
|
360
|
+
return { cost: 0, tokensIn: 0, tokensOut: 0 };
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
async function getLastAssistantMessage(client, sessionId) {
|
|
364
|
+
try {
|
|
365
|
+
const result = await client.session.messages({ path: { id: sessionId } });
|
|
366
|
+
if (!result.data) return "";
|
|
367
|
+
const messages = result.data;
|
|
368
|
+
const assistantMessages = messages.filter((m) => m.info.role === "assistant");
|
|
369
|
+
if (assistantMessages.length === 0) return "";
|
|
370
|
+
const last = assistantMessages[assistantMessages.length - 1];
|
|
371
|
+
if (!last) return "";
|
|
372
|
+
return last.parts.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join("");
|
|
373
|
+
} catch {
|
|
374
|
+
return "";
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// src/server-error-extractor.ts
|
|
379
|
+
import { readdirSync, readFileSync } from "fs";
|
|
380
|
+
import { join } from "path";
|
|
381
|
+
import { homedir } from "os";
|
|
382
|
+
var OPENCODE_LOG_DIR = join(homedir(), ".local", "share", "opencode", "log");
|
|
383
|
+
function extractServerError() {
|
|
384
|
+
try {
|
|
385
|
+
const entries = readdirSync(OPENCODE_LOG_DIR).filter((f) => f.endsWith(".log")).sort().reverse();
|
|
386
|
+
if (entries.length === 0) return null;
|
|
387
|
+
const logPath = join(OPENCODE_LOG_DIR, entries[0]);
|
|
388
|
+
const content = readFileSync(logPath, "utf-8");
|
|
389
|
+
const lines = content.split("\n").reverse();
|
|
390
|
+
for (const line of lines) {
|
|
391
|
+
if (line.includes("service=session.processor") && line.includes("error=")) {
|
|
392
|
+
const match = line.match(/error=(.+?)(?:\s+stack=|$)/);
|
|
393
|
+
if (match) return match[1].trim();
|
|
394
|
+
}
|
|
395
|
+
if (line.includes("service=llm") && line.includes("error=")) {
|
|
396
|
+
if (line.includes('error={"error":{}}')) continue;
|
|
397
|
+
const match = line.match(/error=(.+?)(?:\s+stream|$)/);
|
|
398
|
+
if (match) return match[1].trim();
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return null;
|
|
402
|
+
} catch {
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
function enhanceSessionError(genericMessage) {
|
|
407
|
+
if (genericMessage !== "session error" && genericMessage.length > 20) {
|
|
408
|
+
return genericMessage;
|
|
409
|
+
}
|
|
410
|
+
const detail = extractServerError();
|
|
411
|
+
if (detail) return detail;
|
|
412
|
+
return genericMessage;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// src/opencode-adapter-class.ts
|
|
416
|
+
var OpenCodeHandle = class {
|
|
417
|
+
id;
|
|
418
|
+
server;
|
|
419
|
+
cwd;
|
|
420
|
+
constructor(server, cwd) {
|
|
421
|
+
this.server = server;
|
|
422
|
+
this.cwd = cwd;
|
|
423
|
+
this.id = server.url;
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
var OpenCodeAdapter = class {
|
|
427
|
+
name = "opencode";
|
|
428
|
+
async start(opts) {
|
|
429
|
+
const server = await startServer({ cwd: opts.cwd });
|
|
430
|
+
return new OpenCodeHandle(server, opts.cwd);
|
|
431
|
+
}
|
|
432
|
+
async createSession(handle, opts) {
|
|
433
|
+
const h = handle;
|
|
434
|
+
return createSession(h.server.client, {
|
|
435
|
+
cwd: h.cwd,
|
|
436
|
+
agentName: opts.agentName
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
async sendAndWait(handle, opts) {
|
|
440
|
+
const h = handle;
|
|
441
|
+
const result = await sendAndWait(h.server.client, {
|
|
442
|
+
sessionId: opts.sessionId,
|
|
443
|
+
message: opts.message,
|
|
444
|
+
stallMs: opts.stallMs,
|
|
445
|
+
abortSignal: opts.abortSignal,
|
|
446
|
+
onToolCall: opts.onToolCall,
|
|
447
|
+
onTextDelta: opts.onTextDelta,
|
|
448
|
+
onCostUpdate: opts.onCostUpdate,
|
|
449
|
+
autoRejectPermissions: true,
|
|
450
|
+
serverUrl: h.server.url
|
|
451
|
+
});
|
|
452
|
+
return result;
|
|
453
|
+
}
|
|
454
|
+
async getLastResponse(handle, sessionId) {
|
|
455
|
+
const h = handle;
|
|
456
|
+
return getLastAssistantMessage(h.server.client, sessionId);
|
|
457
|
+
}
|
|
458
|
+
async getSessionCost(handle, sessionId) {
|
|
459
|
+
const h = handle;
|
|
460
|
+
return getSessionCost(h.server.client, sessionId);
|
|
461
|
+
}
|
|
462
|
+
async getSessionStats(handle, sessionId) {
|
|
463
|
+
const h = handle;
|
|
464
|
+
return getSessionStats(h.server.client, sessionId);
|
|
465
|
+
}
|
|
466
|
+
async shutdown(handle) {
|
|
467
|
+
const h = handle;
|
|
468
|
+
await h.server.shutdown();
|
|
469
|
+
}
|
|
470
|
+
async enhanceError(message) {
|
|
471
|
+
return enhanceSessionError(message);
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
export {
|
|
475
|
+
DEFAULT_STARTUP_TIMEOUT_MS,
|
|
476
|
+
OpenCodeAdapter,
|
|
477
|
+
createSession,
|
|
478
|
+
enhanceSessionError,
|
|
479
|
+
execFileP,
|
|
480
|
+
extractServerError,
|
|
481
|
+
getLastAssistantMessage,
|
|
482
|
+
getSessionCost,
|
|
483
|
+
getSessionStats,
|
|
484
|
+
selfTest,
|
|
485
|
+
sendAndWait,
|
|
486
|
+
startServer,
|
|
487
|
+
waitForIdle
|
|
488
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
generateChangeset,
|
|
3
|
+
inferBumpLevel,
|
|
4
|
+
readPlanGoal,
|
|
5
|
+
readPlanTitle,
|
|
6
|
+
slugifyTitle
|
|
7
|
+
} from "./chunk-E7PWTRFO.js";
|
|
8
|
+
import "./chunk-7OSEI5TF.js";
|
|
9
|
+
export {
|
|
10
|
+
generateChangeset,
|
|
11
|
+
inferBumpLevel,
|
|
12
|
+
readPlanGoal,
|
|
13
|
+
readPlanTitle,
|
|
14
|
+
slugifyTitle
|
|
15
|
+
};
|