@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 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
- return { utilization: window.utilization, resetsAt: window.resets_at };
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(status) {
2712
- return status === 413 ? "file_too_large" : "write_failed";
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, status, reason) {
2746
- const outcome = `${status}${reason ? ` (${reason})` : ""}`;
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, status) {
3134
+ constructor(message, status2) {
2829
3135
  super(message);
2830
3136
  this.name = "ChannelTerminalError";
2831
- this.status = 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(status) {
2840
- return status === 429 || status >= 500 && status <= 599;
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, status) {
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 '${status}' update for message ${messageId.slice(0, 8)} so it is not re-bound to conversation ${conversationId.slice(0, 8)}`,
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 === 1 ? "warn" : "debug",
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 === 1 ? "warn" : "debug",
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]",