@evident-ai/cli 3.6.1-dev.6d23da1 → 3.6.1-dev.6dfa784

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
@@ -1442,7 +1442,7 @@ import ora4 from "ora";
1442
1442
  import { select as select4 } from "@inquirer/prompts";
1443
1443
 
1444
1444
  // src/lib/telemetry.ts
1445
- var CLI_VERSION = (true ? "3.6.1-dev.6d23da1" : void 0) ?? process.env.npm_package_version ?? "unknown";
1445
+ var CLI_VERSION = (true ? "3.6.1-dev.6dfa784" : void 0) ?? process.env.npm_package_version ?? "unknown";
1446
1446
  function getCliVersion() {
1447
1447
  return CLI_VERSION;
1448
1448
  }
@@ -7788,6 +7788,211 @@ function describe(err) {
7788
7788
  return err instanceof Error ? err.message : String(err);
7789
7789
  }
7790
7790
 
7791
+ // src/lib/vault-file-sync.ts
7792
+ import { createHash } from "node:crypto";
7793
+ async function syncVaultFiles(options) {
7794
+ const files = await listVaultFiles(options);
7795
+ if (files === null) return { applied: 0 };
7796
+ const listedIds = new Set(files.map((file) => file.id));
7797
+ for (const fileId of options.outcomes.keys()) {
7798
+ if (!listedIds.has(fileId)) options.outcomes.delete(fileId);
7799
+ }
7800
+ let applied = 0;
7801
+ for (const file of files) {
7802
+ try {
7803
+ if (await reconcileVaultFile(options, file)) applied += 1;
7804
+ } catch (err) {
7805
+ options.log({
7806
+ level: "error",
7807
+ message: `Vault file ${file.id} reconciliation failed unexpectedly (message processing is unaffected): ${describe2(err)}`
7808
+ });
7809
+ }
7810
+ }
7811
+ return { applied };
7812
+ }
7813
+ async function listVaultFiles(options) {
7814
+ let response;
7815
+ try {
7816
+ response = await options.fetchImpl(`${options.apiUrl}/runners/${options.agentId}/vault-files`, {
7817
+ headers: { Authorization: options.getAuthHeader() }
7818
+ });
7819
+ } catch (err) {
7820
+ options.log({
7821
+ level: "warn",
7822
+ message: `Could not list vault files \u2014 retrying on the next drain: ${describe2(err)}`
7823
+ });
7824
+ return null;
7825
+ }
7826
+ if (!response.ok) {
7827
+ options.log({
7828
+ level: response.status === 404 ? "debug" : "warn",
7829
+ message: `Listing vault files returned HTTP ${response.status} \u2014 retrying on the next drain`
7830
+ });
7831
+ return null;
7832
+ }
7833
+ let body;
7834
+ try {
7835
+ body = await response.json();
7836
+ } catch (err) {
7837
+ options.log({
7838
+ level: "warn",
7839
+ message: `Vault file list was not readable JSON \u2014 retrying on the next drain: ${describe2(err)}`
7840
+ });
7841
+ return null;
7842
+ }
7843
+ if (body === null || typeof body !== "object" || !Array.isArray(body.files)) {
7844
+ options.log({
7845
+ level: "warn",
7846
+ message: "Vault file list was not an object with a files array \u2014 ignoring it for this drain"
7847
+ });
7848
+ return null;
7849
+ }
7850
+ const files = [];
7851
+ for (const entry of body.files) {
7852
+ const file = asVaultFile(entry);
7853
+ if (file === null) {
7854
+ options.log({
7855
+ level: "warn",
7856
+ message: "Ignoring a malformed vault file entry"
7857
+ });
7858
+ continue;
7859
+ }
7860
+ files.push(file);
7861
+ }
7862
+ return files;
7863
+ }
7864
+ function asVaultFile(entry) {
7865
+ if (entry === null || typeof entry !== "object") return null;
7866
+ const value = entry;
7867
+ if (typeof value.id !== "string" || value.id === "" || typeof value.destination_path !== "string" || value.destination_path === "" || typeof value.size_bytes !== "number" || !Number.isFinite(value.size_bytes) || value.size_bytes < 0 || typeof value.content_sha256 !== "string" || value.content_sha256 === "" || typeof value.created_at !== "string" || typeof value.updated_at !== "string") {
7868
+ return null;
7869
+ }
7870
+ return {
7871
+ id: value.id,
7872
+ destination_path: value.destination_path,
7873
+ size_bytes: value.size_bytes,
7874
+ content_sha256: value.content_sha256,
7875
+ created_at: value.created_at,
7876
+ updated_at: value.updated_at
7877
+ };
7878
+ }
7879
+ async function reconcileVaultFile(options, file) {
7880
+ const existing = options.outcomes.get(file.id);
7881
+ if (existing?.sha256 === file.content_sha256) {
7882
+ if (existing.reported || existing.reportAttempts >= MAX_ACK_ATTEMPTS) return false;
7883
+ await reportOutcome(options, file, existing);
7884
+ return false;
7885
+ }
7886
+ const content = await downloadVaultFile(options, file);
7887
+ if (content === null) return false;
7888
+ const actualSha256 = createHash("sha256").update(content).digest("hex");
7889
+ if (actualSha256 !== file.content_sha256) {
7890
+ options.log({
7891
+ level: "warn",
7892
+ message: `Vault file ${file.id} content hash mismatch: expected ${file.content_sha256}, received ${actualSha256} \u2014 retrying on the next drain`
7893
+ });
7894
+ return false;
7895
+ }
7896
+ const outcome = await applyVaultFile(options, file, content);
7897
+ const entry = {
7898
+ sha256: file.content_sha256,
7899
+ outcome: outcome.ok ? "applied" : "refused",
7900
+ reason: outcome.ok ? null : outcome.code,
7901
+ reported: false,
7902
+ reportAttempts: 0
7903
+ };
7904
+ options.outcomes.set(file.id, entry);
7905
+ await reportOutcome(options, file, entry);
7906
+ return outcome.ok;
7907
+ }
7908
+ async function downloadVaultFile(options, file) {
7909
+ try {
7910
+ const response = await options.fetchImpl(
7911
+ `${options.apiUrl}/runners/${options.agentId}/vault-files/${file.id}/content`,
7912
+ { headers: { Authorization: options.getAuthHeader() } }
7913
+ );
7914
+ if (!response.ok) {
7915
+ options.log({
7916
+ level: "warn",
7917
+ message: `Downloading vault file ${file.id} returned HTTP ${response.status} \u2014 retrying on the next drain`
7918
+ });
7919
+ return null;
7920
+ }
7921
+ return Buffer.from(await response.arrayBuffer());
7922
+ } catch (err) {
7923
+ options.log({
7924
+ level: "warn",
7925
+ message: `Downloading vault file ${file.id} failed \u2014 retrying on the next drain: ${describe2(err)}`
7926
+ });
7927
+ return null;
7928
+ }
7929
+ }
7930
+ async function applyVaultFile(options, file, content) {
7931
+ try {
7932
+ const outcome = await writePushedFile({
7933
+ requestedPath: file.destination_path,
7934
+ content,
7935
+ allowedDirectories: options.allowedDirectories,
7936
+ homeDir: options.homeDir
7937
+ });
7938
+ if (!outcome.ok) {
7939
+ options.log({
7940
+ level: "warn",
7941
+ message: `Vault file ${file.id} refused (${outcome.code}): ${outcome.message}`
7942
+ });
7943
+ } else {
7944
+ options.log({
7945
+ level: "info",
7946
+ message: `Vault file ${file.id} applied (${content.byteLength} bytes)`
7947
+ });
7948
+ }
7949
+ return outcome;
7950
+ } catch (err) {
7951
+ options.log({
7952
+ level: "error",
7953
+ message: `Vault file ${file.id} could not be written: ${describe2(err)}`
7954
+ });
7955
+ return { ok: false, code: "write_failed", message: "The runner could not write the file." };
7956
+ }
7957
+ }
7958
+ async function reportOutcome(options, file, entry) {
7959
+ const body = {
7960
+ status: entry.outcome === "applied" ? "applied" : "rejected",
7961
+ reported_sha256: entry.sha256
7962
+ };
7963
+ if (entry.outcome === "refused" && entry.reason !== null) body.reason = entry.reason;
7964
+ try {
7965
+ const response = await options.fetchImpl(
7966
+ `${options.apiUrl}/runners/${options.agentId}/vault-files/${file.id}/status`,
7967
+ {
7968
+ method: "POST",
7969
+ headers: {
7970
+ Authorization: options.getAuthHeader(),
7971
+ "Content-Type": "application/json"
7972
+ },
7973
+ body: JSON.stringify(body)
7974
+ }
7975
+ );
7976
+ if (response.ok) {
7977
+ entry.reported = true;
7978
+ return;
7979
+ }
7980
+ recordReportFailure(options, file, entry, `HTTP ${response.status}`);
7981
+ } catch (err) {
7982
+ recordReportFailure(options, file, entry, describe2(err));
7983
+ }
7984
+ }
7985
+ function recordReportFailure(options, file, entry, reason) {
7986
+ entry.reportAttempts += 1;
7987
+ options.log({
7988
+ level: "error",
7989
+ message: entry.reportAttempts >= MAX_ACK_ATTEMPTS ? `Reporting vault file ${file.id} as ${entry.outcome} failed (${reason}) \u2014 giving up after ${entry.reportAttempts} attempts` : `Reporting vault file ${file.id} as ${entry.outcome} failed (${reason}) \u2014 retrying on the next drain (attempt ${entry.reportAttempts} of ${MAX_ACK_ATTEMPTS})`
7990
+ });
7991
+ }
7992
+ function describe2(err) {
7993
+ return err instanceof Error ? err.message : String(err);
7994
+ }
7995
+
7791
7996
  // src/lib/channels/driver.ts
