@evident-ai/cli 3.6.1-dev.d01bfb0 → 3.6.1-dev.d5a3d9d

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 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 requestHeaders = {
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: requestHeaders,
148
+ headers: requestHeaders2,
129
149
  body: body ? JSON.stringify(body) : void 0
130
150
  });
131
151
  if (!response.ok) {
@@ -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.d01bfb0" : void 0) ?? process.env.npm_package_version ?? "unknown";
1445
+ var CLI_VERSION = (true ? "3.6.1-dev.d5a3d9d" : 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 request2 = {
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(request2),
1537
+ body: JSON.stringify(request3),
1422
1538
  signal: controller.signal
1423
1539
  });
1424
1540
  if (!response.ok) {
@@ -4681,11 +4797,11 @@ function failureMessage(status2) {
4681
4797
  function isRetryableStatus(status2) {
4682
4798
  return CONNECT_KEY_RETRY_STATUSES.includes(status2);
4683
4799
  }
4684
- async function importOneCredential(request2, credential, sleepForRetry, log3) {
4800
+ async function importOneCredential(request3, credential, sleepForRetry, log3) {
4685
4801
  const path = `/api/integration/${encodeURIComponent(credential.integrationId)}/connect/key`;
4686
4802
  for (let attempt = 1; attempt <= CONNECT_KEY_MAX_ATTEMPTS; attempt += 1) {
4687
4803
  try {
4688
- const response = await request2(
4804
+ const response = await request3(
4689
4805
  path,
4690
4806
  {
4691
4807
  method: "POST",
@@ -4720,7 +4836,7 @@ async function importOneCredential(request2, credential, sleepForRetry, log3) {
4720
4836
  throw new Error("OpenCode credential import retry loop did not return");
4721
4837
  }
4722
4838
  async function importOpenCodeCredentials({
4723
- request: request2,
4839
+ request: request3,
4724
4840
  classified,
4725
4841
  connectedIntegrationIds,
4726
4842
  sleepForRetry = sleep2,
@@ -4732,7 +4848,7 @@ async function importOpenCodeCredentials({
4732
4848
  result.alreadyConnected.push(credential.integrationId);
4733
4849
  continue;
4734
4850
  }
4735
- const outcome = await importOneCredential(request2, credential, sleepForRetry, log3);
4851
+ const outcome = await importOneCredential(request3, credential, sleepForRetry, log3);
4736
4852
  if (outcome.ok) result.imported.push(credential.integrationId);
4737
4853
  else result.failed.push(outcome.failure);
4738
4854
  }
@@ -4750,7 +4866,7 @@ function parseIntegrationList(value) {
4750
4866
  return { integrationIds, hasData: value.data.length > 0 };
4751
4867
  }
4752
4868
  async function getConnectedOpenCodeIntegrationIds({
4753
- request: request2,
4869
+ request: request3,
4754
4870
  directory,
4755
4871
  sleepForRetry = sleep2,
4756
4872
  log: log3
@@ -4758,7 +4874,7 @@ async function getConnectedOpenCodeIntegrationIds({
4758
4874
  const path = `/api/integration?location%5Bdirectory%5D=${encodeURIComponent(directory)}`;
4759
4875
  for (let attempt = 1; attempt <= INTEGRATION_LIST_MAX_READS; attempt += 1) {
4760
4876
  try {
4761
- const response = await request2(path);
4877
+ const response = await request3(path);
4762
4878
  const parsed = parseIntegrationList(await response.json());
4763
4879
  if (parsed === null) {
4764
4880
  log3(
@@ -4787,7 +4903,7 @@ async function getConnectedOpenCodeIntegrationIds({
4787
4903
  return null;
4788
4904
  }
4789
4905
  async function importOpenCodeV2Credentials({
4790
- request: request2,
4906
+ request: request3,
4791
4907
  homeDir,
4792
4908
  directory,
4793
4909
  sleepForRetry = sleep2,
@@ -4805,7 +4921,7 @@ async function importOpenCodeV2Credentials({
4805
4921
  const classified = classifyOpenCodeCredentials(credentials2);
4806
4922
  if (classified.importable.length === 0) return emptyResult(classified.unsupported);
4807
4923
  const connectedIntegrationIds = await getConnectedOpenCodeIntegrationIds({
4808
- request: request2,
4924
+ request: request3,
4809
4925
  directory,
4810
4926
  sleepForRetry,
4811
4927
  log: log3
@@ -4814,7 +4930,7 @@ async function importOpenCodeV2Credentials({
4814
4930
  return emptyResult(classified.unsupported, "connection-list-unavailable");
4815
4931
  }
4816
4932
  return importOpenCodeCredentials({
4817
- request: request2,
4933
+ request: request3,
4818
4934
  classified,
4819
4935
  connectedIntegrationIds,
4820
4936
  sleepForRetry,
@@ -5505,7 +5621,11 @@ function isSessionDbProvenanceRecord(value) {
5505
5621
  var OPENCODE2_PACKAGE_SPEC = "@opencode-ai/cli@0.0.0-beta-17823";
5506
5622
 
5507
5623
  // src/lib/opencode/opencode-version-gate.ts
5508
- var QUEUE_VALIDATED_OPENCODE_VERSIONS = ["1.17.11", "1.18.3"];
5624
+ var QUEUE_VALIDATED_OPENCODE_VERSIONS = [
5625
+ "1.17.11",
5626
+ "1.18.3",
5627
+ "1.18.35"
5628
+ ];
5509
5629
  function isQueueValidatedVersion(version2) {
5510
5630
  if (!version2) return false;
5511
5631
  return QUEUE_VALIDATED_OPENCODE_VERSIONS.includes(version2);
@@ -6550,6 +6670,7 @@ function connectTunnel(options) {
6550
6670
  }, 3e4);
6551
6671
  let relaySilenceTimeout;
6552
6672
  let relaySilenceTimedOut = false;
6673
+ let relayConnectionId;
6553
6674
  const clearRelaySilenceTimeout = () => {
6554
6675
  if (relaySilenceTimeout === void 0) return;
6555
6676
  clearTimeout(relaySilenceTimeout);
@@ -6635,7 +6756,8 @@ function connectTunnel(options) {
6635
6756
  armRelaySilenceTimeout();
6636
6757
  forwarder.setBinaryFramesSupported(message.binary_frames === true);
6637
6758
  const connectedAgentId = message.agent_id ?? agentId;
6638
- onConnected?.(connectedAgentId);
6759
+ relayConnectionId = message.connection_id;
6760
+ onConnected?.(connectedAgentId, relayConnectionId);
6639
6761
  resolve5({
6640
6762
  ws,
6641
6763
  close: () => ws.close(1e3, "CLI shutdown")
@@ -6672,7 +6794,7 @@ function connectTunnel(options) {
6672
6794
  clearRelaySilenceTimeout();
6673
6795
  const reasonStr = (relaySilenceTimedOut ? "no relay heartbeat for 90s \u2014 reconnecting" : void 0) || reason.toString() || upgradeRejection || (code === 1006 ? "abnormal closure" : "No reason provided");
6674
6796
  forwarder.abortAll();
6675
- onDisconnected?.(code, reasonStr);
6797
+ onDisconnected?.(code, reasonStr, relayConnectionId);
6676
6798
  });
6677
6799
  });
6678
6800
  }
@@ -6735,7 +6857,7 @@ var RunnerConnection = class _RunnerConnection {
6735
6857
  authHeader: this.opts.getAuthHeader(),
6736
6858
  port: this.opts.port,
6737
6859
  openCodePassword: this.opts.openCodePassword,
6738
- onConnected: (agentId) => {
6860
+ onConnected: (agentId, relayConnectionId) => {
6739
6861
  const attempt = this.reconnectAttempt + 1;
6740
6862
  this.reconnectAttempt = 0;
6741
6863
  this.reconnecting = false;
@@ -6745,13 +6867,17 @@ var RunnerConnection = class _RunnerConnection {
6745
6867
  this.disconnectedAtMs = null;
6746
6868
  events.onConnected(agentId, isReconnect, {
6747
6869
  generation: this.generation,
6870
+ relayConnectionId: relayConnectionId ?? null,
6748
6871
  attempt,
6749
6872
  downtimeMs
6750
6873
  });
6751
6874
  },
6752
- onDisconnected: (code, reason) => {
6875
+ onDisconnected: (code, reason, relayConnectionId) => {
6753
6876
  this.disconnectedAtMs = this.now();
6754
- events.onDisconnected(code, reason, { generation: this.generation });
6877
+ events.onDisconnected(code, reason, {
6878
+ generation: this.generation,
6879
+ relayConnectionId: relayConnectionId ?? null
6880
+ });
6755
6881
  const relayRequestedReconnect = code === 1e3 && _RunnerConnection.RELAY_RECONNECT_CLOSE_REASONS.has(reason);
6756
6882
  if (this.opts.isRunning() && (code !== 1e3 || relayRequestedReconnect) && !this.reconnecting) {
6757
6883
  this.reconnectPromise = this.connectWithRetry(true).catch((err) => {
@@ -7199,8 +7325,8 @@ import { chmod, mkdir, open as open3, realpath, rename, unlink } from "node:fs/p
7199
7325
  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";
7200
7326
  var FILE_MODE = 384;
7201
7327
  var DIRECTORY_MODE = 448;
7202
- async function writePushedFile(request2) {
7203
- const { requestedPath, content, allowedDirectories, homeDir } = request2;
7328
+ async function writePushedFile(request3) {
7329
+ const { requestedPath, content, allowedDirectories, homeDir } = request3;
7204
7330
  const bytes = content.byteLength;
7205
7331
  if (allowedDirectories.length === 0) {
7206
7332
  return refuse("file_sync_disabled", "File sync is not enabled on this runner.", {
@@ -13778,6 +13904,22 @@ async function configureGitHubAccess({ env, log: log3 }) {
13778
13904
  import { execFileSync as execFileSync2 } from "node:child_process";
13779
13905
  import { copyFileSync, existsSync as existsSync2, statSync as statSync6 } from "node:fs";
13780
13906
  import { isAbsolute as isAbsolute3, join as join11, resolve as resolve4 } from "node:path";
13907
+ function parseConfigContent(content) {
13908
+ if (!content?.trim()) return {};
13909
+ const parsed = JSON.parse(content);
13910
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
13911
+ throw new Error("OPENCODE_CONFIG_CONTENT must contain a JSON object");
13912
+ }
13913
+ return parsed;
13914
+ }
13915
+ function mergeManagedOpenCodeConfigContent(content, managedProviders) {
13916
+ const config = parseConfigContent(content);
13917
+ const provider = typeof config.provider === "object" && config.provider !== null && !Array.isArray(config.provider) ? { ...config.provider } : {};
13918
+ for (const namespace of CREDENTIAL_PROXY_NATIVE_NAMESPACES) {
13919
+ delete provider[namespace];
13920
+ }
13921
+ return JSON.stringify({ ...config, provider: { ...provider, ...managedProviders } });
13922
+ }
13781
13923
  function isFile(filePath) {
13782
13924
  return existsSync2(filePath) && statSync6(filePath).isFile();
13783
13925
  }
@@ -14199,6 +14341,226 @@ function createCredentialSync({
14199
14341
  };
14200
14342
  }
14201
14343
 
14344
+ // src/lib/managed-inference-setup.ts
14345
+ var defaultStore = {
14346
+ get: getManagedInferenceCredential,
14347
+ set: setManagedInferenceCredential,
14348
+ clear: clearManagedInferenceCredential
14349
+ };
14350
+ function endpoint(options, operation) {
14351
+ return `${options.apiUrl}/runners/${options.agentId}/managed-credentials/${operation}`;
14352
+ }
14353
+ function requestHeaders(options) {
14354
+ return { Authorization: options.getAuthHeader(), "Content-Type": "application/json" };
14355
+ }
14356
+ function request2(options) {
14357
+ return withRequestTimeout(options.fetchImpl ?? fetch, REQUEST_TIMEOUT_MS);
14358
+ }
14359
+ function safeFailure(operation, status2) {
14360
+ return status2 === void 0 ? `${operation} could not reach the managed credential service` : `${operation} returned HTTP ${status2}`;
14361
+ }
14362
+ function logTransportFailure(options, operation, error2, authorization) {
14363
+ let detail = error2 instanceof Error ? error2.message : String(error2);
14364
+ const credential = authorization.split(/\s+/, 2)[1];
14365
+ for (const secret of [authorization, credential]) {
14366
+ if (secret) detail = detail.replaceAll(secret, "[redacted]");
14367
+ }
14368
+ options.log(
14369
+ `MANAGED-INFERENCE-TRANSPORT-FAILED: operation=${operation} detail=${redactDiagnosticText(detail)}`,
14370
+ "debug"
14371
+ );
14372
+ }
14373
+ async function responseJson(response) {
14374
+ try {
14375
+ const value = await response.json();
14376
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
14377
+ } catch (error2) {
14378
+ return null;
14379
+ }
14380
+ }
14381
+ function readSetup(value) {
14382
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
14383
+ const setup = value;
14384
+ if (typeof setup.runner_id !== "string" || typeof setup.generation !== "string" || typeof setup.status !== "string" || setup.token_id !== null && typeof setup.token_id !== "string") {
14385
+ return null;
14386
+ }
14387
+ return setup;
14388
+ }
14389
+ function clearLocalSetup(options) {
14390
+ const env = options.env ?? process.env;
14391
+ (options.store ?? defaultStore).clear(options.agentId);
14392
+ delete env.EVIDENT_PROXY_TOKEN;
14393
+ }
14394
+ async function discoverManagedInferenceSetup(options) {
14395
+ const headers = requestHeaders(options);
14396
+ let response;
14397
+ try {
14398
+ response = await request2(options)(endpoint(options, "current"), {
14399
+ headers
14400
+ });
14401
+ } catch (error2) {
14402
+ logTransportFailure(options, "discovery", error2, headers.Authorization);
14403
+ return { kind: "failed", reason: safeFailure("Managed setup discovery") };
14404
+ }
14405
+ if (response.status === 404) {
14406
+ clearLocalSetup(options);
14407
+ return { kind: "none" };
14408
+ }
14409
+ if (!response.ok) {
14410
+ return { kind: "failed", reason: safeFailure("Managed setup discovery", response.status) };
14411
+ }
14412
+ const body = await responseJson(response);
14413
+ const setup = readSetup(body?.setup);
14414
+ return setup ? { kind: "setup", setup } : { kind: "failed", reason: "Managed setup discovery returned an invalid response" };
14415
+ }
14416
+ async function post(options, operation, body) {
14417
+ const headers = requestHeaders(options);
14418
+ try {
14419
+ return await request2(options)(endpoint(options, operation), {
14420
+ method: "POST",
14421
+ headers,
14422
+ body: JSON.stringify(body)
14423
+ });
14424
+ } catch (error2) {
14425
+ logTransportFailure(options, operation, error2, headers.Authorization);
14426
+ return null;
14427
+ }
14428
+ }
14429
+ function applyCredential(options, credential) {
14430
+ const env = options.env ?? process.env;
14431
+ let merged;
14432
+ if (options.autoConfigureCredentialsProxy) {
14433
+ const { config, unsupportedProviders } = buildCredentialProxyNativeConfig({
14434
+ inferenceBaseUrl: credential.inferenceBaseUrl,
14435
+ modelDescriptors: credential.modelDescriptors
14436
+ });
14437
+ if (unsupportedProviders.length > 0 || Object.keys(config.provider).length === 0) {
14438
+ return "Managed setup contains unsupported provider descriptors";
14439
+ }
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
+ }
14446
+ try {
14447
+ (options.store ?? defaultStore).set(options.agentId, credential);
14448
+ } catch (error2) {
14449
+ const detail = (error2 instanceof Error ? error2.message : String(error2)).replaceAll(
14450
+ credential.token,
14451
+ "[redacted]"
14452
+ );
14453
+ options.log(
14454
+ `MANAGED-INFERENCE-STORE-FAILED: runner=${options.agentId} generation=${credential.generation} detail=${redactDiagnosticText(detail)}`,
14455
+ "warn"
14456
+ );
14457
+ return "Managed credentials could not be written to the protected store";
14458
+ }
14459
+ env.EVIDENT_PROXY_TOKEN = credential.token;
14460
+ if (merged !== void 0) env.OPENCODE_CONFIG_CONTENT = merged;
14461
+ return null;
14462
+ }
14463
+ async function reportManagedInferenceApplyFailure(options, failure) {
14464
+ const response = await post(options, "report-failure", {
14465
+ generation: failure.generation,
14466
+ token_id: failure.tokenId
14467
+ });
14468
+ if (!response?.ok) {
14469
+ options.log(
14470
+ `MANAGED-INFERENCE-REPORT-FAILED: runner=${options.agentId} generation=${failure.generation}${response ? ` status=${response.status}` : ""}`,
14471
+ "warn"
14472
+ );
14473
+ }
14474
+ }
14475
+ async function reaffirmStored(options, credential) {
14476
+ const response = await post(options, "reaffirm", {
14477
+ generation: credential.generation,
14478
+ token_id: credential.tokenId
14479
+ });
14480
+ if (!response?.ok) {
14481
+ return {
14482
+ kind: "failed",
14483
+ 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")
14484
+ };
14485
+ }
14486
+ const body = await responseJson(response);
14487
+ const setup = readSetup(body?.setup);
14488
+ if (!setup || setup.generation !== credential.generation || setup.token_id !== credential.tokenId) {
14489
+ return { kind: "failed", reason: "Managed credential reaffirmation returned a mismatch" };
14490
+ }
14491
+ const failure = applyCredential(options, credential);
14492
+ return failure ? { kind: "failed", reason: failure } : { kind: "ready", generation: credential.generation, tokenId: credential.tokenId };
14493
+ }
14494
+ async function prepareManagedInferenceSetup(options, discovery, health) {
14495
+ if (health.authFailed) {
14496
+ return {
14497
+ kind: "failed",
14498
+ reason: "OpenCode authentication failed; managed credentials were not rotated"
14499
+ };
14500
+ }
14501
+ const stored = (options.store ?? defaultStore).get(options.agentId);
14502
+ if (health.healthy) {
14503
+ if (!stored) {
14504
+ return {
14505
+ kind: "failed",
14506
+ reason: "Managed credentials are missing for the serving OpenCode process; restart required"
14507
+ };
14508
+ }
14509
+ return reaffirmStored(options, stored);
14510
+ }
14511
+ if (discovery.setup.status === "acknowledged" && stored?.generation === discovery.setup.generation && stored.tokenId === discovery.setup.token_id) {
14512
+ return reaffirmStored(options, stored);
14513
+ }
14514
+ const response = await post(options, "bootstrap", { generation: discovery.setup.generation });
14515
+ if (!response?.ok) {
14516
+ return {
14517
+ kind: "failed",
14518
+ reason: response ? safeFailure("Managed credential bootstrap", response.status) : safeFailure("Managed credential bootstrap")
14519
+ };
14520
+ }
14521
+ const body = await responseJson(response);
14522
+ const setup = readSetup(body?.setup);
14523
+ const token = body?.inference_token;
14524
+ const inferenceBaseUrl = body?.inference_base_url;
14525
+ const modelDescriptors = body?.model_descriptors;
14526
+ if (!setup || setup.generation !== discovery.setup.generation || typeof setup.token_id !== "string" || typeof token !== "string" || typeof inferenceBaseUrl !== "string" || !Array.isArray(modelDescriptors) || !modelDescriptors.every(
14527
+ (descriptor) => typeof descriptor === "object" && descriptor !== null && typeof descriptor.id === "string" && typeof descriptor.provider === "string"
14528
+ )) {
14529
+ return { kind: "failed", reason: "Managed credential bootstrap returned an invalid response" };
14530
+ }
14531
+ const credential = {
14532
+ generation: setup.generation,
14533
+ tokenId: setup.token_id,
14534
+ token,
14535
+ inferenceBaseUrl,
14536
+ modelDescriptors
14537
+ };
14538
+ const failure = applyCredential(options, credential);
14539
+ if (failure) {
14540
+ await reportManagedInferenceApplyFailure(options, credential);
14541
+ return { kind: "failed", reason: failure };
14542
+ }
14543
+ return { kind: "ready", generation: credential.generation, tokenId: credential.tokenId };
14544
+ }
14545
+ async function acknowledgeManagedInferenceSetup(options, preparation) {
14546
+ const response = await post(options, "acknowledge", {
14547
+ generation: preparation.generation,
14548
+ token_id: preparation.tokenId,
14549
+ config_validated: options.autoConfigureCredentialsProxy === true
14550
+ });
14551
+ if (!response?.ok) {
14552
+ throw new Error(
14553
+ response ? safeFailure("Managed credential acknowledgment", response.status) : safeFailure("Managed credential acknowledgment")
14554
+ );
14555
+ }
14556
+ const body = await responseJson(response);
14557
+ const setup = readSetup(body?.setup);
14558
+ if (!setup || setup.generation !== preparation.generation || setup.token_id !== preparation.tokenId) {
14559
+ throw new Error("Managed credential acknowledgment returned a mismatch");
14560
+ }
14561
+ return response.status === 200 && setup.status === "acknowledged" ? "acknowledged" : "pending";
14562
+ }
14563
+
14202
14564
  // src/commands/run.ts
14203
14565
  var MAX_ACTIVITY_LOG_ENTRIES = 10;
14204
14566
  var MAX_DISCONNECT_REASON_LENGTH = 200;
@@ -15983,6 +16345,41 @@ async function run(options) {
15983
16345
  for (const warning2 of opencodeVersionWarnings) {
15984
16346
  logActivity(state, { type: "info", level: "warn", message: warning2 });
15985
16347
  }
16348
+ const managedSetupOptions = {
16349
+ agentId: state.agentId,
16350
+ apiUrl: getApiUrlConfig(),
16351
+ getAuthHeader: () => state.authHeader,
16352
+ autoConfigureCredentialsProxy: options.autoConfigureCredentialsProxy,
16353
+ log: (message, level = "info") => logActivity(state, {
16354
+ type: level === "error" ? "error" : "info",
16355
+ level,
16356
+ ...level === "error" ? { error: message } : { message }
16357
+ })
16358
+ };
16359
+ const managedDiscovery = await discoverManagedInferenceSetup(managedSetupOptions);
16360
+ let managedPreparation = { kind: "none" };
16361
+ if (managedDiscovery.kind === "failed") {
16362
+ managedPreparation = managedDiscovery;
16363
+ managedSetupOptions.log(`MANAGED-INFERENCE-NOT-READY: ${managedDiscovery.reason}`, "warn");
16364
+ } else if (managedDiscovery.kind === "setup") {
16365
+ const managedHealth = opencodeVersion === "v2" ? await checkOpenCode2Health(state.port) : await checkOpenCodeHealth(state.port);
16366
+ managedPreparation = await prepareManagedInferenceSetup(
16367
+ managedSetupOptions,
16368
+ managedDiscovery,
16369
+ managedHealth
16370
+ );
16371
+ if (managedPreparation.kind === "failed") {
16372
+ managedSetupOptions.log(
16373
+ `MANAGED-INFERENCE-NOT-READY: ${managedPreparation.reason}`,
16374
+ "warn"
16375
+ );
16376
+ } else if (managedPreparation.kind === "ready") {
16377
+ managedSetupOptions.log(
16378
+ `MANAGED-INFERENCE-PREPARED: runner=${state.agentId} generation=${managedPreparation.generation}`,
16379
+ "info"
16380
+ );
16381
+ }
16382
+ }
15986
16383
  const { value: maxActiveSessions, warnings: maxActiveSessionsWarnings } = resolveMaxActiveSessions(options, process.env);
15987
16384
  for (const warning2 of maxActiveSessionsWarnings) {
15988
16385
  logActivity(state, { type: "info", level: "warn", message: warning2 });
@@ -16077,6 +16474,30 @@ async function run(options) {
16077
16474
  }
16078
16475
  }
16079
16476
  state.opencodeConnected = oc.notReadyReason === null;
16477
+ if (managedPreparation.kind === "ready" && oc.notReadyReason === null) {
16478
+ try {
16479
+ const acknowledgement = await acknowledgeManagedInferenceSetup(
16480
+ managedSetupOptions,
16481
+ managedPreparation
16482
+ );
16483
+ if (acknowledgement === "acknowledged") {
16484
+ managedSetupOptions.log(
16485
+ `MANAGED-INFERENCE-ACKNOWLEDGED: runner=${state.agentId} generation=${managedPreparation.generation}`,
16486
+ "info"
16487
+ );
16488
+ } else {
16489
+ managedSetupOptions.log(
16490
+ `MANAGED-INFERENCE-NOT-READY: policy refresh is pending for runner=${state.agentId} generation=${managedPreparation.generation}`,
16491
+ "warn"
16492
+ );
16493
+ }
16494
+ } catch (error2) {
16495
+ managedSetupOptions.log(
16496
+ `MANAGED-INFERENCE-NOT-READY: ${error2 instanceof Error ? error2.message : String(error2)}`,
16497
+ "warn"
16498
+ );
16499
+ }
16500
+ }
16080
16501
  const version2 = state.opencodeVersion ? ` (v${state.opencodeVersion})` : "";
16081
16502
  ocSpinner?.succeed(`OpenCode running on port ${state.port}${version2}`);
16082
16503
  if (!state.interactive && oc.notReadyReason !== null) {
@@ -16259,7 +16680,7 @@ async function run(options) {
16259
16680
  onConnected: (agentId, isReconnect, transition) => {
16260
16681
  state.connected = true;
16261
16682
  state.agentId = agentId;
16262
- 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})`;
16683
+ 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"})`;
16263
16684
  logActivity(state, {
16264
16685
  type: "info",
16265
16686
  message: connectionMessage
@@ -16292,6 +16713,7 @@ async function run(options) {
16292
16713
  connected: state.opencodeConnected
16293
16714
  }),
16294
16715
  connection_generation: transition.generation,
16716
+ ...transition.relayConnectionId !== null ? { relay_connection_id: transition.relayConnectionId } : {},
16295
16717
  ...isReconnect && transition.downtimeMs !== null ? {
16296
16718
  reconnect_attempt: transition.attempt,
16297
16719
  downtime_ms: transition.downtimeMs
@@ -16322,12 +16744,13 @@ async function run(options) {
16322
16744
  const safeReason2 = redactDiagnosticText(reason).slice(0, MAX_DISCONNECT_REASON_LENGTH);
16323
16745
  logActivity(state, {
16324
16746
  type: "info",
16325
- message: `Tunnel disconnected (generation: ${transition.generation}, code: ${code}, reason: ${safeReason2})`
16747
+ message: `Tunnel disconnected (generation: ${transition.generation}, relay connection: ${transition.relayConnectionId ?? "unknown"}, code: ${code}, reason: ${safeReason2})`
16326
16748
  });
16327
16749
  emitAgentDisconnected(state.agentId, {
16328
16750
  code,
16329
16751
  reason: safeReason2,
16330
- connection_generation: transition.generation
16752
+ connection_generation: transition.generation,
16753
+ ...transition.relayConnectionId !== null ? { relay_connection_id: transition.relayConnectionId } : {}
16331
16754
  });
16332
16755
  if (state.interactive) displayStatus(state);
16333
16756
  },
@@ -16478,9 +16901,9 @@ program.name("evident").description("Run OpenCode locally and connect it to Evid
16478
16901
  "--endpoint <url>",
16479
16902
  "Evident API base URL (default: production; e.g. http://localhost:3001)"
16480
16903
  ).option("--tunnel <url>", "Tunnel WebSocket URL (default: production; e.g. ws://localhost:8787)").hook("preAction", (thisCommand) => {
16481
- const { endpoint, tunnel } = thisCommand.opts();
16482
- if (endpoint) {
16483
- setEndpoint(endpoint);
16904
+ const { endpoint: endpoint2, tunnel } = thisCommand.opts();
16905
+ if (endpoint2) {
16906
+ setEndpoint(endpoint2);
16484
16907
  }
16485
16908
  if (tunnel) {
16486
16909
  setTunnelUrl(tunnel);
@@ -16554,6 +16977,9 @@ program.command("run").description("Connect to Evident and process messages").op
16554
16977
  ).option(
16555
16978
  "--opencode-config-overlay <path>",
16556
16979
  "Apply this runner-provided OpenCode config before starting OpenCode."
16980
+ ).option(
16981
+ "--auto-configure-credentials-proxy",
16982
+ "Install Evident's generated OpenCode providers for the runner's credential pool (off by default)."
16557
16983
  ).option(
16558
16984
  "--credential-sync-marker <path>",
16559
16985
  "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."
@@ -16597,6 +17023,7 @@ program.command("run").description("Connect to Evident and process messages").op
16597
17023
  restoreSessionDb: options.restoreSessionDb,
16598
17024
  restoreRunnerCredentials: options.restoreRunnerCredentials,
16599
17025
  opencodeConfigOverlay: options.opencodeConfigOverlay,
17026
+ autoConfigureCredentialsProxy: options.autoConfigureCredentialsProxy,
16600
17027
  credentialSyncMarker: options.credentialSyncMarker
16601
17028
  });
16602
17029
  }