@evident-ai/cli 3.1.1-dev.1e66164 → 3.1.1-dev.2aaaa4d
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 +398 -224
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -532,6 +532,343 @@ async function whoami() {
|
|
|
532
532
|
blank();
|
|
533
533
|
}
|
|
534
534
|
|
|
535
|
+
// src/lib/auth.ts
|
|
536
|
+
async function getAuthCredentials() {
|
|
537
|
+
const runnerKey = process.env.EVIDENT_RUNNER_KEY;
|
|
538
|
+
const agentKey = process.env.EVIDENT_AGENT_KEY;
|
|
539
|
+
if (runnerKey) {
|
|
540
|
+
return {
|
|
541
|
+
token: runnerKey,
|
|
542
|
+
authType: "agent_key",
|
|
543
|
+
keySource: "runner_key",
|
|
544
|
+
notice: agentKey ? "Both EVIDENT_RUNNER_KEY and EVIDENT_AGENT_KEY are set; using EVIDENT_RUNNER_KEY." : void 0
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
if (agentKey) {
|
|
548
|
+
return { token: agentKey, authType: "agent_key", keySource: "agent_key" };
|
|
549
|
+
}
|
|
550
|
+
const userToken = process.env.EVIDENT_TOKEN;
|
|
551
|
+
if (userToken) {
|
|
552
|
+
return { token: userToken, authType: "bearer" };
|
|
553
|
+
}
|
|
554
|
+
const keychainCreds = await getToken();
|
|
555
|
+
if (keychainCreds) {
|
|
556
|
+
return {
|
|
557
|
+
token: keychainCreds.token,
|
|
558
|
+
authType: "bearer",
|
|
559
|
+
user: keychainCreds.user
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
564
|
+
function getAuthHeader(credentials2) {
|
|
565
|
+
if (credentials2.authType === "agent_key") {
|
|
566
|
+
return `SandboxKey ${credentials2.token}`;
|
|
567
|
+
}
|
|
568
|
+
return `Bearer ${credentials2.token}`;
|
|
569
|
+
}
|
|
570
|
+
function isInteractive(jsonOutput) {
|
|
571
|
+
if (jsonOutput) return false;
|
|
572
|
+
if (process.env.CI) return false;
|
|
573
|
+
if (process.env.GITHUB_ACTIONS) return false;
|
|
574
|
+
if (!process.stdin.isTTY) return false;
|
|
575
|
+
return true;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// src/commands/agent-lookup.ts
|
|
579
|
+
async function readErrorMessage(response) {
|
|
580
|
+
const text = await response.text().catch(() => "");
|
|
581
|
+
if (!text) return response.statusText || void 0;
|
|
582
|
+
try {
|
|
583
|
+
const data = JSON.parse(text);
|
|
584
|
+
const message = data.message ?? data.error;
|
|
585
|
+
if (typeof message === "string" && message.trim()) {
|
|
586
|
+
return message;
|
|
587
|
+
}
|
|
588
|
+
} catch {
|
|
589
|
+
}
|
|
590
|
+
return text.trim() || response.statusText || void 0;
|
|
591
|
+
}
|
|
592
|
+
function authFailureHint(apiUrl, serverMessage) {
|
|
593
|
+
const reason = serverMessage ? `: ${serverMessage}` : "";
|
|
594
|
+
return `Authentication failed${reason}. Your credentials were rejected by ${apiUrl}. This usually means you logged in against a different environment, or your session expired \u2014 log in again pointing at this endpoint and retry.`;
|
|
595
|
+
}
|
|
596
|
+
async function resolveAgentIdFromKey(authHeader) {
|
|
597
|
+
const apiUrl = getApiUrlConfig();
|
|
598
|
+
try {
|
|
599
|
+
const response = await fetch(`${apiUrl}/me`, {
|
|
600
|
+
headers: { Authorization: authHeader }
|
|
601
|
+
});
|
|
602
|
+
if (response.status === 401) {
|
|
603
|
+
const serverMessage = await readErrorMessage(response);
|
|
604
|
+
return { error: authFailureHint(apiUrl, serverMessage), authFailed: true };
|
|
605
|
+
}
|
|
606
|
+
if (!response.ok) {
|
|
607
|
+
const serverMessage = await readErrorMessage(response);
|
|
608
|
+
return {
|
|
609
|
+
error: `Failed to resolve runner from key (HTTP ${response.status})${serverMessage ? `: ${serverMessage}` : ""}`
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
const data = await response.json();
|
|
613
|
+
if (data.auth_type === "agent_key" && data.agent_id) {
|
|
614
|
+
return { agent_id: data.agent_id };
|
|
615
|
+
}
|
|
616
|
+
return {
|
|
617
|
+
error: "Cannot resolve runner ID: auth type is not agent_key. Please provide --runner explicitly."
|
|
618
|
+
};
|
|
619
|
+
} catch (error2) {
|
|
620
|
+
const message = error2 instanceof Error ? error2.message : "Unknown error";
|
|
621
|
+
return { error: `Failed to resolve runner from key: ${message}` };
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
var BEST_EFFORT_NOTIFY_TIMEOUT_MS = 2e3;
|
|
625
|
+
async function notifyAgentDisconnected(agentId, authHeader) {
|
|
626
|
+
const apiUrl = getApiUrlConfig();
|
|
627
|
+
try {
|
|
628
|
+
const response = await fetch(`${apiUrl}/runners/${agentId}/disconnect`, {
|
|
629
|
+
method: "POST",
|
|
630
|
+
headers: { Authorization: authHeader },
|
|
631
|
+
signal: AbortSignal.timeout(BEST_EFFORT_NOTIFY_TIMEOUT_MS)
|
|
632
|
+
});
|
|
633
|
+
if (!response.ok) {
|
|
634
|
+
const serverMessage = await readErrorMessage(response);
|
|
635
|
+
return {
|
|
636
|
+
ok: false,
|
|
637
|
+
error: `HTTP ${response.status}${serverMessage ? `: ${serverMessage}` : ""}`
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
return { ok: true };
|
|
641
|
+
} catch (error2) {
|
|
642
|
+
return { ok: false, error: describeBestEffortError(error2) };
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
function describeBestEffortError(error2) {
|
|
646
|
+
const name = error2?.name;
|
|
647
|
+
if (name === "TimeoutError" || name === "AbortError") {
|
|
648
|
+
return `timed out after ${BEST_EFFORT_NOTIFY_TIMEOUT_MS}ms`;
|
|
649
|
+
}
|
|
650
|
+
return error2 instanceof Error ? error2.message : String(error2);
|
|
651
|
+
}
|
|
652
|
+
async function reportMicrovmId(agentId, authHeader, microvmId) {
|
|
653
|
+
try {
|
|
654
|
+
const apiUrl = getApiUrlConfig();
|
|
655
|
+
const response = await fetch(`${apiUrl}/runners/${agentId}/microvm`, {
|
|
656
|
+
method: "POST",
|
|
657
|
+
headers: { Authorization: authHeader, "Content-Type": "application/json" },
|
|
658
|
+
body: JSON.stringify({ microvm_id: microvmId }),
|
|
659
|
+
signal: AbortSignal.timeout(BEST_EFFORT_NOTIFY_TIMEOUT_MS)
|
|
660
|
+
});
|
|
661
|
+
if (!response.ok) {
|
|
662
|
+
const serverMessage = await readErrorMessage(response);
|
|
663
|
+
return {
|
|
664
|
+
ok: false,
|
|
665
|
+
error: `HTTP ${response.status}${serverMessage ? `: ${serverMessage}` : ""}`
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
return { ok: true };
|
|
669
|
+
} catch (error2) {
|
|
670
|
+
return { ok: false, error: describeBestEffortError(error2) };
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
function toReportedWindow(window) {
|
|
674
|
+
if (!window) return null;
|
|
675
|
+
return { utilization: window.utilization, resets_at: window.resetsAt };
|
|
676
|
+
}
|
|
677
|
+
async function reportClaudeUsage(agentId, authHeader, snapshot) {
|
|
678
|
+
try {
|
|
679
|
+
const apiUrl = getApiUrlConfig();
|
|
680
|
+
const response = await fetch(`${apiUrl}/runners/${agentId}/claude-usage`, {
|
|
681
|
+
method: "POST",
|
|
682
|
+
headers: { Authorization: authHeader, "Content-Type": "application/json" },
|
|
683
|
+
body: JSON.stringify({
|
|
684
|
+
five_hour: toReportedWindow(snapshot.fiveHour),
|
|
685
|
+
seven_day: toReportedWindow(snapshot.sevenDay)
|
|
686
|
+
}),
|
|
687
|
+
signal: AbortSignal.timeout(BEST_EFFORT_NOTIFY_TIMEOUT_MS)
|
|
688
|
+
});
|
|
689
|
+
if (!response.ok) {
|
|
690
|
+
const serverMessage = await readErrorMessage(response);
|
|
691
|
+
return {
|
|
692
|
+
ok: false,
|
|
693
|
+
error: `HTTP ${response.status}${serverMessage ? `: ${serverMessage}` : ""}`
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
return { ok: true };
|
|
697
|
+
} catch (error2) {
|
|
698
|
+
return { ok: false, error: describeBestEffortError(error2) };
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
async function getAgentInfo(agentId, authHeader) {
|
|
702
|
+
const apiUrl = getApiUrlConfig();
|
|
703
|
+
try {
|
|
704
|
+
const response = await fetch(`${apiUrl}/runners/${agentId}`, {
|
|
705
|
+
headers: { Authorization: authHeader }
|
|
706
|
+
});
|
|
707
|
+
if (response.status === 401) {
|
|
708
|
+
const serverMessage = await readErrorMessage(response);
|
|
709
|
+
return { valid: false, error: authFailureHint(apiUrl, serverMessage), authFailed: true };
|
|
710
|
+
}
|
|
711
|
+
if (response.status === 403) {
|
|
712
|
+
const serverMessage = await readErrorMessage(response);
|
|
713
|
+
return {
|
|
714
|
+
valid: false,
|
|
715
|
+
error: serverMessage ?? "You do not have access to this runner (it may belong to a different team or organization)."
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
if (response.status === 404) {
|
|
719
|
+
const serverMessage = await readErrorMessage(response);
|
|
720
|
+
return { valid: false, error: serverMessage ?? `Runner ${agentId} not found` };
|
|
721
|
+
}
|
|
722
|
+
if (!response.ok) {
|
|
723
|
+
const serverMessage = await readErrorMessage(response);
|
|
724
|
+
return {
|
|
725
|
+
valid: false,
|
|
726
|
+
error: `API error (HTTP ${response.status})${serverMessage ? `: ${serverMessage}` : ""}`
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
const agent = await response.json();
|
|
730
|
+
if (agent.agent_type !== "local") {
|
|
731
|
+
return {
|
|
732
|
+
valid: false,
|
|
733
|
+
error: `Runner is type '${agent.agent_type}', must be 'local' for CLI connection`
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
return { valid: true, agent };
|
|
737
|
+
} catch (error2) {
|
|
738
|
+
const message = error2 instanceof Error ? error2.message : "Unknown error";
|
|
739
|
+
return { valid: false, error: `Failed to validate runner: ${message}` };
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// src/commands/status.ts
|
|
744
|
+
var STATUS_TIMEOUT_MS = 1e4;
|
|
745
|
+
function authLabelFor(credentials2) {
|
|
746
|
+
if (credentials2.authType === "agent_key") {
|
|
747
|
+
return credentials2.keySource === "agent_key" ? "runner key (EVIDENT_AGENT_KEY)" : "runner key (EVIDENT_RUNNER_KEY)";
|
|
748
|
+
}
|
|
749
|
+
return "user token";
|
|
750
|
+
}
|
|
751
|
+
function describeFetchError(error2) {
|
|
752
|
+
const name = error2?.name;
|
|
753
|
+
if (name === "TimeoutError" || name === "AbortError") {
|
|
754
|
+
return `timed out after ${STATUS_TIMEOUT_MS}ms waiting for a response`;
|
|
755
|
+
}
|
|
756
|
+
return error2 instanceof Error ? error2.message : String(error2);
|
|
757
|
+
}
|
|
758
|
+
async function checkStatus(jsonMode) {
|
|
759
|
+
const apiUrl = getApiUrlConfig();
|
|
760
|
+
const credentials2 = await getAuthCredentials();
|
|
761
|
+
if (!credentials2) {
|
|
762
|
+
return {
|
|
763
|
+
ok: false,
|
|
764
|
+
endpoint: apiUrl,
|
|
765
|
+
reason: "no_credentials",
|
|
766
|
+
error: "No credentials configured. Set EVIDENT_RUNNER_KEY (or EVIDENT_AGENT_KEY), or EVIDENT_TOKEN, or run `evident login`.",
|
|
767
|
+
exitCode: 1
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
if (credentials2.notice && !jsonMode) {
|
|
771
|
+
printWarning(credentials2.notice);
|
|
772
|
+
}
|
|
773
|
+
let response;
|
|
774
|
+
try {
|
|
775
|
+
response = await fetch(`${apiUrl}/me`, {
|
|
776
|
+
headers: { Authorization: getAuthHeader(credentials2) },
|
|
777
|
+
signal: AbortSignal.timeout(STATUS_TIMEOUT_MS)
|
|
778
|
+
});
|
|
779
|
+
} catch (error2) {
|
|
780
|
+
return {
|
|
781
|
+
ok: false,
|
|
782
|
+
endpoint: apiUrl,
|
|
783
|
+
authLabel: authLabelFor(credentials2),
|
|
784
|
+
reason: "unreachable",
|
|
785
|
+
error: `Could not reach ${apiUrl}: ${describeFetchError(error2)}. The credentials were NOT validated.`,
|
|
786
|
+
exitCode: 75
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
if (response.status === 401) {
|
|
790
|
+
const serverMessage = await readErrorMessage(response);
|
|
791
|
+
return {
|
|
792
|
+
ok: false,
|
|
793
|
+
endpoint: apiUrl,
|
|
794
|
+
authLabel: authLabelFor(credentials2),
|
|
795
|
+
reason: "unauthorized",
|
|
796
|
+
error: authFailureHint(apiUrl, serverMessage),
|
|
797
|
+
exitCode: 1
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
if (response.status >= 500) {
|
|
801
|
+
const serverMessage = await readErrorMessage(response);
|
|
802
|
+
return {
|
|
803
|
+
ok: false,
|
|
804
|
+
endpoint: apiUrl,
|
|
805
|
+
authLabel: authLabelFor(credentials2),
|
|
806
|
+
reason: "unreachable",
|
|
807
|
+
error: `${apiUrl} returned HTTP ${response.status}${serverMessage ? `: ${serverMessage}` : ""}. The credentials were NOT validated.`,
|
|
808
|
+
exitCode: 75
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
if (!response.ok) {
|
|
812
|
+
const serverMessage = await readErrorMessage(response);
|
|
813
|
+
return {
|
|
814
|
+
ok: false,
|
|
815
|
+
endpoint: apiUrl,
|
|
816
|
+
authLabel: authLabelFor(credentials2),
|
|
817
|
+
reason: "http_error",
|
|
818
|
+
error: `HTTP ${response.status}${serverMessage ? `: ${serverMessage}` : ""}`,
|
|
819
|
+
exitCode: 1
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
const data = await response.json();
|
|
823
|
+
return {
|
|
824
|
+
ok: true,
|
|
825
|
+
endpoint: apiUrl,
|
|
826
|
+
authType: data.auth_type,
|
|
827
|
+
authLabel: authLabelFor(credentials2),
|
|
828
|
+
runnerId: data.auth_type === "agent_key" ? data.agent_id : void 0,
|
|
829
|
+
reason: "ok",
|
|
830
|
+
exitCode: 0
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
function printJson(result) {
|
|
834
|
+
const payload = {
|
|
835
|
+
ok: result.ok,
|
|
836
|
+
endpoint: result.endpoint
|
|
837
|
+
};
|
|
838
|
+
if (result.authType) payload.auth_type = result.authType;
|
|
839
|
+
if (result.runnerId) payload.runner_id = result.runnerId;
|
|
840
|
+
if (result.reason) payload.reason = result.reason;
|
|
841
|
+
if (result.error) payload.error = result.error;
|
|
842
|
+
console.log(JSON.stringify(payload));
|
|
843
|
+
}
|
|
844
|
+
function printHuman(result) {
|
|
845
|
+
blank();
|
|
846
|
+
console.log(keyValue("Endpoint", result.endpoint));
|
|
847
|
+
if (result.ok) {
|
|
848
|
+
console.log(keyValue("Auth", result.authLabel ?? "\u2014"));
|
|
849
|
+
if (result.runnerId) {
|
|
850
|
+
console.log(keyValue("Runner", result.runnerId));
|
|
851
|
+
}
|
|
852
|
+
console.log(keyValue("Status", "OK \u2014 credentials accepted"));
|
|
853
|
+
blank();
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
if (result.authLabel) {
|
|
857
|
+
console.log(keyValue("Auth", result.authLabel));
|
|
858
|
+
}
|
|
859
|
+
blank();
|
|
860
|
+
printError(result.error ?? "Unknown error");
|
|
861
|
+
}
|
|
862
|
+
async function status(options = {}) {
|
|
863
|
+
const result = await checkStatus(Boolean(options.json));
|
|
864
|
+
if (options.json) {
|
|
865
|
+
printJson(result);
|
|
866
|
+
} else {
|
|
867
|
+
printHuman(result);
|
|
868
|
+
}
|
|
869
|
+
process.exit(result.exitCode);
|
|
870
|
+
}
|
|
871
|
+
|
|
535
872
|
// src/lib/claude-usage.ts
|
|
536
873
|
import { execFileSync } from "child_process";
|
|
537
874
|
import { readFileSync } from "fs";
|
|
@@ -582,6 +919,10 @@ var ClaudeUsageError = class extends Error {
|
|
|
582
919
|
function isLocalCredentialProblem(err) {
|
|
583
920
|
return err instanceof ClaudeUsageError && (err.reason === "no_credentials" || err.reason === "credentials_expired");
|
|
584
921
|
}
|
|
922
|
+
function normalizeResetsAt(value) {
|
|
923
|
+
const ms = Date.parse(value);
|
|
924
|
+
return Number.isNaN(ms) ? null : new Date(ms).toISOString();
|
|
925
|
+
}
|
|
585
926
|
function toWindow(value) {
|
|
586
927
|
if (!value || typeof value !== "object") {
|
|
587
928
|
return null;
|
|
@@ -590,7 +931,11 @@ function toWindow(value) {
|
|
|
590
931
|
if (typeof window.utilization !== "number" || typeof window.resets_at !== "string") {
|
|
591
932
|
return null;
|
|
592
933
|
}
|
|
593
|
-
|
|
934
|
+
const resetsAt = normalizeResetsAt(window.resets_at);
|
|
935
|
+
if (resetsAt === null) {
|
|
936
|
+
return null;
|
|
937
|
+
}
|
|
938
|
+
return { utilization: window.utilization, resetsAt };
|
|
594
939
|
}
|
|
595
940
|
async function getClaudeUsage() {
|
|
596
941
|
const credentials2 = readClaudeCliCredentials();
|
|
@@ -934,49 +1279,6 @@ function forwardRunnerActivity(entry, context) {
|
|
|
934
1279
|
}
|
|
935
1280
|
}
|
|
936
1281
|
|
|
937
|
-
// src/lib/auth.ts
|
|
938
|
-
async function getAuthCredentials() {
|
|
939
|
-
const runnerKey = process.env.EVIDENT_RUNNER_KEY;
|
|
940
|
-
const agentKey = process.env.EVIDENT_AGENT_KEY;
|
|
941
|
-
if (runnerKey) {
|
|
942
|
-
return {
|
|
943
|
-
token: runnerKey,
|
|
944
|
-
authType: "agent_key",
|
|
945
|
-
keySource: "runner_key",
|
|
946
|
-
notice: agentKey ? "Both EVIDENT_RUNNER_KEY and EVIDENT_AGENT_KEY are set; using EVIDENT_RUNNER_KEY." : void 0
|
|
947
|
-
};
|
|
948
|
-
}
|
|
949
|
-
if (agentKey) {
|
|
950
|
-
return { token: agentKey, authType: "agent_key", keySource: "agent_key" };
|
|
951
|
-
}
|
|
952
|
-
const userToken = process.env.EVIDENT_TOKEN;
|
|
953
|
-
if (userToken) {
|
|
954
|
-
return { token: userToken, authType: "bearer" };
|
|
955
|
-
}
|
|
956
|
-
const keychainCreds = await getToken();
|
|
957
|
-
if (keychainCreds) {
|
|
958
|
-
return {
|
|
959
|
-
token: keychainCreds.token,
|
|
960
|
-
authType: "bearer",
|
|
961
|
-
user: keychainCreds.user
|
|
962
|
-
};
|
|
963
|
-
}
|
|
964
|
-
return null;
|
|
965
|
-
}
|
|
966
|
-
function getAuthHeader(credentials2) {
|
|
967
|
-
if (credentials2.authType === "agent_key") {
|
|
968
|
-
return `SandboxKey ${credentials2.token}`;
|
|
969
|
-
}
|
|
970
|
-
return `Bearer ${credentials2.token}`;
|
|
971
|
-
}
|
|
972
|
-
function isInteractive(jsonOutput) {
|
|
973
|
-
if (jsonOutput) return false;
|
|
974
|
-
if (process.env.CI) return false;
|
|
975
|
-
if (process.env.GITHUB_ACTIONS) return false;
|
|
976
|
-
if (!process.stdin.isTTY) return false;
|
|
977
|
-
return true;
|
|
978
|
-
}
|
|
979
|
-
|
|
980
1282
|
// src/lib/opencode/health.ts
|
|
981
1283
|
async function checkOpenCodeHealth(port) {
|
|
982
1284
|
try {
|
|
@@ -2386,6 +2688,10 @@ function nextReportDelayMs(random = Math.random) {
|
|
|
2386
2688
|
return BASE_REPORT_DELAY_MS - jitterRangeMs + random() * (2 * jitterRangeMs);
|
|
2387
2689
|
}
|
|
2388
2690
|
var FIRST_REPORT_DELAY_MS = 5e3 + Math.random() * 1e4;
|
|
2691
|
+
var CLAUDE_USAGE_FAILURE_REESCALATION_TICKS = 6;
|
|
2692
|
+
function claudeUsageFailureLogLevel(consecutiveFailures) {
|
|
2693
|
+
return consecutiveFailures === 1 || consecutiveFailures % CLAUDE_USAGE_FAILURE_REESCALATION_TICKS === 0 ? "warn" : "debug";
|
|
2694
|
+
}
|
|
2389
2695
|
|
|
2390
2696
|
// src/lib/channels/driver.ts
|
|
2391
2697
|
import { homedir as homedir2 } from "os";
|
|
@@ -2708,8 +3014,8 @@ async function applyOne(options, file) {
|
|
|
2708
3014
|
await ack(options, file, "applied");
|
|
2709
3015
|
return true;
|
|
2710
3016
|
}
|
|
2711
|
-
function durableDownloadCode(
|
|
2712
|
-
return
|
|
3017
|
+
function durableDownloadCode(status2) {
|
|
3018
|
+
return status2 === 413 ? "file_too_large" : "write_failed";
|
|
2713
3019
|
}
|
|
2714
3020
|
async function downloadContent(options, file, label) {
|
|
2715
3021
|
try {
|
|
@@ -2742,8 +3048,8 @@ async function downloadContent(options, file, label) {
|
|
|
2742
3048
|
return { ok: false, terminal: false };
|
|
2743
3049
|
}
|
|
2744
3050
|
}
|
|
2745
|
-
async function ack(options, file,
|
|
2746
|
-
const outcome = `${
|
|
3051
|
+
async function ack(options, file, status2, reason) {
|
|
3052
|
+
const outcome = `${status2}${reason ? ` (${reason})` : ""}`;
|
|
2747
3053
|
try {
|
|
2748
3054
|
const res = await options.fetchImpl(
|
|
2749
3055
|
`${options.apiUrl}/runners/${options.agentId}/files/${file.id}/ack`,
|
|
@@ -2753,7 +3059,7 @@ async function ack(options, file, status, reason) {
|
|
|
2753
3059
|
Authorization: options.getAuthHeader(),
|
|
2754
3060
|
"Content-Type": "application/json"
|
|
2755
3061
|
},
|
|
2756
|
-
body: JSON.stringify(reason ? { status, reason } : { status })
|
|
3062
|
+
body: JSON.stringify(reason ? { status: status2, reason } : { status: status2 })
|
|
2757
3063
|
}
|
|
2758
3064
|
);
|
|
2759
3065
|
if (!res.ok) {
|
|
@@ -2825,10 +3131,10 @@ var ChannelAuthError = class extends Error {
|
|
|
2825
3131
|
};
|
|
2826
3132
|
var ChannelTerminalError = class extends Error {
|
|
2827
3133
|
status;
|
|
2828
|
-
constructor(message,
|
|
3134
|
+
constructor(message, status2) {
|
|
2829
3135
|
super(message);
|
|
2830
3136
|
this.name = "ChannelTerminalError";
|
|
2831
|
-
this.status =
|
|
3137
|
+
this.status = status2;
|
|
2832
3138
|
}
|
|
2833
3139
|
};
|
|
2834
3140
|
function backoffDelay(attempt, policy) {
|
|
@@ -2836,8 +3142,8 @@ function backoffDelay(attempt, policy) {
|
|
|
2836
3142
|
const capped = Math.min(policy.maxDelayMs, exp);
|
|
2837
3143
|
return Math.floor(Math.random() * capped);
|
|
2838
3144
|
}
|
|
2839
|
-
function isRetryableStatus(
|
|
2840
|
-
return
|
|
3145
|
+
function isRetryableStatus(status2) {
|
|
3146
|
+
return status2 === 429 || status2 >= 500 && status2 <= 599;
|
|
2841
3147
|
}
|
|
2842
3148
|
var ChannelDriver = class _ChannelDriver {
|
|
2843
3149
|
agentId;
|
|
@@ -5159,11 +5465,11 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
5159
5465
|
* `ensureSession` guard, not this suppression, is what makes the self-heal
|
|
5160
5466
|
* stick.
|
|
5161
5467
|
*/
|
|
5162
|
-
sessionIdBody(sessionId, conversationId, messageId,
|
|
5468
|
+
sessionIdBody(sessionId, conversationId, messageId, status2) {
|
|
5163
5469
|
if (!this.isSuperseded(conversationId, sessionId)) return { opencode_session_id: sessionId };
|
|
5164
5470
|
this.log({
|
|
5165
5471
|
level: "debug",
|
|
5166
|
-
message: `Omitting the abandoned OpenCode session ${sessionId.slice(0, 8)} from the '${
|
|
5472
|
+
message: `Omitting the abandoned OpenCode session ${sessionId.slice(0, 8)} from the '${status2}' update for message ${messageId.slice(0, 8)} so it is not re-bound to conversation ${conversationId.slice(0, 8)}`,
|
|
5167
5473
|
conversation_id: conversationId,
|
|
5168
5474
|
message_id: messageId
|
|
5169
5475
|
});
|
|
@@ -5609,171 +5915,6 @@ Port ${port} is already in use.`));
|
|
|
5609
5915
|
return { port, process: null, version: null, notReadyReason: "you chose to continue without it" };
|
|
5610
5916
|
}
|
|
5611
5917
|
|
|
5612
|
-
// src/commands/agent-lookup.ts
|
|
5613
|
-
async function readErrorMessage(response) {
|
|
5614
|
-
const text = await response.text().catch(() => "");
|
|
5615
|
-
if (!text) return response.statusText || void 0;
|
|
5616
|
-
try {
|
|
5617
|
-
const data = JSON.parse(text);
|
|
5618
|
-
const message = data.message ?? data.error;
|
|
5619
|
-
if (typeof message === "string" && message.trim()) {
|
|
5620
|
-
return message;
|
|
5621
|
-
}
|
|
5622
|
-
} catch {
|
|
5623
|
-
}
|
|
5624
|
-
return text.trim() || response.statusText || void 0;
|
|
5625
|
-
}
|
|
5626
|
-
function authFailureHint(apiUrl, serverMessage) {
|
|
5627
|
-
const reason = serverMessage ? `: ${serverMessage}` : "";
|
|
5628
|
-
return `Authentication failed${reason}. Your credentials were rejected by ${apiUrl}. This usually means you logged in against a different environment, or your session expired \u2014 log in again pointing at this endpoint and retry.`;
|
|
5629
|
-
}
|
|
5630
|
-
async function resolveAgentIdFromKey(authHeader) {
|
|
5631
|
-
const apiUrl = getApiUrlConfig();
|
|
5632
|
-
try {
|
|
5633
|
-
const response = await fetch(`${apiUrl}/me`, {
|
|
5634
|
-
headers: { Authorization: authHeader }
|
|
5635
|
-
});
|
|
5636
|
-
if (response.status === 401) {
|
|
5637
|
-
const serverMessage = await readErrorMessage(response);
|
|
5638
|
-
return { error: authFailureHint(apiUrl, serverMessage), authFailed: true };
|
|
5639
|
-
}
|
|
5640
|
-
if (!response.ok) {
|
|
5641
|
-
const serverMessage = await readErrorMessage(response);
|
|
5642
|
-
return {
|
|
5643
|
-
error: `Failed to resolve runner from key (HTTP ${response.status})${serverMessage ? `: ${serverMessage}` : ""}`
|
|
5644
|
-
};
|
|
5645
|
-
}
|
|
5646
|
-
const data = await response.json();
|
|
5647
|
-
if (data.auth_type === "agent_key" && data.agent_id) {
|
|
5648
|
-
return { agent_id: data.agent_id };
|
|
5649
|
-
}
|
|
5650
|
-
return {
|
|
5651
|
-
error: "Cannot resolve runner ID: auth type is not agent_key. Please provide --runner explicitly."
|
|
5652
|
-
};
|
|
5653
|
-
} catch (error2) {
|
|
5654
|
-
const message = error2 instanceof Error ? error2.message : "Unknown error";
|
|
5655
|
-
return { error: `Failed to resolve runner from key: ${message}` };
|
|
5656
|
-
}
|
|
5657
|
-
}
|
|
5658
|
-
var BEST_EFFORT_NOTIFY_TIMEOUT_MS = 2e3;
|
|
5659
|
-
async function notifyAgentDisconnected(agentId, authHeader) {
|
|
5660
|
-
const apiUrl = getApiUrlConfig();
|
|
5661
|
-
try {
|
|
5662
|
-
const response = await fetch(`${apiUrl}/runners/${agentId}/disconnect`, {
|
|
5663
|
-
method: "POST",
|
|
5664
|
-
headers: { Authorization: authHeader },
|
|
5665
|
-
signal: AbortSignal.timeout(BEST_EFFORT_NOTIFY_TIMEOUT_MS)
|
|
5666
|
-
});
|
|
5667
|
-
if (!response.ok) {
|
|
5668
|
-
const serverMessage = await readErrorMessage(response);
|
|
5669
|
-
return {
|
|
5670
|
-
ok: false,
|
|
5671
|
-
error: `HTTP ${response.status}${serverMessage ? `: ${serverMessage}` : ""}`
|
|
5672
|
-
};
|
|
5673
|
-
}
|
|
5674
|
-
return { ok: true };
|
|
5675
|
-
} catch (error2) {
|
|
5676
|
-
return { ok: false, error: describeBestEffortError(error2) };
|
|
5677
|
-
}
|
|
5678
|
-
}
|
|
5679
|
-
function describeBestEffortError(error2) {
|
|
5680
|
-
const name = error2?.name;
|
|
5681
|
-
if (name === "TimeoutError" || name === "AbortError") {
|
|
5682
|
-
return `timed out after ${BEST_EFFORT_NOTIFY_TIMEOUT_MS}ms`;
|
|
5683
|
-
}
|
|
5684
|
-
return error2 instanceof Error ? error2.message : String(error2);
|
|
5685
|
-
}
|
|
5686
|
-
async function reportMicrovmId(agentId, authHeader, microvmId) {
|
|
5687
|
-
try {
|
|
5688
|
-
const apiUrl = getApiUrlConfig();
|
|
5689
|
-
const response = await fetch(`${apiUrl}/runners/${agentId}/microvm`, {
|
|
5690
|
-
method: "POST",
|
|
5691
|
-
headers: { Authorization: authHeader, "Content-Type": "application/json" },
|
|
5692
|
-
body: JSON.stringify({ microvm_id: microvmId }),
|
|
5693
|
-
signal: AbortSignal.timeout(BEST_EFFORT_NOTIFY_TIMEOUT_MS)
|
|
5694
|
-
});
|
|
5695
|
-
if (!response.ok) {
|
|
5696
|
-
const serverMessage = await readErrorMessage(response);
|
|
5697
|
-
return {
|
|
5698
|
-
ok: false,
|
|
5699
|
-
error: `HTTP ${response.status}${serverMessage ? `: ${serverMessage}` : ""}`
|
|
5700
|
-
};
|
|
5701
|
-
}
|
|
5702
|
-
return { ok: true };
|
|
5703
|
-
} catch (error2) {
|
|
5704
|
-
return { ok: false, error: describeBestEffortError(error2) };
|
|
5705
|
-
}
|
|
5706
|
-
}
|
|
5707
|
-
function toReportedWindow(window) {
|
|
5708
|
-
if (!window) return null;
|
|
5709
|
-
return { utilization: window.utilization, resets_at: window.resetsAt };
|
|
5710
|
-
}
|
|
5711
|
-
async function reportClaudeUsage(agentId, authHeader, snapshot) {
|
|
5712
|
-
try {
|
|
5713
|
-
const apiUrl = getApiUrlConfig();
|
|
5714
|
-
const response = await fetch(`${apiUrl}/runners/${agentId}/claude-usage`, {
|
|
5715
|
-
method: "POST",
|
|
5716
|
-
headers: { Authorization: authHeader, "Content-Type": "application/json" },
|
|
5717
|
-
body: JSON.stringify({
|
|
5718
|
-
five_hour: toReportedWindow(snapshot.fiveHour),
|
|
5719
|
-
seven_day: toReportedWindow(snapshot.sevenDay)
|
|
5720
|
-
}),
|
|
5721
|
-
signal: AbortSignal.timeout(BEST_EFFORT_NOTIFY_TIMEOUT_MS)
|
|
5722
|
-
});
|
|
5723
|
-
if (!response.ok) {
|
|
5724
|
-
const serverMessage = await readErrorMessage(response);
|
|
5725
|
-
return {
|
|
5726
|
-
ok: false,
|
|
5727
|
-
error: `HTTP ${response.status}${serverMessage ? `: ${serverMessage}` : ""}`
|
|
5728
|
-
};
|
|
5729
|
-
}
|
|
5730
|
-
return { ok: true };
|
|
5731
|
-
} catch (error2) {
|
|
5732
|
-
return { ok: false, error: describeBestEffortError(error2) };
|
|
5733
|
-
}
|
|
5734
|
-
}
|
|
5735
|
-
async function getAgentInfo(agentId, authHeader) {
|
|
5736
|
-
const apiUrl = getApiUrlConfig();
|
|
5737
|
-
try {
|
|
5738
|
-
const response = await fetch(`${apiUrl}/runners/${agentId}`, {
|
|
5739
|
-
headers: { Authorization: authHeader }
|
|
5740
|
-
});
|
|
5741
|
-
if (response.status === 401) {
|
|
5742
|
-
const serverMessage = await readErrorMessage(response);
|
|
5743
|
-
return { valid: false, error: authFailureHint(apiUrl, serverMessage), authFailed: true };
|
|
5744
|
-
}
|
|
5745
|
-
if (response.status === 403) {
|
|
5746
|
-
const serverMessage = await readErrorMessage(response);
|
|
5747
|
-
return {
|
|
5748
|
-
valid: false,
|
|
5749
|
-
error: serverMessage ?? "You do not have access to this runner (it may belong to a different team or organization)."
|
|
5750
|
-
};
|
|
5751
|
-
}
|
|
5752
|
-
if (response.status === 404) {
|
|
5753
|
-
const serverMessage = await readErrorMessage(response);
|
|
5754
|
-
return { valid: false, error: serverMessage ?? `Runner ${agentId} not found` };
|
|
5755
|
-
}
|
|
5756
|
-
if (!response.ok) {
|
|
5757
|
-
const serverMessage = await readErrorMessage(response);
|
|
5758
|
-
return {
|
|
5759
|
-
valid: false,
|
|
5760
|
-
error: `API error (HTTP ${response.status})${serverMessage ? `: ${serverMessage}` : ""}`
|
|
5761
|
-
};
|
|
5762
|
-
}
|
|
5763
|
-
const agent = await response.json();
|
|
5764
|
-
if (agent.agent_type !== "local") {
|
|
5765
|
-
return {
|
|
5766
|
-
valid: false,
|
|
5767
|
-
error: `Runner is type '${agent.agent_type}', must be 'local' for CLI connection`
|
|
5768
|
-
};
|
|
5769
|
-
}
|
|
5770
|
-
return { valid: true, agent };
|
|
5771
|
-
} catch (error2) {
|
|
5772
|
-
const message = error2 instanceof Error ? error2.message : "Unknown error";
|
|
5773
|
-
return { valid: false, error: `Failed to validate runner: ${message}` };
|
|
5774
|
-
}
|
|
5775
|
-
}
|
|
5776
|
-
|
|
5777
5918
|
// src/commands/run.ts
|
|
5778
5919
|
var MAX_ACTIVITY_LOG_ENTRIES = 10;
|
|
5779
5920
|
var CHANNEL_POLL_INTERVAL_MS = Number(process.env.EVIDENT_CHANNEL_POLL_INTERVAL_MS) || 2e3;
|
|
@@ -5984,6 +6125,7 @@ async function handleAuthError(state, error2) {
|
|
|
5984
6125
|
}
|
|
5985
6126
|
async function driveChannels(state, driver) {
|
|
5986
6127
|
let idlePolls = 0;
|
|
6128
|
+
let consecutiveDrainFailures = 0;
|
|
5987
6129
|
let lastSeenProxiedActivityAt = state.lastProxiedActivityAt;
|
|
5988
6130
|
let lastSeenAppliedFiles = driver.fileSyncActivity().appliedFiles;
|
|
5989
6131
|
while (state.running) {
|
|
@@ -6001,6 +6143,7 @@ async function driveChannels(state, driver) {
|
|
|
6001
6143
|
);
|
|
6002
6144
|
try {
|
|
6003
6145
|
const processed = await driver.drainPending();
|
|
6146
|
+
consecutiveDrainFailures = 0;
|
|
6004
6147
|
state.messageCount += processed;
|
|
6005
6148
|
const proxiedActivity = state.lastProxiedActivityAt !== lastSeenProxiedActivityAt;
|
|
6006
6149
|
lastSeenProxiedActivityAt = state.lastProxiedActivityAt;
|
|
@@ -6035,8 +6178,32 @@ async function driveChannels(state, driver) {
|
|
|
6035
6178
|
const errorMessage = error2 instanceof Error ? error2.message : String(error2);
|
|
6036
6179
|
logActivity(state, { type: "error", error: `Channel processing error: ${errorMessage}` });
|
|
6037
6180
|
if (state.interactive) displayStatus(state);
|
|
6181
|
+
if (driver.hasInFlightWatchers()) {
|
|
6182
|
+
consecutiveDrainFailures = 0;
|
|
6183
|
+
} else if (state.idleTimeout !== null) {
|
|
6184
|
+
consecutiveDrainFailures++;
|
|
6185
|
+
if (consecutiveDrainFailures === 1) {
|
|
6186
|
+
logActivity(state, {
|
|
6187
|
+
type: "info",
|
|
6188
|
+
message: `Cannot reach Evident, will exit if this persists past the idle timeout (timeout: ${state.idleTimeout}s)...`
|
|
6189
|
+
});
|
|
6190
|
+
if (state.interactive) displayStatus(state);
|
|
6191
|
+
}
|
|
6192
|
+
}
|
|
6038
6193
|
}
|
|
6039
6194
|
await new Promise((resolve3) => setTimeout(resolve3, CHANNEL_POLL_INTERVAL_MS));
|
|
6195
|
+
if (state.idleTimeout !== null && consecutiveDrainFailures >= 2) {
|
|
6196
|
+
const unreachableMs = consecutiveDrainFailures * CHANNEL_POLL_INTERVAL_MS;
|
|
6197
|
+
if (unreachableMs > state.idleTimeout * 1e3) {
|
|
6198
|
+
logActivity(state, {
|
|
6199
|
+
type: "info",
|
|
6200
|
+
level: "warn",
|
|
6201
|
+
message: `Exiting: could not reach Evident for ${consecutiveDrainFailures} consecutive polls (${Math.round(unreachableMs / 1e3)}s)`
|
|
6202
|
+
});
|
|
6203
|
+
if (state.interactive) displayStatus(state);
|
|
6204
|
+
break;
|
|
6205
|
+
}
|
|
6206
|
+
}
|
|
6040
6207
|
if (state.idleTimeout !== null && idlePolls >= 2) {
|
|
6041
6208
|
const idleMs = idlePolls * CHANNEL_POLL_INTERVAL_MS;
|
|
6042
6209
|
if (idleMs > state.idleTimeout * 1e3) {
|
|
@@ -6122,6 +6289,9 @@ function scheduleSessionCleanup(state, driver, options) {
|
|
|
6122
6289
|
);
|
|
6123
6290
|
state.sessionCleanupTimers.push(interval, firstSweep);
|
|
6124
6291
|
}
|
|
6292
|
+
function claudeUsageFailureStreakSuffix(consecutiveFailures) {
|
|
6293
|
+
return consecutiveFailures > 1 ? ` (${consecutiveFailures} consecutive failures)` : "";
|
|
6294
|
+
}
|
|
6125
6295
|
function scheduleClaudeUsageReporting(state, options) {
|
|
6126
6296
|
const { mode, warnings } = resolveClaudeUsageReportingMode(
|
|
6127
6297
|
options.claudeUsageReporting,
|
|
@@ -6168,8 +6338,8 @@ function scheduleClaudeUsageReporting(state, options) {
|
|
|
6168
6338
|
consecutiveFailures++;
|
|
6169
6339
|
logActivity(state, {
|
|
6170
6340
|
type: "info",
|
|
6171
|
-
level: consecutiveFailures
|
|
6172
|
-
message: `Failed to report Claude usage: ${result.error}`
|
|
6341
|
+
level: claudeUsageFailureLogLevel(consecutiveFailures),
|
|
6342
|
+
message: `Failed to report Claude usage: ${result.error}${claudeUsageFailureStreakSuffix(consecutiveFailures)}`
|
|
6173
6343
|
});
|
|
6174
6344
|
}
|
|
6175
6345
|
scheduleNextTick();
|
|
@@ -6201,8 +6371,8 @@ function scheduleClaudeUsageReporting(state, options) {
|
|
|
6201
6371
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
6202
6372
|
logActivity(state, {
|
|
6203
6373
|
type: "info",
|
|
6204
|
-
level: consecutiveFailures
|
|
6205
|
-
message: `Claude usage reporting failed: ${message}`
|
|
6374
|
+
level: claudeUsageFailureLogLevel(consecutiveFailures),
|
|
6375
|
+
message: `Claude usage reporting failed: ${message}${claudeUsageFailureStreakSuffix(consecutiveFailures)}`
|
|
6206
6376
|
});
|
|
6207
6377
|
scheduleNextTick();
|
|
6208
6378
|
}
|
|
@@ -6409,6 +6579,7 @@ async function run(options) {
|
|
|
6409
6579
|
console.log(chalk6.dim("Or run `evident login` for interactive authentication"));
|
|
6410
6580
|
blank();
|
|
6411
6581
|
process.exit(1);
|
|
6582
|
+
return;
|
|
6412
6583
|
}
|
|
6413
6584
|
blank();
|
|
6414
6585
|
console.log(chalk6.yellow("You are not logged in to Evident."));
|
|
@@ -6453,6 +6624,7 @@ async function run(options) {
|
|
|
6453
6624
|
} else {
|
|
6454
6625
|
printError(resolved.error || "Failed to resolve runner ID from key");
|
|
6455
6626
|
process.exit(1);
|
|
6627
|
+
return;
|
|
6456
6628
|
}
|
|
6457
6629
|
} else {
|
|
6458
6630
|
printError(
|
|
@@ -6466,6 +6638,7 @@ async function run(options) {
|
|
|
6466
6638
|
);
|
|
6467
6639
|
blank();
|
|
6468
6640
|
process.exit(1);
|
|
6641
|
+
return;
|
|
6469
6642
|
}
|
|
6470
6643
|
}
|
|
6471
6644
|
telemetry.info(
|
|
@@ -6768,6 +6941,7 @@ program.name("evident").description("Run OpenCode locally and connect it to Evid
|
|
|
6768
6941
|
program.command("login").description("Authenticate with Evident").option("--token", "Use token-based authentication (for CI/CD)").option("--no-browser", "Do not open the browser automatically").action(login);
|
|
6769
6942
|
program.command("logout").description("Remove stored credentials for the current endpoint").option("--all", "Remove stored credentials for all endpoints").action((options) => logout({ all: options.all }));
|
|
6770
6943
|
program.command("whoami").description("Show the currently logged in user").action(whoami);
|
|
6944
|
+
program.command("status").description("Check whether the configured credentials can reach Evident").option("--json", "Output in JSON format").action((options) => status({ json: options.json }));
|
|
6771
6945
|
program.command("claude-usage").description("[spike] Show Claude subscription usage (requires a local `claude login`)").action(claudeUsage);
|
|
6772
6946
|
program.command("run").description("Connect to Evident and process messages").option("--runner [id]", "Runner ID to connect to (optional when EVIDENT_RUNNER_KEY is set)").option(
|
|
6773
6947
|
"-a, --agent [id]",
|