7792
7997
  function messageIdOf(m) {
7793
7998
  if (!m || typeof m !== "object") return void 0;
@@ -8196,6 +8401,7 @@ var ChannelDriver = class _ChannelDriver {
8196
8401
  * re-downloaded and re-written every ~2s until the server expires it.
8197
8402
  */
8198
8403
  fileAckFailures = /* @__PURE__ */ new Map();
8404
+ vaultFileOutcomes = /* @__PURE__ */ new Map();
8199
8405
  /**
8200
8406
  * Monotonic count of files this runner has pulled and written (#559). Only
8201
8407
  * ever increases, so `run.ts` detects work by comparing it against the value
@@ -8353,10 +8559,21 @@ var ChannelDriver = class _ChannelDriver {
8353
8559
  ackFailures: this.fileAckFailures,
8354
8560
  log: this.log
8355
8561
  });
8356
- this.appliedFileCount += result.applied;
8562
+ const vaultResult = await syncVaultFiles({
8563
+ agentId: this.agentId,
8564
+ apiUrl: this.apiUrl,
8565
+ getAuthHeader: this.getAuthHeader,
8566
+ fetchImpl: this.fetchImpl,
8567
+ allowedDirectories: this.fileSyncDirectories,
8568
+ homeDir: this.homeDir,
8569
+ outcomes: this.vaultFileOutcomes,
8570
+ log: this.log
8571
+ });
8572
+ const applied = result.applied + vaultResult.applied;
8573
+ this.appliedFileCount += applied;
8357
8574
  if (result.claudeCredentialApplied) this.claudeCredentialApplyCount += 1;
8358
8575
  if (result.opencodeAuthApplied) this.opencodeAuthApplyCount += 1;
8359
- return result.applied;
8576
+ return applied;
8360
8577
  } catch (err) {
8361
8578
  this.log({
8362
8579
  level: "error",
@@ -14475,8 +14692,18 @@ function readSetup(value) {
14475
14692
  }
14476
14693
  function clearLocalSetup(options) {
14477
14694
  const env = options.env ?? process.env;
14478
- (options.store ?? defaultStore).clear(options.agentId);
14479
- delete env.EVIDENT_PROXY_TOKEN;
14695
+ const store = options.store ?? defaultStore;
14696
+ const stored = store.get(options.agentId);
14697
+ store.clear(options.agentId);
14698
+ const token = env.EVIDENT_PROXY_TOKEN;
14699
+ if (token !== void 0 && token === stored?.token) {
14700
+ delete env.EVIDENT_PROXY_TOKEN;
14701
+ } else if (token !== void 0) {
14702
+ options.log(
14703
+ `MANAGED-INFERENCE-CLEANUP-PRESERVED-EXTERNAL-TOKEN: runner=${options.agentId}`,
14704
+ "debug"
14705
+ );
14706
+ }
14480
14707
  }
14481
14708
  async function discoverManagedInferenceSetup(options) {
14482
14709
  const headers = requestHeaders(options);