teamshare-bridge 0.26.6 → 0.26.7
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/dist/bridge/daemon-support.d.ts +9 -0
- package/dist/bridge/daemon-support.js +12 -0
- package/dist/bridge/daemon-support.js.map +1 -1
- package/dist/bridge/daemon.js +37 -0
- package/dist/bridge/daemon.js.map +1 -1
- package/dist/bridge/local-server.d.ts +12 -0
- package/dist/bridge/local-server.js +36 -0
- package/dist/bridge/local-server.js.map +1 -1
- package/dist/lib/browser/batch.d.ts +67 -2
- package/dist/lib/browser/batch.js +109 -4
- package/dist/lib/browser/batch.js.map +1 -1
- package/dist/lib/browser/download.d.ts +19 -0
- package/dist/lib/browser/download.js +251 -0
- package/dist/lib/browser/download.js.map +1 -0
- package/dist/lib/browser/render-image.d.ts +30 -0
- package/dist/lib/browser/render-image.js +285 -0
- package/dist/lib/browser/render-image.js.map +1 -0
- package/dist/lib/browser/session.d.ts +79 -1
- package/dist/lib/browser/session.js +174 -3
- package/dist/lib/browser/session.js.map +1 -1
- package/dist/lib/browser/stage-file.d.ts +17 -0
- package/dist/lib/browser/stage-file.js +23 -0
- package/dist/lib/browser/stage-file.js.map +1 -1
- package/dist/lib/browser/xlsx-template.d.ts +59 -0
- package/dist/lib/browser/xlsx-template.js +380 -0
- package/dist/lib/browser/xlsx-template.js.map +1 -0
- package/dist/lib/harness/opencode-control.d.ts +14 -0
- package/dist/lib/harness/opencode-control.js +63 -0
- package/dist/lib/harness/opencode-control.js.map +1 -0
- package/dist/lib/harness/session-store.d.ts +33 -0
- package/dist/lib/harness/session-store.js +38 -0
- package/dist/lib/harness/session-store.js.map +1 -1
- package/dist/lib/harness/spawn.js +56 -18
- package/dist/lib/harness/spawn.js.map +1 -1
- package/dist/lib/orchestrator/handlers/browser.js +181 -1
- package/dist/lib/orchestrator/handlers/browser.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.QA_TEXT_MAX_BYTES = exports.QA_XLSX_MAX_COLS = exports.QA_XLSX_MAX_ROWS = void 0;
|
|
7
|
+
exports.parseCellAddress = parseCellAddress;
|
|
8
|
+
exports.colToLetters = colToLetters;
|
|
9
|
+
exports.readStagedFile = readStagedFile;
|
|
10
|
+
exports.fillStagedSpreadsheet = fillStagedSpreadsheet;
|
|
11
|
+
/**
|
|
12
|
+
* Read and fill spreadsheet templates inside the run's staging directory
|
|
13
|
+
* (2026-10, QA files).
|
|
14
|
+
*
|
|
15
|
+
* WHY THIS EXISTS: a QA flow that must "download the template, fill it with
|
|
16
|
+
* test data and upload it" needs the template's STRUCTURE, not a guess at
|
|
17
|
+
* it. The agent reads headers first (`readStagedFile`), writes values into a
|
|
18
|
+
* COPY (`fillExcel` — the original download stays intact for a retry), and
|
|
19
|
+
* `attachFile` picks up the copy. All paths stay inside the run's staging
|
|
20
|
+
* directory, so the `safeStagedPath` guard keeps holding.
|
|
21
|
+
*
|
|
22
|
+
* Implementation notes:
|
|
23
|
+
* - `xlsx-populate` is already a bridge dependency (the workbook builder
|
|
24
|
+
* uses it), so no new package is needed.
|
|
25
|
+
* - `fillExcel` preserves everything it does not touch (styles, formulas,
|
|
26
|
+
* validation) because it edits the template in place rather than
|
|
27
|
+
* rebuilding it.
|
|
28
|
+
* - Text formats (.csv/.tsv/.txt/.json) read as text — the agent composes
|
|
29
|
+
* new content itself and stages it with `stageFile({ text })`.
|
|
30
|
+
*/
|
|
31
|
+
const node_fs_1 = require("node:fs");
|
|
32
|
+
const xlsx_populate_1 = __importDefault(require("xlsx-populate"));
|
|
33
|
+
const stage_file_1 = require("./stage-file");
|
|
34
|
+
exports.QA_XLSX_MAX_ROWS = 2000;
|
|
35
|
+
exports.QA_XLSX_MAX_COLS = 200;
|
|
36
|
+
exports.QA_TEXT_MAX_BYTES = 200 * 1024;
|
|
37
|
+
const SPREADSHEET_EXTS = ['.xlsx', '.xlsm'];
|
|
38
|
+
function extOf(path) {
|
|
39
|
+
const i = path.lastIndexOf('.');
|
|
40
|
+
return i >= 0 ? path.slice(i).toLowerCase() : '';
|
|
41
|
+
}
|
|
42
|
+
function cellToJson(v) {
|
|
43
|
+
if (v instanceof Date)
|
|
44
|
+
return v.toISOString();
|
|
45
|
+
return v ?? null;
|
|
46
|
+
}
|
|
47
|
+
/** Split "Sheet1!B3" / "'My Sheet'!C2" / "B3" into sheet + column + row. */
|
|
48
|
+
function parseCellAddress(addr) {
|
|
49
|
+
const m = /^(?:'((?:[^']|'')+)'!|([^!]+)!)?([A-Za-z]+)(\d+)$/.exec(addr.trim());
|
|
50
|
+
if (!m)
|
|
51
|
+
return null;
|
|
52
|
+
const raw = m[1] ?? m[2] ?? null;
|
|
53
|
+
const sheet = raw === null ? null : raw.replace(/''/g, "'");
|
|
54
|
+
const letters = m[3].toUpperCase();
|
|
55
|
+
let col = 0;
|
|
56
|
+
for (const ch of letters) {
|
|
57
|
+
col = col * 26 + (ch.charCodeAt(0) - 64);
|
|
58
|
+
if (col > exports.QA_XLSX_MAX_COLS + 26)
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const row = Number(m[4]);
|
|
62
|
+
if (!Number.isInteger(row) || row < 1 || row > exports.QA_XLSX_MAX_ROWS + 100) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
return { sheet, col, row };
|
|
66
|
+
}
|
|
67
|
+
function colToLetters(col) {
|
|
68
|
+
let n = col;
|
|
69
|
+
let s = '';
|
|
70
|
+
while (n > 0) {
|
|
71
|
+
const r = (n - 1) % 26;
|
|
72
|
+
s = String.fromCharCode(65 + r) + s;
|
|
73
|
+
n = Math.floor((n - 1) / 26);
|
|
74
|
+
}
|
|
75
|
+
return s || 'A';
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Read a staged file: spreadsheets come back as sheets + header-keyed rows,
|
|
79
|
+
* text formats come back as text. Caps keep the MCP response small.
|
|
80
|
+
*/
|
|
81
|
+
async function readStagedFile(opts) {
|
|
82
|
+
const safe = (0, stage_file_1.safeStagedPath)(opts.runId, opts.filePath);
|
|
83
|
+
if (!safe) {
|
|
84
|
+
return {
|
|
85
|
+
ok: false,
|
|
86
|
+
error: "the file is not in this run's staging directory",
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const ext = extOf(safe);
|
|
90
|
+
if (SPREADSHEET_EXTS.includes(ext)) {
|
|
91
|
+
return readWorkbook(safe, opts.sheet, opts.maxRows);
|
|
92
|
+
}
|
|
93
|
+
let stat;
|
|
94
|
+
try {
|
|
95
|
+
stat = (0, node_fs_1.readFileSync)(safe).length;
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return { ok: false, error: 'the file could not be read' };
|
|
99
|
+
}
|
|
100
|
+
if (stat > exports.QA_TEXT_MAX_BYTES) {
|
|
101
|
+
return {
|
|
102
|
+
ok: false,
|
|
103
|
+
error: `text file is too large (${Math.round(stat / 1024)} KB, max ${exports.QA_TEXT_MAX_BYTES / 1024} KB) — narrow the download first`,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
const text = (0, node_fs_1.readFileSync)(safe, 'utf8');
|
|
108
|
+
return { ok: true, kind: 'text', text, truncated: false };
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return { ok: false, error: 'the file is not readable as text' };
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async function readWorkbook(path, onlySheet, maxRows) {
|
|
115
|
+
const cap = Math.min(Math.max(typeof maxRows === 'number' ? maxRows : exports.QA_XLSX_MAX_ROWS, 1), exports.QA_XLSX_MAX_ROWS);
|
|
116
|
+
let buffer;
|
|
117
|
+
try {
|
|
118
|
+
buffer = (0, node_fs_1.readFileSync)(path);
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return { ok: false, error: 'the file could not be read' };
|
|
122
|
+
}
|
|
123
|
+
let workbook;
|
|
124
|
+
try {
|
|
125
|
+
workbook = await xlsx_populate_1.default.fromDataAsync(buffer);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return { ok: false, error: 'the file is not a readable workbook' };
|
|
129
|
+
}
|
|
130
|
+
const sheets = [];
|
|
131
|
+
let names;
|
|
132
|
+
try {
|
|
133
|
+
names = workbook.sheets().map((s) => s.name());
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return { ok: false, error: 'the workbook has no readable sheets' };
|
|
137
|
+
}
|
|
138
|
+
const wanted = onlySheet ? names.filter((n) => n === onlySheet) : names;
|
|
139
|
+
if (onlySheet && wanted.length === 0) {
|
|
140
|
+
return {
|
|
141
|
+
ok: false,
|
|
142
|
+
error: `sheet "${onlySheet}" not found (available: ${names.join(', ') || 'none'})`,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
for (const name of wanted) {
|
|
146
|
+
const sheet = workbook.sheet(name);
|
|
147
|
+
let endCol = 0;
|
|
148
|
+
let endRow = 0;
|
|
149
|
+
try {
|
|
150
|
+
const used = sheet.usedRange();
|
|
151
|
+
if (used) {
|
|
152
|
+
endCol = used.endCell().columnNumber();
|
|
153
|
+
endRow = used.endCell().rowNumber();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
/* empty sheet */
|
|
158
|
+
}
|
|
159
|
+
endCol = Math.min(endCol, exports.QA_XLSX_MAX_COLS);
|
|
160
|
+
endRow = Math.min(endRow, cap + 1);
|
|
161
|
+
const grid = [];
|
|
162
|
+
for (let r = 1; r <= endRow; r++) {
|
|
163
|
+
const row = [];
|
|
164
|
+
for (let c = 1; c <= endCol; c++) {
|
|
165
|
+
try {
|
|
166
|
+
row.push(cellToJson(sheet.cell(r, c).value()));
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
row.push(null);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
grid.push(row);
|
|
173
|
+
}
|
|
174
|
+
// Trim trailing empty columns and rows: a template's used range often
|
|
175
|
+
// overshoots its real content, and the agent must see headers and rows,
|
|
176
|
+
// not phantom "Column C" entries and blank objects.
|
|
177
|
+
const cellEmpty = (v) => v === null ||
|
|
178
|
+
v === undefined ||
|
|
179
|
+
(typeof v === 'string' && v.trim() === '');
|
|
180
|
+
let lastCol = -1;
|
|
181
|
+
for (const row of grid) {
|
|
182
|
+
for (let c = row.length - 1; c > lastCol; c--) {
|
|
183
|
+
if (!cellEmpty(row[c])) {
|
|
184
|
+
lastCol = c;
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const trimmed = grid
|
|
190
|
+
.map((row) => row.slice(0, lastCol + 1))
|
|
191
|
+
.filter((row, i, all) => {
|
|
192
|
+
if (i === 0)
|
|
193
|
+
return true;
|
|
194
|
+
const rest = all.slice(i);
|
|
195
|
+
return rest.some((r) => r.some((v) => !cellEmpty(v)));
|
|
196
|
+
});
|
|
197
|
+
const headerRow = trimmed[0] ?? [];
|
|
198
|
+
const headers = headerRow.map((h, i) => String(h ?? '').trim() === ''
|
|
199
|
+
? `Column ${colToLetters(i + 1)}`
|
|
200
|
+
: String(h).trim());
|
|
201
|
+
const rows = trimmed.slice(1).map((cells) => {
|
|
202
|
+
const obj = {};
|
|
203
|
+
headers.forEach((h, i) => {
|
|
204
|
+
obj[h] = cells[i] ?? null;
|
|
205
|
+
});
|
|
206
|
+
return obj;
|
|
207
|
+
});
|
|
208
|
+
sheets.push({
|
|
209
|
+
name,
|
|
210
|
+
headers,
|
|
211
|
+
rows,
|
|
212
|
+
rowCount: rows.length,
|
|
213
|
+
truncated: endRow >= cap + 1,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return { ok: true, kind: 'xlsx', sheets, truncated: false };
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Edit a COPY of the staged template: `cells` writes exact addresses
|
|
220
|
+
* ("Sheet1!B3" or "B3" for the first sheet), `rows` appends header-keyed
|
|
221
|
+
* objects after the last used row (matched against the header row, so the
|
|
222
|
+
* agent must have read the template first). Styles, formulas and validation
|
|
223
|
+
* survive — only the addressed cells change.
|
|
224
|
+
*/
|
|
225
|
+
async function fillStagedSpreadsheet(opts) {
|
|
226
|
+
const safe = (0, stage_file_1.safeStagedPath)(opts.runId, opts.templatePath);
|
|
227
|
+
if (!safe) {
|
|
228
|
+
return {
|
|
229
|
+
ok: false,
|
|
230
|
+
error: "the template is not in this run's staging directory",
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
if (!SPREADSHEET_EXTS.includes(extOf(safe))) {
|
|
234
|
+
return { ok: false, error: 'only .xlsx templates can be filled' };
|
|
235
|
+
}
|
|
236
|
+
const cells = opts.cells ?? {};
|
|
237
|
+
const rows = Array.isArray(opts.rows) ? opts.rows : [];
|
|
238
|
+
if (Object.keys(cells).length === 0 && rows.length === 0) {
|
|
239
|
+
return { ok: false, error: 'nothing to write — pass cells and/or rows' };
|
|
240
|
+
}
|
|
241
|
+
if (rows.length > exports.QA_XLSX_MAX_ROWS) {
|
|
242
|
+
return {
|
|
243
|
+
ok: false,
|
|
244
|
+
error: `too many rows (${rows.length}, max ${exports.QA_XLSX_MAX_ROWS})`,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
let workbook;
|
|
248
|
+
try {
|
|
249
|
+
workbook = await xlsx_populate_1.default.fromDataAsync((0, node_fs_1.readFileSync)(safe));
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
return { ok: false, error: 'the template is not a readable workbook' };
|
|
253
|
+
}
|
|
254
|
+
const firstName = (() => {
|
|
255
|
+
try {
|
|
256
|
+
return workbook.sheets()[0]?.name();
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
return undefined;
|
|
260
|
+
}
|
|
261
|
+
})();
|
|
262
|
+
// Exact-address writes.
|
|
263
|
+
for (const [addr, value] of Object.entries(cells)) {
|
|
264
|
+
const parsed = parseCellAddress(addr);
|
|
265
|
+
if (!parsed) {
|
|
266
|
+
return {
|
|
267
|
+
ok: false,
|
|
268
|
+
error: `bad cell address "${addr}" — use "Sheet1!B3"`,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
const sheetName = parsed.sheet ?? opts.sheet ?? firstName;
|
|
272
|
+
if (!sheetName) {
|
|
273
|
+
return { ok: false, error: 'the workbook has no sheets' };
|
|
274
|
+
}
|
|
275
|
+
let sheet;
|
|
276
|
+
try {
|
|
277
|
+
sheet = workbook.sheet(sheetName);
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
return { ok: false, error: `sheet "${sheetName}" not found` };
|
|
281
|
+
}
|
|
282
|
+
try {
|
|
283
|
+
sheet.cell(parsed.row, parsed.col).value(value ?? null);
|
|
284
|
+
}
|
|
285
|
+
catch {
|
|
286
|
+
return { ok: false, error: `could not write ${addr}` };
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// Header-keyed row appends.
|
|
290
|
+
if (rows.length > 0) {
|
|
291
|
+
const sheetName = opts.sheet ?? firstName;
|
|
292
|
+
if (!sheetName) {
|
|
293
|
+
return { ok: false, error: 'the workbook has no sheets' };
|
|
294
|
+
}
|
|
295
|
+
let sheet;
|
|
296
|
+
try {
|
|
297
|
+
sheet = workbook.sheet(sheetName);
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
return { ok: false, error: `sheet "${sheetName}" not found` };
|
|
301
|
+
}
|
|
302
|
+
let lastRow = 0;
|
|
303
|
+
try {
|
|
304
|
+
const used = sheet.usedRange();
|
|
305
|
+
if (used)
|
|
306
|
+
lastRow = used.endCell().rowNumber();
|
|
307
|
+
}
|
|
308
|
+
catch {
|
|
309
|
+
lastRow = 0;
|
|
310
|
+
}
|
|
311
|
+
// Header map from the first row (same rule as the reader).
|
|
312
|
+
const headers = [];
|
|
313
|
+
const probeCols = Math.min(exports.QA_XLSX_MAX_COLS, 200);
|
|
314
|
+
for (let c = 1; c <= probeCols; c++) {
|
|
315
|
+
let v = null;
|
|
316
|
+
try {
|
|
317
|
+
v = sheet.cell(1, c).value();
|
|
318
|
+
}
|
|
319
|
+
catch {
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
if (v === null || v === undefined || String(v).trim() === '')
|
|
323
|
+
break;
|
|
324
|
+
headers.push(String(v).trim());
|
|
325
|
+
}
|
|
326
|
+
if (headers.length === 0) {
|
|
327
|
+
return {
|
|
328
|
+
ok: false,
|
|
329
|
+
error: `sheet "${sheetName}" has no header row — write with cells instead`,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
const unknownKeys = new Set();
|
|
333
|
+
rows.forEach((row, i) => {
|
|
334
|
+
const target = lastRow + 1 + i;
|
|
335
|
+
for (const [key, value] of Object.entries(row)) {
|
|
336
|
+
const idx = headers.indexOf(key);
|
|
337
|
+
if (idx === -1) {
|
|
338
|
+
unknownKeys.add(key);
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
try {
|
|
342
|
+
sheet.cell(target, idx + 1).value(value ?? null);
|
|
343
|
+
}
|
|
344
|
+
catch {
|
|
345
|
+
/* one bad cell must not lose the row */
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
if (unknownKeys.size > 0) {
|
|
350
|
+
return {
|
|
351
|
+
ok: false,
|
|
352
|
+
error: `unknown column(s): ${[...unknownKeys].join(', ')} (headers: ${headers.join(', ')})`,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
let buffer;
|
|
357
|
+
try {
|
|
358
|
+
buffer = Buffer.from(await workbook.outputAsync());
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
return { ok: false, error: 'the filled workbook could not be rendered' };
|
|
362
|
+
}
|
|
363
|
+
const staged = await (0, stage_file_1.stageFile)({
|
|
364
|
+
runId: opts.runId,
|
|
365
|
+
fileName: opts.fileName && opts.fileName.trim()
|
|
366
|
+
? opts.fileName.trim()
|
|
367
|
+
: 'filled-template.xlsx',
|
|
368
|
+
buffer,
|
|
369
|
+
});
|
|
370
|
+
if (!staged) {
|
|
371
|
+
return { ok: false, error: 'the filled file could not be staged' };
|
|
372
|
+
}
|
|
373
|
+
return {
|
|
374
|
+
ok: true,
|
|
375
|
+
path: staged.path,
|
|
376
|
+
fileName: staged.path.split(/[\\/]/).pop(),
|
|
377
|
+
bytes: staged.bytes,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
//# sourceMappingURL=xlsx-template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xlsx-template.js","sourceRoot":"","sources":["../../../src/lib/browser/xlsx-template.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the CLI argv that interrupts `sessionId`, or null for an invalid id.
|
|
3
|
+
* Exported as a pure function so the contract can be pinned by a unit test.
|
|
4
|
+
*/
|
|
5
|
+
export declare function buildOpencodeInterruptArgs(sessionId: string): string[] | null;
|
|
6
|
+
/**
|
|
7
|
+
* Interrupt an OpenCode V2 session on the shared service.
|
|
8
|
+
*
|
|
9
|
+
* Best-effort by contract: a missing CLI, a stopped service, a timeout or an
|
|
10
|
+
* idle session all answer `false` and must NEVER throw - a stop path must not
|
|
11
|
+
* fail because the harness could not be reached. The caller logs the result;
|
|
12
|
+
* the relay-token revocation is the independent backstop.
|
|
13
|
+
*/
|
|
14
|
+
export declare function interruptOpencodeSession(sessionId: string, timeoutMs?: number): Promise<boolean>;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildOpencodeInterruptArgs = buildOpencodeInterruptArgs;
|
|
4
|
+
exports.interruptOpencodeSession = interruptOpencodeSession;
|
|
5
|
+
/**
|
|
6
|
+
* OpenCode V2 session control (2026-10).
|
|
7
|
+
*
|
|
8
|
+
* THE BUG THIS EXISTS FOR: V2 is a client/server product. `opencode run` is a
|
|
9
|
+
* CLIENT attached to a shared background service that owns the session, the
|
|
10
|
+
* model stream and all tool execution. `session.stop` used to kill the CLI's
|
|
11
|
+
* process tree only - the service kept executing the turn, kept calling MCP
|
|
12
|
+
* tools through the run's still-valid relay token, and kept driving the debug
|
|
13
|
+
* browser. The human saw "stopped" while the QA run went on.
|
|
14
|
+
*
|
|
15
|
+
* The service exposes `POST /api/session/{sessionID}/interrupt` (verified from
|
|
16
|
+
* its OpenAPI document). The CLI's `api` subcommand carries the service auth,
|
|
17
|
+
* so this is one short-lived process with a hard timeout - never a long-lived
|
|
18
|
+
* connection and never a direct HTTP call that would need the service token.
|
|
19
|
+
*/
|
|
20
|
+
const node_child_process_1 = require("node:child_process");
|
|
21
|
+
const harness_1 = require("./harness");
|
|
22
|
+
/** OpenCode session ids are `ses_` + base62-ish; reject anything else. */
|
|
23
|
+
const OPENCODE_SESSION_ID = /^ses_[A-Za-z0-9]+$/;
|
|
24
|
+
/**
|
|
25
|
+
* Build the CLI argv that interrupts `sessionId`, or null for an invalid id.
|
|
26
|
+
* Exported as a pure function so the contract can be pinned by a unit test.
|
|
27
|
+
*/
|
|
28
|
+
function buildOpencodeInterruptArgs(sessionId) {
|
|
29
|
+
if (!OPENCODE_SESSION_ID.test(sessionId))
|
|
30
|
+
return null;
|
|
31
|
+
return ['api', 'POST', `/api/session/${sessionId}/interrupt`];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Interrupt an OpenCode V2 session on the shared service.
|
|
35
|
+
*
|
|
36
|
+
* Best-effort by contract: a missing CLI, a stopped service, a timeout or an
|
|
37
|
+
* idle session all answer `false` and must NEVER throw - a stop path must not
|
|
38
|
+
* fail because the harness could not be reached. The caller logs the result;
|
|
39
|
+
* the relay-token revocation is the independent backstop.
|
|
40
|
+
*/
|
|
41
|
+
function interruptOpencodeSession(sessionId, timeoutMs = 15_000) {
|
|
42
|
+
const args = buildOpencodeInterruptArgs(sessionId);
|
|
43
|
+
if (!args)
|
|
44
|
+
return Promise.resolve(false);
|
|
45
|
+
const bin = (0, harness_1.resolveBin)('opencode');
|
|
46
|
+
if (!bin)
|
|
47
|
+
return Promise.resolve(false);
|
|
48
|
+
return new Promise((resolve) => {
|
|
49
|
+
try {
|
|
50
|
+
(0, node_child_process_1.execFile)(bin.file, [...(bin.args ?? []), ...args], { timeout: timeoutMs, windowsHide: true, maxBuffer: 1024 * 1024 }, (err, stdout) => {
|
|
51
|
+
if (err) {
|
|
52
|
+
resolve(false);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
resolve(/"interrupted"\s*:\s*true/.test(String(stdout ?? '')));
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
resolve(false);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=opencode-control.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode-control.js","sourceRoot":"","sources":["../../../src/lib/harness/opencode-control.ts"],"names":[],"mappings":";;AAyBA,gEAGC;AAUD,4DA0BC;AAhED;;;;;;;;;;;;;;GAcG;AACH,2DAA8C;AAC9C,uCAAuC;AAEvC,0EAA0E;AAC1E,MAAM,mBAAmB,GAAG,oBAAoB,CAAC;AAEjD;;;GAGG;AACH,SAAgB,0BAA0B,CAAC,SAAiB;IAC1D,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IACtD,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,SAAS,YAAY,CAAC,CAAC;AAChE,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,wBAAwB,CACtC,SAAiB,EACjB,SAAS,GAAG,MAAM;IAElB,MAAM,IAAI,GAAG,0BAA0B,CAAC,SAAS,CAAC,CAAC;IACnD,IAAI,CAAC,IAAI;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACzC,MAAM,GAAG,GAAG,IAAA,oBAAU,EAAC,UAAU,CAAC,CAAC;IACnC,IAAI,CAAC,GAAG;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACxC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,CAAC;YACH,IAAA,6BAAQ,EACN,GAAG,CAAC,IAAI,EACR,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,EAC9B,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,GAAG,IAAI,EAAE,EACjE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE;gBACd,IAAI,GAAG,EAAE,CAAC;oBACR,OAAO,CAAC,KAAK,CAAC,CAAC;oBACf,OAAO;gBACT,CAAC;gBACD,OAAO,CAAC,0BAA0B,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YACjE,CAAC,CACF,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -32,3 +32,36 @@ export declare function saveHarnessSession(rec: HarnessSessionRecord): void;
|
|
|
32
32
|
export declare function clearHarnessSession(agentId: string, taskId: string, mode: string): void;
|
|
33
33
|
/** True when a record exists (used to decide whether resume is even possible). */
|
|
34
34
|
export declare function hasHarnessSession(agentId: string, taskId: string, mode: string): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Live harness-session record (2026-10).
|
|
37
|
+
*
|
|
38
|
+
* The resume store above is written only when a run FINISHES. A forced stop
|
|
39
|
+
* kills the CLI before that, so the daemon had no way to learn the
|
|
40
|
+
* harness-native session id of the run it just killed - and on OpenCode V2 the
|
|
41
|
+
* shared service keeps executing that session after the CLI dies.
|
|
42
|
+
*
|
|
43
|
+
* This record is written the moment the harness session id is captured, keyed
|
|
44
|
+
* by the BRIDGE session id (the `--session` value the daemon knows). On a
|
|
45
|
+
* forced stop the daemon reads it, interrupts the harness session, and only
|
|
46
|
+
* then removes the run scope. A clean exit removes it in `cleanMcp`.
|
|
47
|
+
*/
|
|
48
|
+
export interface LiveHarnessSessionRecord {
|
|
49
|
+
bridgeSessionId: string;
|
|
50
|
+
harness: string;
|
|
51
|
+
/** Harness-native session id (opencode `ses_...`, claude/codex id, ...). */
|
|
52
|
+
id: string;
|
|
53
|
+
/** Working directory the harness actually ran in (the run scope when one was created). */
|
|
54
|
+
cwd: string;
|
|
55
|
+
/** `<projectRoot>/.teamshare-runs/<bridgeSessionId>` when a run scope was used. */
|
|
56
|
+
scopeDir: string | null;
|
|
57
|
+
agentId: string | null;
|
|
58
|
+
taskId: string | null;
|
|
59
|
+
mode: string | null;
|
|
60
|
+
createdAt: string;
|
|
61
|
+
}
|
|
62
|
+
/** Persist the live harness id (best-effort; never throws). */
|
|
63
|
+
export declare function saveLiveHarnessSession(rec: LiveHarnessSessionRecord): void;
|
|
64
|
+
/** Load the live harness id for a bridge session, or null. */
|
|
65
|
+
export declare function loadLiveHarnessSession(bridgeSessionId: string): LiveHarnessSessionRecord | null;
|
|
66
|
+
/** Drop the live harness id (clean exit, or after a successful stop). */
|
|
67
|
+
export declare function clearLiveHarnessSession(bridgeSessionId: string): void;
|
|
@@ -4,6 +4,9 @@ exports.loadHarnessSession = loadHarnessSession;
|
|
|
4
4
|
exports.saveHarnessSession = saveHarnessSession;
|
|
5
5
|
exports.clearHarnessSession = clearHarnessSession;
|
|
6
6
|
exports.hasHarnessSession = hasHarnessSession;
|
|
7
|
+
exports.saveLiveHarnessSession = saveLiveHarnessSession;
|
|
8
|
+
exports.loadLiveHarnessSession = loadLiveHarnessSession;
|
|
9
|
+
exports.clearLiveHarnessSession = clearLiveHarnessSession;
|
|
7
10
|
/**
|
|
8
11
|
* Machine-local harness session store.
|
|
9
12
|
*
|
|
@@ -19,6 +22,7 @@ const node_fs_1 = require("node:fs");
|
|
|
19
22
|
const node_os_1 = require("node:os");
|
|
20
23
|
const node_path_1 = require("node:path");
|
|
21
24
|
const STORE_DIR = (0, node_path_1.join)((0, node_os_1.homedir)(), '.teamshare', 'harness-sessions');
|
|
25
|
+
const SESSIONS_DIR = (0, node_path_1.join)((0, node_os_1.homedir)(), '.teamshare', 'sessions');
|
|
22
26
|
function recordPath(agentId, taskId, mode) {
|
|
23
27
|
return (0, node_path_1.join)(STORE_DIR, agentId, `${taskId}.${mode}.json`);
|
|
24
28
|
}
|
|
@@ -77,6 +81,40 @@ function clearHarnessSession(agentId, taskId, mode) {
|
|
|
77
81
|
function hasHarnessSession(agentId, taskId, mode) {
|
|
78
82
|
return (0, node_fs_1.existsSync)(recordPath(agentId, taskId, mode));
|
|
79
83
|
}
|
|
84
|
+
function liveRecordPath(bridgeSessionId) {
|
|
85
|
+
const safe = bridgeSessionId.replace(/[^a-zA-Z0-9._-]/g, '_').slice(0, 80);
|
|
86
|
+
return (0, node_path_1.join)(SESSIONS_DIR, `harness-${safe}.json`);
|
|
87
|
+
}
|
|
88
|
+
/** Persist the live harness id (best-effort; never throws). */
|
|
89
|
+
function saveLiveHarnessSession(rec) {
|
|
90
|
+
try {
|
|
91
|
+
(0, node_fs_1.mkdirSync)(SESSIONS_DIR, { recursive: true });
|
|
92
|
+
(0, node_fs_1.writeFileSync)(liveRecordPath(rec.bridgeSessionId), JSON.stringify(rec, null, 2), 'utf8');
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
/* best-effort: a stop must still work without this file */
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/** Load the live harness id for a bridge session, or null. */
|
|
99
|
+
function loadLiveHarnessSession(bridgeSessionId) {
|
|
100
|
+
try {
|
|
101
|
+
const raw = (0, node_fs_1.readFileSync)(liveRecordPath(bridgeSessionId), 'utf8');
|
|
102
|
+
const rec = JSON.parse(raw);
|
|
103
|
+
return rec?.id ? rec : null;
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/** Drop the live harness id (clean exit, or after a successful stop). */
|
|
110
|
+
function clearLiveHarnessSession(bridgeSessionId) {
|
|
111
|
+
try {
|
|
112
|
+
(0, node_fs_1.rmSync)(liveRecordPath(bridgeSessionId), { force: true });
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
/* best-effort */
|
|
116
|
+
}
|
|
117
|
+
}
|
|
80
118
|
function normalize(p) {
|
|
81
119
|
return p.replace(/\\/g, '/').replace(/\/+$/, '').toLowerCase();
|
|
82
120
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-store.js","sourceRoot":"","sources":["../../../src/lib/harness/session-store.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"session-store.js","sourceRoot":"","sources":["../../../src/lib/harness/session-store.ts"],"names":[],"mappings":"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"}
|