@evident-ai/cli 3.6.0 → 3.6.1-dev.4831868
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 +889 -55
- 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,81 @@ 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
|
+
};
|
|
1337
|
+
function credentialProxyInferenceUrl(baseUrl) {
|
|
1338
|
+
const trimmed = baseUrl.trim().replace(/\/+$/, "");
|
|
1339
|
+
return /\/v1$/i.test(trimmed) ? trimmed : `${trimmed}/v1`;
|
|
1340
|
+
}
|
|
1341
|
+
function buildCredentialProxyNativeConfig({
|
|
1342
|
+
inferenceBaseUrl,
|
|
1343
|
+
modelDescriptors
|
|
1344
|
+
}) {
|
|
1345
|
+
const baseURL = credentialProxyInferenceUrl(inferenceBaseUrl);
|
|
1346
|
+
const providers = {};
|
|
1347
|
+
const unsupportedProviders = /* @__PURE__ */ new Set();
|
|
1348
|
+
for (const descriptor of modelDescriptors) {
|
|
1349
|
+
const definition = NATIVE_PROVIDERS[descriptor.provider.trim().toLowerCase()];
|
|
1350
|
+
if (!definition) {
|
|
1351
|
+
unsupportedProviders.add(descriptor.provider.trim() || descriptor.provider);
|
|
1352
|
+
continue;
|
|
1353
|
+
}
|
|
1354
|
+
providers[definition.namespace] ??= definition.npm === "@ai-sdk/anthropic" ? {
|
|
1355
|
+
name: definition.name,
|
|
1356
|
+
npm: definition.npm,
|
|
1357
|
+
options: { baseURL, authToken: EVIDENT_PROXY_TOKEN_REFERENCE },
|
|
1358
|
+
models: {}
|
|
1359
|
+
} : definition.npm === "@ai-sdk/openai" ? {
|
|
1360
|
+
name: definition.name,
|
|
1361
|
+
npm: definition.npm,
|
|
1362
|
+
options: { baseURL, apiKey: EVIDENT_PROXY_TOKEN_REFERENCE },
|
|
1363
|
+
models: {}
|
|
1364
|
+
} : {
|
|
1365
|
+
name: definition.name,
|
|
1366
|
+
npm: definition.npm,
|
|
1367
|
+
options: {
|
|
1368
|
+
name: definition.sdkName,
|
|
1369
|
+
baseURL,
|
|
1370
|
+
apiKey: EVIDENT_PROXY_TOKEN_REFERENCE
|
|
1371
|
+
},
|
|
1372
|
+
models: {}
|
|
1373
|
+
};
|
|
1374
|
+
providers[definition.namespace].models[descriptor.id] = { name: descriptor.id };
|
|
1375
|
+
}
|
|
1376
|
+
return {
|
|
1377
|
+
unsupportedProviders: [...unsupportedProviders],
|
|
1378
|
+
config: {
|
|
1379
|
+
$schema: "https://opencode.ai/config.json",
|
|
1380
|
+
provider: providers
|
|
1381
|
+
}
|
|
1382
|
+
};
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1290
1385
|
// ../../packages/types/src/logging/index.ts
|
|
1291
1386
|
var CORRELATION_ID_HEADER = "x-evident-correlation-id";
|
|
1292
1387
|
var FORWARD_FAILURE_REASON_HEADER = "X-Evident-Failure-Reason";
|
|
@@ -1326,7 +1421,7 @@ import ora4 from "ora";
|
|
|
1326
1421
|
import { select as select4 } from "@inquirer/prompts";
|
|
1327
1422
|
|
|
1328
1423
|
// src/lib/telemetry.ts
|
|
1329
|
-
var CLI_VERSION = (true ? "3.6.
|
|
1424
|
+
var CLI_VERSION = (true ? "3.6.1-dev.4831868" : void 0) ?? process.env.npm_package_version ?? "unknown";
|
|
1330
1425
|
function getCliVersion() {
|
|
1331
1426
|
return CLI_VERSION;
|
|
1332
1427
|
}
|
|
@@ -1407,7 +1502,7 @@ async function flushEvents() {
|
|
|
1407
1502
|
const controller = new AbortController();
|
|
1408
1503
|
const timeout = setTimeout(() => controller.abort(), FLUSH_TIMEOUT_MS);
|
|
1409
1504
|
try {
|
|
1410
|
-
const
|
|
1505
|
+
const request3 = {
|
|
1411
1506
|
events,
|
|
1412
1507
|
client_type: "cli",
|
|
1413
1508
|
client_version: CLI_VERSION
|
|
@@ -1418,7 +1513,7 @@ async function flushEvents() {
|
|
|
1418
1513
|
"Content-Type": "application/json",
|
|
1419
1514
|
Authorization: authHeader
|
|
1420
1515
|
},
|
|
1421
|
-
body: JSON.stringify(
|
|
1516
|
+
body: JSON.stringify(request3),
|
|
1422
1517
|
signal: controller.signal
|
|
1423
1518
|
});
|
|
1424
1519
|
if (!response.ok) {
|
|
@@ -2075,7 +2170,8 @@ function createDependencyInstallDiagnosticReporter({
|
|
|
2075
2170
|
var FORWARDED_LEVELS_BY_SOURCE = {
|
|
2076
2171
|
"cli.run": /* @__PURE__ */ new Set(["warn", "error"]),
|
|
2077
2172
|
opencode: /* @__PURE__ */ new Set(["warn", "error"]),
|
|
2078
|
-
"cli.session-cleanup": /* @__PURE__ */ new Set(["info", "warn", "error"])
|
|
2173
|
+
"cli.session-cleanup": /* @__PURE__ */ new Set(["info", "warn", "error"]),
|
|
2174
|
+
"cli.runtime-provenance": /* @__PURE__ */ new Set(["info", "warn", "error"])
|
|
2079
2175
|
};
|
|
2080
2176
|
var SEVERITY_BY_LEVEL = {
|
|
2081
2177
|
info: "info",
|
|
@@ -2871,6 +2967,58 @@ async function listAllSessions(port, client) {
|
|
|
2871
2967
|
}
|
|
2872
2968
|
return { sessions };
|
|
2873
2969
|
}
|
|
2970
|
+
var INTERRUPT_NETWORK_CODES = /* @__PURE__ */ new Set([
|
|
2971
|
+
"ABORT_ERR",
|
|
2972
|
+
"EAI_AGAIN",
|
|
2973
|
+
"ECONNREFUSED",
|
|
2974
|
+
"ECONNRESET",
|
|
2975
|
+
"ENETUNREACH",
|
|
2976
|
+
"ENOTFOUND",
|
|
2977
|
+
"ETIMEDOUT",
|
|
2978
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
2979
|
+
"UND_ERR_HEADERS_TIMEOUT",
|
|
2980
|
+
"UND_ERR_SOCKET"
|
|
2981
|
+
]);
|
|
2982
|
+
function interruptFailureDiagnostic(error2) {
|
|
2983
|
+
const message = error2 instanceof Error ? error2.message : "";
|
|
2984
|
+
const httpStatus = /^OpenCode request failed: HTTP ([1-5]\d{2})(?::|$)/.exec(message)?.[1];
|
|
2985
|
+
if (httpStatus) return `HTTP ${httpStatus}`;
|
|
2986
|
+
const candidates = [error2, error2 instanceof Error ? error2.cause : void 0];
|
|
2987
|
+
for (const candidate of candidates) {
|
|
2988
|
+
if (typeof candidate !== "object" || candidate === null || !("code" in candidate)) continue;
|
|
2989
|
+
const code = candidate.code;
|
|
2990
|
+
if (typeof code === "string" && INTERRUPT_NETWORK_CODES.has(code)) {
|
|
2991
|
+
return redactDiagnosticText(`network ${code}`).slice(0, 120);
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
const name = error2 instanceof Error && ["AbortError", "Error", "TimeoutError", "TypeError"].includes(error2.name) ? error2.name : "unknown error";
|
|
2995
|
+
return redactDiagnosticText(`request threw ${name}`).slice(0, 120);
|
|
2996
|
+
}
|
|
2997
|
+
async function interruptSession(port, id, client) {
|
|
2998
|
+
if (client?.version === "v2") {
|
|
2999
|
+
try {
|
|
3000
|
+
await interruptV2Session(client, id);
|
|
3001
|
+
return { kind: "confirmed_stopped" };
|
|
3002
|
+
} catch (error2) {
|
|
3003
|
+
return { kind: "not_confirmed", diagnostic: interruptFailureDiagnostic(error2) };
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
try {
|
|
3007
|
+
const res = await requestWithClient(
|
|
3008
|
+
port,
|
|
3009
|
+
client,
|
|
3010
|
+
`/session/${id}/abort`,
|
|
3011
|
+
{ method: "POST" },
|
|
3012
|
+
{ allowStatuses: ALL_HTTP_STATUSES }
|
|
3013
|
+
);
|
|
3014
|
+
if (res.status >= 200 && res.status < 300) {
|
|
3015
|
+
return { kind: "confirmed_stopped", status: res.status };
|
|
3016
|
+
}
|
|
3017
|
+
return { kind: "not_confirmed", status: res.status };
|
|
3018
|
+
} catch (error2) {
|
|
3019
|
+
return { kind: "threw", diagnostic: interruptFailureDiagnostic(error2) };
|
|
3020
|
+
}
|
|
3021
|
+
}
|
|
2874
3022
|
async function deleteSession(port, id, client) {
|
|
2875
3023
|
if (client?.version === "v2") return deleteV2Session(client, id);
|
|
2876
3024
|
try {
|
|
@@ -4221,6 +4369,11 @@ async function listV2Permissions(client, sessionId) {
|
|
|
4221
4369
|
return null;
|
|
4222
4370
|
}
|
|
4223
4371
|
}
|
|
4372
|
+
async function interruptV2Session(client, sessionId) {
|
|
4373
|
+
await client.request(`/api/session/${encodeURIComponent(sessionId)}/interrupt`, {
|
|
4374
|
+
method: "POST"
|
|
4375
|
+
});
|
|
4376
|
+
}
|
|
4224
4377
|
async function getV2ActiveSessions(client) {
|
|
4225
4378
|
try {
|
|
4226
4379
|
const body = await readJson(await client.request("/api/session/active"));
|
|
@@ -4623,11 +4776,11 @@ function failureMessage(status2) {
|
|
|
4623
4776
|
function isRetryableStatus(status2) {
|
|
4624
4777
|
return CONNECT_KEY_RETRY_STATUSES.includes(status2);
|
|
4625
4778
|
}
|
|
4626
|
-
async function importOneCredential(
|
|
4779
|
+
async function importOneCredential(request3, credential, sleepForRetry, log3) {
|
|
4627
4780
|
const path = `/api/integration/${encodeURIComponent(credential.integrationId)}/connect/key`;
|
|
4628
4781
|
for (let attempt = 1; attempt <= CONNECT_KEY_MAX_ATTEMPTS; attempt += 1) {
|
|
4629
4782
|
try {
|
|
4630
|
-
const response = await
|
|
4783
|
+
const response = await request3(
|
|
4631
4784
|
path,
|
|
4632
4785
|
{
|
|
4633
4786
|
method: "POST",
|
|
@@ -4662,7 +4815,7 @@ async function importOneCredential(request2, credential, sleepForRetry, log3) {
|
|
|
4662
4815
|
throw new Error("OpenCode credential import retry loop did not return");
|
|
4663
4816
|
}
|
|
4664
4817
|
async function importOpenCodeCredentials({
|
|
4665
|
-
request:
|
|
4818
|
+
request: request3,
|
|
4666
4819
|
classified,
|
|
4667
4820
|
connectedIntegrationIds,
|
|
4668
4821
|
sleepForRetry = sleep2,
|
|
@@ -4674,7 +4827,7 @@ async function importOpenCodeCredentials({
|
|
|
4674
4827
|
result.alreadyConnected.push(credential.integrationId);
|
|
4675
4828
|
continue;
|
|
4676
4829
|
}
|
|
4677
|
-
const outcome = await importOneCredential(
|
|
4830
|
+
const outcome = await importOneCredential(request3, credential, sleepForRetry, log3);
|
|
4678
4831
|
if (outcome.ok) result.imported.push(credential.integrationId);
|
|
4679
4832
|
else result.failed.push(outcome.failure);
|
|
4680
4833
|
}
|
|
@@ -4692,7 +4845,7 @@ function parseIntegrationList(value) {
|
|
|
4692
4845
|
return { integrationIds, hasData: value.data.length > 0 };
|
|
4693
4846
|
}
|
|
4694
4847
|
async function getConnectedOpenCodeIntegrationIds({
|
|
4695
|
-
request:
|
|
4848
|
+
request: request3,
|
|
4696
4849
|
directory,
|
|
4697
4850
|
sleepForRetry = sleep2,
|
|
4698
4851
|
log: log3
|
|
@@ -4700,7 +4853,7 @@ async function getConnectedOpenCodeIntegrationIds({
|
|
|
4700
4853
|
const path = `/api/integration?location%5Bdirectory%5D=${encodeURIComponent(directory)}`;
|
|
4701
4854
|
for (let attempt = 1; attempt <= INTEGRATION_LIST_MAX_READS; attempt += 1) {
|
|
4702
4855
|
try {
|
|
4703
|
-
const response = await
|
|
4856
|
+
const response = await request3(path);
|
|
4704
4857
|
const parsed = parseIntegrationList(await response.json());
|
|
4705
4858
|
if (parsed === null) {
|
|
4706
4859
|
log3(
|
|
@@ -4729,7 +4882,7 @@ async function getConnectedOpenCodeIntegrationIds({
|
|
|
4729
4882
|
return null;
|
|
4730
4883
|
}
|
|
4731
4884
|
async function importOpenCodeV2Credentials({
|
|
4732
|
-
request:
|
|
4885
|
+
request: request3,
|
|
4733
4886
|
homeDir,
|
|
4734
4887
|
directory,
|
|
4735
4888
|
sleepForRetry = sleep2,
|
|
@@ -4747,7 +4900,7 @@ async function importOpenCodeV2Credentials({
|
|
|
4747
4900
|
const classified = classifyOpenCodeCredentials(credentials2);
|
|
4748
4901
|
if (classified.importable.length === 0) return emptyResult(classified.unsupported);
|
|
4749
4902
|
const connectedIntegrationIds = await getConnectedOpenCodeIntegrationIds({
|
|
4750
|
-
request:
|
|
4903
|
+
request: request3,
|
|
4751
4904
|
directory,
|
|
4752
4905
|
sleepForRetry,
|
|
4753
4906
|
log: log3
|
|
@@ -4756,7 +4909,7 @@ async function importOpenCodeV2Credentials({
|
|
|
4756
4909
|
return emptyResult(classified.unsupported, "connection-list-unavailable");
|
|
4757
4910
|
}
|
|
4758
4911
|
return importOpenCodeCredentials({
|
|
4759
|
-
request:
|
|
4912
|
+
request: request3,
|
|
4760
4913
|
classified,
|
|
4761
4914
|
connectedIntegrationIds,
|
|
4762
4915
|
sleepForRetry,
|
|
@@ -7141,8 +7294,8 @@ import { chmod, mkdir, open as open3, realpath, rename, unlink } from "node:fs/p
|
|
|
7141
7294
|
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
7295
|
var FILE_MODE = 384;
|
|
7143
7296
|
var DIRECTORY_MODE = 448;
|
|
7144
|
-
async function writePushedFile(
|
|
7145
|
-
const { requestedPath, content, allowedDirectories, homeDir } =
|
|
7297
|
+
async function writePushedFile(request3) {
|
|
7298
|
+
const { requestedPath, content, allowedDirectories, homeDir } = request3;
|
|
7146
7299
|
const bytes = content.byteLength;
|
|
7147
7300
|
if (allowedDirectories.length === 0) {
|
|
7148
7301
|
return refuse("file_sync_disabled", "File sync is not enabled on this runner.", {
|
|
@@ -8891,6 +9044,34 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
8891
9044
|
if (state === "failed" && isAbortedTerminalReply(messages, ocId ?? "")) {
|
|
8892
9045
|
const ongoing = await this.isSessionOngoing(sessionId);
|
|
8893
9046
|
if (ongoing === false) {
|
|
9047
|
+
const age = this.absoluteAgeForRow(message.processing_started_at);
|
|
9048
|
+
if (age.tripped && age.anchorMs !== null) {
|
|
9049
|
+
try {
|
|
9050
|
+
await this.markFailed(
|
|
9051
|
+
conv.id,
|
|
9052
|
+
message.id,
|
|
9053
|
+
sessionId,
|
|
9054
|
+
this.absoluteCeilingError(message.id, sessionId, age.anchorMs),
|
|
9055
|
+
void 0,
|
|
9056
|
+
{
|
|
9057
|
+
classification: null,
|
|
9058
|
+
errorShape: extractReplyErrorShape(findLastAssistantReplyFor(messages, ocId ?? ""))
|
|
9059
|
+
}
|
|
9060
|
+
);
|
|
9061
|
+
} catch (err) {
|
|
9062
|
+
if (err instanceof ChannelAuthError) throw err;
|
|
9063
|
+
this.log({
|
|
9064
|
+
level: "warn",
|
|
9065
|
+
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)}`,
|
|
9066
|
+
conversation_id: conv.id,
|
|
9067
|
+
message_id: message.id
|
|
9068
|
+
});
|
|
9069
|
+
return "unresolved";
|
|
9070
|
+
}
|
|
9071
|
+
this.clearRedriveUnresolved(message.id);
|
|
9072
|
+
void this.postSignal(conv.id, message.id, "redrive_ceiling_refused");
|
|
9073
|
+
return "settled";
|
|
9074
|
+
}
|
|
8894
9075
|
this.log({
|
|
8895
9076
|
level: "info",
|
|
8896
9077
|
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 +9142,29 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
8961
9142
|
message_id: message.id
|
|
8962
9143
|
});
|
|
8963
9144
|
}
|
|
9145
|
+
if (this.absoluteAgeForRow(message.processing_started_at).tripped) {
|
|
9146
|
+
try {
|
|
9147
|
+
await this.markFailed(
|
|
9148
|
+
conv.id,
|
|
9149
|
+
message.id,
|
|
9150
|
+
sessionId,
|
|
9151
|
+
this.absoluteCeilingError(message.id, sessionId, anchorMs)
|
|
9152
|
+
);
|
|
9153
|
+
} catch (err) {
|
|
9154
|
+
if (err instanceof ChannelAuthError) throw err;
|
|
9155
|
+
this.log({
|
|
9156
|
+
level: "warn",
|
|
9157
|
+
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)}`,
|
|
9158
|
+
conversation_id: conv.id,
|
|
9159
|
+
message_id: message.id
|
|
9160
|
+
});
|
|
9161
|
+
const bound = await this.boundRedriveOutcome(conv, message, "reattach");
|
|
9162
|
+
return bound === "abandoned" ? "abandoned" : "unresolved";
|
|
9163
|
+
}
|
|
9164
|
+
this.clearRedriveUnresolved(message.id);
|
|
9165
|
+
void this.postSignal(conv.id, message.id, "redrive_ceiling_refused");
|
|
9166
|
+
return "abandoned";
|
|
9167
|
+
}
|
|
8964
9168
|
const title = await this.resolveSessionTitle(sessionId, conv.id);
|
|
8965
9169
|
try {
|
|
8966
9170
|
await this.markProcessing(conv.id, message.id, sessionId, ocId, title);
|
|
@@ -9227,8 +9431,7 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
9227
9431
|
const since = this.redriveOutcomeFailingSince.get(message.id);
|
|
9228
9432
|
if (since === void 0) this.redriveOutcomeFailingSince.set(message.id, now);
|
|
9229
9433
|
const durationTripped = now - (since ?? now) >= this.pausedMaxWaitMs;
|
|
9230
|
-
const
|
|
9231
|
-
const absoluteAgeTripped = !Number.isNaN(parsed) && now - parsed >= ABSOLUTE_MAX_PROCESSING_MS;
|
|
9434
|
+
const absoluteAgeTripped = this.absoluteAgeForRow(message.processing_started_at).tripped;
|
|
9232
9435
|
if (!durationTripped && !absoluteAgeTripped) {
|
|
9233
9436
|
this.signalRedriveOutcomeUnreported(conv, message, outcome);
|
|
9234
9437
|
return "retry";
|
|
@@ -9267,6 +9470,15 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
9267
9470
|
});
|
|
9268
9471
|
return "abandoned";
|
|
9269
9472
|
}
|
|
9473
|
+
/** Row-level guards use the durable server stamp; in-flight guards use processingAnchorMs. */
|
|
9474
|
+
absoluteAgeForRow(stamp) {
|
|
9475
|
+
const anchorMs = stamp ? Date.parse(stamp) : NaN;
|
|
9476
|
+
return Number.isNaN(anchorMs) ? { tripped: false, anchorMs: null } : { tripped: this.now() - anchorMs >= ABSOLUTE_MAX_PROCESSING_MS, anchorMs };
|
|
9477
|
+
}
|
|
9478
|
+
absoluteCeilingError(messageId, sessionId, anchorMs, retainedReason = this.readRetainedSessionError(sessionId)) {
|
|
9479
|
+
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.`;
|
|
9480
|
+
return retainedReason ? `${baseError} ${SESSION_ERROR_RETAINED_DETAIL}${retainedReason}` : baseError;
|
|
9481
|
+
}
|
|
9270
9482
|
/**
|
|
9271
9483
|
* Record one poll outcome toward the re-drive fence's consecutive-identical-
|
|
9272
9484
|
* failure streak (#1348) and return the resulting count. `signature === null`
|
|
@@ -9708,10 +9920,7 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
9708
9920
|
opencodeMessageId,
|
|
9709
9921
|
message,
|
|
9710
9922
|
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,
|
|
9923
|
+
processingAnchorMs: this.absoluteAgeForRow(message.processing_started_at).anchorMs ?? processedAtMs,
|
|
9715
9924
|
deadline: watchDeadlineAnchorMs + this.pausedMaxWaitMs,
|
|
9716
9925
|
// The server row is ALREADY `processing`; do not re-fire markProcessing.
|
|
9717
9926
|
started: true,
|
|
@@ -10162,7 +10371,9 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
10162
10371
|
}
|
|
10163
10372
|
const { openQuestions, openPermissions, questionsPolledOk, permissionsPolledOk } = await this.pollInteractions(sessionId, watcher, messages);
|
|
10164
10373
|
if (watcher.generation !== generation) return;
|
|
10374
|
+
const ceilingAttemptsThisTick = /* @__PURE__ */ new Set();
|
|
10165
10375
|
for (const inFlight of [...watcher.inFlight.values()]) {
|
|
10376
|
+
if (ceilingAttemptsThisTick.has(inFlight.evidentMessageId)) continue;
|
|
10166
10377
|
await this.serviceInFlightMessage(
|
|
10167
10378
|
sessionId,
|
|
10168
10379
|
watcher,
|
|
@@ -10171,7 +10382,8 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
10171
10382
|
openQuestions,
|
|
10172
10383
|
openPermissions,
|
|
10173
10384
|
questionsPolledOk,
|
|
10174
|
-
permissionsPolledOk
|
|
10385
|
+
permissionsPolledOk,
|
|
10386
|
+
ceilingAttemptsThisTick
|
|
10175
10387
|
);
|
|
10176
10388
|
}
|
|
10177
10389
|
}
|
|
@@ -10212,13 +10424,117 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
10212
10424
|
inFlight.deadline = this.now() + this.pausedMaxWaitMs;
|
|
10213
10425
|
}
|
|
10214
10426
|
}
|
|
10427
|
+
async settleAbsoluteCeiling(sessionId, watcher, messages, ceilingAttemptsThisTick) {
|
|
10428
|
+
const entries = [...watcher.inFlight.values()];
|
|
10429
|
+
const pastCeiling = entries.filter(
|
|
10430
|
+
(entry) => this.now() - entry.processingAnchorMs >= ABSOLUTE_MAX_PROCESSING_MS
|
|
10431
|
+
);
|
|
10432
|
+
const healthySiblings = entries.length - pastCeiling.length;
|
|
10433
|
+
const landed = [];
|
|
10434
|
+
let allRecordsLanded = true;
|
|
10435
|
+
const retainedReason = this.readRetainedSessionError(sessionId);
|
|
10436
|
+
for (const entry of pastCeiling) {
|
|
10437
|
+
if (!watcher.inFlight.has(entry.evidentMessageId)) continue;
|
|
10438
|
+
ceilingAttemptsThisTick.add(entry.evidentMessageId);
|
|
10439
|
+
const error2 = this.absoluteCeilingError(
|
|
10440
|
+
entry.evidentMessageId,
|
|
10441
|
+
sessionId,
|
|
10442
|
+
entry.processingAnchorMs,
|
|
10443
|
+
retainedReason
|
|
10444
|
+
);
|
|
10445
|
+
this.log({
|
|
10446
|
+
level: "warn",
|
|
10447
|
+
message: error2,
|
|
10448
|
+
conversation_id: watcher.conv.id,
|
|
10449
|
+
message_id: entry.evidentMessageId
|
|
10450
|
+
});
|
|
10451
|
+
try {
|
|
10452
|
+
await this.markFailed(
|
|
10453
|
+
watcher.conv.id,
|
|
10454
|
+
entry.evidentMessageId,
|
|
10455
|
+
sessionId,
|
|
10456
|
+
error2,
|
|
10457
|
+
void 0,
|
|
10458
|
+
{
|
|
10459
|
+
classification: null,
|
|
10460
|
+
errorShape: extractReplyErrorShape(
|
|
10461
|
+
findLastAssistantReplyFor(messages, entry.opencodeMessageId)
|
|
10462
|
+
)
|
|
10463
|
+
}
|
|
10464
|
+
);
|
|
10465
|
+
} catch (err) {
|
|
10466
|
+
if (err instanceof ChannelAuthError) throw err;
|
|
10467
|
+
allRecordsLanded = false;
|
|
10468
|
+
const watched_for_ms = this.now() - entry.processingAnchorMs;
|
|
10469
|
+
if (err instanceof ChannelTerminalError) {
|
|
10470
|
+
void this.postSignal(watcher.conv.id, entry.evidentMessageId, "gave_up", {
|
|
10471
|
+
watched_for_ms,
|
|
10472
|
+
interrupt_outcome: "record_terminal"
|
|
10473
|
+
});
|
|
10474
|
+
this.removeInFlight(watcher, entry.evidentMessageId);
|
|
10475
|
+
continue;
|
|
10476
|
+
}
|
|
10477
|
+
if (this.now() >= entry.deadline) {
|
|
10478
|
+
void this.postSignal(watcher.conv.id, entry.evidentMessageId, "gave_up", {
|
|
10479
|
+
watched_for_ms,
|
|
10480
|
+
interrupt_outcome: "record_deadline"
|
|
10481
|
+
});
|
|
10482
|
+
this.removeInFlight(watcher, entry.evidentMessageId);
|
|
10483
|
+
continue;
|
|
10484
|
+
}
|
|
10485
|
+
void this.postSignal(watcher.conv.id, entry.evidentMessageId, "gave_up", {
|
|
10486
|
+
watched_for_ms,
|
|
10487
|
+
interrupt_outcome: "record_retry"
|
|
10488
|
+
});
|
|
10489
|
+
this.log({
|
|
10490
|
+
level: "warn",
|
|
10491
|
+
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)}`,
|
|
10492
|
+
conversation_id: watcher.conv.id,
|
|
10493
|
+
message_id: entry.evidentMessageId
|
|
10494
|
+
});
|
|
10495
|
+
continue;
|
|
10496
|
+
}
|
|
10497
|
+
entry.done = true;
|
|
10498
|
+
landed.push(entry);
|
|
10499
|
+
}
|
|
10500
|
+
if (!allRecordsLanded) {
|
|
10501
|
+
for (const entry of landed) this.removeInFlight(watcher, entry.evidentMessageId);
|
|
10502
|
+
return;
|
|
10503
|
+
}
|
|
10504
|
+
if (healthySiblings > 0) {
|
|
10505
|
+
for (const entry of landed) {
|
|
10506
|
+
void this.postSignal(watcher.conv.id, entry.evidentMessageId, "gave_up", {
|
|
10507
|
+
watched_for_ms: this.now() - entry.processingAnchorMs,
|
|
10508
|
+
interrupt_outcome: "deferred_for_healthy_sibling",
|
|
10509
|
+
healthy_siblings: healthySiblings
|
|
10510
|
+
});
|
|
10511
|
+
this.removeInFlight(watcher, entry.evidentMessageId);
|
|
10512
|
+
}
|
|
10513
|
+
return;
|
|
10514
|
+
}
|
|
10515
|
+
const interrupt = await interruptSession(this.port, sessionId, this.openCodeClient);
|
|
10516
|
+
for (const entry of landed) {
|
|
10517
|
+
void this.postSignal(watcher.conv.id, entry.evidentMessageId, "gave_up", {
|
|
10518
|
+
watched_for_ms: this.now() - entry.processingAnchorMs,
|
|
10519
|
+
interrupt_outcome: interrupt.kind
|
|
10520
|
+
});
|
|
10521
|
+
this.removeInFlight(watcher, entry.evidentMessageId);
|
|
10522
|
+
}
|
|
10523
|
+
if (interrupt.kind !== "confirmed_stopped") {
|
|
10524
|
+
this.log({
|
|
10525
|
+
level: "warn",
|
|
10526
|
+
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`,
|
|
10527
|
+
conversation_id: watcher.conv.id
|
|
10528
|
+
});
|
|
10529
|
+
}
|
|
10530
|
+
}
|
|
10215
10531
|
/**
|
|
10216
10532
|
* Drive ONE in-flight message's lifecycle from the tick's message snapshot.
|
|
10217
10533
|
* Fires markProcessing on queued→running and markDone on done (each once),
|
|
10218
10534
|
* applies the idle-path re-dispatch guard, and removes the message from the
|
|
10219
10535
|
* in-flight set on completion or timeout.
|
|
10220
10536
|
*/
|
|
10221
|
-
async serviceInFlightMessage(sessionId, watcher, inFlight, messages, openQuestions, openPermissions, questionsPolledOk, permissionsPolledOk) {
|
|
10537
|
+
async serviceInFlightMessage(sessionId, watcher, inFlight, messages, openQuestions, openPermissions, questionsPolledOk, permissionsPolledOk, ceilingAttemptsThisTick) {
|
|
10222
10538
|
const conv = watcher.conv;
|
|
10223
10539
|
const state = messageRunState(messages, inFlight.opencodeMessageId);
|
|
10224
10540
|
const id = inFlight.evidentMessageId;
|
|
@@ -10562,17 +10878,8 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
10562
10878
|
return;
|
|
10563
10879
|
}
|
|
10564
10880
|
}
|
|
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);
|
|
10881
|
+
if (watcher.inFlight.has(inFlight.evidentMessageId) && activelyRunning && this.now() - inFlight.processingAnchorMs >= ABSOLUTE_MAX_PROCESSING_MS) {
|
|
10882
|
+
await this.settleAbsoluteCeiling(sessionId, watcher, messages, ceilingAttemptsThisTick);
|
|
10576
10883
|
return;
|
|
10577
10884
|
}
|
|
10578
10885
|
const heartbeatDue = activelyRunning && !inFlight.awaitingHumanLatched && !inFlight.aliveInFlight && this.now() - inFlight.lastAliveAt >= HEARTBEAT_MS;
|
|
@@ -11195,6 +11502,33 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
11195
11502
|
}
|
|
11196
11503
|
const restartAborted = state === "failed" && sessionOngoing === false && isAbortedTerminalReply(messages, ocId ?? "");
|
|
11197
11504
|
if (restartAborted) {
|
|
11505
|
+
const age = this.absoluteAgeForRow(row.processing_started_at);
|
|
11506
|
+
if (age.tripped && age.anchorMs !== null) {
|
|
11507
|
+
try {
|
|
11508
|
+
await this.markFailed(
|
|
11509
|
+
row.conversation_id,
|
|
11510
|
+
row.id,
|
|
11511
|
+
sessionId,
|
|
11512
|
+
this.absoluteCeilingError(row.id, sessionId, age.anchorMs),
|
|
11513
|
+
void 0,
|
|
11514
|
+
{
|
|
11515
|
+
classification: null,
|
|
11516
|
+
errorShape: extractReplyErrorShape(findLastAssistantReplyFor(messages, ocId ?? ""))
|
|
11517
|
+
}
|
|
11518
|
+
);
|
|
11519
|
+
} catch (err) {
|
|
11520
|
+
if (err instanceof ChannelAuthError) throw err;
|
|
11521
|
+
this.log({
|
|
11522
|
+
level: "warn",
|
|
11523
|
+
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)}`,
|
|
11524
|
+
conversation_id: row.conversation_id,
|
|
11525
|
+
message_id: row.id
|
|
11526
|
+
});
|
|
11527
|
+
return;
|
|
11528
|
+
}
|
|
11529
|
+
void this.postSignal(row.conversation_id, row.id, "redrive_ceiling_refused");
|
|
11530
|
+
return;
|
|
11531
|
+
}
|
|
11198
11532
|
this.log({
|
|
11199
11533
|
level: "info",
|
|
11200
11534
|
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 +12074,8 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
11740
12074
|
source_message_id: row.source_message_id,
|
|
11741
12075
|
slack_user_id: row.slack_user_id,
|
|
11742
12076
|
attachments: row.attachments ?? null,
|
|
11743
|
-
opencode_message_id: row.opencode_message_id
|
|
12077
|
+
opencode_message_id: row.opencode_message_id,
|
|
12078
|
+
processing_started_at: row.processing_started_at
|
|
11744
12079
|
};
|
|
11745
12080
|
}
|
|
11746
12081
|
/**
|
|
@@ -13538,6 +13873,44 @@ async function configureGitHubAccess({ env, log: log3 }) {
|
|
|
13538
13873
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
13539
13874
|
import { copyFileSync, existsSync as existsSync2, statSync as statSync6 } from "node:fs";
|
|
13540
13875
|
import { isAbsolute as isAbsolute3, join as join11, resolve as resolve4 } from "node:path";
|
|
13876
|
+
var MANAGED_PROVIDER_NAMESPACES = [
|
|
13877
|
+
"evident-claude",
|
|
13878
|
+
"evident-codex",
|
|
13879
|
+
"evident-openai",
|
|
13880
|
+
"evident-kimi"
|
|
13881
|
+
];
|
|
13882
|
+
function parseConfigContent(content) {
|
|
13883
|
+
if (!content?.trim()) return {};
|
|
13884
|
+
const parsed = JSON.parse(content);
|
|
13885
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
13886
|
+
throw new Error("OPENCODE_CONFIG_CONTENT must contain a JSON object");
|
|
13887
|
+
}
|
|
13888
|
+
return parsed;
|
|
13889
|
+
}
|
|
13890
|
+
function mergeManagedOpenCodeConfigContent(content, managedProviders) {
|
|
13891
|
+
const config = parseConfigContent(content);
|
|
13892
|
+
const provider = typeof config.provider === "object" && config.provider !== null && !Array.isArray(config.provider) ? { ...config.provider } : {};
|
|
13893
|
+
for (const namespace of MANAGED_PROVIDER_NAMESPACES) {
|
|
13894
|
+
delete provider[namespace];
|
|
13895
|
+
}
|
|
13896
|
+
return JSON.stringify({ ...config, provider: { ...provider, ...managedProviders } });
|
|
13897
|
+
}
|
|
13898
|
+
function removeManagedOpenCodeConfigContent(content) {
|
|
13899
|
+
if (!content?.trim()) return content;
|
|
13900
|
+
const config = parseConfigContent(content);
|
|
13901
|
+
if (typeof config.provider !== "object" || config.provider === null || Array.isArray(config.provider)) {
|
|
13902
|
+
return content;
|
|
13903
|
+
}
|
|
13904
|
+
const provider = { ...config.provider };
|
|
13905
|
+
let changed = false;
|
|
13906
|
+
for (const namespace of MANAGED_PROVIDER_NAMESPACES) {
|
|
13907
|
+
if (namespace in provider) {
|
|
13908
|
+
delete provider[namespace];
|
|
13909
|
+
changed = true;
|
|
13910
|
+
}
|
|
13911
|
+
}
|
|
13912
|
+
return changed ? JSON.stringify({ ...config, provider }) : content;
|
|
13913
|
+
}
|
|
13541
13914
|
function isFile(filePath) {
|
|
13542
13915
|
return existsSync2(filePath) && statSync6(filePath).isFile();
|
|
13543
13916
|
}
|
|
@@ -13571,6 +13944,159 @@ function applyRunnerOpenCodeConfig({
|
|
|
13571
13944
|
log3(`Applied runner OpenCode config ${source} to ${join11(cwd, target)}`);
|
|
13572
13945
|
}
|
|
13573
13946
|
|
|
13947
|
+
// src/lib/opencode/runtime-provenance.ts
|
|
13948
|
+
import { readFileSync as readFileSync7, readdirSync } from "node:fs";
|
|
13949
|
+
import { homedir as homedir7 } from "node:os";
|
|
13950
|
+
import { join as join12 } from "node:path";
|
|
13951
|
+
var PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/i;
|
|
13952
|
+
var REGISTRY_SPEC = /^[a-z0-9.*+^~<>=|-]+$/i;
|
|
13953
|
+
function errorKind(error2) {
|
|
13954
|
+
if (!(error2 instanceof Error)) return "unknown error";
|
|
13955
|
+
const code = "code" in error2 && typeof error2.code === "string" ? ` (${error2.code})` : "";
|
|
13956
|
+
return `${error2.name}${code}`;
|
|
13957
|
+
}
|
|
13958
|
+
function parseRegistryPluginSpec(value) {
|
|
13959
|
+
const separator = value.lastIndexOf("@");
|
|
13960
|
+
if (separator <= 0) return null;
|
|
13961
|
+
const name = value.slice(0, separator);
|
|
13962
|
+
const spec = value.slice(separator + 1);
|
|
13963
|
+
if (!PACKAGE_NAME.test(name) || !REGISTRY_SPEC.test(spec)) return null;
|
|
13964
|
+
return { name, spec };
|
|
13965
|
+
}
|
|
13966
|
+
function parseJsonc(value) {
|
|
13967
|
+
let output = "";
|
|
13968
|
+
let inString = false;
|
|
13969
|
+
let escaped = false;
|
|
13970
|
+
let lineComment = false;
|
|
13971
|
+
let blockComment = false;
|
|
13972
|
+
for (let index = 0; index < value.length; index++) {
|
|
13973
|
+
const character = value[index];
|
|
13974
|
+
const next = value[index + 1];
|
|
13975
|
+
if (lineComment) {
|
|
13976
|
+
if (character === "\n") {
|
|
13977
|
+
lineComment = false;
|
|
13978
|
+
output += character;
|
|
13979
|
+
}
|
|
13980
|
+
continue;
|
|
13981
|
+
}
|
|
13982
|
+
if (blockComment) {
|
|
13983
|
+
if (character === "*" && next === "/") {
|
|
13984
|
+
blockComment = false;
|
|
13985
|
+
index++;
|
|
13986
|
+
} else if (character === "\n") {
|
|
13987
|
+
output += character;
|
|
13988
|
+
}
|
|
13989
|
+
continue;
|
|
13990
|
+
}
|
|
13991
|
+
if (inString) {
|
|
13992
|
+
output += character;
|
|
13993
|
+
if (escaped) escaped = false;
|
|
13994
|
+
else if (character === "\\") escaped = true;
|
|
13995
|
+
else if (character === '"') inString = false;
|
|
13996
|
+
continue;
|
|
13997
|
+
}
|
|
13998
|
+
if (character === '"') {
|
|
13999
|
+
inString = true;
|
|
14000
|
+
output += character;
|
|
14001
|
+
} else if (character === "/" && next === "/") {
|
|
14002
|
+
lineComment = true;
|
|
14003
|
+
index++;
|
|
14004
|
+
} else if (character === "/" && next === "*") {
|
|
14005
|
+
blockComment = true;
|
|
14006
|
+
index++;
|
|
14007
|
+
} else if (character === ",") {
|
|
14008
|
+
let lookahead = index + 1;
|
|
14009
|
+
while (/\s/.test(value[lookahead] ?? "")) lookahead++;
|
|
14010
|
+
if (value[lookahead] !== "]" && value[lookahead] !== "}") output += character;
|
|
14011
|
+
} else {
|
|
14012
|
+
output += character;
|
|
14013
|
+
}
|
|
14014
|
+
}
|
|
14015
|
+
return JSON.parse(output);
|
|
14016
|
+
}
|
|
14017
|
+
function openCodeCacheRoot(env = process.env, homeDir = homedir7()) {
|
|
14018
|
+
return join12(env.XDG_CACHE_HOME ?? join12(homeDir, ".cache"), "opencode");
|
|
14019
|
+
}
|
|
14020
|
+
function readConfiguredPluginSpecs({
|
|
14021
|
+
cwd = process.cwd(),
|
|
14022
|
+
log: log3
|
|
14023
|
+
}) {
|
|
14024
|
+
let config;
|
|
14025
|
+
try {
|
|
14026
|
+
try {
|
|
14027
|
+
config = parseJsonc(readFileSync7(join12(cwd, "opencode.jsonc"), "utf8"));
|
|
14028
|
+
} catch (error2) {
|
|
14029
|
+
if (!("code" in Object(error2)) || Object(error2).code !== "ENOENT") throw error2;
|
|
14030
|
+
config = parseJsonc(readFileSync7(join12(cwd, "opencode.json"), "utf8"));
|
|
14031
|
+
}
|
|
14032
|
+
} catch (error2) {
|
|
14033
|
+
log3(`Could not read OpenCode plugin configuration: ${errorKind(error2)}`);
|
|
14034
|
+
return [];
|
|
14035
|
+
}
|
|
14036
|
+
if (typeof config !== "object" || config === null || !("plugin" in config)) return [];
|
|
14037
|
+
const plugins = config.plugin;
|
|
14038
|
+
if (!Array.isArray(plugins)) {
|
|
14039
|
+
log3("Could not read OpenCode plugin configuration: plugin is not an array");
|
|
14040
|
+
return [];
|
|
14041
|
+
}
|
|
14042
|
+
const configured = plugins.filter(
|
|
14043
|
+
(plugin) => typeof plugin === "string" && parseRegistryPluginSpec(plugin) !== null
|
|
14044
|
+
);
|
|
14045
|
+
if (configured.length !== plugins.length) {
|
|
14046
|
+
log3(
|
|
14047
|
+
`Skipped ${plugins.length - configured.length} non-registry OpenCode plugin spec(s); their values were not recorded`
|
|
14048
|
+
);
|
|
14049
|
+
}
|
|
14050
|
+
return configured;
|
|
14051
|
+
}
|
|
14052
|
+
function resolveInstalledPluginVersions({
|
|
14053
|
+
cacheRoot,
|
|
14054
|
+
env = process.env,
|
|
14055
|
+
homeDir = homedir7(),
|
|
14056
|
+
log: log3
|
|
14057
|
+
}) {
|
|
14058
|
+
const packagesRoot = join12(cacheRoot ?? openCodeCacheRoot(env, homeDir), "packages");
|
|
14059
|
+
let directories;
|
|
14060
|
+
try {
|
|
14061
|
+
directories = readdirSync(packagesRoot, { withFileTypes: true });
|
|
14062
|
+
} catch (error2) {
|
|
14063
|
+
if ("code" in Object(error2) && Object(error2).code === "ENOENT") return [];
|
|
14064
|
+
log3(`Could not inspect the OpenCode plugin cache: ${errorKind(error2)}`);
|
|
14065
|
+
return [];
|
|
14066
|
+
}
|
|
14067
|
+
const plugins = [];
|
|
14068
|
+
let skipped = 0;
|
|
14069
|
+
for (const directory of directories) {
|
|
14070
|
+
if (!directory.isDirectory()) continue;
|
|
14071
|
+
const parsed = parseRegistryPluginSpec(directory.name);
|
|
14072
|
+
if (parsed === null) {
|
|
14073
|
+
skipped++;
|
|
14074
|
+
continue;
|
|
14075
|
+
}
|
|
14076
|
+
let resolvedVersion = null;
|
|
14077
|
+
try {
|
|
14078
|
+
const packageJson = JSON.parse(
|
|
14079
|
+
readFileSync7(
|
|
14080
|
+
join12(packagesRoot, directory.name, "node_modules", parsed.name, "package.json"),
|
|
14081
|
+
"utf8"
|
|
14082
|
+
)
|
|
14083
|
+
);
|
|
14084
|
+
if (typeof packageJson.version === "string" && REGISTRY_SPEC.test(packageJson.version)) {
|
|
14085
|
+
resolvedVersion = packageJson.version;
|
|
14086
|
+
} else {
|
|
14087
|
+
log3(`Could not resolve OpenCode plugin ${parsed.name}@${parsed.spec}: invalid version`);
|
|
14088
|
+
}
|
|
14089
|
+
} catch (error2) {
|
|
14090
|
+
log3(`Could not resolve OpenCode plugin ${parsed.name}@${parsed.spec}: ${errorKind(error2)}`);
|
|
14091
|
+
}
|
|
14092
|
+
plugins.push({ ...parsed, resolvedVersion });
|
|
14093
|
+
}
|
|
14094
|
+
if (skipped > 0) {
|
|
14095
|
+
log3(`Skipped ${skipped} non-registry OpenCode package cache entry; its value was not recorded`);
|
|
14096
|
+
}
|
|
14097
|
+
return plugins;
|
|
14098
|
+
}
|
|
14099
|
+
|
|
13574
14100
|
// src/lib/credential-sync.ts
|
|
13575
14101
|
import { renameSync, writeFileSync as writeFileSync5 } from "node:fs";
|
|
13576
14102
|
var CREDENTIAL_SYNC_TICK_TIMEOUT_MS = 3e4;
|
|
@@ -13806,6 +14332,230 @@ function createCredentialSync({
|
|
|
13806
14332
|
};
|
|
13807
14333
|
}
|
|
13808
14334
|
|
|
14335
|
+
// src/lib/managed-inference-setup.ts
|
|
14336
|
+
var defaultStore = {
|
|
14337
|
+
get: getManagedInferenceCredential,
|
|
14338
|
+
set: setManagedInferenceCredential,
|
|
14339
|
+
clear: clearManagedInferenceCredential
|
|
14340
|
+
};
|
|
14341
|
+
function endpoint(options, operation) {
|
|
14342
|
+
return `${options.apiUrl}/runners/${options.agentId}/managed-credentials/${operation}`;
|
|
14343
|
+
}
|
|
14344
|
+
function requestHeaders(options) {
|
|
14345
|
+
return { Authorization: options.getAuthHeader(), "Content-Type": "application/json" };
|
|
14346
|
+
}
|
|
14347
|
+
function request2(options) {
|
|
14348
|
+
return withRequestTimeout(options.fetchImpl ?? fetch, REQUEST_TIMEOUT_MS);
|
|
14349
|
+
}
|
|
14350
|
+
function safeFailure(operation, status2) {
|
|
14351
|
+
return status2 === void 0 ? `${operation} could not reach the managed credential service` : `${operation} returned HTTP ${status2}`;
|
|
14352
|
+
}
|
|
14353
|
+
function logTransportFailure(options, operation, error2, authorization) {
|
|
14354
|
+
let detail = error2 instanceof Error ? error2.message : String(error2);
|
|
14355
|
+
const credential = authorization.split(/\s+/, 2)[1];
|
|
14356
|
+
for (const secret of [authorization, credential]) {
|
|
14357
|
+
if (secret) detail = detail.replaceAll(secret, "[redacted]");
|
|
14358
|
+
}
|
|
14359
|
+
options.log(
|
|
14360
|
+
`MANAGED-INFERENCE-TRANSPORT-FAILED: operation=${operation} detail=${redactDiagnosticText(detail)}`,
|
|
14361
|
+
"debug"
|
|
14362
|
+
);
|
|
14363
|
+
}
|
|
14364
|
+
async function responseJson(response) {
|
|
14365
|
+
try {
|
|
14366
|
+
const value = await response.json();
|
|
14367
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
14368
|
+
} catch (error2) {
|
|
14369
|
+
return null;
|
|
14370
|
+
}
|
|
14371
|
+
}
|
|
14372
|
+
function readSetup(value) {
|
|
14373
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
|
|
14374
|
+
const setup = value;
|
|
14375
|
+
if (typeof setup.runner_id !== "string" || typeof setup.generation !== "string" || typeof setup.status !== "string" || setup.token_id !== null && typeof setup.token_id !== "string") {
|
|
14376
|
+
return null;
|
|
14377
|
+
}
|
|
14378
|
+
return setup;
|
|
14379
|
+
}
|
|
14380
|
+
function clearLocalSetup(options) {
|
|
14381
|
+
const env = options.env ?? process.env;
|
|
14382
|
+
(options.store ?? defaultStore).clear(options.agentId);
|
|
14383
|
+
delete env.EVIDENT_PROXY_TOKEN;
|
|
14384
|
+
try {
|
|
14385
|
+
const content = removeManagedOpenCodeConfigContent(env.OPENCODE_CONFIG_CONTENT);
|
|
14386
|
+
if (content === void 0) delete env.OPENCODE_CONFIG_CONTENT;
|
|
14387
|
+
else env.OPENCODE_CONFIG_CONTENT = content;
|
|
14388
|
+
} catch (error2) {
|
|
14389
|
+
options.log(
|
|
14390
|
+
"MANAGED-INFERENCE-CLEANUP-PARTIAL: stored credentials were removed, but malformed OPENCODE_CONFIG_CONTENT could not be inspected",
|
|
14391
|
+
"warn"
|
|
14392
|
+
);
|
|
14393
|
+
}
|
|
14394
|
+
}
|
|
14395
|
+
async function discoverManagedInferenceSetup(options) {
|
|
14396
|
+
const headers = requestHeaders(options);
|
|
14397
|
+
let response;
|
|
14398
|
+
try {
|
|
14399
|
+
response = await request2(options)(endpoint(options, "current"), {
|
|
14400
|
+
headers
|
|
14401
|
+
});
|
|
14402
|
+
} catch (error2) {
|
|
14403
|
+
logTransportFailure(options, "discovery", error2, headers.Authorization);
|
|
14404
|
+
return { kind: "failed", reason: safeFailure("Managed setup discovery") };
|
|
14405
|
+
}
|
|
14406
|
+
if (response.status === 404) {
|
|
14407
|
+
clearLocalSetup(options);
|
|
14408
|
+
return { kind: "none" };
|
|
14409
|
+
}
|
|
14410
|
+
if (!response.ok) {
|
|
14411
|
+
return { kind: "failed", reason: safeFailure("Managed setup discovery", response.status) };
|
|
14412
|
+
}
|
|
14413
|
+
const body = await responseJson(response);
|
|
14414
|
+
const setup = readSetup(body?.setup);
|
|
14415
|
+
return setup ? { kind: "setup", setup } : { kind: "failed", reason: "Managed setup discovery returned an invalid response" };
|
|
14416
|
+
}
|
|
14417
|
+
async function post(options, operation, body) {
|
|
14418
|
+
const headers = requestHeaders(options);
|
|
14419
|
+
try {
|
|
14420
|
+
return await request2(options)(endpoint(options, operation), {
|
|
14421
|
+
method: "POST",
|
|
14422
|
+
headers,
|
|
14423
|
+
body: JSON.stringify(body)
|
|
14424
|
+
});
|
|
14425
|
+
} catch (error2) {
|
|
14426
|
+
logTransportFailure(options, operation, error2, headers.Authorization);
|
|
14427
|
+
return null;
|
|
14428
|
+
}
|
|
14429
|
+
}
|
|
14430
|
+
function applyCredential(options, credential) {
|
|
14431
|
+
const { config, unsupportedProviders } = buildCredentialProxyNativeConfig({
|
|
14432
|
+
inferenceBaseUrl: credential.inferenceBaseUrl,
|
|
14433
|
+
modelDescriptors: credential.modelDescriptors
|
|
14434
|
+
});
|
|
14435
|
+
if (unsupportedProviders.length > 0 || Object.keys(config.provider).length === 0) {
|
|
14436
|
+
return "Managed setup contains unsupported provider descriptors";
|
|
14437
|
+
}
|
|
14438
|
+
const env = options.env ?? process.env;
|
|
14439
|
+
let merged;
|
|
14440
|
+
try {
|
|
14441
|
+
merged = mergeManagedOpenCodeConfigContent(env.OPENCODE_CONFIG_CONTENT, config.provider);
|
|
14442
|
+
} catch (error2) {
|
|
14443
|
+
return "OPENCODE_CONFIG_CONTENT is malformed; managed providers were not installed";
|
|
14444
|
+
}
|
|
14445
|
+
try {
|
|
14446
|
+
(options.store ?? defaultStore).set(options.agentId, credential);
|
|
14447
|
+
} catch (error2) {
|
|
14448
|
+
options.log(
|
|
14449
|
+
`MANAGED-INFERENCE-STORE-FAILED: runner=${options.agentId} generation=${credential.generation}`,
|
|
14450
|
+
"warn"
|
|
14451
|
+
);
|
|
14452
|
+
return "Managed credentials could not be written to the protected store";
|
|
14453
|
+
}
|
|
14454
|
+
env.EVIDENT_PROXY_TOKEN = credential.token;
|
|
14455
|
+
env.OPENCODE_CONFIG_CONTENT = merged;
|
|
14456
|
+
return null;
|
|
14457
|
+
}
|
|
14458
|
+
async function reportManagedInferenceApplyFailure(options, failure) {
|
|
14459
|
+
const response = await post(options, "report-failure", {
|
|
14460
|
+
generation: failure.generation,
|
|
14461
|
+
token_id: failure.tokenId
|
|
14462
|
+
});
|
|
14463
|
+
if (!response?.ok) {
|
|
14464
|
+
options.log(
|
|
14465
|
+
`MANAGED-INFERENCE-REPORT-FAILED: runner=${options.agentId} generation=${failure.generation}${response ? ` status=${response.status}` : ""}`,
|
|
14466
|
+
"warn"
|
|
14467
|
+
);
|
|
14468
|
+
}
|
|
14469
|
+
}
|
|
14470
|
+
async function reaffirmStored(options, credential) {
|
|
14471
|
+
const response = await post(options, "reaffirm", {
|
|
14472
|
+
generation: credential.generation,
|
|
14473
|
+
token_id: credential.tokenId
|
|
14474
|
+
});
|
|
14475
|
+
if (!response?.ok) {
|
|
14476
|
+
return {
|
|
14477
|
+
kind: "failed",
|
|
14478
|
+
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")
|
|
14479
|
+
};
|
|
14480
|
+
}
|
|
14481
|
+
const body = await responseJson(response);
|
|
14482
|
+
const setup = readSetup(body?.setup);
|
|
14483
|
+
if (!setup || setup.generation !== credential.generation || setup.token_id !== credential.tokenId) {
|
|
14484
|
+
return { kind: "failed", reason: "Managed credential reaffirmation returned a mismatch" };
|
|
14485
|
+
}
|
|
14486
|
+
const failure = applyCredential(options, credential);
|
|
14487
|
+
return failure ? { kind: "failed", reason: failure } : { kind: "ready", generation: credential.generation, tokenId: credential.tokenId };
|
|
14488
|
+
}
|
|
14489
|
+
async function prepareManagedInferenceSetup(options, discovery, health) {
|
|
14490
|
+
if (health.authFailed) {
|
|
14491
|
+
return {
|
|
14492
|
+
kind: "failed",
|
|
14493
|
+
reason: "OpenCode authentication failed; managed credentials were not rotated"
|
|
14494
|
+
};
|
|
14495
|
+
}
|
|
14496
|
+
const stored = (options.store ?? defaultStore).get(options.agentId);
|
|
14497
|
+
if (health.healthy) {
|
|
14498
|
+
if (!stored) {
|
|
14499
|
+
return {
|
|
14500
|
+
kind: "failed",
|
|
14501
|
+
reason: "Managed credentials are missing for the serving OpenCode process; restart required"
|
|
14502
|
+
};
|
|
14503
|
+
}
|
|
14504
|
+
return reaffirmStored(options, stored);
|
|
14505
|
+
}
|
|
14506
|
+
if (discovery.setup.status === "acknowledged" && stored?.generation === discovery.setup.generation && stored.tokenId === discovery.setup.token_id) {
|
|
14507
|
+
return reaffirmStored(options, stored);
|
|
14508
|
+
}
|
|
14509
|
+
const response = await post(options, "bootstrap", { generation: discovery.setup.generation });
|
|
14510
|
+
if (!response?.ok) {
|
|
14511
|
+
return {
|
|
14512
|
+
kind: "failed",
|
|
14513
|
+
reason: response ? safeFailure("Managed credential bootstrap", response.status) : safeFailure("Managed credential bootstrap")
|
|
14514
|
+
};
|
|
14515
|
+
}
|
|
14516
|
+
const body = await responseJson(response);
|
|
14517
|
+
const setup = readSetup(body?.setup);
|
|
14518
|
+
const token = body?.inference_token;
|
|
14519
|
+
const inferenceBaseUrl = body?.inference_base_url;
|
|
14520
|
+
const modelDescriptors = body?.model_descriptors;
|
|
14521
|
+
if (!setup || setup.generation !== discovery.setup.generation || typeof setup.token_id !== "string" || typeof token !== "string" || typeof inferenceBaseUrl !== "string" || !Array.isArray(modelDescriptors) || !modelDescriptors.every(
|
|
14522
|
+
(descriptor) => typeof descriptor === "object" && descriptor !== null && typeof descriptor.id === "string" && typeof descriptor.provider === "string"
|
|
14523
|
+
)) {
|
|
14524
|
+
return { kind: "failed", reason: "Managed credential bootstrap returned an invalid response" };
|
|
14525
|
+
}
|
|
14526
|
+
const credential = {
|
|
14527
|
+
generation: setup.generation,
|
|
14528
|
+
tokenId: setup.token_id,
|
|
14529
|
+
token,
|
|
14530
|
+
inferenceBaseUrl,
|
|
14531
|
+
modelDescriptors
|
|
14532
|
+
};
|
|
14533
|
+
const failure = applyCredential(options, credential);
|
|
14534
|
+
if (failure) {
|
|
14535
|
+
await reportManagedInferenceApplyFailure(options, credential);
|
|
14536
|
+
return { kind: "failed", reason: failure };
|
|
14537
|
+
}
|
|
14538
|
+
return { kind: "ready", generation: credential.generation, tokenId: credential.tokenId };
|
|
14539
|
+
}
|
|
14540
|
+
async function acknowledgeManagedInferenceSetup(options, preparation) {
|
|
14541
|
+
const response = await post(options, "acknowledge", {
|
|
14542
|
+
generation: preparation.generation,
|
|
14543
|
+
token_id: preparation.tokenId,
|
|
14544
|
+
config_validated: true
|
|
14545
|
+
});
|
|
14546
|
+
if (!response?.ok) {
|
|
14547
|
+
throw new Error(
|
|
14548
|
+
response ? safeFailure("Managed credential acknowledgment", response.status) : safeFailure("Managed credential acknowledgment")
|
|
14549
|
+
);
|
|
14550
|
+
}
|
|
14551
|
+
const body = await responseJson(response);
|
|
14552
|
+
const setup = readSetup(body?.setup);
|
|
14553
|
+
if (!setup || setup.generation !== preparation.generation || setup.token_id !== preparation.tokenId) {
|
|
14554
|
+
throw new Error("Managed credential acknowledgment returned a mismatch");
|
|
14555
|
+
}
|
|
14556
|
+
return response.status === 200 && setup.status === "acknowledged" ? "acknowledged" : "pending";
|
|
14557
|
+
}
|
|
14558
|
+
|
|
13809
14559
|
// src/commands/run.ts
|
|
13810
14560
|
var MAX_ACTIVITY_LOG_ENTRIES = 10;
|
|
13811
14561
|
var MAX_DISCONNECT_REASON_LENGTH = 200;
|
|
@@ -13846,7 +14596,7 @@ function resolveFileSyncDirectories(raw, homeDir) {
|
|
|
13846
14596
|
if (trimmed === "") {
|
|
13847
14597
|
throw new Error("--enable-file-sync-to requires a directory path (got an empty value)");
|
|
13848
14598
|
}
|
|
13849
|
-
const expanded = trimmed === "~" ? homeDir : trimmed.startsWith("~/") ?
|
|
14599
|
+
const expanded = trimmed === "~" ? homeDir : trimmed.startsWith("~/") ? join13(homeDir, trimmed.slice(2)) : trimmed;
|
|
13850
14600
|
if (!isAbsolute4(expanded)) {
|
|
13851
14601
|
throw new Error(`--enable-file-sync-to requires an absolute directory path; got "${entry}"`);
|
|
13852
14602
|
}
|
|
@@ -14001,7 +14751,7 @@ function logActivity(state, entry) {
|
|
|
14001
14751
|
}
|
|
14002
14752
|
function reportSessionDbRecovery(state) {
|
|
14003
14753
|
try {
|
|
14004
|
-
const report = drainSessionDbRecoveryReport({ homeDir:
|
|
14754
|
+
const report = drainSessionDbRecoveryReport({ homeDir: homedir8(), env: process.env });
|
|
14005
14755
|
for (const record of report.records) {
|
|
14006
14756
|
const activity = buildSessionDbRecoveryActivity(record);
|
|
14007
14757
|
if (!activity) throw new Error("could not map session-DB recovery record");
|
|
@@ -14082,7 +14832,7 @@ async function importOpenCodeV2CredentialsForState(state) {
|
|
|
14082
14832
|
}
|
|
14083
14833
|
const result = await importOpenCodeV2Credentials({
|
|
14084
14834
|
request: client.request,
|
|
14085
|
-
homeDir:
|
|
14835
|
+
homeDir: homedir8(),
|
|
14086
14836
|
directory,
|
|
14087
14837
|
guard: state.opencodeV2CredentialImportGuard,
|
|
14088
14838
|
log: (message, level = "info") => logActivity(state, { type: "info", level, message })
|
|
@@ -14502,7 +15252,7 @@ function shouldWarnForReclaimSkip(reason) {
|
|
|
14502
15252
|
return major > 22 || major === 22 && (minor > 13 || minor === 13 && patch >= 0);
|
|
14503
15253
|
}
|
|
14504
15254
|
function sessionDbPath() {
|
|
14505
|
-
return
|
|
15255
|
+
return join13(homedir8(), ".local", "share", "opencode", "opencode.db");
|
|
14506
15256
|
}
|
|
14507
15257
|
function logSessionCleanupActivity(state, phase, entry) {
|
|
14508
15258
|
logActivity(state, {
|
|
@@ -14746,7 +15496,7 @@ function scheduleSessionCleanup(state, driver, options) {
|
|
|
14746
15496
|
for (const warning2 of config.warnings) {
|
|
14747
15497
|
logActivity(state, { type: "info", level: "warn", message: `Session cleanup: ${warning2}` });
|
|
14748
15498
|
}
|
|
14749
|
-
const dbBytes = statSessionDbBytes(
|
|
15499
|
+
const dbBytes = statSessionDbBytes(homedir8());
|
|
14750
15500
|
void (async () => {
|
|
14751
15501
|
const reclaimAvailability = dbBytes !== null && config.enabled ? await probeReclaimAvailability({ dbPath: sessionDbPath(), requiredBytes: dbBytes }) : null;
|
|
14752
15502
|
if (reclaimAvailability !== null) {
|
|
@@ -14965,7 +15715,7 @@ function scheduleResourceUsageReporting(state, options) {
|
|
|
14965
15715
|
});
|
|
14966
15716
|
return;
|
|
14967
15717
|
}
|
|
14968
|
-
const { collect, stop } = createResourceUsageCollector(
|
|
15718
|
+
const { collect, stop } = createResourceUsageCollector(homedir8());
|
|
14969
15719
|
state.stopResourceUsageSampling = stop;
|
|
14970
15720
|
let consecutiveFailures = 0;
|
|
14971
15721
|
const tick = async () => {
|
|
@@ -15206,7 +15956,7 @@ async function run(options) {
|
|
|
15206
15956
|
let fileSyncDirectories;
|
|
15207
15957
|
try {
|
|
15208
15958
|
logLevel = resolveLogLevel(options);
|
|
15209
|
-
fileSyncDirectories = resolveFileSyncDirectories(options.enableFileSyncTo,
|
|
15959
|
+
fileSyncDirectories = resolveFileSyncDirectories(options.enableFileSyncTo, homedir8());
|
|
15210
15960
|
if (options.restoreSessionDb && !options.sessionDbNoReplicateMarker) {
|
|
15211
15961
|
throw new Error(
|
|
15212
15962
|
"--restore-session-db requires --session-db-no-replicate-marker <path>; restore failures must block Litestream replication"
|
|
@@ -15492,7 +16242,7 @@ async function run(options) {
|
|
|
15492
16242
|
state.credentialSync?.arm();
|
|
15493
16243
|
const dependencyInstallDiagnosticReporter = createDependencyInstallDiagnosticReporter({
|
|
15494
16244
|
context: {
|
|
15495
|
-
homeDir:
|
|
16245
|
+
homeDir: homedir8(),
|
|
15496
16246
|
cwd: process.cwd(),
|
|
15497
16247
|
microvmId: process.env.MICROVM_ID?.trim(),
|
|
15498
16248
|
euid: process.getuid?.(),
|
|
@@ -15515,7 +16265,7 @@ async function run(options) {
|
|
|
15515
16265
|
dependencyInstallDiagnosticReporter
|
|
15516
16266
|
);
|
|
15517
16267
|
const openCodeLogTail = tailOpenCodeLogFile(
|
|
15518
|
-
resolveOpenCodeLogPath(
|
|
16268
|
+
resolveOpenCodeLogPath(homedir8(), process.env),
|
|
15519
16269
|
openCodeActivityForwarder
|
|
15520
16270
|
);
|
|
15521
16271
|
state.stopOpenCodeLogTail = () => {
|
|
@@ -15590,6 +16340,40 @@ async function run(options) {
|
|
|
15590
16340
|
for (const warning2 of opencodeVersionWarnings) {
|
|
15591
16341
|
logActivity(state, { type: "info", level: "warn", message: warning2 });
|
|
15592
16342
|
}
|
|
16343
|
+
const managedSetupOptions = {
|
|
16344
|
+
agentId: state.agentId,
|
|
16345
|
+
apiUrl: getApiUrlConfig(),
|
|
16346
|
+
getAuthHeader: () => state.authHeader,
|
|
16347
|
+
log: (message, level = "info") => logActivity(state, {
|
|
16348
|
+
type: level === "error" ? "error" : "info",
|
|
16349
|
+
level,
|
|
16350
|
+
...level === "error" ? { error: message } : { message }
|
|
16351
|
+
})
|
|
16352
|
+
};
|
|
16353
|
+
const managedDiscovery = await discoverManagedInferenceSetup(managedSetupOptions);
|
|
16354
|
+
let managedPreparation = { kind: "none" };
|
|
16355
|
+
if (managedDiscovery.kind === "failed") {
|
|
16356
|
+
managedPreparation = managedDiscovery;
|
|
16357
|
+
managedSetupOptions.log(`MANAGED-INFERENCE-NOT-READY: ${managedDiscovery.reason}`, "warn");
|
|
16358
|
+
} else if (managedDiscovery.kind === "setup") {
|
|
16359
|
+
const managedHealth = opencodeVersion === "v2" ? await checkOpenCode2Health(state.port) : await checkOpenCodeHealth(state.port);
|
|
16360
|
+
managedPreparation = await prepareManagedInferenceSetup(
|
|
16361
|
+
managedSetupOptions,
|
|
16362
|
+
managedDiscovery,
|
|
16363
|
+
managedHealth
|
|
16364
|
+
);
|
|
16365
|
+
if (managedPreparation.kind === "failed") {
|
|
16366
|
+
managedSetupOptions.log(
|
|
16367
|
+
`MANAGED-INFERENCE-NOT-READY: ${managedPreparation.reason}`,
|
|
16368
|
+
"warn"
|
|
16369
|
+
);
|
|
16370
|
+
} else if (managedPreparation.kind === "ready") {
|
|
16371
|
+
managedSetupOptions.log(
|
|
16372
|
+
`MANAGED-INFERENCE-PREPARED: runner=${state.agentId} generation=${managedPreparation.generation}`,
|
|
16373
|
+
"info"
|
|
16374
|
+
);
|
|
16375
|
+
}
|
|
16376
|
+
}
|
|
15593
16377
|
const { value: maxActiveSessions, warnings: maxActiveSessionsWarnings } = resolveMaxActiveSessions(options, process.env);
|
|
15594
16378
|
for (const warning2 of maxActiveSessionsWarnings) {
|
|
15595
16379
|
logActivity(state, { type: "info", level: "warn", message: warning2 });
|
|
@@ -15616,6 +16400,32 @@ async function run(options) {
|
|
|
15616
16400
|
state.opencodeProcess = options.opencodePidFile ? null : oc.process;
|
|
15617
16401
|
state.opencodeVersion = oc.version;
|
|
15618
16402
|
state.opencodeApiVersion = opencodeVersion;
|
|
16403
|
+
let configuredPlugins = [];
|
|
16404
|
+
let resolvedPlugins = [];
|
|
16405
|
+
try {
|
|
16406
|
+
const provenanceLog = (message) => logActivity(state, { type: "info", level: "warn", message });
|
|
16407
|
+
configuredPlugins = readConfiguredPluginSpecs({ log: provenanceLog });
|
|
16408
|
+
resolvedPlugins = resolveInstalledPluginVersions({ log: provenanceLog });
|
|
16409
|
+
} catch (error2) {
|
|
16410
|
+
const kind = error2 instanceof Error ? error2.name : "unknown error";
|
|
16411
|
+
logActivity(state, {
|
|
16412
|
+
type: "info",
|
|
16413
|
+
level: "warn",
|
|
16414
|
+
message: `Could not collect OpenCode runtime provenance: ${kind}`
|
|
16415
|
+
});
|
|
16416
|
+
}
|
|
16417
|
+
logActivity(state, {
|
|
16418
|
+
type: "info",
|
|
16419
|
+
level: "info",
|
|
16420
|
+
message: "OpenCode runtime provenance",
|
|
16421
|
+
metadata: {
|
|
16422
|
+
opencode_version: state.opencodeVersion,
|
|
16423
|
+
opencode_api_version: state.opencodeApiVersion,
|
|
16424
|
+
configured_plugins: JSON.stringify(configuredPlugins),
|
|
16425
|
+
resolved_plugins: JSON.stringify(resolvedPlugins)
|
|
16426
|
+
},
|
|
16427
|
+
source: "cli.runtime-provenance"
|
|
16428
|
+
});
|
|
15619
16429
|
let opencodePassword = null;
|
|
15620
16430
|
if (opencodeVersion === "v2" && "password" in oc) {
|
|
15621
16431
|
const value = oc.password;
|
|
@@ -15644,7 +16454,7 @@ async function run(options) {
|
|
|
15644
16454
|
const provenance = checkSessionDbProvenance({
|
|
15645
16455
|
dbPath: sessionDbPath(),
|
|
15646
16456
|
currentVersion: state.opencodeVersion,
|
|
15647
|
-
homeDir:
|
|
16457
|
+
homeDir: homedir8(),
|
|
15648
16458
|
env: process.env
|
|
15649
16459
|
});
|
|
15650
16460
|
if (provenance.anomaly) {
|
|
@@ -15658,6 +16468,30 @@ async function run(options) {
|
|
|
15658
16468
|
}
|
|
15659
16469
|
}
|
|
15660
16470
|
state.opencodeConnected = oc.notReadyReason === null;
|
|
16471
|
+
if (managedPreparation.kind === "ready" && oc.notReadyReason === null) {
|
|
16472
|
+
try {
|
|
16473
|
+
const acknowledgement = await acknowledgeManagedInferenceSetup(
|
|
16474
|
+
managedSetupOptions,
|
|
16475
|
+
managedPreparation
|
|
16476
|
+
);
|
|
16477
|
+
if (acknowledgement === "acknowledged") {
|
|
16478
|
+
managedSetupOptions.log(
|
|
16479
|
+
`MANAGED-INFERENCE-ACKNOWLEDGED: runner=${state.agentId} generation=${managedPreparation.generation}`,
|
|
16480
|
+
"info"
|
|
16481
|
+
);
|
|
16482
|
+
} else {
|
|
16483
|
+
managedSetupOptions.log(
|
|
16484
|
+
`MANAGED-INFERENCE-NOT-READY: policy refresh is pending for runner=${state.agentId} generation=${managedPreparation.generation}`,
|
|
16485
|
+
"warn"
|
|
16486
|
+
);
|
|
16487
|
+
}
|
|
16488
|
+
} catch (error2) {
|
|
16489
|
+
managedSetupOptions.log(
|
|
16490
|
+
`MANAGED-INFERENCE-NOT-READY: ${error2 instanceof Error ? error2.message : String(error2)}`,
|
|
16491
|
+
"warn"
|
|
16492
|
+
);
|
|
16493
|
+
}
|
|
16494
|
+
}
|
|
15661
16495
|
const version2 = state.opencodeVersion ? ` (v${state.opencodeVersion})` : "";
|
|
15662
16496
|
ocSpinner?.succeed(`OpenCode running on port ${state.port}${version2}`);
|
|
15663
16497
|
if (!state.interactive && oc.notReadyReason !== null) {
|
|
@@ -15716,7 +16550,7 @@ async function run(options) {
|
|
|
15716
16550
|
let existingPid;
|
|
15717
16551
|
if (existsSync3(options.litestreamPidFile)) {
|
|
15718
16552
|
try {
|
|
15719
|
-
const rawPid =
|
|
16553
|
+
const rawPid = readFileSync8(options.litestreamPidFile, "utf8").trim();
|
|
15720
16554
|
const parsedPid = Number(rawPid);
|
|
15721
16555
|
if (/^\d+$/.test(rawPid) && Number.isSafeInteger(parsedPid) && parsedPid > 0) {
|
|
15722
16556
|
existingPid = parsedPid;
|
|
@@ -15815,7 +16649,7 @@ async function run(options) {
|
|
|
15815
16649
|
// #559: the `--enable-file-sync-to` allow-list. Empty ⇒ pending files are
|
|
15816
16650
|
// REJECTED with `file_sync_disabled` on the ack, not silently ignored.
|
|
15817
16651
|
fileSyncDirectories,
|
|
15818
|
-
homeDir:
|
|
16652
|
+
homeDir: homedir8(),
|
|
15819
16653
|
maxActiveSessions,
|
|
15820
16654
|
log: (entry) => (
|
|
15821
16655
|
// Thread the driver's real level straight through so `debug`/`warn`
|
|
@@ -16059,9 +16893,9 @@ program.name("evident").description("Run OpenCode locally and connect it to Evid
|
|
|
16059
16893
|
"--endpoint <url>",
|
|
16060
16894
|
"Evident API base URL (default: production; e.g. http://localhost:3001)"
|
|
16061
16895
|
).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(
|
|
16896
|
+
const { endpoint: endpoint2, tunnel } = thisCommand.opts();
|
|
16897
|
+
if (endpoint2) {
|
|
16898
|
+
setEndpoint(endpoint2);
|
|
16065
16899
|
}
|
|
16066
16900
|
if (tunnel) {
|
|
16067
16901
|
setTunnelUrl(tunnel);
|