@evident-ai/cli 3.5.2-dev.da3340e → 3.5.2-dev.e67eb8b
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 +192 -40
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -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.2-dev.
|
|
1161
|
+
var CLI_VERSION = (true ? "3.5.2-dev.e67eb8b" : 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 {
|
|
@@ -1317,25 +1338,72 @@ function parseOpenCodeLogLine(line) {
|
|
|
1317
1338
|
const level = levelMatch[1].toUpperCase();
|
|
1318
1339
|
if (level !== "WARN" && level !== "ERROR") return null;
|
|
1319
1340
|
const sessionMatch = normalisedLine.match(/(?:^|\s)sessionID=(\S+)/);
|
|
1320
|
-
|
|
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;
|
|
1321
1349
|
}
|
|
1322
1350
|
var MAX_LINE_BUFFER_BYTES = 16 * 1024;
|
|
1351
|
+
var REPEAT_SUPPRESSION_WINDOW_MS = 6e4;
|
|
1323
1352
|
function createOpenCodeActivityForwarder(getContext) {
|
|
1324
1353
|
let buffer = Buffer.alloc(0);
|
|
1325
|
-
const
|
|
1326
|
-
|
|
1327
|
-
|
|
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
|
+
};
|
|
1328
1360
|
forwardRunnerActivity(
|
|
1329
1361
|
{
|
|
1330
1362
|
level: parsed.level,
|
|
1331
1363
|
error: line,
|
|
1332
|
-
metadata:
|
|
1364
|
+
metadata: Object.keys(metadata).length > 0 ? metadata : void 0,
|
|
1333
1365
|
source: "opencode"
|
|
1334
1366
|
},
|
|
1335
1367
|
getContext()
|
|
1336
1368
|
);
|
|
1337
1369
|
};
|
|
1338
|
-
|
|
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) => {
|
|
1339
1407
|
buffer = Buffer.concat([buffer, Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, "utf-8")]);
|
|
1340
1408
|
let newlineIndex;
|
|
1341
1409
|
while ((newlineIndex = buffer.indexOf(10)) !== -1) {
|
|
@@ -1346,7 +1414,16 @@ function createOpenCodeActivityForwarder(getContext) {
|
|
|
1346
1414
|
flushLine(buffer.toString("utf-8"));
|
|
1347
1415
|
buffer = Buffer.alloc(0);
|
|
1348
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();
|
|
1349
1425
|
};
|
|
1426
|
+
return forward;
|
|
1350
1427
|
}
|
|
1351
1428
|
var MAX_MESSAGE_LENGTH = 500;
|
|
1352
1429
|
var MAX_METADATA_VALUE_LENGTH = 200;
|
|
@@ -1372,7 +1449,7 @@ function redactHomeDirectory(message) {
|
|
|
1372
1449
|
}
|
|
1373
1450
|
return result + message.slice(searchFrom);
|
|
1374
1451
|
}
|
|
1375
|
-
function
|
|
1452
|
+
function redactDiagnosticText(message) {
|
|
1376
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>");
|
|
1377
1454
|
return redactHomeDirectory(redacted);
|
|
1378
1455
|
}
|
|
@@ -1384,14 +1461,17 @@ function sanitiseMetadata(metadata) {
|
|
|
1384
1461
|
if (!metadata) return {};
|
|
1385
1462
|
const entries = Object.entries(metadata).slice(0, MAX_METADATA_ENTRIES);
|
|
1386
1463
|
if (Object.keys(metadata).length > entries.length) {
|
|
1387
|
-
|
|
1388
|
-
`
|
|
1464
|
+
reportTelemetryDiagnostic(
|
|
1465
|
+
`Runner activity telemetry dropped ${Object.keys(metadata).length - entries.length} metadata entries; those entries were not sent`
|
|
1389
1466
|
);
|
|
1390
1467
|
}
|
|
1391
1468
|
const sanitised = [];
|
|
1392
1469
|
for (const [key, value] of entries) {
|
|
1393
1470
|
if (typeof value === "string") {
|
|
1394
|
-
sanitised.push([
|
|
1471
|
+
sanitised.push([
|
|
1472
|
+
key,
|
|
1473
|
+
truncate(redactDiagnosticText(value).slice(0, MAX_METADATA_VALUE_LENGTH))
|
|
1474
|
+
]);
|
|
1395
1475
|
} else if (value === null || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) {
|
|
1396
1476
|
sanitised.push([key, value]);
|
|
1397
1477
|
}
|
|
@@ -1409,8 +1489,8 @@ function admitUnderRateLimit(source, now) {
|
|
|
1409
1489
|
}
|
|
1410
1490
|
if (now - window.windowStartedAt >= RATE_LIMIT_WINDOW_MS) {
|
|
1411
1491
|
if (window.windowDroppedCount > 0) {
|
|
1412
|
-
|
|
1413
|
-
`
|
|
1492
|
+
reportTelemetryDiagnostic(
|
|
1493
|
+
`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}"`
|
|
1414
1494
|
);
|
|
1415
1495
|
}
|
|
1416
1496
|
window.windowStartedAt = now;
|
|
@@ -1420,8 +1500,8 @@ function admitUnderRateLimit(source, now) {
|
|
|
1420
1500
|
if (window.windowCount >= RATE_LIMIT_MAX_EVENTS) {
|
|
1421
1501
|
window.windowDroppedCount++;
|
|
1422
1502
|
if (window.windowDroppedCount === 1) {
|
|
1423
|
-
|
|
1424
|
-
`
|
|
1503
|
+
reportTelemetryDiagnostic(
|
|
1504
|
+
`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}"`
|
|
1425
1505
|
);
|
|
1426
1506
|
}
|
|
1427
1507
|
return false;
|
|
@@ -1436,7 +1516,7 @@ function forwardRunnerActivity(entry, context) {
|
|
|
1436
1516
|
if (!context.agentId || !context.authHeader) return;
|
|
1437
1517
|
if (!admitUnderRateLimit(source, Date.now())) return;
|
|
1438
1518
|
const rawMessage = entry.error ?? entry.message ?? "";
|
|
1439
|
-
const message = truncate(
|
|
1519
|
+
const message = truncate(redactDiagnosticText(rawMessage));
|
|
1440
1520
|
logEvent(TelemetryEventTypes.RUNNER_ACTIVITY, {
|
|
1441
1521
|
severity: SEVERITY_BY_LEVEL[entry.level],
|
|
1442
1522
|
message,
|
|
@@ -1444,8 +1524,8 @@ function forwardRunnerActivity(entry, context) {
|
|
|
1444
1524
|
agentId: context.agentId
|
|
1445
1525
|
});
|
|
1446
1526
|
} catch (err) {
|
|
1447
|
-
|
|
1448
|
-
`
|
|
1527
|
+
reportTelemetryDiagnostic(
|
|
1528
|
+
`Runner activity telemetry failed to forward runner activity: ${err instanceof Error ? err.message : String(err)}`
|
|
1449
1529
|
);
|
|
1450
1530
|
}
|
|
1451
1531
|
}
|
|
@@ -1735,8 +1815,46 @@ async function waitForOpenCode2Health(port, password, timeoutMs = 3e4) {
|
|
|
1735
1815
|
|
|
1736
1816
|
// src/lib/http-timeout.ts
|
|
1737
1817
|
var REQUEST_TIMEOUT_MS = 6e4;
|
|
1818
|
+
var RequestTimeoutError = class extends Error {
|
|
1819
|
+
constructor(operation, timeoutMs, elapsedMs) {
|
|
1820
|
+
super(`${operation} timed out after ${elapsedMs}ms (limit ${timeoutMs}ms)`);
|
|
1821
|
+
this.operation = operation;
|
|
1822
|
+
this.timeoutMs = timeoutMs;
|
|
1823
|
+
this.elapsedMs = elapsedMs;
|
|
1824
|
+
this.name = "TimeoutError";
|
|
1825
|
+
}
|
|
1826
|
+
};
|
|
1827
|
+
function describeRequestOperation(input, init) {
|
|
1828
|
+
const method = (init?.method ?? input.method ?? "GET").toUpperCase();
|
|
1829
|
+
const rawUrl = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
1830
|
+
let path;
|
|
1831
|
+
try {
|
|
1832
|
+
path = new URL(rawUrl).pathname;
|
|
1833
|
+
} catch (error2) {
|
|
1834
|
+
if (!(error2 instanceof TypeError)) throw error2;
|
|
1835
|
+
path = rawUrl.split(/[?#]/, 1)[0];
|
|
1836
|
+
}
|
|
1837
|
+
const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1838
|
+
const prefixedIdentifier = /^[a-z]+_[A-Za-z0-9_-]{10,}$/;
|
|
1839
|
+
const maskedPath = path.split("/").map((segment) => uuid.test(segment) || prefixedIdentifier.test(segment) ? ":id" : segment).join("/");
|
|
1840
|
+
return `${method} ${maskedPath}`;
|
|
1841
|
+
}
|
|
1738
1842
|
function withRequestTimeout(fetchImpl, timeoutMs) {
|
|
1739
|
-
return ((input, init) =>
|
|
1843
|
+
return (async (input, init) => {
|
|
1844
|
+
const startedAt = Date.now();
|
|
1845
|
+
try {
|
|
1846
|
+
return await fetchImpl(input, { ...init, signal: AbortSignal.timeout(timeoutMs) });
|
|
1847
|
+
} catch (error2) {
|
|
1848
|
+
if (error2?.name === "TimeoutError") {
|
|
1849
|
+
throw new RequestTimeoutError(
|
|
1850
|
+
describeRequestOperation(input, init),
|
|
1851
|
+
timeoutMs,
|
|
1852
|
+
Date.now() - startedAt
|
|
1853
|
+
);
|
|
1854
|
+
}
|
|
1855
|
+
throw error2;
|
|
1856
|
+
}
|
|
1857
|
+
});
|
|
1740
1858
|
}
|
|
1741
1859
|
|
|
1742
1860
|
// src/lib/opencode/client.ts
|
|
@@ -3397,16 +3515,17 @@ function hasPrimaryHeaders(headers) {
|
|
|
3397
3515
|
].some((name) => headers.has(name));
|
|
3398
3516
|
}
|
|
3399
3517
|
async function getOpenAiUsage(port, client) {
|
|
3518
|
+
const openCodeVersion = client?.version ?? "v1";
|
|
3400
3519
|
const credentials2 = readOpenCodeChatGptCredentials();
|
|
3401
3520
|
if (!credentials2) {
|
|
3402
3521
|
throw new OpenAiUsageError(
|
|
3403
|
-
"No ChatGPT login found. Connect a ChatGPT account to this runner, or run `opencode auth login`.",
|
|
3522
|
+
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`.",
|
|
3404
3523
|
"no_credentials"
|
|
3405
3524
|
);
|
|
3406
3525
|
}
|
|
3407
3526
|
if (credentials2.expiresAt < Date.now()) {
|
|
3408
3527
|
throw new OpenAiUsageError(
|
|
3409
|
-
"ChatGPT credentials have expired. Run `opencode auth login` to refresh them.",
|
|
3528
|
+
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.",
|
|
3410
3529
|
"credentials_expired"
|
|
3411
3530
|
);
|
|
3412
3531
|
}
|
|
@@ -5617,6 +5736,7 @@ function connectTunnel(options) {
|
|
|
5617
5736
|
var RunnerConnection = class {
|
|
5618
5737
|
opts;
|
|
5619
5738
|
sleep;
|
|
5739
|
+
now;
|
|
5620
5740
|
connection = null;
|
|
5621
5741
|
resolvedAgentId;
|
|
5622
5742
|
/** True while a (re)connect loop is in flight. */
|
|
@@ -5625,10 +5745,13 @@ var RunnerConnection = class {
|
|
|
5625
5745
|
reconnectPromise = null;
|
|
5626
5746
|
/** 1-based count of the current reconnect attempt streak. */
|
|
5627
5747
|
reconnectAttempt = 0;
|
|
5748
|
+
generation = 0;
|
|
5749
|
+
disconnectedAtMs = null;
|
|
5628
5750
|
constructor(opts) {
|
|
5629
5751
|
this.opts = opts;
|
|
5630
5752
|
this.resolvedAgentId = opts.agentId;
|
|
5631
5753
|
this.sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
5754
|
+
this.now = opts.now ?? Date.now;
|
|
5632
5755
|
}
|
|
5633
5756
|
get agentId() {
|
|
5634
5757
|
return this.resolvedAgentId;
|
|
@@ -5664,13 +5787,22 @@ var RunnerConnection = class {
|
|
|
5664
5787
|
port: this.opts.port,
|
|
5665
5788
|
openCodePassword: this.opts.openCodePassword,
|
|
5666
5789
|
onConnected: (agentId) => {
|
|
5790
|
+
const attempt = this.reconnectAttempt + 1;
|
|
5667
5791
|
this.reconnectAttempt = 0;
|
|
5668
5792
|
this.reconnecting = false;
|
|
5669
5793
|
this.resolvedAgentId = agentId;
|
|
5670
|
-
|
|
5794
|
+
this.generation++;
|
|
5795
|
+
const downtimeMs = this.disconnectedAtMs === null ? null : this.now() - this.disconnectedAtMs;
|
|
5796
|
+
this.disconnectedAtMs = null;
|
|
5797
|
+
events.onConnected(agentId, isReconnect, {
|
|
5798
|
+
generation: this.generation,
|
|
5799
|
+
attempt,
|
|
5800
|
+
downtimeMs
|
|
5801
|
+
});
|
|
5671
5802
|
},
|
|
5672
5803
|
onDisconnected: (code, reason) => {
|
|
5673
|
-
|
|
5804
|
+
this.disconnectedAtMs = this.now();
|
|
5805
|
+
events.onDisconnected(code, reason, { generation: this.generation });
|
|
5674
5806
|
if (this.opts.isRunning() && code !== 1e3 && !this.reconnecting) {
|
|
5675
5807
|
this.reconnectPromise = this.connectWithRetry(true).catch((err) => {
|
|
5676
5808
|
events.onError?.(`Reconnection failed: ${err.message}`);
|
|
@@ -12147,6 +12279,7 @@ function createCredentialSync({
|
|
|
12147
12279
|
|
|
12148
12280
|
// src/commands/run.ts
|
|
12149
12281
|
var MAX_ACTIVITY_LOG_ENTRIES = 10;
|
|
12282
|
+
var MAX_DISCONNECT_REASON_LENGTH = 200;
|
|
12150
12283
|
var CHANNEL_POLL_INTERVAL_MS = Number(process.env.EVIDENT_CHANNEL_POLL_INTERVAL_MS) || 2e3;
|
|
12151
12284
|
var CHANNEL_STUCK_QUEUED_MS = Number(process.env.EVIDENT_STUCK_QUEUED_MS) || void 0;
|
|
12152
12285
|
var SHUTDOWN_DRAIN_TIMEOUT_MS = Number(process.env.EVIDENT_SHUTDOWN_DRAIN_MS) || 25e3;
|
|
@@ -12365,7 +12498,9 @@ function displayStatus(state) {
|
|
|
12365
12498
|
const opencode = state.opencodeConnected ? chalk7.green(`opencode: :${state.port}`) : chalk7.red(`opencode: :${state.port} (down)`);
|
|
12366
12499
|
const messages = state.messageCount > 0 ? chalk7.dim(` \xB7 ${state.messageCount} processed`) : "";
|
|
12367
12500
|
const last = state.activityLog[state.activityLog.length - 1];
|
|
12368
|
-
const detail = last ? chalk7.dim(
|
|
12501
|
+
const detail = last ? chalk7.dim(
|
|
12502
|
+
` \xB7 ${last.timestamp.toISOString()} ${last.type === "error" ? last.error ?? "" : last.message ?? ""}`
|
|
12503
|
+
) : "";
|
|
12369
12504
|
const agent = state.agentName ?? state.agentId;
|
|
12370
12505
|
console.log(
|
|
12371
12506
|
`${chalk7.bold("Evident")} ${chalk7.dim(agent)} ${tunnel} ${opencode}${messages}${detail}`
|
|
@@ -12384,7 +12519,7 @@ function reportOpenCodeV2CredentialImport(state, result) {
|
|
|
12384
12519
|
logActivity(state, {
|
|
12385
12520
|
type: "info",
|
|
12386
12521
|
level: "warn",
|
|
12387
|
-
message: `OpenCode V2 saved login(s) for ${integrationIds} on this machine, but OpenCode V2 will not use them.
|
|
12522
|
+
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.`
|
|
12388
12523
|
});
|
|
12389
12524
|
}
|
|
12390
12525
|
if (result.imported.length === 0 && result.unsupported.length === 0 && result.failed.length === 0) {
|
|
@@ -13280,6 +13415,7 @@ async function run(options) {
|
|
|
13280
13415
|
authHeader: ""
|
|
13281
13416
|
};
|
|
13282
13417
|
setTelemetryAuthProvider(() => ({ authHeader: state.authHeader, agentId: state.agentId }));
|
|
13418
|
+
setTelemetryDiagnosticsReporter((message) => log2(state, message, "warn"));
|
|
13283
13419
|
if (options.credentialSyncMarker) {
|
|
13284
13420
|
state.credentialSync = createCredentialSync({
|
|
13285
13421
|
markerPath: options.credentialSyncMarker,
|
|
@@ -13465,13 +13601,18 @@ async function run(options) {
|
|
|
13465
13601
|
if (githubTokenPopulated) await configureGitHubAccess(credentialContext);
|
|
13466
13602
|
}
|
|
13467
13603
|
state.credentialSync?.arm();
|
|
13468
|
-
|
|
13604
|
+
const openCodeActivityForwarder = createOpenCodeActivityForwarder(() => ({
|
|
13605
|
+
agentId: state.agentId,
|
|
13606
|
+
authHeader: state.authHeader
|
|
13607
|
+
}));
|
|
13608
|
+
const openCodeLogTail = tailOpenCodeLogFile(
|
|
13469
13609
|
resolveOpenCodeLogPath(homedir7(), process.env),
|
|
13470
|
-
|
|
13471
|
-
|
|
13472
|
-
|
|
13473
|
-
|
|
13474
|
-
|
|
13610
|
+
openCodeActivityForwarder
|
|
13611
|
+
);
|
|
13612
|
+
state.stopOpenCodeLogTail = () => {
|
|
13613
|
+
openCodeActivityForwarder.flush();
|
|
13614
|
+
openCodeLogTail.stop();
|
|
13615
|
+
};
|
|
13475
13616
|
let sessionDbVerifyFatal = false;
|
|
13476
13617
|
if (!options.restoreSessionDb) {
|
|
13477
13618
|
log2(state, "Skipping session-DB restore: --restore-session-db was not passed", "debug");
|
|
@@ -13780,12 +13921,13 @@ async function run(options) {
|
|
|
13780
13921
|
openCodePassword: state.opencodePassword,
|
|
13781
13922
|
isRunning: () => state.running,
|
|
13782
13923
|
events: {
|
|
13783
|
-
onConnected: (agentId, isReconnect) => {
|
|
13924
|
+
onConnected: (agentId, isReconnect, transition) => {
|
|
13784
13925
|
state.connected = true;
|
|
13785
13926
|
state.agentId = agentId;
|
|
13927
|
+
const connectionMessage = isReconnect ? `Tunnel reconnected (runner: ${agentId}, generation: ${transition.generation}, attempt: ${transition.attempt}, offline for ${transition.downtimeMs}ms)` : `Tunnel connected (runner: ${agentId}, generation: ${transition.generation})`;
|
|
13786
13928
|
logActivity(state, {
|
|
13787
13929
|
type: "info",
|
|
13788
|
-
message:
|
|
13930
|
+
message: connectionMessage
|
|
13789
13931
|
});
|
|
13790
13932
|
if (options.tunnelReadyFile) {
|
|
13791
13933
|
const marker = writeTunnelReadyMarker(options.tunnelReadyFile, agentId);
|
|
@@ -13806,7 +13948,12 @@ async function run(options) {
|
|
|
13806
13948
|
version: state.opencodeVersion,
|
|
13807
13949
|
major: state.opencodeApiVersion,
|
|
13808
13950
|
connected: state.opencodeConnected
|
|
13809
|
-
})
|
|
13951
|
+
}),
|
|
13952
|
+
connection_generation: transition.generation,
|
|
13953
|
+
...isReconnect && transition.downtimeMs !== null ? {
|
|
13954
|
+
reconnect_attempt: transition.attempt,
|
|
13955
|
+
downtime_ms: transition.downtimeMs
|
|
13956
|
+
} : {}
|
|
13810
13957
|
});
|
|
13811
13958
|
if (!isReconnect) tunnelSpinner?.succeed("Tunnel connected");
|
|
13812
13959
|
if (state.interactive) displayStatus(state);
|
|
@@ -13828,13 +13975,18 @@ async function run(options) {
|
|
|
13828
13975
|
if (state.interactive) displayStatus(state);
|
|
13829
13976
|
});
|
|
13830
13977
|
},
|
|
13831
|
-
onDisconnected: (code, reason) => {
|
|
13978
|
+
onDisconnected: (code, reason, transition) => {
|
|
13832
13979
|
state.connected = false;
|
|
13980
|
+
const safeReason = redactDiagnosticText(reason).slice(0, MAX_DISCONNECT_REASON_LENGTH);
|
|
13833
13981
|
logActivity(state, {
|
|
13834
13982
|
type: "info",
|
|
13835
|
-
message: `Tunnel disconnected (code: ${code}, reason: ${
|
|
13983
|
+
message: `Tunnel disconnected (generation: ${transition.generation}, code: ${code}, reason: ${safeReason})`
|
|
13984
|
+
});
|
|
13985
|
+
emitAgentDisconnected(state.agentId, {
|
|
13986
|
+
code,
|
|
13987
|
+
reason: safeReason,
|
|
13988
|
+
connection_generation: transition.generation
|
|
13836
13989
|
});
|
|
13837
|
-
emitAgentDisconnected(state.agentId, { code, reason });
|
|
13838
13990
|
if (state.interactive) displayStatus(state);
|
|
13839
13991
|
},
|
|
13840
13992
|
onError: (error2) => {
|
|
@@ -13934,7 +14086,7 @@ async function run(options) {
|
|
|
13934
14086
|
},
|
|
13935
14087
|
report: (usage) => reportOpenAiUsage(state.agentId, state.authHeader, usage),
|
|
13936
14088
|
isLocalCredentialProblem: isLocalCredentialProblem2,
|
|
13937
|
-
forcedOnHint: "connect a ChatGPT account to this runner, or run `opencode auth login`",
|
|
14089
|
+
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`",
|
|
13938
14090
|
firstDelayMs: firstReportDelayMs,
|
|
13939
14091
|
nextDelayMs: usageReportDelayMs,
|
|
13940
14092
|
failureLogLevel: usageReportFailureLogLevel
|