@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 +232 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
14479
|
-
|
|
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);
|