@evident-ai/cli 3.6.1-dev.e0da455 → 3.6.1-dev.e5cb54f
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/README.md +4 -0
- package/dist/index.js +1029 -64
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -100,6 +100,26 @@ function clearCredentials() {
|
|
|
100
100
|
credentials.set("byEndpoint", byEndpoint);
|
|
101
101
|
hardenCredentialsPermissions();
|
|
102
102
|
}
|
|
103
|
+
function getManagedInferenceCredential(runnerId) {
|
|
104
|
+
hardenCredentialsPermissions();
|
|
105
|
+
return credentials.get("managedInferenceByEndpoint")?.[credentialsKey()]?.[runnerId];
|
|
106
|
+
}
|
|
107
|
+
function setManagedInferenceCredential(runnerId, credential) {
|
|
108
|
+
const byEndpoint = credentials.get("managedInferenceByEndpoint") ?? {};
|
|
109
|
+
byEndpoint[credentialsKey()] ??= {};
|
|
110
|
+
byEndpoint[credentialsKey()][runnerId] = credential;
|
|
111
|
+
credentials.set("managedInferenceByEndpoint", byEndpoint);
|
|
112
|
+
hardenCredentialsPermissions();
|
|
113
|
+
}
|
|
114
|
+
function clearManagedInferenceCredential(runnerId) {
|
|
115
|
+
const byEndpoint = credentials.get("managedInferenceByEndpoint") ?? {};
|
|
116
|
+
const runners = byEndpoint[credentialsKey()];
|
|
117
|
+
if (!runners || !(runnerId in runners)) return;
|
|
118
|
+
delete runners[runnerId];
|
|
119
|
+
if (Object.keys(runners).length === 0) delete byEndpoint[credentialsKey()];
|
|
120
|
+
credentials.set("managedInferenceByEndpoint", byEndpoint);
|
|
121
|
+
hardenCredentialsPermissions();
|
|
122
|
+
}
|
|
103
123
|
function clearAllCredentials() {
|
|
104
124
|
credentials.clear();
|
|
105
125
|
hardenCredentialsPermissions();
|
|
@@ -119,13 +139,13 @@ function getCliName() {
|
|
|
119
139
|
// src/lib/api.ts
|
|
120
140
|
async function request(path, method, body, headers = {}) {
|
|
121
141
|
const url = `${getApiUrlConfig()}${path}`;
|
|
122
|
-
const
|
|
142
|
+
const requestHeaders2 = {
|
|
123
143
|
"Content-Type": "application/json",
|
|
124
144
|
...headers
|
|
125
145
|
};
|
|
126
146
|
const response = await fetch(url, {
|
|
127
147
|
method,
|
|
128
|
-
headers:
|
|
148
|
+
headers: requestHeaders2,
|
|
129
149
|
body: body ? JSON.stringify(body) : void 0
|
|
130
150
|
});
|
|
131
151
|
if (!response.ok) {
|
|
@@ -1156,9 +1176,9 @@ async function claudeUsage() {
|
|
|
1156
1176
|
}
|
|
1157
1177
|
|
|
1158
1178
|
// src/commands/run.ts
|
|
1159
|
-
import { chmodSync as chmodSync3, existsSync as existsSync3, readFileSync as
|
|
1160
|
-
import { homedir as
|
|
1161
|
-
import { isAbsolute as isAbsolute4, join as
|
|
1179
|
+
import { chmodSync as chmodSync3, existsSync as existsSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "node:fs";
|
|
1180
|
+
import { homedir as homedir8 } from "node:os";
|
|
1181
|
+
import { isAbsolute as isAbsolute4, join as join13, parse, resolve as resolvePath2 } from "node:path";
|
|
1162
1182
|
import chalk7 from "chalk";
|
|
1163
1183
|
|
|
1164
1184
|
// ../../packages/types/src/agents/index.ts
|
|
@@ -1287,6 +1307,102 @@ var TUNNEL_USAGE_REARM_PING_PATH = "/__evident/usage-rearm";
|
|
|
1287
1307
|
var MAX_FILE_PUSH_BYTES = 64 * 1024;
|
|
1288
1308
|
var MAX_FILE_SYNC_DIRECTORIES = 16;
|
|
1289
1309
|
|
|
1310
|
+
// ../../packages/types/src/credential-proxy-native-config.ts
|
|
1311
|
+
var EVIDENT_PROXY_TOKEN_REFERENCE = "{env:EVIDENT_PROXY_TOKEN}";
|
|
1312
|
+
var CLAUDE_PROVIDER = {
|
|
1313
|
+
namespace: "evident-claude",
|
|
1314
|
+
name: "Evident Claude credential proxy",
|
|
1315
|
+
npm: "@ai-sdk/anthropic"
|
|
1316
|
+
};
|
|
1317
|
+
var NATIVE_PROVIDERS = {
|
|
1318
|
+
claude: CLAUDE_PROVIDER,
|
|
1319
|
+
anthropic: CLAUDE_PROVIDER,
|
|
1320
|
+
codex: {
|
|
1321
|
+
namespace: "evident-codex",
|
|
1322
|
+
name: "Evident Codex credential proxy",
|
|
1323
|
+
npm: "@ai-sdk/openai"
|
|
1324
|
+
},
|
|
1325
|
+
openai: {
|
|
1326
|
+
namespace: "evident-openai",
|
|
1327
|
+
name: "Evident OpenAI credential proxy",
|
|
1328
|
+
npm: "@ai-sdk/openai"
|
|
1329
|
+
},
|
|
1330
|
+
kimi: {
|
|
1331
|
+
namespace: "evident-kimi",
|
|
1332
|
+
name: "Evident Kimi credential proxy",
|
|
1333
|
+
npm: "@ai-sdk/openai-compatible",
|
|
1334
|
+
sdkName: "evident-kimi"
|
|
1335
|
+
},
|
|
1336
|
+
antigravity: {
|
|
1337
|
+
namespace: "evident-antigravity",
|
|
1338
|
+
name: "Evident Antigravity credential proxy",
|
|
1339
|
+
npm: "@ai-sdk/openai-compatible",
|
|
1340
|
+
sdkName: "evident-antigravity"
|
|
1341
|
+
},
|
|
1342
|
+
xai: {
|
|
1343
|
+
namespace: "evident-xai",
|
|
1344
|
+
name: "Evident xAI credential proxy",
|
|
1345
|
+
npm: "@ai-sdk/openai-compatible",
|
|
1346
|
+
sdkName: "evident-xai"
|
|
1347
|
+
},
|
|
1348
|
+
mistral: {
|
|
1349
|
+
namespace: "evident-mistral",
|
|
1350
|
+
name: "Evident Mistral credential proxy",
|
|
1351
|
+
npm: "@ai-sdk/openai-compatible",
|
|
1352
|
+
sdkName: "evident-mistral"
|
|
1353
|
+
}
|
|
1354
|
+
};
|
|
1355
|
+
var CREDENTIAL_PROXY_NATIVE_NAMESPACES = [
|
|
1356
|
+
...new Set(Object.values(NATIVE_PROVIDERS).map(({ namespace }) => namespace))
|
|
1357
|
+
];
|
|
1358
|
+
function credentialProxyInferenceUrl(baseUrl) {
|
|
1359
|
+
const trimmed = baseUrl.trim().replace(/\/+$/, "");
|
|
1360
|
+
return /\/v1$/i.test(trimmed) ? trimmed : `${trimmed}/v1`;
|
|
1361
|
+
}
|
|
1362
|
+
function buildCredentialProxyNativeConfig({
|
|
1363
|
+
inferenceBaseUrl,
|
|
1364
|
+
modelDescriptors
|
|
1365
|
+
}) {
|
|
1366
|
+
const baseURL = credentialProxyInferenceUrl(inferenceBaseUrl);
|
|
1367
|
+
const providers = {};
|
|
1368
|
+
const unsupportedProviders = /* @__PURE__ */ new Set();
|
|
1369
|
+
for (const descriptor of modelDescriptors) {
|
|
1370
|
+
const definition = NATIVE_PROVIDERS[descriptor.provider.trim().toLowerCase()];
|
|
1371
|
+
if (!definition) {
|
|
1372
|
+
unsupportedProviders.add(descriptor.provider.trim() || descriptor.provider);
|
|
1373
|
+
continue;
|
|
1374
|
+
}
|
|
1375
|
+
providers[definition.namespace] ??= definition.npm === "@ai-sdk/anthropic" ? {
|
|
1376
|
+
name: definition.name,
|
|
1377
|
+
npm: definition.npm,
|
|
1378
|
+
options: { baseURL, authToken: EVIDENT_PROXY_TOKEN_REFERENCE },
|
|
1379
|
+
models: {}
|
|
1380
|
+
} : definition.npm === "@ai-sdk/openai" ? {
|
|
1381
|
+
name: definition.name,
|
|
1382
|
+
npm: definition.npm,
|
|
1383
|
+
options: { baseURL, apiKey: EVIDENT_PROXY_TOKEN_REFERENCE },
|
|
1384
|
+
models: {}
|
|
1385
|
+
} : {
|
|
1386
|
+
name: definition.name,
|
|
1387
|
+
npm: definition.npm,
|
|
1388
|
+
options: {
|
|
1389
|
+
name: definition.sdkName,
|
|
1390
|
+
baseURL,
|
|
1391
|
+
apiKey: EVIDENT_PROXY_TOKEN_REFERENCE
|
|
1392
|
+
},
|
|
1393
|
+
models: {}
|
|
1394
|
+
};
|
|
1395
|
+
providers[definition.namespace].models[descriptor.id] = { name: descriptor.id };
|
|
1396
|
+
}
|
|
1397
|
+
return {
|
|
1398
|
+
unsupportedProviders: [...unsupportedProviders],
|
|
1399
|
+
config: {
|
|
1400
|
+
$schema: "https://opencode.ai/config.json",
|
|
1401
|
+
provider: providers
|
|
1402
|
+
}
|
|
1403
|
+
};
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1290
1406
|
// ../../packages/types/src/logging/index.ts
|
|
1291
1407
|
var CORRELATION_ID_HEADER = "x-evident-correlation-id";
|
|
1292
1408
|
var FORWARD_FAILURE_REASON_HEADER = "X-Evident-Failure-Reason";
|
|
@@ -1326,7 +1442,7 @@ import ora4 from "ora";
|
|
|
1326
1442
|
import { select as select4 } from "@inquirer/prompts";
|
|
1327
1443
|
|
|
1328
1444
|
// src/lib/telemetry.ts
|
|
1329
|
-
var CLI_VERSION = (true ? "3.6.1-dev.
|
|
1445
|
+
var CLI_VERSION = (true ? "3.6.1-dev.e5cb54f" : void 0) ?? process.env.npm_package_version ?? "unknown";
|
|
1330
1446
|
function getCliVersion() {
|
|
1331
1447
|
return CLI_VERSION;
|
|
1332
1448
|
}
|
|
@@ -1407,7 +1523,7 @@ async function flushEvents() {
|
|
|
1407
1523
|
const controller = new AbortController();
|
|
1408
1524
|
const timeout = setTimeout(() => controller.abort(), FLUSH_TIMEOUT_MS);
|
|
1409
1525
|
try {
|
|
1410
|
-
const
|
|
1526
|
+
const request3 = {
|
|
1411
1527
|
events,
|
|
1412
1528
|
client_type: "cli",
|
|
1413
1529
|
client_version: CLI_VERSION
|
|
@@ -1418,7 +1534,7 @@ async function flushEvents() {
|
|
|
1418
1534
|
"Content-Type": "application/json",
|
|
1419
1535
|
Authorization: authHeader
|
|
1420
1536
|
},
|
|
1421
|
-
body: JSON.stringify(
|
|
1537
|
+
body: JSON.stringify(request3),
|
|
1422
1538
|
signal: controller.signal
|
|
1423
1539
|
});
|
|
1424
1540
|
if (!response.ok) {
|
|
@@ -2075,7 +2191,8 @@ function createDependencyInstallDiagnosticReporter({
|
|
|
2075
2191
|
var FORWARDED_LEVELS_BY_SOURCE = {
|
|
2076
2192
|
"cli.run": /* @__PURE__ */ new Set(["warn", "error"]),
|
|
2077
2193
|
opencode: /* @__PURE__ */ new Set(["warn", "error"]),
|
|
2078
|
-
"cli.session-cleanup": /* @__PURE__ */ new Set(["info", "warn", "error"])
|
|
2194
|
+
"cli.session-cleanup": /* @__PURE__ */ new Set(["info", "warn", "error"]),
|
|
2195
|
+
"cli.runtime-provenance": /* @__PURE__ */ new Set(["info", "warn", "error"])
|
|
2079
2196
|
};
|
|
2080
2197
|
var SEVERITY_BY_LEVEL = {
|
|
2081
2198
|
info: "info",
|
|
@@ -2871,6 +2988,58 @@ async function listAllSessions(port, client) {
|
|
|
2871
2988
|
}
|
|
2872
2989
|
return { sessions };
|
|
2873
2990
|
}
|
|
2991
|
+
var INTERRUPT_NETWORK_CODES = /* @__PURE__ */ new Set([
|
|
2992
|
+
"ABORT_ERR",
|
|
2993
|
+
"EAI_AGAIN",
|
|
2994
|
+
"ECONNREFUSED",
|
|
2995
|
+
"ECONNRESET",
|
|
2996
|
+
"ENETUNREACH",
|
|
2997
|
+
"ENOTFOUND",
|
|
2998
|
+
"ETIMEDOUT",
|
|
2999
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
3000
|
+
"UND_ERR_HEADERS_TIMEOUT",
|
|
3001
|
+
"UND_ERR_SOCKET"
|
|
3002
|
+
]);
|
|
3003
|
+
function interruptFailureDiagnostic(error2) {
|
|
3004
|
+
const message = error2 instanceof Error ? error2.message : "";
|
|
3005
|
+
const httpStatus = /^OpenCode request failed: HTTP ([1-5]\d{2})(?::|$)/.exec(message)?.[1];
|
|
3006
|
+
if (httpStatus) return `HTTP ${httpStatus}`;
|
|
3007
|
+
const candidates = [error2, error2 instanceof Error ? error2.cause : void 0];
|
|
3008
|
+
for (const candidate of candidates) {
|
|
3009
|
+
if (typeof candidate !== "object" || candidate === null || !("code" in candidate)) continue;
|
|
3010
|
+
const code = candidate.code;
|
|
3011
|
+
if (typeof code === "string" && INTERRUPT_NETWORK_CODES.has(code)) {
|
|
3012
|
+
return redactDiagnosticText(`network ${code}`).slice(0, 120);
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
const name = error2 instanceof Error && ["AbortError", "Error", "TimeoutError", "TypeError"].includes(error2.name) ? error2.name : "unknown error";
|
|
3016
|
+
return redactDiagnosticText(`request threw ${name}`).slice(0, 120);
|
|
3017
|
+
}
|
|
3018
|
+
async function interruptSession(port, id, client) {
|
|
3019
|
+
if (client?.version === "v2") {
|
|
3020
|
+
try {
|
|
3021
|
+
await interruptV2Session(client, id);
|
|
3022
|
+
return { kind: "confirmed_stopped" };
|
|
3023
|
+
} catch (error2) {
|
|
3024
|
+
return { kind: "not_confirmed", diagnostic: interruptFailureDiagnostic(error2) };
|
|
3025
|
+
}
|
|
3026
|
+
}
|
|
3027
|
+
try {
|
|
3028
|
+
const res = await requestWithClient(
|
|
3029
|
+
port,
|
|
3030
|
+
client,
|
|
3031
|
+
`/session/${id}/abort`,
|
|
3032
|
+
{ method: "POST" },
|
|
3033
|
+
{ allowStatuses: ALL_HTTP_STATUSES }
|
|
3034
|
+
);
|
|
3035
|
+
if (res.status >= 200 && res.status < 300) {
|
|
3036
|
+
return { kind: "confirmed_stopped", status: res.status };
|
|
3037
|
+
}
|
|
3038
|
+
return { kind: "not_confirmed", status: res.status };
|
|
3039
|
+
} catch (error2) {
|
|
3040
|
+
return { kind: "threw", diagnostic: interruptFailureDiagnostic(error2) };
|
|
3041
|
+
}
|
|
3042
|
+
}
|
|
2874
3043
|
async function deleteSession(port, id, client) {
|
|
2875
3044
|
if (client?.version === "v2") return deleteV2Session(client, id);
|
|
2876
3045
|
try {
|
|
@@ -3040,6 +3209,52 @@ async function getModelAttachmentCapability(port, model, client) {
|
|
|
3040
3209
|
return null;
|
|
3041
3210
|
}
|
|
3042
3211
|
}
|
|
3212
|
+
async function isPinnedProviderLoaded(port, model, client) {
|
|
3213
|
+
if (client?.version === "v2") return null;
|
|
3214
|
+
const { model: baseModel } = splitModelVariant(model);
|
|
3215
|
+
if (!baseModel) return null;
|
|
3216
|
+
const slash = baseModel.indexOf("/");
|
|
3217
|
+
if (slash <= 0) return null;
|
|
3218
|
+
const providerId = baseModel.slice(0, slash);
|
|
3219
|
+
try {
|
|
3220
|
+
const res = await requestWithClient(port, client, "/config/providers");
|
|
3221
|
+
if (!res.ok) {
|
|
3222
|
+
console.error(
|
|
3223
|
+
`[isPinnedProviderLoaded] GET /config/providers returned HTTP ${res.status} (port ${port})`
|
|
3224
|
+
);
|
|
3225
|
+
return null;
|
|
3226
|
+
}
|
|
3227
|
+
const body = await res.json();
|
|
3228
|
+
if (body === null || typeof body !== "object" || Array.isArray(body)) {
|
|
3229
|
+
console.error(
|
|
3230
|
+
`[isPinnedProviderLoaded] GET /config/providers body was not a plain object (port ${port})`
|
|
3231
|
+
);
|
|
3232
|
+
return null;
|
|
3233
|
+
}
|
|
3234
|
+
const providers = body.providers;
|
|
3235
|
+
if (!Array.isArray(providers)) {
|
|
3236
|
+
console.error(
|
|
3237
|
+
`[isPinnedProviderLoaded] GET /config/providers body had no providers array (port ${port})`
|
|
3238
|
+
);
|
|
3239
|
+
return null;
|
|
3240
|
+
}
|
|
3241
|
+
const providerIds = providers.filter(
|
|
3242
|
+
(provider) => provider !== null && typeof provider === "object" && typeof provider.id === "string"
|
|
3243
|
+
).map((provider) => provider.id);
|
|
3244
|
+
if (providerIds.length === 0) {
|
|
3245
|
+
console.error(
|
|
3246
|
+
`[isPinnedProviderLoaded] GET /config/providers had no provider entries with string ids (port ${port})`
|
|
3247
|
+
);
|
|
3248
|
+
return null;
|
|
3249
|
+
}
|
|
3250
|
+
return providerIds.includes(providerId);
|
|
3251
|
+
} catch (err) {
|
|
3252
|
+
console.error(
|
|
3253
|
+
`[isPinnedProviderLoaded] GET /config/providers failed (port ${port}): ${err instanceof Error ? err.message : String(err)}`
|
|
3254
|
+
);
|
|
3255
|
+
return null;
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3043
3258
|
async function buildFileParts(attachments, capable) {
|
|
3044
3259
|
const outcomes = [];
|
|
3045
3260
|
const parts = [];
|
|
@@ -4221,6 +4436,11 @@ async function listV2Permissions(client, sessionId) {
|
|
|
4221
4436
|
return null;
|
|
4222
4437
|
}
|
|
4223
4438
|
}
|
|
4439
|
+
async function interruptV2Session(client, sessionId) {
|
|
4440
|
+
await client.request(`/api/session/${encodeURIComponent(sessionId)}/interrupt`, {
|
|
4441
|
+
method: "POST"
|
|
4442
|
+
});
|
|
4443
|
+
}
|
|
4224
4444
|
async function getV2ActiveSessions(client) {
|
|
4225
4445
|
try {
|
|
4226
4446
|
const body = await readJson(await client.request("/api/session/active"));
|
|
@@ -4623,11 +4843,11 @@ function failureMessage(status2) {
|
|
|
4623
4843
|
function isRetryableStatus(status2) {
|
|
4624
4844
|
return CONNECT_KEY_RETRY_STATUSES.includes(status2);
|
|
4625
4845
|
}
|
|
4626
|
-
async function importOneCredential(
|
|
4846
|
+
async function importOneCredential(request3, credential, sleepForRetry, log3) {
|
|
4627
4847
|
const path = `/api/integration/${encodeURIComponent(credential.integrationId)}/connect/key`;
|
|
4628
4848
|
for (let attempt = 1; attempt <= CONNECT_KEY_MAX_ATTEMPTS; attempt += 1) {
|
|
4629
4849
|
try {
|
|
4630
|
-
const response = await
|
|
4850
|
+
const response = await request3(
|
|
4631
4851
|
path,
|
|
4632
4852
|
{
|
|
4633
4853
|
method: "POST",
|
|
@@ -4662,7 +4882,7 @@ async function importOneCredential(request2, credential, sleepForRetry, log3) {
|
|
|
4662
4882
|
throw new Error("OpenCode credential import retry loop did not return");
|
|
4663
4883
|
}
|
|
4664
4884
|
async function importOpenCodeCredentials({
|
|
4665
|
-
request:
|
|
4885
|
+
request: request3,
|
|
4666
4886
|
classified,
|
|
4667
4887
|
connectedIntegrationIds,
|
|
4668
4888
|
sleepForRetry = sleep2,
|
|
@@ -4674,7 +4894,7 @@ async function importOpenCodeCredentials({
|
|
|
4674
4894
|
result.alreadyConnected.push(credential.integrationId);
|
|
4675
4895
|
continue;
|
|
4676
4896
|
}
|
|
4677
|
-
const outcome = await importOneCredential(
|
|
4897
|
+
const outcome = await importOneCredential(request3, credential, sleepForRetry, log3);
|
|
4678
4898
|
if (outcome.ok) result.imported.push(credential.integrationId);
|
|
4679
4899
|
else result.failed.push(outcome.failure);
|
|
4680
4900
|
}
|
|
@@ -4692,7 +4912,7 @@ function parseIntegrationList(value) {
|
|
|
4692
4912
|
return { integrationIds, hasData: value.data.length > 0 };
|
|
4693
4913
|
}
|
|
4694
4914
|
async function getConnectedOpenCodeIntegrationIds({
|
|
4695
|
-
request:
|
|
4915
|
+
request: request3,
|
|
4696
4916
|
directory,
|
|
4697
4917
|
sleepForRetry = sleep2,
|
|
4698
4918
|
log: log3
|
|
@@ -4700,7 +4920,7 @@ async function getConnectedOpenCodeIntegrationIds({
|
|
|
4700
4920
|
const path = `/api/integration?location%5Bdirectory%5D=${encodeURIComponent(directory)}`;
|
|
4701
4921
|
for (let attempt = 1; attempt <= INTEGRATION_LIST_MAX_READS; attempt += 1) {
|
|
4702
4922
|
try {
|
|
4703
|
-
const response = await
|
|
4923
|
+
const response = await request3(path);
|
|
4704
4924
|
const parsed = parseIntegrationList(await response.json());
|
|
4705
4925
|
if (parsed === null) {
|
|
4706
4926
|
log3(
|
|
@@ -4729,7 +4949,7 @@ async function getConnectedOpenCodeIntegrationIds({
|
|
|
4729
4949
|
return null;
|
|
4730
4950
|
}
|
|
4731
4951
|
async function importOpenCodeV2Credentials({
|
|
4732
|
-
request:
|
|
4952
|
+
request: request3,
|
|
4733
4953
|
homeDir,
|
|
4734
4954
|
directory,
|
|
4735
4955
|
sleepForRetry = sleep2,
|
|
@@ -4747,7 +4967,7 @@ async function importOpenCodeV2Credentials({
|
|
|
4747
4967
|
const classified = classifyOpenCodeCredentials(credentials2);
|
|
4748
4968
|
if (classified.importable.length === 0) return emptyResult(classified.unsupported);
|
|
4749
4969
|
const connectedIntegrationIds = await getConnectedOpenCodeIntegrationIds({
|
|
4750
|
-
request:
|
|
4970
|
+
request: request3,
|
|
4751
4971
|
directory,
|
|
4752
4972
|
sleepForRetry,
|
|
4753
4973
|
log: log3
|
|
@@ -4756,7 +4976,7 @@ async function importOpenCodeV2Credentials({
|
|
|
4756
4976
|
return emptyResult(classified.unsupported, "connection-list-unavailable");
|
|
4757
4977
|
}
|
|
4758
4978
|
return importOpenCodeCredentials({
|
|
4759
|
-
request:
|
|
4979
|
+
request: request3,
|
|
4760
4980
|
classified,
|
|
4761
4981
|
connectedIntegrationIds,
|
|
4762
4982
|
sleepForRetry,
|
|
@@ -5447,7 +5667,11 @@ function isSessionDbProvenanceRecord(value) {
|
|
|
5447
5667
|
var OPENCODE2_PACKAGE_SPEC = "@opencode-ai/cli@0.0.0-beta-17823";
|
|
5448
5668
|
|
|
5449
5669
|
// src/lib/opencode/opencode-version-gate.ts
|
|
5450
|
-
var QUEUE_VALIDATED_OPENCODE_VERSIONS = [
|
|
5670
|
+
var QUEUE_VALIDATED_OPENCODE_VERSIONS = [
|
|
5671
|
+
"1.17.11",
|
|
5672
|
+
"1.18.3",
|
|
5673
|
+
"1.18.35"
|
|
5674
|
+
];
|
|
5451
5675
|
function isQueueValidatedVersion(version2) {
|
|
5452
5676
|
if (!version2) return false;
|
|
5453
5677
|
return QUEUE_VALIDATED_OPENCODE_VERSIONS.includes(version2);
|
|
@@ -6492,6 +6716,7 @@ function connectTunnel(options) {
|
|
|
6492
6716
|
}, 3e4);
|
|
6493
6717
|
let relaySilenceTimeout;
|
|
6494
6718
|
let relaySilenceTimedOut = false;
|
|
6719
|
+
let relayConnectionId;
|
|
6495
6720
|
const clearRelaySilenceTimeout = () => {
|
|
6496
6721
|
if (relaySilenceTimeout === void 0) return;
|
|
6497
6722
|
clearTimeout(relaySilenceTimeout);
|
|
@@ -6577,7 +6802,8 @@ function connectTunnel(options) {
|
|
|
6577
6802
|
armRelaySilenceTimeout();
|
|
6578
6803
|
forwarder.setBinaryFramesSupported(message.binary_frames === true);
|
|
6579
6804
|
const connectedAgentId = message.agent_id ?? agentId;
|
|
6580
|
-
|
|
6805
|
+
relayConnectionId = message.connection_id;
|
|
6806
|
+
onConnected?.(connectedAgentId, relayConnectionId);
|
|
6581
6807
|
resolve5({
|
|
6582
6808
|
ws,
|
|
6583
6809
|
close: () => ws.close(1e3, "CLI shutdown")
|
|
@@ -6614,7 +6840,7 @@ function connectTunnel(options) {
|
|
|
6614
6840
|
clearRelaySilenceTimeout();
|
|
6615
6841
|
const reasonStr = (relaySilenceTimedOut ? "no relay heartbeat for 90s \u2014 reconnecting" : void 0) || reason.toString() || upgradeRejection || (code === 1006 ? "abnormal closure" : "No reason provided");
|
|
6616
6842
|
forwarder.abortAll();
|
|
6617
|
-
onDisconnected?.(code, reasonStr);
|
|
6843
|
+
onDisconnected?.(code, reasonStr, relayConnectionId);
|
|
6618
6844
|
});
|
|
6619
6845
|
});
|
|
6620
6846
|
}
|
|
@@ -6677,7 +6903,7 @@ var RunnerConnection = class _RunnerConnection {
|
|
|
6677
6903
|
authHeader: this.opts.getAuthHeader(),
|
|
6678
6904
|
port: this.opts.port,
|
|
6679
6905
|
openCodePassword: this.opts.openCodePassword,
|
|
6680
|
-
onConnected: (agentId) => {
|
|
6906
|
+
onConnected: (agentId, relayConnectionId) => {
|
|
6681
6907
|
const attempt = this.reconnectAttempt + 1;
|
|
6682
6908
|
this.reconnectAttempt = 0;
|
|
6683
6909
|
this.reconnecting = false;
|
|
@@ -6687,13 +6913,17 @@ var RunnerConnection = class _RunnerConnection {
|
|
|
6687
6913
|
this.disconnectedAtMs = null;
|
|
6688
6914
|
events.onConnected(agentId, isReconnect, {
|
|
6689
6915
|
generation: this.generation,
|
|
6916
|
+
relayConnectionId: relayConnectionId ?? null,
|
|
6690
6917
|
attempt,
|
|
6691
6918
|
downtimeMs
|
|
6692
6919
|
});
|
|
6693
6920
|
},
|
|
6694
|
-
onDisconnected: (code, reason) => {
|
|
6921
|
+
onDisconnected: (code, reason, relayConnectionId) => {
|
|
6695
6922
|
this.disconnectedAtMs = this.now();
|
|
6696
|
-
events.onDisconnected(code, reason, {
|
|
6923
|
+
events.onDisconnected(code, reason, {
|
|
6924
|
+
generation: this.generation,
|
|
6925
|
+
relayConnectionId: relayConnectionId ?? null
|
|
6926
|
+
});
|
|
6697
6927
|
const relayRequestedReconnect = code === 1e3 && _RunnerConnection.RELAY_RECONNECT_CLOSE_REASONS.has(reason);
|
|
6698
6928
|
if (this.opts.isRunning() && (code !== 1e3 || relayRequestedReconnect) && !this.reconnecting) {
|
|
6699
6929
|
this.reconnectPromise = this.connectWithRetry(true).catch((err) => {
|
|
@@ -7141,8 +7371,8 @@ import { chmod, mkdir, open as open3, realpath, rename, unlink } from "node:fs/p
|
|
|
7141
7371
|
import { basename as basename2, dirname as dirname6, isAbsolute as isAbsolute2, join as join9, relative as relative2, resolve as resolve3, sep as sep2 } from "node:path";
|
|
7142
7372
|
var FILE_MODE = 384;
|
|
7143
7373
|
var DIRECTORY_MODE = 448;
|
|
7144
|
-
async function writePushedFile(
|
|
7145
|
-
const { requestedPath, content, allowedDirectories, homeDir } =
|
|
7374
|
+
async function writePushedFile(request3) {
|
|
7375
|
+
const { requestedPath, content, allowedDirectories, homeDir } = request3;
|
|
7146
7376
|
const bytes = content.byteLength;
|
|
7147
7377
|
if (allowedDirectories.length === 0) {
|
|
7148
7378
|
return refuse("file_sync_disabled", "File sync is not enabled on this runner.", {
|
|
@@ -8538,6 +8768,47 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
8538
8768
|
agent: message.opencode_agent ?? void 0,
|
|
8539
8769
|
model: message.opencode_model ?? void 0
|
|
8540
8770
|
};
|
|
8771
|
+
const pinnedProviderLoaded = await isPinnedProviderLoaded(
|
|
8772
|
+
this.port,
|
|
8773
|
+
options.model,
|
|
8774
|
+
this.openCodeClient
|
|
8775
|
+
);
|
|
8776
|
+
if (pinnedProviderLoaded === false) {
|
|
8777
|
+
const dispatchedModel = message.opencode_model;
|
|
8778
|
+
const separator = dispatchedModel?.indexOf("/") ?? -1;
|
|
8779
|
+
const hasProviderAndModel = dispatchedModel !== null && separator > 0 && separator < dispatchedModel.length - 1;
|
|
8780
|
+
const providerId = hasProviderAndModel ? dispatchedModel.slice(0, separator) : null;
|
|
8781
|
+
const modelId = hasProviderAndModel ? dispatchedModel.slice(separator + 1) : null;
|
|
8782
|
+
const errorMessage3 = `Provider namespace not loaded on this runner: ${message.opencode_model}. Refused before dispatch; no turn was started.`;
|
|
8783
|
+
this.log({
|
|
8784
|
+
level: "error",
|
|
8785
|
+
message: `Refusing message ${message.id.slice(0, 8)} in conversation ${conv.id.slice(0, 8)}: provider namespace ${providerId ?? "(unknown)"} is not loaded; no turn was started`,
|
|
8786
|
+
conversation_id: conv.id,
|
|
8787
|
+
message_id: message.id
|
|
8788
|
+
});
|
|
8789
|
+
try {
|
|
8790
|
+
await this.markFailed(conv.id, message.id, sessionId, errorMessage3, void 0, {
|
|
8791
|
+
classification: {
|
|
8792
|
+
kind: "model_auth",
|
|
8793
|
+
providerId,
|
|
8794
|
+
modelId,
|
|
8795
|
+
reason: "missing"
|
|
8796
|
+
},
|
|
8797
|
+
errorShape: { name: null, statusCode: null }
|
|
8798
|
+
});
|
|
8799
|
+
this.signalDispatchNotStarted(conv, message, "model_provider_absent");
|
|
8800
|
+
} catch (markErr) {
|
|
8801
|
+
this.log({
|
|
8802
|
+
level: "warn",
|
|
8803
|
+
message: `markFailed PATCH for refused message ${message.id.slice(0, 8)} (conversation ${conv.id.slice(0, 8)}) failed: ${markErr instanceof Error ? markErr.message : String(markErr)}`,
|
|
8804
|
+
conversation_id: conv.id,
|
|
8805
|
+
message_id: message.id
|
|
8806
|
+
});
|
|
8807
|
+
this.signalDispatchNotStarted(conv, message, "failure_unreported");
|
|
8808
|
+
}
|
|
8809
|
+
await this.repairProviderRegistry(conv.id, message.id);
|
|
8810
|
+
break;
|
|
8811
|
+
}
|
|
8541
8812
|
let opencodeMessageId;
|
|
8542
8813
|
let preDispatchUserIds = null;
|
|
8543
8814
|
try {
|
|
@@ -8891,6 +9162,34 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
8891
9162
|
if (state === "failed" && isAbortedTerminalReply(messages, ocId ?? "")) {
|
|
8892
9163
|
const ongoing = await this.isSessionOngoing(sessionId);
|
|
8893
9164
|
if (ongoing === false) {
|
|
9165
|
+
const age = this.absoluteAgeForRow(message.processing_started_at);
|
|
9166
|
+
if (age.tripped && age.anchorMs !== null) {
|
|
9167
|
+
try {
|
|
9168
|
+
await this.markFailed(
|
|
9169
|
+
conv.id,
|
|
9170
|
+
message.id,
|
|
9171
|
+
sessionId,
|
|
9172
|
+
this.absoluteCeilingError(message.id, sessionId, age.anchorMs),
|
|
9173
|
+
void 0,
|
|
9174
|
+
{
|
|
9175
|
+
classification: null,
|
|
9176
|
+
errorShape: extractReplyErrorShape(findLastAssistantReplyFor(messages, ocId ?? ""))
|
|
9177
|
+
}
|
|
9178
|
+
);
|
|
9179
|
+
} catch (err) {
|
|
9180
|
+
if (err instanceof ChannelAuthError) throw err;
|
|
9181
|
+
this.log({
|
|
9182
|
+
level: "warn",
|
|
9183
|
+
message: `Re-drive: failed to report past-ceiling restart orphan ${message.id.slice(0, 8)} terminal; refusing to re-dispatch: ${err instanceof Error ? err.message : String(err)}`,
|
|
9184
|
+
conversation_id: conv.id,
|
|
9185
|
+
message_id: message.id
|
|
9186
|
+
});
|
|
9187
|
+
return "unresolved";
|
|
9188
|
+
}
|
|
9189
|
+
this.clearRedriveUnresolved(message.id);
|
|
9190
|
+
void this.postSignal(conv.id, message.id, "redrive_ceiling_refused");
|
|
9191
|
+
return "settled";
|
|
9192
|
+
}
|
|
8894
9193
|
this.log({
|
|
8895
9194
|
level: "info",
|
|
8896
9195
|
message: `Re-drive: message ${message.id.slice(0, 8)} carries an abort-shaped terminal error and session ${sessionId.slice(0, 8)} is not-ongoing per the active-session status check \u2014 restart orphan, re-dispatching instead of marking it permanently failed`,
|
|
@@ -8961,6 +9260,29 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
8961
9260
|
message_id: message.id
|
|
8962
9261
|
});
|
|
8963
9262
|
}
|
|
9263
|
+
if (this.absoluteAgeForRow(message.processing_started_at).tripped) {
|
|
9264
|
+
try {
|
|
9265
|
+
await this.markFailed(
|
|
9266
|
+
conv.id,
|
|
9267
|
+
message.id,
|
|
9268
|
+
sessionId,
|
|
9269
|
+
this.absoluteCeilingError(message.id, sessionId, anchorMs)
|
|
9270
|
+
);
|
|
9271
|
+
} catch (err) {
|
|
9272
|
+
if (err instanceof ChannelAuthError) throw err;
|
|
9273
|
+
this.log({
|
|
9274
|
+
level: "warn",
|
|
9275
|
+
message: `Re-drive: failed to report past-ceiling message ${message.id.slice(0, 8)} terminal; refusing to re-attach: ${err instanceof Error ? err.message : String(err)}`,
|
|
9276
|
+
conversation_id: conv.id,
|
|
9277
|
+
message_id: message.id
|
|
9278
|
+
});
|
|
9279
|
+
const bound = await this.boundRedriveOutcome(conv, message, "reattach");
|
|
9280
|
+
return bound === "abandoned" ? "abandoned" : "unresolved";
|
|
9281
|
+
}
|
|
9282
|
+
this.clearRedriveUnresolved(message.id);
|
|
9283
|
+
void this.postSignal(conv.id, message.id, "redrive_ceiling_refused");
|
|
9284
|
+
return "abandoned";
|
|
9285
|
+
}
|
|
8964
9286
|
const title = await this.resolveSessionTitle(sessionId, conv.id);
|
|
8965
9287
|
try {
|
|
8966
9288
|
await this.markProcessing(conv.id, message.id, sessionId, ocId, title);
|
|
@@ -9227,8 +9549,7 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
9227
9549
|
const since = this.redriveOutcomeFailingSince.get(message.id);
|
|
9228
9550
|
if (since === void 0) this.redriveOutcomeFailingSince.set(message.id, now);
|
|
9229
9551
|
const durationTripped = now - (since ?? now) >= this.pausedMaxWaitMs;
|
|
9230
|
-
const
|
|
9231
|
-
const absoluteAgeTripped = !Number.isNaN(parsed) && now - parsed >= ABSOLUTE_MAX_PROCESSING_MS;
|
|
9552
|
+
const absoluteAgeTripped = this.absoluteAgeForRow(message.processing_started_at).tripped;
|
|
9232
9553
|
if (!durationTripped && !absoluteAgeTripped) {
|
|
9233
9554
|
this.signalRedriveOutcomeUnreported(conv, message, outcome);
|
|
9234
9555
|
return "retry";
|
|
@@ -9267,6 +9588,15 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
9267
9588
|
});
|
|
9268
9589
|
return "abandoned";
|
|
9269
9590
|
}
|
|
9591
|
+
/** Row-level guards use the durable server stamp; in-flight guards use processingAnchorMs. */
|
|
9592
|
+
absoluteAgeForRow(stamp) {
|
|
9593
|
+
const anchorMs = stamp ? Date.parse(stamp) : NaN;
|
|
9594
|
+
return Number.isNaN(anchorMs) ? { tripped: false, anchorMs: null } : { tripped: this.now() - anchorMs >= ABSOLUTE_MAX_PROCESSING_MS, anchorMs };
|
|
9595
|
+
}
|
|
9596
|
+
absoluteCeilingError(messageId, sessionId, anchorMs, retainedReason = this.readRetainedSessionError(sessionId)) {
|
|
9597
|
+
const baseError = `Message ${messageId.slice(0, 8)} exceeded the absolute processing ceiling (${Math.round((this.now() - anchorMs) / 6e4)}min, session ${sessionId}) while still actively running.`;
|
|
9598
|
+
return retainedReason ? `${baseError} ${SESSION_ERROR_RETAINED_DETAIL}${retainedReason}` : baseError;
|
|
9599
|
+
}
|
|
9270
9600
|
/**
|
|
9271
9601
|
* Record one poll outcome toward the re-drive fence's consecutive-identical-
|
|
9272
9602
|
* failure streak (#1348) and return the resulting count. `signature === null`
|
|
@@ -9708,10 +10038,7 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
9708
10038
|
opencodeMessageId,
|
|
9709
10039
|
message,
|
|
9710
10040
|
dispatchedAt: this.now(),
|
|
9711
|
-
|
|
9712
|
-
// `dispatchedAt` — so a re-adopted zombie's age reflects the real turn duration
|
|
9713
|
-
// and the ceiling fires on the ORIGINAL turn.
|
|
9714
|
-
processingAnchorMs: processedAtMs,
|
|
10041
|
+
processingAnchorMs: this.absoluteAgeForRow(message.processing_started_at).anchorMs ?? processedAtMs,
|
|
9715
10042
|
deadline: watchDeadlineAnchorMs + this.pausedMaxWaitMs,
|
|
9716
10043
|
// The server row is ALREADY `processing`; do not re-fire markProcessing.
|
|
9717
10044
|
started: true,
|
|
@@ -10162,7 +10489,9 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
10162
10489
|
}
|
|
10163
10490
|
const { openQuestions, openPermissions, questionsPolledOk, permissionsPolledOk } = await this.pollInteractions(sessionId, watcher, messages);
|
|
10164
10491
|
if (watcher.generation !== generation) return;
|
|
10492
|
+
const ceilingAttemptsThisTick = /* @__PURE__ */ new Set();
|
|
10165
10493
|
for (const inFlight of [...watcher.inFlight.values()]) {
|
|
10494
|
+
if (ceilingAttemptsThisTick.has(inFlight.evidentMessageId)) continue;
|
|
10166
10495
|
await this.serviceInFlightMessage(
|
|
10167
10496
|
sessionId,
|
|
10168
10497
|
watcher,
|
|
@@ -10171,7 +10500,8 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
10171
10500
|
openQuestions,
|
|
10172
10501
|
openPermissions,
|
|
10173
10502
|
questionsPolledOk,
|
|
10174
|
-
permissionsPolledOk
|
|
10503
|
+
permissionsPolledOk,
|
|
10504
|
+
ceilingAttemptsThisTick
|
|
10175
10505
|
);
|
|
10176
10506
|
}
|
|
10177
10507
|
}
|
|
@@ -10212,13 +10542,117 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
10212
10542
|
inFlight.deadline = this.now() + this.pausedMaxWaitMs;
|
|
10213
10543
|
}
|
|
10214
10544
|
}
|
|
10545
|
+
async settleAbsoluteCeiling(sessionId, watcher, messages, ceilingAttemptsThisTick) {
|
|
10546
|
+
const entries = [...watcher.inFlight.values()];
|
|
10547
|
+
const pastCeiling = entries.filter(
|
|
10548
|
+
(entry) => this.now() - entry.processingAnchorMs >= ABSOLUTE_MAX_PROCESSING_MS
|
|
10549
|
+
);
|
|
10550
|
+
const healthySiblings = entries.length - pastCeiling.length;
|
|
10551
|
+
const landed = [];
|
|
10552
|
+
let allRecordsLanded = true;
|
|
10553
|
+
const retainedReason = this.readRetainedSessionError(sessionId);
|
|
10554
|
+
for (const entry of pastCeiling) {
|
|
10555
|
+
if (!watcher.inFlight.has(entry.evidentMessageId)) continue;
|
|
10556
|
+
ceilingAttemptsThisTick.add(entry.evidentMessageId);
|
|
10557
|
+
const error2 = this.absoluteCeilingError(
|
|
10558
|
+
entry.evidentMessageId,
|
|
10559
|
+
sessionId,
|
|
10560
|
+
entry.processingAnchorMs,
|
|
10561
|
+
retainedReason
|
|
10562
|
+
);
|
|
10563
|
+
this.log({
|
|
10564
|
+
level: "warn",
|
|
10565
|
+
message: error2,
|
|
10566
|
+
conversation_id: watcher.conv.id,
|
|
10567
|
+
message_id: entry.evidentMessageId
|
|
10568
|
+
});
|
|
10569
|
+
try {
|
|
10570
|
+
await this.markFailed(
|
|
10571
|
+
watcher.conv.id,
|
|
10572
|
+
entry.evidentMessageId,
|
|
10573
|
+
sessionId,
|
|
10574
|
+
error2,
|
|
10575
|
+
void 0,
|
|
10576
|
+
{
|
|
10577
|
+
classification: null,
|
|
10578
|
+
errorShape: extractReplyErrorShape(
|
|
10579
|
+
findLastAssistantReplyFor(messages, entry.opencodeMessageId)
|
|
10580
|
+
)
|
|
10581
|
+
}
|
|
10582
|
+
);
|
|
10583
|
+
} catch (err) {
|
|
10584
|
+
if (err instanceof ChannelAuthError) throw err;
|
|
10585
|
+
allRecordsLanded = false;
|
|
10586
|
+
const watched_for_ms = this.now() - entry.processingAnchorMs;
|
|
10587
|
+
if (err instanceof ChannelTerminalError) {
|
|
10588
|
+
void this.postSignal(watcher.conv.id, entry.evidentMessageId, "gave_up", {
|
|
10589
|
+
watched_for_ms,
|
|
10590
|
+
interrupt_outcome: "record_terminal"
|
|
10591
|
+
});
|
|
10592
|
+
this.removeInFlight(watcher, entry.evidentMessageId);
|
|
10593
|
+
continue;
|
|
10594
|
+
}
|
|
10595
|
+
if (this.now() >= entry.deadline) {
|
|
10596
|
+
void this.postSignal(watcher.conv.id, entry.evidentMessageId, "gave_up", {
|
|
10597
|
+
watched_for_ms,
|
|
10598
|
+
interrupt_outcome: "record_deadline"
|
|
10599
|
+
});
|
|
10600
|
+
this.removeInFlight(watcher, entry.evidentMessageId);
|
|
10601
|
+
continue;
|
|
10602
|
+
}
|
|
10603
|
+
void this.postSignal(watcher.conv.id, entry.evidentMessageId, "gave_up", {
|
|
10604
|
+
watched_for_ms,
|
|
10605
|
+
interrupt_outcome: "record_retry"
|
|
10606
|
+
});
|
|
10607
|
+
this.log({
|
|
10608
|
+
level: "warn",
|
|
10609
|
+
message: `Failed to record the absolute-ceiling failure for message ${entry.evidentMessageId.slice(0, 8)}; leaving its running turn untouched and retrying next tick: ${err instanceof Error ? err.message : String(err)}`,
|
|
10610
|
+
conversation_id: watcher.conv.id,
|
|
10611
|
+
message_id: entry.evidentMessageId
|
|
10612
|
+
});
|
|
10613
|
+
continue;
|
|
10614
|
+
}
|
|
10615
|
+
entry.done = true;
|
|
10616
|
+
landed.push(entry);
|
|
10617
|
+
}
|
|
10618
|
+
if (!allRecordsLanded) {
|
|
10619
|
+
for (const entry of landed) this.removeInFlight(watcher, entry.evidentMessageId);
|
|
10620
|
+
return;
|
|
10621
|
+
}
|
|
10622
|
+
if (healthySiblings > 0) {
|
|
10623
|
+
for (const entry of landed) {
|
|
10624
|
+
void this.postSignal(watcher.conv.id, entry.evidentMessageId, "gave_up", {
|
|
10625
|
+
watched_for_ms: this.now() - entry.processingAnchorMs,
|
|
10626
|
+
interrupt_outcome: "deferred_for_healthy_sibling",
|
|
10627
|
+
healthy_siblings: healthySiblings
|
|
10628
|
+
});
|
|
10629
|
+
this.removeInFlight(watcher, entry.evidentMessageId);
|
|
10630
|
+
}
|
|
10631
|
+
return;
|
|
10632
|
+
}
|
|
10633
|
+
const interrupt = await interruptSession(this.port, sessionId, this.openCodeClient);
|
|
10634
|
+
for (const entry of landed) {
|
|
10635
|
+
void this.postSignal(watcher.conv.id, entry.evidentMessageId, "gave_up", {
|
|
10636
|
+
watched_for_ms: this.now() - entry.processingAnchorMs,
|
|
10637
|
+
interrupt_outcome: interrupt.kind
|
|
10638
|
+
});
|
|
10639
|
+
this.removeInFlight(watcher, entry.evidentMessageId);
|
|
10640
|
+
}
|
|
10641
|
+
if (interrupt.kind !== "confirmed_stopped") {
|
|
10642
|
+
this.log({
|
|
10643
|
+
level: "warn",
|
|
10644
|
+
message: `Absolute-ceiling records landed for session ${sessionId}, but OpenCode interruption was ${interrupt.kind}${interrupt.diagnostic ? ` (${interrupt.diagnostic})` : "status" in interrupt && typeof interrupt.status === "number" ? ` (HTTP ${interrupt.status})` : ""}; the rows are terminal and released, while the session may still be running`,
|
|
10645
|
+
conversation_id: watcher.conv.id
|
|
10646
|
+
});
|
|
10647
|
+
}
|
|
10648
|
+
}
|
|
10215
10649
|
/**
|
|
10216
10650
|
* Drive ONE in-flight message's lifecycle from the tick's message snapshot.
|
|
10217
10651
|
* Fires markProcessing on queued→running and markDone on done (each once),
|
|
10218
10652
|
* applies the idle-path re-dispatch guard, and removes the message from the
|
|
10219
10653
|
* in-flight set on completion or timeout.
|
|
10220
10654
|
*/
|
|
10221
|
-
async serviceInFlightMessage(sessionId, watcher, inFlight, messages, openQuestions, openPermissions, questionsPolledOk, permissionsPolledOk) {
|
|
10655
|
+
async serviceInFlightMessage(sessionId, watcher, inFlight, messages, openQuestions, openPermissions, questionsPolledOk, permissionsPolledOk, ceilingAttemptsThisTick) {
|
|
10222
10656
|
const conv = watcher.conv;
|
|
10223
10657
|
const state = messageRunState(messages, inFlight.opencodeMessageId);
|
|
10224
10658
|
const id = inFlight.evidentMessageId;
|
|
@@ -10562,17 +10996,8 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
10562
10996
|
return;
|
|
10563
10997
|
}
|
|
10564
10998
|
}
|
|
10565
|
-
if (activelyRunning && this.now() - inFlight.processingAnchorMs >= ABSOLUTE_MAX_PROCESSING_MS) {
|
|
10566
|
-
this.
|
|
10567
|
-
level: "warn",
|
|
10568
|
-
message: `Message ${inFlight.evidentMessageId.slice(0, 8)} exceeded the absolute processing ceiling (${Math.round((this.now() - inFlight.processingAnchorMs) / 6e4)}min, session ${sessionId}) while still actively running \u2014 releasing so the cron can reclaim it`,
|
|
10569
|
-
conversation_id: conv.id,
|
|
10570
|
-
message_id: inFlight.evidentMessageId
|
|
10571
|
-
});
|
|
10572
|
-
void this.postSignal(conv.id, inFlight.evidentMessageId, "gave_up", {
|
|
10573
|
-
watched_for_ms: this.now() - inFlight.processingAnchorMs
|
|
10574
|
-
});
|
|
10575
|
-
this.removeInFlight(watcher, inFlight.evidentMessageId);
|
|
10999
|
+
if (watcher.inFlight.has(inFlight.evidentMessageId) && activelyRunning && this.now() - inFlight.processingAnchorMs >= ABSOLUTE_MAX_PROCESSING_MS) {
|
|
11000
|
+
await this.settleAbsoluteCeiling(sessionId, watcher, messages, ceilingAttemptsThisTick);
|
|
10576
11001
|
return;
|
|
10577
11002
|
}
|
|
10578
11003
|
const heartbeatDue = activelyRunning && !inFlight.awaitingHumanLatched && !inFlight.aliveInFlight && this.now() - inFlight.lastAliveAt >= HEARTBEAT_MS;
|
|
@@ -11195,6 +11620,33 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
11195
11620
|
}
|
|
11196
11621
|
const restartAborted = state === "failed" && sessionOngoing === false && isAbortedTerminalReply(messages, ocId ?? "");
|
|
11197
11622
|
if (restartAborted) {
|
|
11623
|
+
const age = this.absoluteAgeForRow(row.processing_started_at);
|
|
11624
|
+
if (age.tripped && age.anchorMs !== null) {
|
|
11625
|
+
try {
|
|
11626
|
+
await this.markFailed(
|
|
11627
|
+
row.conversation_id,
|
|
11628
|
+
row.id,
|
|
11629
|
+
sessionId,
|
|
11630
|
+
this.absoluteCeilingError(row.id, sessionId, age.anchorMs),
|
|
11631
|
+
void 0,
|
|
11632
|
+
{
|
|
11633
|
+
classification: null,
|
|
11634
|
+
errorShape: extractReplyErrorShape(findLastAssistantReplyFor(messages, ocId ?? ""))
|
|
11635
|
+
}
|
|
11636
|
+
);
|
|
11637
|
+
} catch (err) {
|
|
11638
|
+
if (err instanceof ChannelAuthError) throw err;
|
|
11639
|
+
this.log({
|
|
11640
|
+
level: "warn",
|
|
11641
|
+
message: `Re-adopt: failed to report past-ceiling restart orphan ${row.id.slice(0, 8)} terminal; refusing to re-dispatch: ${err instanceof Error ? err.message : String(err)}`,
|
|
11642
|
+
conversation_id: row.conversation_id,
|
|
11643
|
+
message_id: row.id
|
|
11644
|
+
});
|
|
11645
|
+
return;
|
|
11646
|
+
}
|
|
11647
|
+
void this.postSignal(row.conversation_id, row.id, "redrive_ceiling_refused");
|
|
11648
|
+
return;
|
|
11649
|
+
}
|
|
11198
11650
|
this.log({
|
|
11199
11651
|
level: "info",
|
|
11200
11652
|
message: `Re-adopt: message ${row.id.slice(0, 8)} carries an abort-shaped terminal error and session ${sessionId.slice(0, 8)} is not-ongoing per the active-session status check \u2014 restart orphan, re-dispatching instead of marking it permanently failed`,
|
|
@@ -11740,7 +12192,8 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
11740
12192
|
source_message_id: row.source_message_id,
|
|
11741
12193
|
slack_user_id: row.slack_user_id,
|
|
11742
12194
|
attachments: row.attachments ?? null,
|
|
11743
|
-
opencode_message_id: row.opencode_message_id
|
|
12195
|
+
opencode_message_id: row.opencode_message_id,
|
|
12196
|
+
processing_started_at: row.processing_started_at
|
|
11744
12197
|
};
|
|
11745
12198
|
}
|
|
11746
12199
|
/**
|
|
@@ -13538,6 +13991,22 @@ async function configureGitHubAccess({ env, log: log3 }) {
|
|
|
13538
13991
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
13539
13992
|
import { copyFileSync, existsSync as existsSync2, statSync as statSync6 } from "node:fs";
|
|
13540
13993
|
import { isAbsolute as isAbsolute3, join as join11, resolve as resolve4 } from "node:path";
|
|
13994
|
+
function parseConfigContent(content) {
|
|
13995
|
+
if (!content?.trim()) return {};
|
|
13996
|
+
const parsed = JSON.parse(content);
|
|
13997
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
13998
|
+
throw new Error("OPENCODE_CONFIG_CONTENT must contain a JSON object");
|
|
13999
|
+
}
|
|
14000
|
+
return parsed;
|
|
14001
|
+
}
|
|
14002
|
+
function mergeManagedOpenCodeConfigContent(content, managedProviders) {
|
|
14003
|
+
const config = parseConfigContent(content);
|
|
14004
|
+
const provider = typeof config.provider === "object" && config.provider !== null && !Array.isArray(config.provider) ? { ...config.provider } : {};
|
|
14005
|
+
for (const namespace of CREDENTIAL_PROXY_NATIVE_NAMESPACES) {
|
|
14006
|
+
delete provider[namespace];
|
|
14007
|
+
}
|
|
14008
|
+
return JSON.stringify({ ...config, provider: { ...provider, ...managedProviders } });
|
|
14009
|
+
}
|
|
13541
14010
|
function isFile(filePath) {
|
|
13542
14011
|
return existsSync2(filePath) && statSync6(filePath).isFile();
|
|
13543
14012
|
}
|
|
@@ -13571,6 +14040,159 @@ function applyRunnerOpenCodeConfig({
|
|
|
13571
14040
|
log3(`Applied runner OpenCode config ${source} to ${join11(cwd, target)}`);
|
|
13572
14041
|
}
|
|
13573
14042
|
|
|
14043
|
+
// src/lib/opencode/runtime-provenance.ts
|
|
14044
|
+
import { readFileSync as readFileSync7, readdirSync } from "node:fs";
|
|
14045
|
+
import { homedir as homedir7 } from "node:os";
|
|
14046
|
+
import { join as join12 } from "node:path";
|
|
14047
|
+
var PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/i;
|
|
14048
|
+
var REGISTRY_SPEC = /^[a-z0-9.*+^~<>=|-]+$/i;
|
|
14049
|
+
function errorKind(error2) {
|
|
14050
|
+
if (!(error2 instanceof Error)) return "unknown error";
|
|
14051
|
+
const code = "code" in error2 && typeof error2.code === "string" ? ` (${error2.code})` : "";
|
|
14052
|
+
return `${error2.name}${code}`;
|
|
14053
|
+
}
|
|
14054
|
+
function parseRegistryPluginSpec(value) {
|
|
14055
|
+
const separator = value.lastIndexOf("@");
|
|
14056
|
+
if (separator <= 0) return null;
|
|
14057
|
+
const name = value.slice(0, separator);
|
|
14058
|
+
const spec = value.slice(separator + 1);
|
|
14059
|
+
if (!PACKAGE_NAME.test(name) || !REGISTRY_SPEC.test(spec)) return null;
|
|
14060
|
+
return { name, spec };
|
|
14061
|
+
}
|
|
14062
|
+
function parseJsonc(value) {
|
|
14063
|
+
let output = "";
|
|
14064
|
+
let inString = false;
|
|
14065
|
+
let escaped = false;
|
|
14066
|
+
let lineComment = false;
|
|
14067
|
+
let blockComment = false;
|
|
14068
|
+
for (let index = 0; index < value.length; index++) {
|
|
14069
|
+
const character = value[index];
|
|
14070
|
+
const next = value[index + 1];
|
|
14071
|
+
if (lineComment) {
|
|
14072
|
+
if (character === "\n") {
|
|
14073
|
+
lineComment = false;
|
|
14074
|
+
output += character;
|
|
14075
|
+
}
|
|
14076
|
+
continue;
|
|
14077
|
+
}
|
|
14078
|
+
if (blockComment) {
|
|
14079
|
+
if (character === "*" && next === "/") {
|
|
14080
|
+
blockComment = false;
|
|
14081
|
+
index++;
|
|
14082
|
+
} else if (character === "\n") {
|
|
14083
|
+
output += character;
|
|
14084
|
+
}
|
|
14085
|
+
continue;
|
|
14086
|
+
}
|
|
14087
|
+
if (inString) {
|
|
14088
|
+
output += character;
|
|
14089
|
+
if (escaped) escaped = false;
|
|
14090
|
+
else if (character === "\\") escaped = true;
|
|
14091
|
+
else if (character === '"') inString = false;
|
|
14092
|
+
continue;
|
|
14093
|
+
}
|
|
14094
|
+
if (character === '"') {
|
|
14095
|
+
inString = true;
|
|
14096
|
+
output += character;
|
|
14097
|
+
} else if (character === "/" && next === "/") {
|
|
14098
|
+
lineComment = true;
|
|
14099
|
+
index++;
|
|
14100
|
+
} else if (character === "/" && next === "*") {
|
|
14101
|
+
blockComment = true;
|
|
14102
|
+
index++;
|
|
14103
|
+
} else if (character === ",") {
|
|
14104
|
+
let lookahead = index + 1;
|
|
14105
|
+
while (/\s/.test(value[lookahead] ?? "")) lookahead++;
|
|
14106
|
+
if (value[lookahead] !== "]" && value[lookahead] !== "}") output += character;
|
|
14107
|
+
} else {
|
|
14108
|
+
output += character;
|
|
14109
|
+
}
|
|
14110
|
+
}
|
|
14111
|
+
return JSON.parse(output);
|
|
14112
|
+
}
|
|
14113
|
+
function openCodeCacheRoot(env = process.env, homeDir = homedir7()) {
|
|
14114
|
+
return join12(env.XDG_CACHE_HOME ?? join12(homeDir, ".cache"), "opencode");
|
|
14115
|
+
}
|
|
14116
|
+
function readConfiguredPluginSpecs({
|
|
14117
|
+
cwd = process.cwd(),
|
|
14118
|
+
log: log3
|
|
14119
|
+
}) {
|
|
14120
|
+
let config;
|
|
14121
|
+
try {
|
|
14122
|
+
try {
|
|
14123
|
+
config = parseJsonc(readFileSync7(join12(cwd, "opencode.jsonc"), "utf8"));
|
|
14124
|
+
} catch (error2) {
|
|
14125
|
+
if (!("code" in Object(error2)) || Object(error2).code !== "ENOENT") throw error2;
|
|
14126
|
+
config = parseJsonc(readFileSync7(join12(cwd, "opencode.json"), "utf8"));
|
|
14127
|
+
}
|
|
14128
|
+
} catch (error2) {
|
|
14129
|
+
log3(`Could not read OpenCode plugin configuration: ${errorKind(error2)}`);
|
|
14130
|
+
return [];
|
|
14131
|
+
}
|
|
14132
|
+
if (typeof config !== "object" || config === null || !("plugin" in config)) return [];
|
|
14133
|
+
const plugins = config.plugin;
|
|
14134
|
+
if (!Array.isArray(plugins)) {
|
|
14135
|
+
log3("Could not read OpenCode plugin configuration: plugin is not an array");
|
|
14136
|
+
return [];
|
|
14137
|
+
}
|
|
14138
|
+
const configured = plugins.filter(
|
|
14139
|
+
(plugin) => typeof plugin === "string" && parseRegistryPluginSpec(plugin) !== null
|
|
14140
|
+
);
|
|
14141
|
+
if (configured.length !== plugins.length) {
|
|
14142
|
+
log3(
|
|
14143
|
+
`Skipped ${plugins.length - configured.length} non-registry OpenCode plugin spec(s); their values were not recorded`
|
|
14144
|
+
);
|
|
14145
|
+
}
|
|
14146
|
+
return configured;
|
|
14147
|
+
}
|
|
14148
|
+
function resolveInstalledPluginVersions({
|
|
14149
|
+
cacheRoot,
|
|
14150
|
+
env = process.env,
|
|
14151
|
+
homeDir = homedir7(),
|
|
14152
|
+
log: log3
|
|
14153
|
+
}) {
|
|
14154
|
+
const packagesRoot = join12(cacheRoot ?? openCodeCacheRoot(env, homeDir), "packages");
|
|
14155
|
+
let directories;
|
|
14156
|
+
try {
|
|
14157
|
+
directories = readdirSync(packagesRoot, { withFileTypes: true });
|
|
14158
|
+
} catch (error2) {
|
|
14159
|
+
if ("code" in Object(error2) && Object(error2).code === "ENOENT") return [];
|
|
14160
|
+
log3(`Could not inspect the OpenCode plugin cache: ${errorKind(error2)}`);
|
|
14161
|
+
return [];
|
|
14162
|
+
}
|
|
14163
|
+
const plugins = [];
|
|
14164
|
+
let skipped = 0;
|
|
14165
|
+
for (const directory of directories) {
|
|
14166
|
+
if (!directory.isDirectory()) continue;
|
|
14167
|
+
const parsed = parseRegistryPluginSpec(directory.name);
|
|
14168
|
+
if (parsed === null) {
|
|
14169
|
+
skipped++;
|
|
14170
|
+
continue;
|
|
14171
|
+
}
|
|
14172
|
+
let resolvedVersion = null;
|
|
14173
|
+
try {
|
|
14174
|
+
const packageJson = JSON.parse(
|
|
14175
|
+
readFileSync7(
|
|
14176
|
+
join12(packagesRoot, directory.name, "node_modules", parsed.name, "package.json"),
|
|
14177
|
+
"utf8"
|
|
14178
|
+
)
|
|
14179
|
+
);
|
|
14180
|
+
if (typeof packageJson.version === "string" && REGISTRY_SPEC.test(packageJson.version)) {
|
|
14181
|
+
resolvedVersion = packageJson.version;
|
|
14182
|
+
} else {
|
|
14183
|
+
log3(`Could not resolve OpenCode plugin ${parsed.name}@${parsed.spec}: invalid version`);
|
|
14184
|
+
}
|
|
14185
|
+
} catch (error2) {
|
|
14186
|
+
log3(`Could not resolve OpenCode plugin ${parsed.name}@${parsed.spec}: ${errorKind(error2)}`);
|
|
14187
|
+
}
|
|
14188
|
+
plugins.push({ ...parsed, resolvedVersion });
|
|
14189
|
+
}
|
|
14190
|
+
if (skipped > 0) {
|
|
14191
|
+
log3(`Skipped ${skipped} non-registry OpenCode package cache entry; its value was not recorded`);
|
|
14192
|
+
}
|
|
14193
|
+
return plugins;
|
|
14194
|
+
}
|
|
14195
|
+
|
|
13574
14196
|
// src/lib/credential-sync.ts
|
|
13575
14197
|
import { renameSync, writeFileSync as writeFileSync5 } from "node:fs";
|
|
13576
14198
|
var CREDENTIAL_SYNC_TICK_TIMEOUT_MS = 3e4;
|
|
@@ -13806,6 +14428,255 @@ function createCredentialSync({
|
|
|
13806
14428
|
};
|
|
13807
14429
|
}
|
|
13808
14430
|
|
|
14431
|
+
// src/lib/managed-inference-setup.ts
|
|
14432
|
+
var defaultStore = {
|
|
14433
|
+
get: getManagedInferenceCredential,
|
|
14434
|
+
set: setManagedInferenceCredential,
|
|
14435
|
+
clear: clearManagedInferenceCredential
|
|
14436
|
+
};
|
|
14437
|
+
function endpoint(options, operation) {
|
|
14438
|
+
return `${options.apiUrl}/runners/${options.agentId}/managed-credentials/${operation}`;
|
|
14439
|
+
}
|
|
14440
|
+
function requestHeaders(options) {
|
|
14441
|
+
return { Authorization: options.getAuthHeader(), "Content-Type": "application/json" };
|
|
14442
|
+
}
|
|
14443
|
+
function request2(options) {
|
|
14444
|
+
return withRequestTimeout(options.fetchImpl ?? fetch, REQUEST_TIMEOUT_MS);
|
|
14445
|
+
}
|
|
14446
|
+
function safeFailure(operation, status2) {
|
|
14447
|
+
return status2 === void 0 ? `${operation} could not reach the managed credential service` : `${operation} returned HTTP ${status2}`;
|
|
14448
|
+
}
|
|
14449
|
+
function logTransportFailure(options, operation, error2, authorization) {
|
|
14450
|
+
let detail = error2 instanceof Error ? error2.message : String(error2);
|
|
14451
|
+
const credential = authorization.split(/\s+/, 2)[1];
|
|
14452
|
+
for (const secret of [authorization, credential]) {
|
|
14453
|
+
if (secret) detail = detail.replaceAll(secret, "[redacted]");
|
|
14454
|
+
}
|
|
14455
|
+
options.log(
|
|
14456
|
+
`MANAGED-INFERENCE-TRANSPORT-FAILED: operation=${operation} detail=${redactDiagnosticText(detail)}`,
|
|
14457
|
+
"debug"
|
|
14458
|
+
);
|
|
14459
|
+
}
|
|
14460
|
+
async function responseJson(response) {
|
|
14461
|
+
try {
|
|
14462
|
+
const value = await response.json();
|
|
14463
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
14464
|
+
} catch (error2) {
|
|
14465
|
+
return null;
|
|
14466
|
+
}
|
|
14467
|
+
}
|
|
14468
|
+
function readSetup(value) {
|
|
14469
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
|
|
14470
|
+
const setup = value;
|
|
14471
|
+
if (typeof setup.runner_id !== "string" || typeof setup.generation !== "string" || typeof setup.status !== "string" || setup.token_id !== null && typeof setup.token_id !== "string" || typeof setup.acknowledged_at !== "string" && setup.acknowledged_at !== null) {
|
|
14472
|
+
return null;
|
|
14473
|
+
}
|
|
14474
|
+
return setup;
|
|
14475
|
+
}
|
|
14476
|
+
function clearLocalSetup(options) {
|
|
14477
|
+
const env = options.env ?? process.env;
|
|
14478
|
+
const store = options.store ?? defaultStore;
|
|
14479
|
+
const stored = store.get(options.agentId);
|
|
14480
|
+
store.clear(options.agentId);
|
|
14481
|
+
const token = env.EVIDENT_PROXY_TOKEN;
|
|
14482
|
+
if (token !== void 0 && token === stored?.token) {
|
|
14483
|
+
delete env.EVIDENT_PROXY_TOKEN;
|
|
14484
|
+
} else if (token !== void 0) {
|
|
14485
|
+
options.log(
|
|
14486
|
+
`MANAGED-INFERENCE-CLEANUP-PRESERVED-EXTERNAL-TOKEN: runner=${options.agentId}`,
|
|
14487
|
+
"debug"
|
|
14488
|
+
);
|
|
14489
|
+
}
|
|
14490
|
+
}
|
|
14491
|
+
async function discoverManagedInferenceSetup(options) {
|
|
14492
|
+
const headers = requestHeaders(options);
|
|
14493
|
+
let response;
|
|
14494
|
+
try {
|
|
14495
|
+
response = await request2(options)(endpoint(options, "current"), {
|
|
14496
|
+
headers
|
|
14497
|
+
});
|
|
14498
|
+
} catch (error2) {
|
|
14499
|
+
logTransportFailure(options, "discovery", error2, headers.Authorization);
|
|
14500
|
+
return { kind: "failed", reason: safeFailure("Managed setup discovery") };
|
|
14501
|
+
}
|
|
14502
|
+
if (response.status === 404) {
|
|
14503
|
+
clearLocalSetup(options);
|
|
14504
|
+
return { kind: "none" };
|
|
14505
|
+
}
|
|
14506
|
+
if (!response.ok) {
|
|
14507
|
+
return { kind: "failed", reason: safeFailure("Managed setup discovery", response.status) };
|
|
14508
|
+
}
|
|
14509
|
+
const body = await responseJson(response);
|
|
14510
|
+
const setup = readSetup(body?.setup);
|
|
14511
|
+
return setup ? { kind: "setup", setup } : { kind: "failed", reason: "Managed setup discovery returned an invalid response" };
|
|
14512
|
+
}
|
|
14513
|
+
async function enrollManagedInferenceSetup(options) {
|
|
14514
|
+
const response = await post(options, "enroll", {});
|
|
14515
|
+
if (!response) {
|
|
14516
|
+
return { kind: "failed", reason: safeFailure("Managed credential enrollment") };
|
|
14517
|
+
}
|
|
14518
|
+
if (response.status === 404) {
|
|
14519
|
+
return { kind: "none" };
|
|
14520
|
+
}
|
|
14521
|
+
const body = await responseJson(response);
|
|
14522
|
+
if (!response.ok) {
|
|
14523
|
+
const serverError = body?.error;
|
|
14524
|
+
return {
|
|
14525
|
+
kind: "failed",
|
|
14526
|
+
reason: typeof serverError === "string" && serverError.trim() ? redactDiagnosticText(serverError).slice(0, 500) : safeFailure("Managed credential enrollment", response.status)
|
|
14527
|
+
};
|
|
14528
|
+
}
|
|
14529
|
+
const setup = readSetup(body?.setup);
|
|
14530
|
+
return setup ? { kind: "setup", setup } : { kind: "failed", reason: "Managed credential enrollment returned an invalid response" };
|
|
14531
|
+
}
|
|
14532
|
+
async function post(options, operation, body) {
|
|
14533
|
+
const headers = requestHeaders(options);
|
|
14534
|
+
try {
|
|
14535
|
+
return await request2(options)(endpoint(options, operation), {
|
|
14536
|
+
method: "POST",
|
|
14537
|
+
headers,
|
|
14538
|
+
body: JSON.stringify(body)
|
|
14539
|
+
});
|
|
14540
|
+
} catch (error2) {
|
|
14541
|
+
logTransportFailure(options, operation, error2, headers.Authorization);
|
|
14542
|
+
return null;
|
|
14543
|
+
}
|
|
14544
|
+
}
|
|
14545
|
+
function applyCredential(options, credential) {
|
|
14546
|
+
const env = options.env ?? process.env;
|
|
14547
|
+
let merged;
|
|
14548
|
+
if (options.autoConfigureCredentialsProxy) {
|
|
14549
|
+
const { config, unsupportedProviders } = buildCredentialProxyNativeConfig({
|
|
14550
|
+
inferenceBaseUrl: credential.inferenceBaseUrl,
|
|
14551
|
+
modelDescriptors: credential.modelDescriptors
|
|
14552
|
+
});
|
|
14553
|
+
if (unsupportedProviders.length > 0 || Object.keys(config.provider).length === 0) {
|
|
14554
|
+
return "Managed setup contains unsupported provider descriptors";
|
|
14555
|
+
}
|
|
14556
|
+
try {
|
|
14557
|
+
merged = mergeManagedOpenCodeConfigContent(env.OPENCODE_CONFIG_CONTENT, config.provider);
|
|
14558
|
+
} catch (error2) {
|
|
14559
|
+
return "OPENCODE_CONFIG_CONTENT is malformed; managed providers were not installed";
|
|
14560
|
+
}
|
|
14561
|
+
}
|
|
14562
|
+
try {
|
|
14563
|
+
(options.store ?? defaultStore).set(options.agentId, credential);
|
|
14564
|
+
} catch (error2) {
|
|
14565
|
+
const detail = (error2 instanceof Error ? error2.message : String(error2)).replaceAll(
|
|
14566
|
+
credential.token,
|
|
14567
|
+
"[redacted]"
|
|
14568
|
+
);
|
|
14569
|
+
options.log(
|
|
14570
|
+
`MANAGED-INFERENCE-STORE-FAILED: runner=${options.agentId} generation=${credential.generation} detail=${redactDiagnosticText(detail)}`,
|
|
14571
|
+
"warn"
|
|
14572
|
+
);
|
|
14573
|
+
return "Managed credentials could not be written to the protected store";
|
|
14574
|
+
}
|
|
14575
|
+
env.EVIDENT_PROXY_TOKEN = credential.token;
|
|
14576
|
+
if (merged !== void 0) env.OPENCODE_CONFIG_CONTENT = merged;
|
|
14577
|
+
return null;
|
|
14578
|
+
}
|
|
14579
|
+
async function reportManagedInferenceApplyFailure(options, failure) {
|
|
14580
|
+
const response = await post(options, "report-failure", {
|
|
14581
|
+
generation: failure.generation,
|
|
14582
|
+
token_id: failure.tokenId
|
|
14583
|
+
});
|
|
14584
|
+
if (!response?.ok) {
|
|
14585
|
+
options.log(
|
|
14586
|
+
`MANAGED-INFERENCE-REPORT-FAILED: runner=${options.agentId} generation=${failure.generation}${response ? ` status=${response.status}` : ""}`,
|
|
14587
|
+
"warn"
|
|
14588
|
+
);
|
|
14589
|
+
}
|
|
14590
|
+
}
|
|
14591
|
+
async function reaffirmStored(options, credential) {
|
|
14592
|
+
const response = await post(options, "reaffirm", {
|
|
14593
|
+
generation: credential.generation,
|
|
14594
|
+
token_id: credential.tokenId
|
|
14595
|
+
});
|
|
14596
|
+
if (!response?.ok) {
|
|
14597
|
+
return {
|
|
14598
|
+
kind: "failed",
|
|
14599
|
+
reason: response?.status === 409 ? "Stored managed credential no longer matches this setup; restart required" : response ? safeFailure("Managed credential reaffirmation", response.status) : safeFailure("Managed credential reaffirmation")
|
|
14600
|
+
};
|
|
14601
|
+
}
|
|
14602
|
+
const body = await responseJson(response);
|
|
14603
|
+
const setup = readSetup(body?.setup);
|
|
14604
|
+
if (!setup || setup.generation !== credential.generation || setup.token_id !== credential.tokenId) {
|
|
14605
|
+
return { kind: "failed", reason: "Managed credential reaffirmation returned a mismatch" };
|
|
14606
|
+
}
|
|
14607
|
+
const failure = applyCredential(options, credential);
|
|
14608
|
+
return failure ? { kind: "failed", reason: failure } : { kind: "ready", generation: credential.generation, tokenId: credential.tokenId };
|
|
14609
|
+
}
|
|
14610
|
+
async function prepareManagedInferenceSetup(options, discovery, health) {
|
|
14611
|
+
if (health.authFailed) {
|
|
14612
|
+
return {
|
|
14613
|
+
kind: "failed",
|
|
14614
|
+
reason: "OpenCode authentication failed; managed credentials were not rotated"
|
|
14615
|
+
};
|
|
14616
|
+
}
|
|
14617
|
+
const stored = (options.store ?? defaultStore).get(options.agentId);
|
|
14618
|
+
if (health.healthy) {
|
|
14619
|
+
if (!stored) {
|
|
14620
|
+
return {
|
|
14621
|
+
kind: "failed",
|
|
14622
|
+
reason: "Managed credentials are missing for the serving OpenCode process; restart required"
|
|
14623
|
+
};
|
|
14624
|
+
}
|
|
14625
|
+
return reaffirmStored(options, stored);
|
|
14626
|
+
}
|
|
14627
|
+
if (discovery.setup.acknowledged_at !== null && stored?.generation === discovery.setup.generation && stored.tokenId === discovery.setup.token_id) {
|
|
14628
|
+
return reaffirmStored(options, stored);
|
|
14629
|
+
}
|
|
14630
|
+
const response = await post(options, "bootstrap", { generation: discovery.setup.generation });
|
|
14631
|
+
if (!response?.ok) {
|
|
14632
|
+
return {
|
|
14633
|
+
kind: "failed",
|
|
14634
|
+
reason: response ? safeFailure("Managed credential bootstrap", response.status) : safeFailure("Managed credential bootstrap")
|
|
14635
|
+
};
|
|
14636
|
+
}
|
|
14637
|
+
const body = await responseJson(response);
|
|
14638
|
+
const setup = readSetup(body?.setup);
|
|
14639
|
+
const token = body?.inference_token;
|
|
14640
|
+
const inferenceBaseUrl = body?.inference_base_url;
|
|
14641
|
+
const modelDescriptors = body?.model_descriptors;
|
|
14642
|
+
if (!setup || setup.generation !== discovery.setup.generation || typeof setup.token_id !== "string" || typeof token !== "string" || typeof inferenceBaseUrl !== "string" || !Array.isArray(modelDescriptors) || !modelDescriptors.every(
|
|
14643
|
+
(descriptor) => typeof descriptor === "object" && descriptor !== null && typeof descriptor.id === "string" && typeof descriptor.provider === "string"
|
|
14644
|
+
)) {
|
|
14645
|
+
return { kind: "failed", reason: "Managed credential bootstrap returned an invalid response" };
|
|
14646
|
+
}
|
|
14647
|
+
const credential = {
|
|
14648
|
+
generation: setup.generation,
|
|
14649
|
+
tokenId: setup.token_id,
|
|
14650
|
+
token,
|
|
14651
|
+
inferenceBaseUrl,
|
|
14652
|
+
modelDescriptors
|
|
14653
|
+
};
|
|
14654
|
+
const failure = applyCredential(options, credential);
|
|
14655
|
+
if (failure) {
|
|
14656
|
+
await reportManagedInferenceApplyFailure(options, credential);
|
|
14657
|
+
return { kind: "failed", reason: failure };
|
|
14658
|
+
}
|
|
14659
|
+
return { kind: "ready", generation: credential.generation, tokenId: credential.tokenId };
|
|
14660
|
+
}
|
|
14661
|
+
async function acknowledgeManagedInferenceSetup(options, preparation) {
|
|
14662
|
+
const response = await post(options, "acknowledge", {
|
|
14663
|
+
generation: preparation.generation,
|
|
14664
|
+
token_id: preparation.tokenId,
|
|
14665
|
+
config_validated: options.autoConfigureCredentialsProxy === true
|
|
14666
|
+
});
|
|
14667
|
+
if (!response?.ok) {
|
|
14668
|
+
throw new Error(
|
|
14669
|
+
response ? safeFailure("Managed credential acknowledgment", response.status) : safeFailure("Managed credential acknowledgment")
|
|
14670
|
+
);
|
|
14671
|
+
}
|
|
14672
|
+
const body = await responseJson(response);
|
|
14673
|
+
const setup = readSetup(body?.setup);
|
|
14674
|
+
if (!setup || setup.generation !== preparation.generation || setup.token_id !== preparation.tokenId) {
|
|
14675
|
+
throw new Error("Managed credential acknowledgment returned a mismatch");
|
|
14676
|
+
}
|
|
14677
|
+
return response.status === 200 && setup.status === "acknowledged" ? "acknowledged" : "pending";
|
|
14678
|
+
}
|
|
14679
|
+
|
|
13809
14680
|
// src/commands/run.ts
|
|
13810
14681
|
var MAX_ACTIVITY_LOG_ENTRIES = 10;
|
|
13811
14682
|
var MAX_DISCONNECT_REASON_LENGTH = 200;
|
|
@@ -13846,7 +14717,7 @@ function resolveFileSyncDirectories(raw, homeDir) {
|
|
|
13846
14717
|
if (trimmed === "") {
|
|
13847
14718
|
throw new Error("--enable-file-sync-to requires a directory path (got an empty value)");
|
|
13848
14719
|
}
|
|
13849
|
-
const expanded = trimmed === "~" ? homeDir : trimmed.startsWith("~/") ?
|
|
14720
|
+
const expanded = trimmed === "~" ? homeDir : trimmed.startsWith("~/") ? join13(homeDir, trimmed.slice(2)) : trimmed;
|
|
13850
14721
|
if (!isAbsolute4(expanded)) {
|
|
13851
14722
|
throw new Error(`--enable-file-sync-to requires an absolute directory path; got "${entry}"`);
|
|
13852
14723
|
}
|
|
@@ -14001,7 +14872,7 @@ function logActivity(state, entry) {
|
|
|
14001
14872
|
}
|
|
14002
14873
|
function reportSessionDbRecovery(state) {
|
|
14003
14874
|
try {
|
|
14004
|
-
const report = drainSessionDbRecoveryReport({ homeDir:
|
|
14875
|
+
const report = drainSessionDbRecoveryReport({ homeDir: homedir8(), env: process.env });
|
|
14005
14876
|
for (const record of report.records) {
|
|
14006
14877
|
const activity = buildSessionDbRecoveryActivity(record);
|
|
14007
14878
|
if (!activity) throw new Error("could not map session-DB recovery record");
|
|
@@ -14082,7 +14953,7 @@ async function importOpenCodeV2CredentialsForState(state) {
|
|
|
14082
14953
|
}
|
|
14083
14954
|
const result = await importOpenCodeV2Credentials({
|
|
14084
14955
|
request: client.request,
|
|
14085
|
-
homeDir:
|
|
14956
|
+
homeDir: homedir8(),
|
|
14086
14957
|
directory,
|
|
14087
14958
|
guard: state.opencodeV2CredentialImportGuard,
|
|
14088
14959
|
log: (message, level = "info") => logActivity(state, { type: "info", level, message })
|
|
@@ -14502,7 +15373,7 @@ function shouldWarnForReclaimSkip(reason) {
|
|
|
14502
15373
|
return major > 22 || major === 22 && (minor > 13 || minor === 13 && patch >= 0);
|
|
14503
15374
|
}
|
|
14504
15375
|
function sessionDbPath() {
|
|
14505
|
-
return
|
|
15376
|
+
return join13(homedir8(), ".local", "share", "opencode", "opencode.db");
|
|
14506
15377
|
}
|
|
14507
15378
|
function logSessionCleanupActivity(state, phase, entry) {
|
|
14508
15379
|
logActivity(state, {
|
|
@@ -14746,7 +15617,7 @@ function scheduleSessionCleanup(state, driver, options) {
|
|
|
14746
15617
|
for (const warning2 of config.warnings) {
|
|
14747
15618
|
logActivity(state, { type: "info", level: "warn", message: `Session cleanup: ${warning2}` });
|
|
14748
15619
|
}
|
|
14749
|
-
const dbBytes = statSessionDbBytes(
|
|
15620
|
+
const dbBytes = statSessionDbBytes(homedir8());
|
|
14750
15621
|
void (async () => {
|
|
14751
15622
|
const reclaimAvailability = dbBytes !== null && config.enabled ? await probeReclaimAvailability({ dbPath: sessionDbPath(), requiredBytes: dbBytes }) : null;
|
|
14752
15623
|
if (reclaimAvailability !== null) {
|
|
@@ -14965,7 +15836,7 @@ function scheduleResourceUsageReporting(state, options) {
|
|
|
14965
15836
|
});
|
|
14966
15837
|
return;
|
|
14967
15838
|
}
|
|
14968
|
-
const { collect, stop } = createResourceUsageCollector(
|
|
15839
|
+
const { collect, stop } = createResourceUsageCollector(homedir8());
|
|
14969
15840
|
state.stopResourceUsageSampling = stop;
|
|
14970
15841
|
let consecutiveFailures = 0;
|
|
14971
15842
|
const tick = async () => {
|
|
@@ -15206,7 +16077,7 @@ async function run(options) {
|
|
|
15206
16077
|
let fileSyncDirectories;
|
|
15207
16078
|
try {
|
|
15208
16079
|
logLevel = resolveLogLevel(options);
|
|
15209
|
-
fileSyncDirectories = resolveFileSyncDirectories(options.enableFileSyncTo,
|
|
16080
|
+
fileSyncDirectories = resolveFileSyncDirectories(options.enableFileSyncTo, homedir8());
|
|
15210
16081
|
if (options.restoreSessionDb && !options.sessionDbNoReplicateMarker) {
|
|
15211
16082
|
throw new Error(
|
|
15212
16083
|
"--restore-session-db requires --session-db-no-replicate-marker <path>; restore failures must block Litestream replication"
|
|
@@ -15492,7 +16363,7 @@ async function run(options) {
|
|
|
15492
16363
|
state.credentialSync?.arm();
|
|
15493
16364
|
const dependencyInstallDiagnosticReporter = createDependencyInstallDiagnosticReporter({
|
|
15494
16365
|
context: {
|
|
15495
|
-
homeDir:
|
|
16366
|
+
homeDir: homedir8(),
|
|
15496
16367
|
cwd: process.cwd(),
|
|
15497
16368
|
microvmId: process.env.MICROVM_ID?.trim(),
|
|
15498
16369
|
euid: process.getuid?.(),
|
|
@@ -15515,7 +16386,7 @@ async function run(options) {
|
|
|
15515
16386
|
dependencyInstallDiagnosticReporter
|
|
15516
16387
|
);
|
|
15517
16388
|
const openCodeLogTail = tailOpenCodeLogFile(
|
|
15518
|
-
resolveOpenCodeLogPath(
|
|
16389
|
+
resolveOpenCodeLogPath(homedir8(), process.env),
|
|
15519
16390
|
openCodeActivityForwarder
|
|
15520
16391
|
);
|
|
15521
16392
|
state.stopOpenCodeLogTail = () => {
|
|
@@ -15590,6 +16461,44 @@ async function run(options) {
|
|
|
15590
16461
|
for (const warning2 of opencodeVersionWarnings) {
|
|
15591
16462
|
logActivity(state, { type: "info", level: "warn", message: warning2 });
|
|
15592
16463
|
}
|
|
16464
|
+
const managedSetupOptions = {
|
|
16465
|
+
agentId: state.agentId,
|
|
16466
|
+
apiUrl: getApiUrlConfig(),
|
|
16467
|
+
getAuthHeader: () => state.authHeader,
|
|
16468
|
+
autoConfigureCredentialsProxy: options.autoConfigureCredentialsProxy,
|
|
16469
|
+
log: (message, level = "info") => logActivity(state, {
|
|
16470
|
+
type: level === "error" ? "error" : "info",
|
|
16471
|
+
level,
|
|
16472
|
+
...level === "error" ? { error: message } : { message }
|
|
16473
|
+
})
|
|
16474
|
+
};
|
|
16475
|
+
let managedDiscovery = await discoverManagedInferenceSetup(managedSetupOptions);
|
|
16476
|
+
if (managedDiscovery.kind === "none") {
|
|
16477
|
+
managedDiscovery = await enrollManagedInferenceSetup(managedSetupOptions);
|
|
16478
|
+
}
|
|
16479
|
+
let managedPreparation = { kind: "none" };
|
|
16480
|
+
if (managedDiscovery.kind === "failed") {
|
|
16481
|
+
managedPreparation = managedDiscovery;
|
|
16482
|
+
managedSetupOptions.log(`MANAGED-INFERENCE-NOT-READY: ${managedDiscovery.reason}`, "warn");
|
|
16483
|
+
} else if (managedDiscovery.kind === "setup") {
|
|
16484
|
+
const managedHealth = opencodeVersion === "v2" ? await checkOpenCode2Health(state.port) : await checkOpenCodeHealth(state.port);
|
|
16485
|
+
managedPreparation = await prepareManagedInferenceSetup(
|
|
16486
|
+
managedSetupOptions,
|
|
16487
|
+
managedDiscovery,
|
|
16488
|
+
managedHealth
|
|
16489
|
+
);
|
|
16490
|
+
if (managedPreparation.kind === "failed") {
|
|
16491
|
+
managedSetupOptions.log(
|
|
16492
|
+
`MANAGED-INFERENCE-NOT-READY: ${managedPreparation.reason}`,
|
|
16493
|
+
"warn"
|
|
16494
|
+
);
|
|
16495
|
+
} else if (managedPreparation.kind === "ready") {
|
|
16496
|
+
managedSetupOptions.log(
|
|
16497
|
+
`MANAGED-INFERENCE-PREPARED: runner=${state.agentId} generation=${managedPreparation.generation}`,
|
|
16498
|
+
"info"
|
|
16499
|
+
);
|
|
16500
|
+
}
|
|
16501
|
+
}
|
|
15593
16502
|
const { value: maxActiveSessions, warnings: maxActiveSessionsWarnings } = resolveMaxActiveSessions(options, process.env);
|
|
15594
16503
|
for (const warning2 of maxActiveSessionsWarnings) {
|
|
15595
16504
|
logActivity(state, { type: "info", level: "warn", message: warning2 });
|
|
@@ -15616,6 +16525,32 @@ async function run(options) {
|
|
|
15616
16525
|
state.opencodeProcess = options.opencodePidFile ? null : oc.process;
|
|
15617
16526
|
state.opencodeVersion = oc.version;
|
|
15618
16527
|
state.opencodeApiVersion = opencodeVersion;
|
|
16528
|
+
let configuredPlugins = [];
|
|
16529
|
+
let resolvedPlugins = [];
|
|
16530
|
+
try {
|
|
16531
|
+
const provenanceLog = (message) => logActivity(state, { type: "info", level: "warn", message });
|
|
16532
|
+
configuredPlugins = readConfiguredPluginSpecs({ log: provenanceLog });
|
|
16533
|
+
resolvedPlugins = resolveInstalledPluginVersions({ log: provenanceLog });
|
|
16534
|
+
} catch (error2) {
|
|
16535
|
+
const kind = error2 instanceof Error ? error2.name : "unknown error";
|
|
16536
|
+
logActivity(state, {
|
|
16537
|
+
type: "info",
|
|
16538
|
+
level: "warn",
|
|
16539
|
+
message: `Could not collect OpenCode runtime provenance: ${kind}`
|
|
16540
|
+
});
|
|
16541
|
+
}
|
|
16542
|
+
logActivity(state, {
|
|
16543
|
+
type: "info",
|
|
16544
|
+
level: "info",
|
|
16545
|
+
message: "OpenCode runtime provenance",
|
|
16546
|
+
metadata: {
|
|
16547
|
+
opencode_version: state.opencodeVersion,
|
|
16548
|
+
opencode_api_version: state.opencodeApiVersion,
|
|
16549
|
+
configured_plugins: JSON.stringify(configuredPlugins),
|
|
16550
|
+
resolved_plugins: JSON.stringify(resolvedPlugins)
|
|
16551
|
+
},
|
|
16552
|
+
source: "cli.runtime-provenance"
|
|
16553
|
+
});
|
|
15619
16554
|
let opencodePassword = null;
|
|
15620
16555
|
if (opencodeVersion === "v2" && "password" in oc) {
|
|
15621
16556
|
const value = oc.password;
|
|
@@ -15644,7 +16579,7 @@ async function run(options) {
|
|
|
15644
16579
|
const provenance = checkSessionDbProvenance({
|
|
15645
16580
|
dbPath: sessionDbPath(),
|
|
15646
16581
|
currentVersion: state.opencodeVersion,
|
|
15647
|
-
homeDir:
|
|
16582
|
+
homeDir: homedir8(),
|
|
15648
16583
|
env: process.env
|
|
15649
16584
|
});
|
|
15650
16585
|
if (provenance.anomaly) {
|
|
@@ -15658,6 +16593,30 @@ async function run(options) {
|
|
|
15658
16593
|
}
|
|
15659
16594
|
}
|
|
15660
16595
|
state.opencodeConnected = oc.notReadyReason === null;
|
|
16596
|
+
if (managedPreparation.kind === "ready" && oc.notReadyReason === null) {
|
|
16597
|
+
try {
|
|
16598
|
+
const acknowledgement = await acknowledgeManagedInferenceSetup(
|
|
16599
|
+
managedSetupOptions,
|
|
16600
|
+
managedPreparation
|
|
16601
|
+
);
|
|
16602
|
+
if (acknowledgement === "acknowledged") {
|
|
16603
|
+
managedSetupOptions.log(
|
|
16604
|
+
`MANAGED-INFERENCE-ACKNOWLEDGED: runner=${state.agentId} generation=${managedPreparation.generation}`,
|
|
16605
|
+
"info"
|
|
16606
|
+
);
|
|
16607
|
+
} else {
|
|
16608
|
+
managedSetupOptions.log(
|
|
16609
|
+
`MANAGED-INFERENCE-NOT-READY: policy refresh is pending for runner=${state.agentId} generation=${managedPreparation.generation}`,
|
|
16610
|
+
"warn"
|
|
16611
|
+
);
|
|
16612
|
+
}
|
|
16613
|
+
} catch (error2) {
|
|
16614
|
+
managedSetupOptions.log(
|
|
16615
|
+
`MANAGED-INFERENCE-NOT-READY: ${error2 instanceof Error ? error2.message : String(error2)}`,
|
|
16616
|
+
"warn"
|
|
16617
|
+
);
|
|
16618
|
+
}
|
|
16619
|
+
}
|
|
15661
16620
|
const version2 = state.opencodeVersion ? ` (v${state.opencodeVersion})` : "";
|
|
15662
16621
|
ocSpinner?.succeed(`OpenCode running on port ${state.port}${version2}`);
|
|
15663
16622
|
if (!state.interactive && oc.notReadyReason !== null) {
|
|
@@ -15716,7 +16675,7 @@ async function run(options) {
|
|
|
15716
16675
|
let existingPid;
|
|
15717
16676
|
if (existsSync3(options.litestreamPidFile)) {
|
|
15718
16677
|
try {
|
|
15719
|
-
const rawPid =
|
|
16678
|
+
const rawPid = readFileSync8(options.litestreamPidFile, "utf8").trim();
|
|
15720
16679
|
const parsedPid = Number(rawPid);
|
|
15721
16680
|
if (/^\d+$/.test(rawPid) && Number.isSafeInteger(parsedPid) && parsedPid > 0) {
|
|
15722
16681
|
existingPid = parsedPid;
|
|
@@ -15815,7 +16774,7 @@ async function run(options) {
|
|
|
15815
16774
|
// #559: the `--enable-file-sync-to` allow-list. Empty ⇒ pending files are
|
|
15816
16775
|
// REJECTED with `file_sync_disabled` on the ack, not silently ignored.
|
|
15817
16776
|
fileSyncDirectories,
|
|
15818
|
-
homeDir:
|
|
16777
|
+
homeDir: homedir8(),
|
|
15819
16778
|
maxActiveSessions,
|
|
15820
16779
|
log: (entry) => (
|
|
15821
16780
|
// Thread the driver's real level straight through so `debug`/`warn`
|
|
@@ -15840,7 +16799,7 @@ async function run(options) {
|
|
|
15840
16799
|
onConnected: (agentId, isReconnect, transition) => {
|
|
15841
16800
|
state.connected = true;
|
|
15842
16801
|
state.agentId = agentId;
|
|
15843
|
-
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})`;
|
|
16802
|
+
const connectionMessage = isReconnect ? `Tunnel reconnected (runner: ${agentId}, generation: ${transition.generation}, relay connection: ${transition.relayConnectionId ?? "unknown"}, attempt: ${transition.attempt}, offline for ${transition.downtimeMs}ms)` : `Tunnel connected (runner: ${agentId}, generation: ${transition.generation}, relay connection: ${transition.relayConnectionId ?? "unknown"})`;
|
|
15844
16803
|
logActivity(state, {
|
|
15845
16804
|
type: "info",
|
|
15846
16805
|
message: connectionMessage
|
|
@@ -15873,6 +16832,7 @@ async function run(options) {
|
|
|
15873
16832
|
connected: state.opencodeConnected
|
|
15874
16833
|
}),
|
|
15875
16834
|
connection_generation: transition.generation,
|
|
16835
|
+
...transition.relayConnectionId !== null ? { relay_connection_id: transition.relayConnectionId } : {},
|
|
15876
16836
|
...isReconnect && transition.downtimeMs !== null ? {
|
|
15877
16837
|
reconnect_attempt: transition.attempt,
|
|
15878
16838
|
downtime_ms: transition.downtimeMs
|
|
@@ -15903,12 +16863,13 @@ async function run(options) {
|
|
|
15903
16863
|
const safeReason2 = redactDiagnosticText(reason).slice(0, MAX_DISCONNECT_REASON_LENGTH);
|
|
15904
16864
|
logActivity(state, {
|
|
15905
16865
|
type: "info",
|
|
15906
|
-
message: `Tunnel disconnected (generation: ${transition.generation}, code: ${code}, reason: ${safeReason2})`
|
|
16866
|
+
message: `Tunnel disconnected (generation: ${transition.generation}, relay connection: ${transition.relayConnectionId ?? "unknown"}, code: ${code}, reason: ${safeReason2})`
|
|
15907
16867
|
});
|
|
15908
16868
|
emitAgentDisconnected(state.agentId, {
|
|
15909
16869
|
code,
|
|
15910
16870
|
reason: safeReason2,
|
|
15911
|
-
connection_generation: transition.generation
|
|
16871
|
+
connection_generation: transition.generation,
|
|
16872
|
+
...transition.relayConnectionId !== null ? { relay_connection_id: transition.relayConnectionId } : {}
|
|
15912
16873
|
});
|
|
15913
16874
|
if (state.interactive) displayStatus(state);
|
|
15914
16875
|
},
|
|
@@ -16059,9 +17020,9 @@ program.name("evident").description("Run OpenCode locally and connect it to Evid
|
|
|
16059
17020
|
"--endpoint <url>",
|
|
16060
17021
|
"Evident API base URL (default: production; e.g. http://localhost:3001)"
|
|
16061
17022
|
).option("--tunnel <url>", "Tunnel WebSocket URL (default: production; e.g. ws://localhost:8787)").hook("preAction", (thisCommand) => {
|
|
16062
|
-
const { endpoint, tunnel } = thisCommand.opts();
|
|
16063
|
-
if (
|
|
16064
|
-
setEndpoint(
|
|
17023
|
+
const { endpoint: endpoint2, tunnel } = thisCommand.opts();
|
|
17024
|
+
if (endpoint2) {
|
|
17025
|
+
setEndpoint(endpoint2);
|
|
16065
17026
|
}
|
|
16066
17027
|
if (tunnel) {
|
|
16067
17028
|
setTunnelUrl(tunnel);
|
|
@@ -16135,6 +17096,9 @@ program.command("run").description("Connect to Evident and process messages").op
|
|
|
16135
17096
|
).option(
|
|
16136
17097
|
"--opencode-config-overlay <path>",
|
|
16137
17098
|
"Apply this runner-provided OpenCode config before starting OpenCode."
|
|
17099
|
+
).option(
|
|
17100
|
+
"--auto-configure-credentials-proxy",
|
|
17101
|
+
"Install Evident's generated OpenCode providers for the runner's credential pool (off by default)."
|
|
16138
17102
|
).option(
|
|
16139
17103
|
"--credential-sync-marker <path>",
|
|
16140
17104
|
"Own the interval credential sync and write this marker once the shutdown flush has finished, so the runner image's lifecycle hooks can wait on it."
|
|
@@ -16178,6 +17142,7 @@ program.command("run").description("Connect to Evident and process messages").op
|
|
|
16178
17142
|
restoreSessionDb: options.restoreSessionDb,
|
|
16179
17143
|
restoreRunnerCredentials: options.restoreRunnerCredentials,
|
|
16180
17144
|
opencodeConfigOverlay: options.opencodeConfigOverlay,
|
|
17145
|
+
autoConfigureCredentialsProxy: options.autoConfigureCredentialsProxy,
|
|
16181
17146
|
credentialSyncMarker: options.credentialSyncMarker
|
|
16182
17147
|
});
|
|
16183
17148
|
}
|