@evident-ai/cli 3.5.1-dev.bf9e537 → 3.5.2-dev.2828873
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/index.js +282 -77
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1063,7 +1063,7 @@ async function claudeUsage() {
|
|
|
1063
1063
|
|
|
1064
1064
|
// src/commands/run.ts
|
|
1065
1065
|
import { chmodSync as chmodSync3, existsSync as existsSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "node:fs";
|
|
1066
|
-
import { homedir as
|
|
1066
|
+
import { homedir as homedir7 } from "node:os";
|
|
1067
1067
|
import { isAbsolute as isAbsolute3, join as join11, parse, resolve as resolvePath2 } from "node:path";
|
|
1068
1068
|
import chalk7 from "chalk";
|
|
1069
1069
|
|
|
@@ -1158,7 +1158,7 @@ import ora4 from "ora";
|
|
|
1158
1158
|
import { select as select4 } from "@inquirer/prompts";
|
|
1159
1159
|
|
|
1160
1160
|
// src/lib/telemetry.ts
|
|
1161
|
-
var CLI_VERSION = (true ? "3.5.
|
|
1161
|
+
var CLI_VERSION = (true ? "3.5.2-dev.2828873" : void 0) ?? process.env.npm_package_version ?? "unknown";
|
|
1162
1162
|
function getCliVersion() {
|
|
1163
1163
|
return CLI_VERSION;
|
|
1164
1164
|
}
|
|
@@ -1172,6 +1172,23 @@ var authProvider = null;
|
|
|
1172
1172
|
function setTelemetryAuthProvider(provider) {
|
|
1173
1173
|
authProvider = provider;
|
|
1174
1174
|
}
|
|
1175
|
+
var diagnosticsReporter = null;
|
|
1176
|
+
function setTelemetryDiagnosticsReporter(reporter) {
|
|
1177
|
+
diagnosticsReporter = reporter;
|
|
1178
|
+
}
|
|
1179
|
+
function reportTelemetryDiagnostic(message) {
|
|
1180
|
+
if (!diagnosticsReporter) {
|
|
1181
|
+
console.error(message);
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
try {
|
|
1185
|
+
diagnosticsReporter(message);
|
|
1186
|
+
} catch (error2) {
|
|
1187
|
+
console.error(
|
|
1188
|
+
`Telemetry diagnostics reporter failed: ${error2 instanceof Error ? error2.message : String(error2)}; original diagnostic: ${message}`
|
|
1189
|
+
);
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1175
1192
|
var FLUSH_FAILURE_LOG_INTERVAL_MS = 6e4;
|
|
1176
1193
|
var lastFlushFailureLoggedAt = 0;
|
|
1177
1194
|
var suppressedFlushFailureCount = 0;
|
|
@@ -1237,7 +1254,9 @@ async function flushEvents() {
|
|
|
1237
1254
|
signal: controller.signal
|
|
1238
1255
|
});
|
|
1239
1256
|
if (!response.ok) {
|
|
1240
|
-
|
|
1257
|
+
reportTelemetryDiagnostic(
|
|
1258
|
+
`Telemetry could not send events (HTTP ${response.status}); runner and OpenCode are unaffected`
|
|
1259
|
+
);
|
|
1241
1260
|
}
|
|
1242
1261
|
} finally {
|
|
1243
1262
|
clearTimeout(timeout);
|
|
@@ -1247,7 +1266,9 @@ async function flushEvents() {
|
|
|
1247
1266
|
if (now - lastFlushFailureLoggedAt >= FLUSH_FAILURE_LOG_INTERVAL_MS) {
|
|
1248
1267
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
1249
1268
|
const suffix = suppressedFlushFailureCount > 0 ? ` (${suppressedFlushFailureCount} more suppressed in the last ${FLUSH_FAILURE_LOG_INTERVAL_MS / 1e3}s)` : "";
|
|
1250
|
-
|
|
1269
|
+
reportTelemetryDiagnostic(
|
|
1270
|
+
`Telemetry flush failed: ${message}; runner and OpenCode are unaffected${suffix}`
|
|
1271
|
+
);
|
|
1251
1272
|
lastFlushFailureLoggedAt = now;
|
|
1252
1273
|
suppressedFlushFailureCount = 0;
|
|
1253
1274
|
} else {
|
|
@@ -1299,8 +1320,14 @@ var EventTypes = {
|
|
|
1299
1320
|
};
|
|
1300
1321
|
|
|
1301
1322
|
// src/lib/runner-activity-telemetry.ts
|
|
1302
|
-
|
|
1323
|
+
import { homedir as homedir2 } from "node:os";
|
|
1324
|
+
var FORWARDED_LEVELS_BY_SOURCE = {
|
|
1325
|
+
"cli.run": /* @__PURE__ */ new Set(["warn", "error"]),
|
|
1326
|
+
opencode: /* @__PURE__ */ new Set(["warn", "error"]),
|
|
1327
|
+
"cli.session-cleanup": /* @__PURE__ */ new Set(["info", "warn", "error"])
|
|
1328
|
+
};
|
|
1303
1329
|
var SEVERITY_BY_LEVEL = {
|
|
1330
|
+
info: "info",
|
|
1304
1331
|
warn: "warning",
|
|
1305
1332
|
error: "error"
|
|
1306
1333
|
};
|
|
@@ -1311,25 +1338,72 @@ function parseOpenCodeLogLine(line) {
|
|
|
1311
1338
|
const level = levelMatch[1].toUpperCase();
|
|
1312
1339
|
if (level !== "WARN" && level !== "ERROR") return null;
|
|
1313
1340
|
const sessionMatch = normalisedLine.match(/(?:^|\s)sessionID=(\S+)/);
|
|
1314
|
-
|
|
1341
|
+
const messageMatch = normalisedLine.match(/(?:^|\s)message=(?:"((?:\\.|[^"])*)"|(\S+))/);
|
|
1342
|
+
const parsed = {
|
|
1343
|
+
level: level === "WARN" ? "warn" : "error",
|
|
1344
|
+
sessionID: sessionMatch?.[1]
|
|
1345
|
+
};
|
|
1346
|
+
const message = messageMatch?.[1] ?? messageMatch?.[2];
|
|
1347
|
+
if (message !== void 0) parsed.message = message;
|
|
1348
|
+
return parsed;
|
|
1315
1349
|
}
|
|
1316
1350
|
var MAX_LINE_BUFFER_BYTES = 16 * 1024;
|
|
1351
|
+
var REPEAT_SUPPRESSION_WINDOW_MS = 6e4;
|
|
1317
1352
|
function createOpenCodeActivityForwarder(getContext) {
|
|
1318
1353
|
let buffer = Buffer.alloc(0);
|
|
1319
|
-
const
|
|
1320
|
-
|
|
1321
|
-
|
|
1354
|
+
const repeatStates = /* @__PURE__ */ new Map();
|
|
1355
|
+
const forwardLine = (line, parsed, repeatsSuppressed = 0) => {
|
|
1356
|
+
const metadata = {
|
|
1357
|
+
...parsed.sessionID ? { sessionID: parsed.sessionID } : {},
|
|
1358
|
+
...repeatsSuppressed > 0 ? { repeats_suppressed: repeatsSuppressed } : {}
|
|
1359
|
+
};
|
|
1322
1360
|
forwardRunnerActivity(
|
|
1323
1361
|
{
|
|
1324
1362
|
level: parsed.level,
|
|
1325
1363
|
error: line,
|
|
1326
|
-
metadata:
|
|
1364
|
+
metadata: Object.keys(metadata).length > 0 ? metadata : void 0,
|
|
1327
1365
|
source: "opencode"
|
|
1328
1366
|
},
|
|
1329
1367
|
getContext()
|
|
1330
1368
|
);
|
|
1331
1369
|
};
|
|
1332
|
-
|
|
1370
|
+
const expireRepeatStates = (now, currentKey) => {
|
|
1371
|
+
for (const [key, state] of repeatStates) {
|
|
1372
|
+
if (key === currentKey || now - state.lastForwardedAt < REPEAT_SUPPRESSION_WINDOW_MS) {
|
|
1373
|
+
continue;
|
|
1374
|
+
}
|
|
1375
|
+
if (state.repeatsSuppressed > 0) {
|
|
1376
|
+
forwardLine(state.line, state.parsed, state.repeatsSuppressed);
|
|
1377
|
+
}
|
|
1378
|
+
repeatStates.delete(key);
|
|
1379
|
+
}
|
|
1380
|
+
};
|
|
1381
|
+
const flushLine = (line) => {
|
|
1382
|
+
const parsed = parseOpenCodeLogLine(line);
|
|
1383
|
+
if (!parsed) return;
|
|
1384
|
+
const now = Date.now();
|
|
1385
|
+
const key = parsed.message === void 0 ? void 0 : `${parsed.level}\0${parsed.message}`;
|
|
1386
|
+
expireRepeatStates(now, key);
|
|
1387
|
+
if (key === void 0) {
|
|
1388
|
+
forwardLine(line, parsed);
|
|
1389
|
+
return;
|
|
1390
|
+
}
|
|
1391
|
+
const previous = repeatStates.get(key);
|
|
1392
|
+
if (!previous || now - previous.lastForwardedAt >= REPEAT_SUPPRESSION_WINDOW_MS) {
|
|
1393
|
+
forwardLine(line, parsed, previous?.repeatsSuppressed ?? 0);
|
|
1394
|
+
repeatStates.set(key, {
|
|
1395
|
+
line,
|
|
1396
|
+
parsed,
|
|
1397
|
+
lastForwardedAt: now,
|
|
1398
|
+
repeatsSuppressed: 0
|
|
1399
|
+
});
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1402
|
+
previous.line = line;
|
|
1403
|
+
previous.parsed = parsed;
|
|
1404
|
+
previous.repeatsSuppressed++;
|
|
1405
|
+
};
|
|
1406
|
+
const forward = ((chunk) => {
|
|
1333
1407
|
buffer = Buffer.concat([buffer, Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, "utf-8")]);
|
|
1334
1408
|
let newlineIndex;
|
|
1335
1409
|
while ((newlineIndex = buffer.indexOf(10)) !== -1) {
|
|
@@ -1340,14 +1414,44 @@ function createOpenCodeActivityForwarder(getContext) {
|
|
|
1340
1414
|
flushLine(buffer.toString("utf-8"));
|
|
1341
1415
|
buffer = Buffer.alloc(0);
|
|
1342
1416
|
}
|
|
1417
|
+
});
|
|
1418
|
+
forward.flush = () => {
|
|
1419
|
+
for (const state of repeatStates.values()) {
|
|
1420
|
+
if (state.repeatsSuppressed > 0) {
|
|
1421
|
+
forwardLine(state.line, state.parsed, state.repeatsSuppressed);
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
repeatStates.clear();
|
|
1343
1425
|
};
|
|
1426
|
+
return forward;
|
|
1344
1427
|
}
|
|
1345
1428
|
var MAX_MESSAGE_LENGTH = 500;
|
|
1346
1429
|
var MAX_METADATA_VALUE_LENGTH = 200;
|
|
1347
1430
|
var MAX_METADATA_ENTRIES = 20;
|
|
1348
1431
|
var TRUNCATION_MARKER = "\u2026";
|
|
1432
|
+
function redactHomeDirectory(message) {
|
|
1433
|
+
const home = homedir2();
|
|
1434
|
+
if (!home || home === "/") {
|
|
1435
|
+
return message;
|
|
1436
|
+
}
|
|
1437
|
+
const pathSegmentCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-";
|
|
1438
|
+
let result = "";
|
|
1439
|
+
let searchFrom = 0;
|
|
1440
|
+
let homeIndex;
|
|
1441
|
+
while ((homeIndex = message.indexOf(home, searchFrom)) !== -1) {
|
|
1442
|
+
const homeEnd = homeIndex + home.length;
|
|
1443
|
+
const previousCharacter = message[homeIndex - 1];
|
|
1444
|
+
const nextCharacter = message[homeEnd];
|
|
1445
|
+
const atPathBoundary = home === "/" || (!previousCharacter || !pathSegmentCharacters.includes(previousCharacter)) && (!nextCharacter || !pathSegmentCharacters.includes(nextCharacter));
|
|
1446
|
+
result += message.slice(searchFrom, homeIndex);
|
|
1447
|
+
result += atPathBoundary ? "~" : home;
|
|
1448
|
+
searchFrom = homeEnd;
|
|
1449
|
+
}
|
|
1450
|
+
return result + message.slice(searchFrom);
|
|
1451
|
+
}
|
|
1349
1452
|
function redact(message) {
|
|
1350
|
-
|
|
1453
|
+
const redacted = message.replace(/esk_[A-Za-z0-9_-]+/g, "esk_***").replace(/ct_[A-Za-z0-9_-]+/g, "ct_***").replace(/(?<![A-Za-z0-9_-])sk-ant-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g, "sk-ant-***").replace(/(?<![A-Za-z0-9_-])sk-proj-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g, "sk-proj-***").replace(/(?<![A-Za-z0-9_-])sk-[A-Za-z0-9]{20,}(?![A-Za-z0-9_-])/g, "sk-***").replace(/(?<![A-Za-z0-9_-])(gh[oprsu])_[A-Za-z0-9]{20,}(?![A-Za-z0-9_-])/g, "$1_***").replace(/(?<![A-Za-z0-9_-])github_pat_[A-Za-z0-9_]{20,}(?![A-Za-z0-9_-])/g, "github_pat_***").replace(/https?:\/\/\S+/g, "<url>");
|
|
1454
|
+
return redactHomeDirectory(redacted);
|
|
1351
1455
|
}
|
|
1352
1456
|
function truncate(message) {
|
|
1353
1457
|
if (message.length <= MAX_MESSAGE_LENGTH) return message;
|
|
@@ -1357,8 +1461,8 @@ function sanitiseMetadata(metadata) {
|
|
|
1357
1461
|
if (!metadata) return {};
|
|
1358
1462
|
const entries = Object.entries(metadata).slice(0, MAX_METADATA_ENTRIES);
|
|
1359
1463
|
if (Object.keys(metadata).length > entries.length) {
|
|
1360
|
-
|
|
1361
|
-
`
|
|
1464
|
+
reportTelemetryDiagnostic(
|
|
1465
|
+
`Runner activity telemetry dropped ${Object.keys(metadata).length - entries.length} metadata entries; those entries were not sent`
|
|
1362
1466
|
);
|
|
1363
1467
|
}
|
|
1364
1468
|
const sanitised = [];
|
|
@@ -1382,8 +1486,8 @@ function admitUnderRateLimit(source, now) {
|
|
|
1382
1486
|
}
|
|
1383
1487
|
if (now - window.windowStartedAt >= RATE_LIMIT_WINDOW_MS) {
|
|
1384
1488
|
if (window.windowDroppedCount > 0) {
|
|
1385
|
-
|
|
1386
|
-
`
|
|
1489
|
+
reportTelemetryDiagnostic(
|
|
1490
|
+
`Runner activity telemetry rate cap reached: dropped ${window.windowDroppedCount} ${window.windowDroppedCount === 1 ? "entry" : "entries"} in the last ${RATE_LIMIT_WINDOW_MS / 1e3}s (limit ${RATE_LIMIT_MAX_EVENTS}/min); those entries were not sent for source "${source}"`
|
|
1387
1491
|
);
|
|
1388
1492
|
}
|
|
1389
1493
|
window.windowStartedAt = now;
|
|
@@ -1393,8 +1497,8 @@ function admitUnderRateLimit(source, now) {
|
|
|
1393
1497
|
if (window.windowCount >= RATE_LIMIT_MAX_EVENTS) {
|
|
1394
1498
|
window.windowDroppedCount++;
|
|
1395
1499
|
if (window.windowDroppedCount === 1) {
|
|
1396
|
-
|
|
1397
|
-
`
|
|
1500
|
+
reportTelemetryDiagnostic(
|
|
1501
|
+
`Runner activity telemetry rate cap reached (${RATE_LIMIT_MAX_EVENTS} entries per ${RATE_LIMIT_WINDOW_MS / 1e3}s limit; further entries were not sent for source "${source}"`
|
|
1398
1502
|
);
|
|
1399
1503
|
}
|
|
1400
1504
|
return false;
|
|
@@ -1404,9 +1508,9 @@ function admitUnderRateLimit(source, now) {
|
|
|
1404
1508
|
}
|
|
1405
1509
|
function forwardRunnerActivity(entry, context) {
|
|
1406
1510
|
try {
|
|
1407
|
-
if (!FORWARDED_LEVELS.has(entry.level)) return;
|
|
1408
|
-
if (!context.agentId || !context.authHeader) return;
|
|
1409
1511
|
const source = entry.source ?? "cli.run";
|
|
1512
|
+
if (!FORWARDED_LEVELS_BY_SOURCE[source].has(entry.level)) return;
|
|
1513
|
+
if (!context.agentId || !context.authHeader) return;
|
|
1410
1514
|
if (!admitUnderRateLimit(source, Date.now())) return;
|
|
1411
1515
|
const rawMessage = entry.error ?? entry.message ?? "";
|
|
1412
1516
|
const message = truncate(redact(rawMessage));
|
|
@@ -1417,8 +1521,8 @@ function forwardRunnerActivity(entry, context) {
|
|
|
1417
1521
|
agentId: context.agentId
|
|
1418
1522
|
});
|
|
1419
1523
|
} catch (err) {
|
|
1420
|
-
|
|
1421
|
-
`
|
|
1524
|
+
reportTelemetryDiagnostic(
|
|
1525
|
+
`Runner activity telemetry failed to forward runner activity: ${err instanceof Error ? err.message : String(err)}`
|
|
1422
1526
|
);
|
|
1423
1527
|
}
|
|
1424
1528
|
}
|
|
@@ -1620,6 +1724,9 @@ async function checkOpenCodeHealth(port) {
|
|
|
1620
1724
|
signal: AbortSignal.timeout(2e3)
|
|
1621
1725
|
// 2 second timeout
|
|
1622
1726
|
});
|
|
1727
|
+
if (response.status === 401) {
|
|
1728
|
+
return { healthy: false, authFailed: true, error: "HTTP 401" };
|
|
1729
|
+
}
|
|
1623
1730
|
if (!response.ok) {
|
|
1624
1731
|
return { healthy: false, error: `HTTP ${response.status}` };
|
|
1625
1732
|
}
|
|
@@ -1684,7 +1791,7 @@ async function waitForOpenCodeHealth(port, timeoutMs = 3e4) {
|
|
|
1684
1791
|
const startTime = Date.now();
|
|
1685
1792
|
while (Date.now() - startTime < timeoutMs) {
|
|
1686
1793
|
const health = await checkOpenCodeHealth(port);
|
|
1687
|
-
if (health.healthy) {
|
|
1794
|
+
if (health.healthy || health.authFailed) {
|
|
1688
1795
|
return health;
|
|
1689
1796
|
}
|
|
1690
1797
|
await new Promise((resolve4) => setTimeout(resolve4, 1e3));
|
|
@@ -3211,7 +3318,7 @@ import { join as join4 } from "node:path";
|
|
|
3211
3318
|
|
|
3212
3319
|
// src/lib/openai-usage.ts
|
|
3213
3320
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
3214
|
-
import { homedir as
|
|
3321
|
+
import { homedir as homedir3 } from "node:os";
|
|
3215
3322
|
import { join as join3 } from "node:path";
|
|
3216
3323
|
var CODEX_RESPONSES_URL = "https://chatgpt.com/backend-api/codex/responses";
|
|
3217
3324
|
var OPENCODE_AUTH_SEGMENTS = [".local", "share", "opencode", "auth.json"];
|
|
@@ -3226,7 +3333,7 @@ function isLocalCredentialProblem2(err) {
|
|
|
3226
3333
|
}
|
|
3227
3334
|
function readOpenCodeChatGptCredentials() {
|
|
3228
3335
|
try {
|
|
3229
|
-
const raw = readFileSync3(join3(
|
|
3336
|
+
const raw = readFileSync3(join3(homedir3(), ...OPENCODE_AUTH_SEGMENTS), "utf-8");
|
|
3230
3337
|
let parsed;
|
|
3231
3338
|
try {
|
|
3232
3339
|
parsed = JSON.parse(raw);
|
|
@@ -3367,16 +3474,17 @@ function hasPrimaryHeaders(headers) {
|
|
|
3367
3474
|
].some((name) => headers.has(name));
|
|
3368
3475
|
}
|
|
3369
3476
|
async function getOpenAiUsage(port, client) {
|
|
3477
|
+
const openCodeVersion = client?.version ?? "v1";
|
|
3370
3478
|
const credentials2 = readOpenCodeChatGptCredentials();
|
|
3371
3479
|
if (!credentials2) {
|
|
3372
3480
|
throw new OpenAiUsageError(
|
|
3373
|
-
"No ChatGPT login found. Connect a ChatGPT account to this runner, or run `opencode auth login`.",
|
|
3481
|
+
openCodeVersion === "v2" ? "OpenAI usage reporting is not available on OpenCode V2 because V2 keeps a connected ChatGPT login in a store Evident cannot read." : "No ChatGPT login found. Connect a ChatGPT account to this runner, or run `opencode auth login`.",
|
|
3374
3482
|
"no_credentials"
|
|
3375
3483
|
);
|
|
3376
3484
|
}
|
|
3377
3485
|
if (credentials2.expiresAt < Date.now()) {
|
|
3378
3486
|
throw new OpenAiUsageError(
|
|
3379
|
-
"ChatGPT credentials have expired. Run `opencode auth login` to refresh them.",
|
|
3487
|
+
openCodeVersion === "v2" ? "The delivered ChatGPT login has expired. OpenCode V2 cannot be given a replacement by file; connect the account again from the runner's page in Evident." : "ChatGPT credentials have expired. Run `opencode auth login` to refresh them.",
|
|
3380
3488
|
"credentials_expired"
|
|
3381
3489
|
);
|
|
3382
3490
|
}
|
|
@@ -3658,7 +3766,7 @@ async function importOpenCodeV2Credentials({
|
|
|
3658
3766
|
// src/lib/opencode/session-db-boot.ts
|
|
3659
3767
|
import { spawn as spawn2 } from "node:child_process";
|
|
3660
3768
|
import { mkdirSync, statSync as statSync2, unlinkSync as unlinkSync2, writeFileSync } from "node:fs";
|
|
3661
|
-
import { homedir as
|
|
3769
|
+
import { homedir as homedir4 } from "node:os";
|
|
3662
3770
|
import { dirname as dirname2, resolve as resolvePath } from "node:path";
|
|
3663
3771
|
|
|
3664
3772
|
// src/lib/runner-synchroniser.ts
|
|
@@ -4014,14 +4122,14 @@ async function restoreSessionDb(options, configPath, env) {
|
|
|
4014
4122
|
case 0:
|
|
4015
4123
|
return;
|
|
4016
4124
|
case 31:
|
|
4017
|
-
acknowledgeSessionDbRecoveryReport(sessionDbRecoveryReportPath(
|
|
4125
|
+
acknowledgeSessionDbRecoveryReport(sessionDbRecoveryReportPath(homedir4(), env));
|
|
4018
4126
|
options.log(
|
|
4019
4127
|
"SESSION-DB-REPLICA-UNUSABLE: booting with a fresh opencode.db and replicating into the existing prefix this boot",
|
|
4020
4128
|
"warn"
|
|
4021
4129
|
);
|
|
4022
4130
|
return;
|
|
4023
4131
|
case 32:
|
|
4024
|
-
acknowledgeSessionDbRecoveryReport(sessionDbRecoveryReportPath(
|
|
4132
|
+
acknowledgeSessionDbRecoveryReport(sessionDbRecoveryReportPath(homedir4(), env));
|
|
4025
4133
|
discardSessionDbDebris(options);
|
|
4026
4134
|
markNoReplicate(
|
|
4027
4135
|
options,
|
|
@@ -4110,7 +4218,7 @@ function fileExists(path) {
|
|
|
4110
4218
|
async function restoreAndVerifySessionDb(options) {
|
|
4111
4219
|
const env = options.env ?? process.env;
|
|
4112
4220
|
clearMarker(options);
|
|
4113
|
-
acknowledgeSessionDbRecoveryReport(sessionDbRecoveryReportPath(
|
|
4221
|
+
acknowledgeSessionDbRecoveryReport(sessionDbRecoveryReportPath(homedir4(), env));
|
|
4114
4222
|
const synchroniserEnv = await runSynchroniser(["env"], {
|
|
4115
4223
|
timeoutMs: SESSION_DB_SYNCHRONISER_TIMEOUT_MS,
|
|
4116
4224
|
env
|
|
@@ -4311,6 +4419,9 @@ function isSessionDbProvenanceRecord(value) {
|
|
|
4311
4419
|
return typeof record.opencodeVersion === "string" && Array.isArray(record.migrationIds) && record.migrationIds.every((id) => typeof id === "string") && typeof record.updatedAt === "string";
|
|
4312
4420
|
}
|
|
4313
4421
|
|
|
4422
|
+
// src/lib/opencode/opencode2-package.ts
|
|
4423
|
+
var OPENCODE2_PACKAGE_SPEC = "@opencode-ai/cli@0.0.0-beta-17823";
|
|
4424
|
+
|
|
4314
4425
|
// src/lib/opencode/opencode-version-gate.ts
|
|
4315
4426
|
var QUEUE_VALIDATED_OPENCODE_VERSIONS = ["1.17.11", "1.18.3"];
|
|
4316
4427
|
function isQueueValidatedVersion(version2) {
|
|
@@ -4487,12 +4598,15 @@ function findProcessesByPattern(pgrepPattern, psPattern) {
|
|
|
4487
4598
|
}
|
|
4488
4599
|
} catch {
|
|
4489
4600
|
try {
|
|
4490
|
-
const psOutput = execSync(
|
|
4601
|
+
const psOutput = execSync("ps aux", {
|
|
4491
4602
|
encoding: "utf-8",
|
|
4492
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
4603
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
4604
|
+
// The whole table reaches Node; the 1 MiB default would surface a spurious failure.
|
|
4605
|
+
maxBuffer: 16 * 1024 * 1024
|
|
4493
4606
|
}).trim();
|
|
4494
4607
|
if (psOutput) {
|
|
4495
4608
|
for (const line of psOutput.split("\n")) {
|
|
4609
|
+
if (!psPattern.test(line)) continue;
|
|
4496
4610
|
const parts = line.trim().split(/\s+/);
|
|
4497
4611
|
if (parts.length >= 2) {
|
|
4498
4612
|
const pid = parseInt(parts[1], 10);
|
|
@@ -4501,8 +4615,9 @@ function findProcessesByPattern(pgrepPattern, psPattern) {
|
|
|
4501
4615
|
}
|
|
4502
4616
|
}
|
|
4503
4617
|
} catch (err) {
|
|
4618
|
+
const reason = (typeof err === "object" && err !== null && "stderr" in err && typeof err.stderr === "string" ? err.stderr : "").split("\n").map((line) => line.trim()).find(Boolean) ?? (err instanceof Error ? err.message : String(err)).split("\n").map((line) => line.trim()).find(Boolean) ?? "no error detail available";
|
|
4504
4619
|
console.warn(
|
|
4505
|
-
`
|
|
4620
|
+
`Could not list running processes, so an already-running OpenCode may not be detected: ${reason}`
|
|
4506
4621
|
);
|
|
4507
4622
|
}
|
|
4508
4623
|
}
|
|
@@ -4534,7 +4649,7 @@ function findProcessesByPattern(pgrepPattern, psPattern) {
|
|
|
4534
4649
|
return instances;
|
|
4535
4650
|
}
|
|
4536
4651
|
function findOpenCodeProcesses() {
|
|
4537
|
-
return findProcessesByPattern("opencode serve|opencode-serve",
|
|
4652
|
+
return findProcessesByPattern("opencode serve|opencode-serve", /opencode (serve|--port)/);
|
|
4538
4653
|
}
|
|
4539
4654
|
async function scanPortsForOpenCode() {
|
|
4540
4655
|
const instances = [];
|
|
@@ -4601,7 +4716,8 @@ async function startOpenCode(port, options = {}) {
|
|
|
4601
4716
|
const child = spawn3(command, args, {
|
|
4602
4717
|
detached: true,
|
|
4603
4718
|
stdio: options.inheritStdio ? "inherit" : "ignore",
|
|
4604
|
-
cwd: process.cwd()
|
|
4719
|
+
cwd: process.cwd(),
|
|
4720
|
+
env: { ...process.env, OPENCODE_SERVER_PASSWORD: void 0 }
|
|
4605
4721
|
});
|
|
4606
4722
|
return child;
|
|
4607
4723
|
}
|
|
@@ -4617,7 +4733,7 @@ async function startOpenCode2(port, options = {}) {
|
|
|
4617
4733
|
args = [
|
|
4618
4734
|
"-y",
|
|
4619
4735
|
"-p",
|
|
4620
|
-
|
|
4736
|
+
OPENCODE2_PACKAGE_SPEC,
|
|
4621
4737
|
"--",
|
|
4622
4738
|
"opencode2",
|
|
4623
4739
|
"serve",
|
|
@@ -4694,7 +4810,7 @@ async function promptOpenCodeInstall(interactive) {
|
|
|
4694
4810
|
npm: "npm install -g opencode-ai",
|
|
4695
4811
|
curl: "curl -fsSL https://opencode.ai/install.sh | sh",
|
|
4696
4812
|
v2: {
|
|
4697
|
-
npm:
|
|
4813
|
+
npm: `npm install -g ${OPENCODE2_PACKAGE_SPEC}`,
|
|
4698
4814
|
curl: "curl -fsSL https://opencode.ai/v2/install | bash"
|
|
4699
4815
|
}
|
|
4700
4816
|
}
|
|
@@ -4921,11 +5037,11 @@ function buildSessionStoreSizeWarning(input) {
|
|
|
4921
5037
|
|
|
4922
5038
|
// src/lib/opencode/log-tail.ts
|
|
4923
5039
|
import { statSync as statSync4 } from "node:fs";
|
|
4924
|
-
import { homedir as
|
|
5040
|
+
import { homedir as homedir5 } from "node:os";
|
|
4925
5041
|
import { join as join7 } from "node:path";
|
|
4926
5042
|
import { open as open2, stat } from "node:fs/promises";
|
|
4927
5043
|
var DEFAULT_POLL_INTERVAL_MS = 1e3;
|
|
4928
|
-
function resolveOpenCodeLogPath(homeDir =
|
|
5044
|
+
function resolveOpenCodeLogPath(homeDir = homedir5(), env = process.env) {
|
|
4929
5045
|
const dataDir = env.XDG_DATA_HOME || join7(homeDir, ".local", "share");
|
|
4930
5046
|
return join7(dataDir, "opencode", "log", "opencode.log");
|
|
4931
5047
|
}
|
|
@@ -5080,7 +5196,11 @@ async function reclaimSessionDbSpace(input) {
|
|
|
5080
5196
|
console.warn(
|
|
5081
5197
|
`[reclaimSessionDbSpace] skipping VACUUM conversion of ${dbPath}: a session turn is live`
|
|
5082
5198
|
);
|
|
5083
|
-
return {
|
|
5199
|
+
return {
|
|
5200
|
+
ok: false,
|
|
5201
|
+
skipped: "full-vacuum-blocked",
|
|
5202
|
+
detail: "a session turn is live"
|
|
5203
|
+
};
|
|
5084
5204
|
}
|
|
5085
5205
|
const fileBytesForGuard = statSync5(dbPath).size;
|
|
5086
5206
|
const skipReason = insufficientSpaceReason(dbPath, fileBytesForGuard);
|
|
@@ -5088,7 +5208,7 @@ async function reclaimSessionDbSpace(input) {
|
|
|
5088
5208
|
console.warn(
|
|
5089
5209
|
`[reclaimSessionDbSpace] skipping VACUUM conversion of ${dbPath}: ${skipReason}`
|
|
5090
5210
|
);
|
|
5091
|
-
return { ok: false, skipped: "insufficient-disk-space" };
|
|
5211
|
+
return { ok: false, skipped: "insufficient-disk-space", detail: skipReason };
|
|
5092
5212
|
}
|
|
5093
5213
|
const beforeBytes = readLogicalBytes(db);
|
|
5094
5214
|
db.exec("PRAGMA auto_vacuum=INCREMENTAL");
|
|
@@ -5969,7 +6089,7 @@ function createResourceUsageCollector(homeDir) {
|
|
|
5969
6089
|
}
|
|
5970
6090
|
|
|
5971
6091
|
// src/lib/channels/driver.ts
|
|
5972
|
-
import { homedir as
|
|
6092
|
+
import { homedir as homedir6 } from "node:os";
|
|
5973
6093
|
|
|
5974
6094
|
// src/lib/runner-file-sync.ts
|
|
5975
6095
|
import { join as join9 } from "node:path";
|
|
@@ -6852,7 +6972,7 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
6852
6972
|
this.stuckQueuedMs = config.stuckQueuedMs ?? DEFAULT_STUCK_QUEUED_MS;
|
|
6853
6973
|
this.now = config.now ?? (() => Date.now());
|
|
6854
6974
|
this.fileSyncDirectories = config.fileSyncDirectories ?? [];
|
|
6855
|
-
this.homeDir = config.homeDir ??
|
|
6975
|
+
this.homeDir = config.homeDir ?? homedir6();
|
|
6856
6976
|
this.maxActiveSessions = config.maxActiveSessions;
|
|
6857
6977
|
this.watcherStallMs = config.watcherStallMs ?? WATCHER_STALL_MS;
|
|
6858
6978
|
this.wedgeWarningIntervalMs = config.wedgeWarningIntervalMs ?? WEDGE_WARNING_INTERVAL_MS;
|
|
@@ -11279,8 +11399,16 @@ function checkNonInteractivePortConflict(port, isPortInUseFn) {
|
|
|
11279
11399
|
);
|
|
11280
11400
|
}
|
|
11281
11401
|
}
|
|
11402
|
+
function unknownPasswordError(port) {
|
|
11403
|
+
return new Error(
|
|
11404
|
+
`OpenCode is already running on port ${port}, but this runner cannot authenticate to it. It may have been started with OPENCODE_SERVER_PASSWORD set in its environment. Free the port or pass --port.`
|
|
11405
|
+
);
|
|
11406
|
+
}
|
|
11282
11407
|
async function ensureOpenCodeRunning(ctx) {
|
|
11283
11408
|
const healthCheck = await checkOpenCodeHealth(ctx.port);
|
|
11409
|
+
if (healthCheck.authFailed) {
|
|
11410
|
+
throw unknownPasswordError(ctx.port);
|
|
11411
|
+
}
|
|
11284
11412
|
if (healthCheck.healthy) {
|
|
11285
11413
|
return {
|
|
11286
11414
|
port: ctx.port,
|
|
@@ -11331,6 +11459,14 @@ async function ensureOpenCodeRunning(ctx) {
|
|
|
11331
11459
|
const proc = await startOpenCode(ctx.port, { inheritStdio: ctx.inheritStdio });
|
|
11332
11460
|
const health = await waitForOpenCodeHealth(ctx.port, ctx.startTimeoutMs);
|
|
11333
11461
|
if (!health.healthy) {
|
|
11462
|
+
if (health.authFailed) {
|
|
11463
|
+
return {
|
|
11464
|
+
port: ctx.port,
|
|
11465
|
+
process: proc,
|
|
11466
|
+
version: null,
|
|
11467
|
+
notReadyReason: "it is answering with an authentication challenge, likely because OPENCODE_SERVER_PASSWORD is set in the environment of the process that started it; a longer start timeout will not resolve this"
|
|
11468
|
+
};
|
|
11469
|
+
}
|
|
11334
11470
|
return {
|
|
11335
11471
|
port: ctx.port,
|
|
11336
11472
|
process: proc,
|
|
@@ -11400,6 +11536,9 @@ Port ${port} is already in use.`));
|
|
|
11400
11536
|
const health = await waitForOpenCodeHealth(port, INTERACTIVE_START_TIMEOUT_MS);
|
|
11401
11537
|
if (!health.healthy) {
|
|
11402
11538
|
spinner.fail("Failed to start OpenCode");
|
|
11539
|
+
if (health.authFailed) {
|
|
11540
|
+
throw unknownPasswordError(port);
|
|
11541
|
+
}
|
|
11403
11542
|
throw new Error("OpenCode failed to start");
|
|
11404
11543
|
}
|
|
11405
11544
|
spinner.stop();
|
|
@@ -11412,7 +11551,7 @@ Port ${port} is already in use.`));
|
|
|
11412
11551
|
import chalk6 from "chalk";
|
|
11413
11552
|
import ora3 from "ora";
|
|
11414
11553
|
import { select as select3 } from "@inquirer/prompts";
|
|
11415
|
-
function
|
|
11554
|
+
function unknownPasswordError2(port) {
|
|
11416
11555
|
return new Error(
|
|
11417
11556
|
`OpenCode V2 (opencode2) is already running on port ${port} with a password this runner doesn't know. Free the port or pass --port.`
|
|
11418
11557
|
);
|
|
@@ -11421,7 +11560,7 @@ var INTERACTIVE_START_TIMEOUT_MS2 = 3e4;
|
|
|
11421
11560
|
async function ensureOpenCode2Running(ctx) {
|
|
11422
11561
|
const initialHealth = await checkOpenCode2Health(ctx.port);
|
|
11423
11562
|
if (initialHealth.authFailed) {
|
|
11424
|
-
throw
|
|
11563
|
+
throw unknownPasswordError2(ctx.port);
|
|
11425
11564
|
}
|
|
11426
11565
|
if (initialHealth.healthy) {
|
|
11427
11566
|
return {
|
|
@@ -11434,7 +11573,7 @@ async function ensureOpenCode2Running(ctx) {
|
|
|
11434
11573
|
}
|
|
11435
11574
|
if (!isOpenCode2Installed()) {
|
|
11436
11575
|
throw new Error(
|
|
11437
|
-
|
|
11576
|
+
`OpenCode V2 (opencode2) is not installed. Install it with: npm install -g ${OPENCODE2_PACKAGE_SPEC}`
|
|
11438
11577
|
);
|
|
11439
11578
|
}
|
|
11440
11579
|
let port = ctx.port;
|
|
@@ -12241,7 +12380,13 @@ function logActivity(state, entry) {
|
|
|
12241
12380
|
const level = entry.level ?? (entry.type === "error" ? "error" : "info");
|
|
12242
12381
|
if (!meetsThreshold(state, level)) return;
|
|
12243
12382
|
forwardRunnerActivity(
|
|
12244
|
-
{
|
|
12383
|
+
{
|
|
12384
|
+
level,
|
|
12385
|
+
message: entry.message,
|
|
12386
|
+
error: entry.error,
|
|
12387
|
+
metadata: entry.metadata,
|
|
12388
|
+
...entry.source ? { source: entry.source } : {}
|
|
12389
|
+
},
|
|
12245
12390
|
{ agentId: state.agentId, authHeader: state.authHeader }
|
|
12246
12391
|
);
|
|
12247
12392
|
const fullEntry = {
|
|
@@ -12263,7 +12408,7 @@ function logActivity(state, entry) {
|
|
|
12263
12408
|
}
|
|
12264
12409
|
function reportSessionDbRecovery(state) {
|
|
12265
12410
|
try {
|
|
12266
|
-
const report = drainSessionDbRecoveryReport({ homeDir:
|
|
12411
|
+
const report = drainSessionDbRecoveryReport({ homeDir: homedir7(), env: process.env });
|
|
12267
12412
|
for (const record of report.records) {
|
|
12268
12413
|
const activity = buildSessionDbRecoveryActivity(record);
|
|
12269
12414
|
if (!activity) throw new Error("could not map session-DB recovery record");
|
|
@@ -12317,7 +12462,7 @@ function reportOpenCodeV2CredentialImport(state, result) {
|
|
|
12317
12462
|
logActivity(state, {
|
|
12318
12463
|
type: "info",
|
|
12319
12464
|
level: "warn",
|
|
12320
|
-
message: `OpenCode V2 saved login(s) for ${integrationIds} on this machine, but OpenCode V2 will not use them.
|
|
12465
|
+
message: `OpenCode V2 saved login(s) for ${integrationIds} on this machine, but OpenCode V2 will not use them. Connect the provider again from the runner's page in Evident; no shell on this machine is needed.`
|
|
12321
12466
|
});
|
|
12322
12467
|
}
|
|
12323
12468
|
if (result.imported.length === 0 && result.unsupported.length === 0 && result.failed.length === 0) {
|
|
@@ -12342,7 +12487,7 @@ async function importOpenCodeV2CredentialsForState(state) {
|
|
|
12342
12487
|
}
|
|
12343
12488
|
const result = await importOpenCodeV2Credentials({
|
|
12344
12489
|
request: client.request,
|
|
12345
|
-
homeDir:
|
|
12490
|
+
homeDir: homedir7(),
|
|
12346
12491
|
directory,
|
|
12347
12492
|
guard: state.opencodeV2CredentialImportGuard,
|
|
12348
12493
|
log: (message, level = "info") => logActivity(state, { type: "info", level, message })
|
|
@@ -12561,7 +12706,14 @@ function shouldWarnForReclaimSkip(reason) {
|
|
|
12561
12706
|
return major > 22 || major === 22 && (minor > 13 || minor === 13 && patch >= 0);
|
|
12562
12707
|
}
|
|
12563
12708
|
function sessionDbPath() {
|
|
12564
|
-
return join11(
|
|
12709
|
+
return join11(homedir7(), ".local", "share", "opencode", "opencode.db");
|
|
12710
|
+
}
|
|
12711
|
+
function logSessionCleanupActivity(state, phase, entry) {
|
|
12712
|
+
logActivity(state, {
|
|
12713
|
+
...entry,
|
|
12714
|
+
source: "cli.session-cleanup",
|
|
12715
|
+
metadata: { ...entry.metadata, phase }
|
|
12716
|
+
});
|
|
12565
12717
|
}
|
|
12566
12718
|
function logSessionDbProvenanceMismatch(state, provenance, currentVersion, preBootMigrationCount) {
|
|
12567
12719
|
const record = {
|
|
@@ -12604,9 +12756,13 @@ async function runSweep(state, driver, config) {
|
|
|
12604
12756
|
try {
|
|
12605
12757
|
const sessions = await listSessions(state.port, state.opencodeClient ?? void 0);
|
|
12606
12758
|
if (sessions === null) {
|
|
12607
|
-
|
|
12759
|
+
logSessionCleanupActivity(state, "sweep", {
|
|
12608
12760
|
type: "info",
|
|
12609
|
-
message: `Session cleanup: could not list sessions (opencode unreachable); skipping this sweep (${mode})
|
|
12761
|
+
message: `Session cleanup: could not list sessions (opencode unreachable); skipping this sweep (${mode})`,
|
|
12762
|
+
metadata: {
|
|
12763
|
+
outcome: "sweep-skipped",
|
|
12764
|
+
reason: "opencode-unreachable"
|
|
12765
|
+
}
|
|
12610
12766
|
});
|
|
12611
12767
|
return;
|
|
12612
12768
|
}
|
|
@@ -12650,21 +12806,47 @@ async function runSweep(state, driver, config) {
|
|
|
12650
12806
|
const beforeMib = (reclaimResult.beforeBytes / 1024 / 1024).toFixed(1);
|
|
12651
12807
|
const afterMib = (reclaimResult.afterBytes / 1024 / 1024).toFixed(1);
|
|
12652
12808
|
const checkpointNote = reclaimResult.checkpoint.busy ? ` (on-disk file truncation deferred: checkpoint busy, ${reclaimResult.checkpoint.log} WAL frames pending)` : "";
|
|
12653
|
-
|
|
12809
|
+
logSessionCleanupActivity(state, "sweep", {
|
|
12654
12810
|
type: "info",
|
|
12655
|
-
message: `Session cleanup: reclaimed session-db space (${reclaimResult.mode}): ${beforeMib} MiB -> ${afterMib} MiB${checkpointNote}
|
|
12811
|
+
message: `Session cleanup: reclaimed session-db space (${reclaimResult.mode}): ${beforeMib} MiB -> ${afterMib} MiB${checkpointNote}`,
|
|
12812
|
+
metadata: {
|
|
12813
|
+
outcome: "reclaimed",
|
|
12814
|
+
mode: reclaimResult.mode,
|
|
12815
|
+
before_bytes: reclaimResult.beforeBytes,
|
|
12816
|
+
after_bytes: reclaimResult.afterBytes,
|
|
12817
|
+
checkpoint_busy: reclaimResult.checkpoint.busy,
|
|
12818
|
+
wal_frames_pending: reclaimResult.checkpoint.log,
|
|
12819
|
+
sessions_inspected: sessions.length,
|
|
12820
|
+
sessions_deleted: deleted,
|
|
12821
|
+
sessions_failed: failed,
|
|
12822
|
+
sessions_skipped_active: skippedNewlyActive
|
|
12823
|
+
}
|
|
12656
12824
|
});
|
|
12657
12825
|
} else {
|
|
12658
|
-
|
|
12826
|
+
logSessionCleanupActivity(state, "sweep", {
|
|
12659
12827
|
type: "info",
|
|
12660
|
-
|
|
12828
|
+
level: reclaimResult.skipped === "reclaim-error" || reclaimResult.skipped === "insufficient-disk-space" ? "warn" : "info",
|
|
12829
|
+
message: `Session cleanup: session-db space reclaim skipped (${reclaimResult.skipped})` + (reclaimResult.detail ? `: ${reclaimResult.detail}` : ""),
|
|
12830
|
+
metadata: {
|
|
12831
|
+
outcome: "reclaim-skipped",
|
|
12832
|
+
reason: reclaimResult.skipped,
|
|
12833
|
+
...reclaimResult.detail !== void 0 ? { detail: reclaimResult.detail } : {},
|
|
12834
|
+
sessions_inspected: sessions.length,
|
|
12835
|
+
sessions_deleted: deleted,
|
|
12836
|
+
sessions_failed: failed,
|
|
12837
|
+
sessions_skipped_active: skippedNewlyActive
|
|
12838
|
+
}
|
|
12661
12839
|
});
|
|
12662
12840
|
}
|
|
12663
12841
|
} catch (error2) {
|
|
12664
12842
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
12665
|
-
|
|
12843
|
+
logSessionCleanupActivity(state, "sweep", {
|
|
12666
12844
|
type: "error",
|
|
12667
|
-
error: `Session cleanup sweep failed (non-fatal, ${mode}): ${message}
|
|
12845
|
+
error: `Session cleanup sweep failed (non-fatal, ${mode}): ${message}`,
|
|
12846
|
+
metadata: {
|
|
12847
|
+
outcome: "sweep-failed",
|
|
12848
|
+
reason: "sweep-error"
|
|
12849
|
+
}
|
|
12668
12850
|
});
|
|
12669
12851
|
}
|
|
12670
12852
|
}
|
|
@@ -12680,14 +12862,19 @@ function scheduleSessionCleanup(state, driver, options) {
|
|
|
12680
12862
|
for (const warning2 of config.warnings) {
|
|
12681
12863
|
logActivity(state, { type: "info", level: "warn", message: `Session cleanup: ${warning2}` });
|
|
12682
12864
|
}
|
|
12683
|
-
const dbBytes = statSessionDbBytes(
|
|
12865
|
+
const dbBytes = statSessionDbBytes(homedir7());
|
|
12684
12866
|
void (async () => {
|
|
12685
12867
|
const reclaimAvailability = dbBytes !== null && config.enabled ? await probeReclaimAvailability({ dbPath: sessionDbPath(), requiredBytes: dbBytes }) : null;
|
|
12686
12868
|
if (reclaimAvailability !== null) {
|
|
12687
|
-
|
|
12869
|
+
logSessionCleanupActivity(state, "startup", {
|
|
12688
12870
|
type: "info",
|
|
12689
12871
|
level: shouldWarnForReclaimSkip(reclaimAvailability.reason) ? "warn" : "info",
|
|
12690
|
-
message: `Session cleanup: reclaim preflight skipped (${reclaimAvailability.reason})` + (reclaimAvailability.detail ? `: ${reclaimAvailability.detail}` : "")
|
|
12872
|
+
message: `Session cleanup: reclaim preflight skipped (${reclaimAvailability.reason})` + (reclaimAvailability.detail ? `: ${reclaimAvailability.detail}` : ""),
|
|
12873
|
+
metadata: {
|
|
12874
|
+
outcome: "preflight-skipped",
|
|
12875
|
+
reason: reclaimAvailability.reason,
|
|
12876
|
+
...reclaimAvailability.detail !== void 0 ? { detail: reclaimAvailability.detail } : {}
|
|
12877
|
+
}
|
|
12691
12878
|
});
|
|
12692
12879
|
}
|
|
12693
12880
|
const sizeWarning = buildSessionStoreSizeWarning({
|
|
@@ -12704,9 +12891,15 @@ function scheduleSessionCleanup(state, driver, options) {
|
|
|
12704
12891
|
);
|
|
12705
12892
|
});
|
|
12706
12893
|
if (!config.enabled) return;
|
|
12707
|
-
|
|
12894
|
+
logSessionCleanupActivity(state, "startup", {
|
|
12708
12895
|
type: "info",
|
|
12709
|
-
message: `Session cleanup enabled (age=${config.maxAgeMs ?? "\u2014"}, count=${config.maxCount ?? "\u2014"}, interval=${config.intervalMs}ms)
|
|
12896
|
+
message: `Session cleanup enabled (age=${config.maxAgeMs ?? "\u2014"}, count=${config.maxCount ?? "\u2014"}, interval=${config.intervalMs}ms)`,
|
|
12897
|
+
metadata: {
|
|
12898
|
+
outcome: "armed",
|
|
12899
|
+
interval_ms: config.intervalMs,
|
|
12900
|
+
max_age_ms: config.maxAgeMs ?? null,
|
|
12901
|
+
max_count: config.maxCount ?? null
|
|
12902
|
+
}
|
|
12710
12903
|
});
|
|
12711
12904
|
const interval = setInterval(() => void runSweep(state, driver, config), config.intervalMs);
|
|
12712
12905
|
const firstSweep = setTimeout(
|
|
@@ -12885,7 +13078,7 @@ function scheduleResourceUsageReporting(state, options) {
|
|
|
12885
13078
|
});
|
|
12886
13079
|
return;
|
|
12887
13080
|
}
|
|
12888
|
-
const { collect, stop } = createResourceUsageCollector(
|
|
13081
|
+
const { collect, stop } = createResourceUsageCollector(homedir7());
|
|
12889
13082
|
state.stopResourceUsageSampling = stop;
|
|
12890
13083
|
let consecutiveFailures = 0;
|
|
12891
13084
|
const tick = async () => {
|
|
@@ -13110,7 +13303,7 @@ async function run(options) {
|
|
|
13110
13303
|
let fileSyncDirectories;
|
|
13111
13304
|
try {
|
|
13112
13305
|
logLevel = resolveLogLevel(options);
|
|
13113
|
-
fileSyncDirectories = resolveFileSyncDirectories(options.enableFileSyncTo,
|
|
13306
|
+
fileSyncDirectories = resolveFileSyncDirectories(options.enableFileSyncTo, homedir7());
|
|
13114
13307
|
if (options.restoreSessionDb && !options.sessionDbNoReplicateMarker) {
|
|
13115
13308
|
throw new Error(
|
|
13116
13309
|
"--restore-session-db requires --session-db-no-replicate-marker <path>; restore failures must block Litestream replication"
|
|
@@ -13165,6 +13358,7 @@ async function run(options) {
|
|
|
13165
13358
|
authHeader: ""
|
|
13166
13359
|
};
|
|
13167
13360
|
setTelemetryAuthProvider(() => ({ authHeader: state.authHeader, agentId: state.agentId }));
|
|
13361
|
+
setTelemetryDiagnosticsReporter((message) => log2(state, message, "warn"));
|
|
13168
13362
|
if (options.credentialSyncMarker) {
|
|
13169
13363
|
state.credentialSync = createCredentialSync({
|
|
13170
13364
|
markerPath: options.credentialSyncMarker,
|
|
@@ -13350,13 +13544,18 @@ async function run(options) {
|
|
|
13350
13544
|
if (githubTokenPopulated) await configureGitHubAccess(credentialContext);
|
|
13351
13545
|
}
|
|
13352
13546
|
state.credentialSync?.arm();
|
|
13353
|
-
|
|
13354
|
-
|
|
13355
|
-
|
|
13356
|
-
|
|
13357
|
-
|
|
13358
|
-
|
|
13359
|
-
|
|
13547
|
+
const openCodeActivityForwarder = createOpenCodeActivityForwarder(() => ({
|
|
13548
|
+
agentId: state.agentId,
|
|
13549
|
+
authHeader: state.authHeader
|
|
13550
|
+
}));
|
|
13551
|
+
const openCodeLogTail = tailOpenCodeLogFile(
|
|
13552
|
+
resolveOpenCodeLogPath(homedir7(), process.env),
|
|
13553
|
+
openCodeActivityForwarder
|
|
13554
|
+
);
|
|
13555
|
+
state.stopOpenCodeLogTail = () => {
|
|
13556
|
+
openCodeActivityForwarder.flush();
|
|
13557
|
+
openCodeLogTail.stop();
|
|
13558
|
+
};
|
|
13360
13559
|
let sessionDbVerifyFatal = false;
|
|
13361
13560
|
if (!options.restoreSessionDb) {
|
|
13362
13561
|
log2(state, "Skipping session-DB restore: --restore-session-db was not passed", "debug");
|
|
@@ -13368,6 +13567,12 @@ async function run(options) {
|
|
|
13368
13567
|
"Skipping session-DB restore: OpenCode is already serving this database",
|
|
13369
13568
|
"debug"
|
|
13370
13569
|
);
|
|
13570
|
+
} else if (health.authFailed) {
|
|
13571
|
+
log2(
|
|
13572
|
+
state,
|
|
13573
|
+
"Skipping session-DB restore: OpenCode answered with an authentication challenge; assuming this database is in use",
|
|
13574
|
+
"debug"
|
|
13575
|
+
);
|
|
13371
13576
|
} else {
|
|
13372
13577
|
const result = await restoreAndVerifySessionDb({
|
|
13373
13578
|
dbPath: sessionDbPath(),
|
|
@@ -13467,7 +13672,7 @@ async function run(options) {
|
|
|
13467
13672
|
const provenance = checkSessionDbProvenance({
|
|
13468
13673
|
dbPath: sessionDbPath(),
|
|
13469
13674
|
currentVersion: state.opencodeVersion,
|
|
13470
|
-
homeDir:
|
|
13675
|
+
homeDir: homedir7(),
|
|
13471
13676
|
env: process.env
|
|
13472
13677
|
});
|
|
13473
13678
|
if (provenance.anomaly) {
|
|
@@ -13637,7 +13842,7 @@ async function run(options) {
|
|
|
13637
13842
|
// #559: the `--enable-file-sync-to` allow-list. Empty ⇒ pending files are
|
|
13638
13843
|
// REJECTED with `file_sync_disabled` on the ack, not silently ignored.
|
|
13639
13844
|
fileSyncDirectories,
|
|
13640
|
-
homeDir:
|
|
13845
|
+
homeDir: homedir7(),
|
|
13641
13846
|
maxActiveSessions,
|
|
13642
13847
|
log: (entry) => (
|
|
13643
13848
|
// Thread the driver's real level straight through so `debug`/`warn`
|
|
@@ -13813,7 +14018,7 @@ async function run(options) {
|
|
|
13813
14018
|
},
|
|
13814
14019
|
report: (usage) => reportOpenAiUsage(state.agentId, state.authHeader, usage),
|
|
13815
14020
|
isLocalCredentialProblem: isLocalCredentialProblem2,
|
|
13816
|
-
forcedOnHint: "connect a ChatGPT account to this runner, or run `opencode auth login`",
|
|
14021
|
+
forcedOnHint: state.opencodeApiVersion === "v2" ? "OpenCode V2 keeps its connected ChatGPT login in a store Evident cannot read" : "connect a ChatGPT account to this runner, or run `opencode auth login`",
|
|
13817
14022
|
firstDelayMs: firstReportDelayMs,
|
|
13818
14023
|
nextDelayMs: usageReportDelayMs,
|
|
13819
14024
|
failureLogLevel: usageReportFailureLogLevel
|