claudish 10.1.1 → 10.2.0
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 +2114 -424
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -715,7 +715,7 @@ var init_onepassword_config = __esm(() => {
|
|
|
715
715
|
});
|
|
716
716
|
|
|
717
717
|
// src/version.ts
|
|
718
|
-
var VERSION = "10.
|
|
718
|
+
var VERSION = "10.2.0";
|
|
719
719
|
|
|
720
720
|
// src/logger.ts
|
|
721
721
|
import { appendFile, existsSync as existsSync2, mkdirSync, readdirSync, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
@@ -802,7 +802,7 @@ function redactDeep(val, key) {
|
|
|
802
802
|
return val;
|
|
803
803
|
}
|
|
804
804
|
function isStructuralLogWorthy(msg) {
|
|
805
|
-
return msg.startsWith("[SSE:") || msg.startsWith("[Suppressed]") || msg.startsWith("[Proxy]") || msg.startsWith("[Claude Code]") || msg.startsWith("[Fallback]") || msg.startsWith("[Streaming] ===") || msg.startsWith("[Streaming] Chunk:") || msg.startsWith("[Streaming] Received") || msg.startsWith("[Streaming] Text-based tool calls") || msg.startsWith("[Streaming] Final usage") || msg.startsWith("[Streaming] Sending") || msg.startsWith("[AnthropicSSE] Stream complete") || msg.startsWith("[AnthropicSSE] Tool use:") || msg.includes("Response status:") || msg.includes("Error") || msg.includes("error") || msg.includes("[Auto-route]");
|
|
805
|
+
return msg.startsWith("[SSE:") || msg.startsWith("[Suppressed]") || msg.startsWith("[Proxy]") || msg.startsWith("[Claude Code]") || msg.startsWith("[Fallback]") || msg.startsWith("[Recovery]") || msg.startsWith("[Streaming] ===") || msg.startsWith("[Streaming] Chunk:") || msg.startsWith("[Streaming] Received") || msg.startsWith("[Streaming] Text-based tool calls") || msg.startsWith("[Streaming] Final usage") || msg.startsWith("[Streaming] Sending") || msg.startsWith("[AnthropicSSE] Stream complete") || msg.startsWith("[AnthropicSSE] Tool use:") || msg.includes("Response status:") || msg.includes("Error") || msg.includes("error") || msg.includes("[Auto-route]");
|
|
806
806
|
}
|
|
807
807
|
function redactLogLine(message, timestamp) {
|
|
808
808
|
if (message.startsWith("[SSE:")) {
|
|
@@ -885,6 +885,9 @@ function logStderr(message) {
|
|
|
885
885
|
}
|
|
886
886
|
log(message);
|
|
887
887
|
}
|
|
888
|
+
function logRecovery(message) {
|
|
889
|
+
logStderr(message);
|
|
890
|
+
}
|
|
888
891
|
function setDiagOutput(output) {
|
|
889
892
|
diagOutput = output;
|
|
890
893
|
}
|
|
@@ -18769,6 +18772,12 @@ function loadConfig() {
|
|
|
18769
18772
|
if (config.proOnUltracode !== undefined) {
|
|
18770
18773
|
merged.proOnUltracode = config.proOnUltracode;
|
|
18771
18774
|
}
|
|
18775
|
+
if (config.recovery !== undefined) {
|
|
18776
|
+
merged.recovery = config.recovery;
|
|
18777
|
+
}
|
|
18778
|
+
if (config.recoveryUi !== undefined) {
|
|
18779
|
+
merged.recoveryUi = config.recoveryUi;
|
|
18780
|
+
}
|
|
18772
18781
|
return merged;
|
|
18773
18782
|
} catch (error) {
|
|
18774
18783
|
console.error(`Warning: Failed to load config, using defaults: ${error}`);
|
|
@@ -18817,6 +18826,26 @@ function readProOnUltracode(paths = defaultScopedConfigPaths) {
|
|
|
18817
18826
|
}
|
|
18818
18827
|
return;
|
|
18819
18828
|
}
|
|
18829
|
+
function readNestedEnabled(key, paths) {
|
|
18830
|
+
for (const pathFn of [paths.project, paths.global]) {
|
|
18831
|
+
try {
|
|
18832
|
+
const path = pathFn();
|
|
18833
|
+
if (!existsSync6(path))
|
|
18834
|
+
continue;
|
|
18835
|
+
const parsed = JSON.parse(readFileSync6(path, "utf-8"));
|
|
18836
|
+
const value = parsed?.[key]?.enabled;
|
|
18837
|
+
if (typeof value === "boolean")
|
|
18838
|
+
return value;
|
|
18839
|
+
} catch {}
|
|
18840
|
+
}
|
|
18841
|
+
return;
|
|
18842
|
+
}
|
|
18843
|
+
function readRecoveryEnabled(paths = defaultScopedConfigPaths) {
|
|
18844
|
+
return readNestedEnabled("recovery", paths);
|
|
18845
|
+
}
|
|
18846
|
+
function readRecoveryUi(paths = defaultScopedConfigPaths) {
|
|
18847
|
+
return readNestedEnabled("recoveryUi", paths);
|
|
18848
|
+
}
|
|
18820
18849
|
function isProjectDirectory() {
|
|
18821
18850
|
const cwd = process.cwd();
|
|
18822
18851
|
return [".git", "package.json", "Cargo.toml", "go.mod", "pyproject.toml", ".claudish.json"].some((f) => existsSync6(join8(cwd, f)));
|
|
@@ -24926,6 +24955,749 @@ Signed in to Grok. Try it with: claudish --model gk@grok-4.6 "hello"
|
|
|
24926
24955
|
};
|
|
24927
24956
|
});
|
|
24928
24957
|
|
|
24958
|
+
// src/handlers/shared/connection-error.ts
|
|
24959
|
+
function markOwnTimeout(error) {
|
|
24960
|
+
if (error && typeof error === "object") {
|
|
24961
|
+
try {
|
|
24962
|
+
Object.defineProperty(error, OWN_TIMEOUT_FLAG, {
|
|
24963
|
+
value: true,
|
|
24964
|
+
enumerable: false,
|
|
24965
|
+
configurable: true,
|
|
24966
|
+
writable: true
|
|
24967
|
+
});
|
|
24968
|
+
} catch {}
|
|
24969
|
+
}
|
|
24970
|
+
return error;
|
|
24971
|
+
}
|
|
24972
|
+
function isOwnTimeout(error) {
|
|
24973
|
+
return Boolean(error?.[OWN_TIMEOUT_FLAG]);
|
|
24974
|
+
}
|
|
24975
|
+
function findConnectionCode(error) {
|
|
24976
|
+
let e = error;
|
|
24977
|
+
const seen = new Set;
|
|
24978
|
+
for (let depth = 0;e && typeof e === "object" && depth < 8 && !seen.has(e); depth++) {
|
|
24979
|
+
seen.add(e);
|
|
24980
|
+
if (typeof e.code === "string" && e.code in CODE_KIND)
|
|
24981
|
+
return e.code;
|
|
24982
|
+
if (typeof e.name === "string" && e.name in NAME_KIND && isOwnTimeout(e))
|
|
24983
|
+
return e.name;
|
|
24984
|
+
e = e.cause;
|
|
24985
|
+
}
|
|
24986
|
+
const msg = String(error?.message ?? error ?? "");
|
|
24987
|
+
if (/getaddrinfo|ENOTFOUND|EAI_AGAIN|nodename nor servname/i.test(msg))
|
|
24988
|
+
return "ENOTFOUND";
|
|
24989
|
+
if (BUN_CONNECT_MESSAGE.test(msg))
|
|
24990
|
+
return "ConnectionRefused";
|
|
24991
|
+
return null;
|
|
24992
|
+
}
|
|
24993
|
+
function isLoopback(endpoint) {
|
|
24994
|
+
try {
|
|
24995
|
+
const { hostname } = new URL(endpoint);
|
|
24996
|
+
return /^(localhost|127\.\d+\.\d+\.\d+|0\.0\.0\.0|\[?::1\]?)$/i.test(hostname);
|
|
24997
|
+
} catch {
|
|
24998
|
+
return false;
|
|
24999
|
+
}
|
|
25000
|
+
}
|
|
25001
|
+
function classifyConnectionError(error) {
|
|
25002
|
+
const code = findConnectionCode(error);
|
|
25003
|
+
if (!code)
|
|
25004
|
+
return null;
|
|
25005
|
+
return { kind: CODE_KIND[code] ?? NAME_KIND[code] ?? "unreachable", code };
|
|
25006
|
+
}
|
|
25007
|
+
function hostOf(endpoint) {
|
|
25008
|
+
try {
|
|
25009
|
+
return new URL(endpoint).host || endpoint;
|
|
25010
|
+
} catch {
|
|
25011
|
+
return endpoint;
|
|
25012
|
+
}
|
|
25013
|
+
}
|
|
25014
|
+
function buildConnectionErrorMessage(kind, displayName, endpoint) {
|
|
25015
|
+
const host = hostOf(endpoint);
|
|
25016
|
+
switch (kind) {
|
|
25017
|
+
case "dns":
|
|
25018
|
+
return `Cannot resolve ${host} for ${displayName}. This is a DNS/network problem on your machine \u2014 check your internet connection, VPN, or DNS resolver (e.g. Tailscale MagicDNS) \u2014 not ${displayName}.`;
|
|
25019
|
+
case "refused":
|
|
25020
|
+
if (isLoopback(endpoint)) {
|
|
25021
|
+
return `Cannot connect to ${displayName} at ${endpoint}. Make sure the server is running.`;
|
|
25022
|
+
}
|
|
25023
|
+
return `Cannot reach ${host} for ${displayName}. This is a network problem on your machine \u2014 check your internet connection, VPN, or DNS resolver (e.g. Tailscale MagicDNS) \u2014 not ${displayName}.`;
|
|
25024
|
+
case "unreachable":
|
|
25025
|
+
return `Cannot reach ${displayName} at ${endpoint}. Check your network connection.`;
|
|
25026
|
+
}
|
|
25027
|
+
}
|
|
25028
|
+
function formatHoldDuration(ms) {
|
|
25029
|
+
const total = Math.max(0, Math.round(ms / 1000));
|
|
25030
|
+
const minutes = Math.floor(total / 60);
|
|
25031
|
+
const seconds = total % 60;
|
|
25032
|
+
return minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`;
|
|
25033
|
+
}
|
|
25034
|
+
function buildRecoveryHoldMessage(reason, attempts, elapsedMs) {
|
|
25035
|
+
return `${reason} claudish retried ${attempts}\xD7 over ${formatHoldDuration(elapsedMs)} ` + "without reaching it \u2014 still trying, watch the recovery pane.";
|
|
25036
|
+
}
|
|
25037
|
+
var CODE_KIND, BUN_CONNECT_MESSAGE, NAME_KIND, OWN_TIMEOUT_FLAG = "claudishOwnTimeout";
|
|
25038
|
+
var init_connection_error = __esm(() => {
|
|
25039
|
+
CODE_KIND = {
|
|
25040
|
+
ENOTFOUND: "dns",
|
|
25041
|
+
EAI_AGAIN: "dns",
|
|
25042
|
+
ECONNREFUSED: "refused",
|
|
25043
|
+
ETIMEDOUT: "unreachable",
|
|
25044
|
+
ECONNRESET: "unreachable",
|
|
25045
|
+
ENETUNREACH: "unreachable",
|
|
25046
|
+
EHOSTUNREACH: "unreachable",
|
|
25047
|
+
EPIPE: "unreachable",
|
|
25048
|
+
UND_ERR_CONNECT_TIMEOUT: "unreachable",
|
|
25049
|
+
UND_ERR_SOCKET: "unreachable",
|
|
25050
|
+
ConnectionRefused: "refused",
|
|
25051
|
+
ConnectionClosed: "unreachable",
|
|
25052
|
+
FailedToOpenSocket: "unreachable",
|
|
25053
|
+
ERR_SOCKET_CLOSED: "unreachable"
|
|
25054
|
+
};
|
|
25055
|
+
BUN_CONNECT_MESSAGE = /unable to connect\. is the computer able to access the url\?/i;
|
|
25056
|
+
NAME_KIND = {
|
|
25057
|
+
TimeoutError: "unreachable"
|
|
25058
|
+
};
|
|
25059
|
+
});
|
|
25060
|
+
|
|
25061
|
+
// src/recovery/clock.ts
|
|
25062
|
+
function recoveryClock() {
|
|
25063
|
+
return active;
|
|
25064
|
+
}
|
|
25065
|
+
var REAL_CLOCK, active;
|
|
25066
|
+
var init_clock = __esm(() => {
|
|
25067
|
+
REAL_CLOCK = {
|
|
25068
|
+
now: () => performance.now(),
|
|
25069
|
+
setTimeout: (fn, ms) => setTimeout(fn, ms),
|
|
25070
|
+
clearTimeout: (handle) => clearTimeout(handle),
|
|
25071
|
+
unref: (handle) => {
|
|
25072
|
+
handle?.unref?.();
|
|
25073
|
+
}
|
|
25074
|
+
};
|
|
25075
|
+
active = REAL_CLOCK;
|
|
25076
|
+
});
|
|
25077
|
+
|
|
25078
|
+
// src/recovery/coordinator.ts
|
|
25079
|
+
import { randomUUID } from "crypto";
|
|
25080
|
+
function hostOf2(endpoint) {
|
|
25081
|
+
try {
|
|
25082
|
+
return new URL(endpoint).host || endpoint;
|
|
25083
|
+
} catch {
|
|
25084
|
+
return endpoint;
|
|
25085
|
+
}
|
|
25086
|
+
}
|
|
25087
|
+
function episodeCount() {
|
|
25088
|
+
return episodes.size;
|
|
25089
|
+
}
|
|
25090
|
+
function registerRecoveryUi(hooks) {
|
|
25091
|
+
uiHooks = hooks;
|
|
25092
|
+
}
|
|
25093
|
+
function uiLeaseValid(episodeId) {
|
|
25094
|
+
if (!uiHooks)
|
|
25095
|
+
return false;
|
|
25096
|
+
try {
|
|
25097
|
+
return uiHooks.leaseValid(episodeId);
|
|
25098
|
+
} catch {
|
|
25099
|
+
return false;
|
|
25100
|
+
}
|
|
25101
|
+
}
|
|
25102
|
+
function markPaneRequested(episodeId) {
|
|
25103
|
+
const ep = byId.get(episodeId);
|
|
25104
|
+
if (!ep || ep.paneRequestedAtPerf !== null)
|
|
25105
|
+
return;
|
|
25106
|
+
ep.paneRequestedAtPerf = recoveryClock().now();
|
|
25107
|
+
}
|
|
25108
|
+
function liveEpisodes() {
|
|
25109
|
+
const live = [...episodes.values()].filter((e) => e.state === "attempting" || e.state === "waiting" || e.state === "handoff");
|
|
25110
|
+
live.sort((a, b) => b.waiters.size - a.waiters.size || a.startedAtMs - b.startedAtMs);
|
|
25111
|
+
return live;
|
|
25112
|
+
}
|
|
25113
|
+
function renderableEpisodeFrame() {
|
|
25114
|
+
const live = liveEpisodes();
|
|
25115
|
+
const ep = live[0];
|
|
25116
|
+
if (!ep)
|
|
25117
|
+
return null;
|
|
25118
|
+
const clock = recoveryClock();
|
|
25119
|
+
const perfNow = clock.now();
|
|
25120
|
+
const epochNow = Date.now();
|
|
25121
|
+
const nextAttemptAtMs = ep.nextAttemptAtPerf === null ? null : epochNow + (ep.nextAttemptAtPerf - perfNow);
|
|
25122
|
+
return {
|
|
25123
|
+
type: "episode",
|
|
25124
|
+
episodeId: ep.episodeId,
|
|
25125
|
+
state: ep.state,
|
|
25126
|
+
tier: ep.maxTier,
|
|
25127
|
+
providerDisplayName: ep.providerDisplayName,
|
|
25128
|
+
host: ep.host,
|
|
25129
|
+
endpoint: ep.endpoint,
|
|
25130
|
+
kind: ep.kind,
|
|
25131
|
+
code: ep.code,
|
|
25132
|
+
loopback: ep.loopback,
|
|
25133
|
+
reason: ep.reason,
|
|
25134
|
+
attempts: ep.attempts,
|
|
25135
|
+
clientRetries: ep.clientRetries,
|
|
25136
|
+
startedAtMs: ep.startedAtMs,
|
|
25137
|
+
nextAttemptAtMs,
|
|
25138
|
+
lastOutcome: ep.lastOutcome,
|
|
25139
|
+
waiters: ep.waiters.size,
|
|
25140
|
+
otherEpisodes: live.length - 1
|
|
25141
|
+
};
|
|
25142
|
+
}
|
|
25143
|
+
function joinEpisode(seed) {
|
|
25144
|
+
const clock = recoveryClock();
|
|
25145
|
+
const host = hostOf2(seed.endpoint);
|
|
25146
|
+
const key = `${seed.providerName}|${host}`;
|
|
25147
|
+
let ep = episodes.get(key);
|
|
25148
|
+
if (ep && (ep.state === "recovered" || ep.state === "abandoned")) {
|
|
25149
|
+
episodes.delete(key);
|
|
25150
|
+
ep = undefined;
|
|
25151
|
+
}
|
|
25152
|
+
if (ep) {
|
|
25153
|
+
if (ep.state === "handoff") {
|
|
25154
|
+
if (ep.graceTimer !== null) {
|
|
25155
|
+
clock.clearTimeout(ep.graceTimer);
|
|
25156
|
+
ep.graceTimer = null;
|
|
25157
|
+
}
|
|
25158
|
+
ep.clientRetries++;
|
|
25159
|
+
ep.state = "attempting";
|
|
25160
|
+
ep.maxTier = 2;
|
|
25161
|
+
logRecovery(`[Recovery] ${ep.providerDisplayName} rejoined episode ${ep.episodeId} ` + `(client retry ${ep.clientRetries}, ladder ${ep.ladderIndex}, attempts ${ep.attempts})`);
|
|
25162
|
+
}
|
|
25163
|
+
} else {
|
|
25164
|
+
const nowPerf = clock.now();
|
|
25165
|
+
ep = {
|
|
25166
|
+
episodeId: randomUUID(),
|
|
25167
|
+
key,
|
|
25168
|
+
providerDisplayName: seed.providerDisplayName,
|
|
25169
|
+
endpoint: seed.endpoint,
|
|
25170
|
+
host,
|
|
25171
|
+
kind: seed.kind,
|
|
25172
|
+
loopback: isLoopback(seed.endpoint),
|
|
25173
|
+
code: seed.code,
|
|
25174
|
+
reason: seed.reason,
|
|
25175
|
+
state: "attempting",
|
|
25176
|
+
startedAtPerf: nowPerf,
|
|
25177
|
+
startedAtMs: Date.now(),
|
|
25178
|
+
paneRequestedAtPerf: null,
|
|
25179
|
+
ladderIndex: 0,
|
|
25180
|
+
attempts: 0,
|
|
25181
|
+
clientRetries: 0,
|
|
25182
|
+
round: 0,
|
|
25183
|
+
advancedForRound: 0,
|
|
25184
|
+
recoveredCount: 0,
|
|
25185
|
+
lastOutcome: seed.code ?? "unknown",
|
|
25186
|
+
maxTier: 1,
|
|
25187
|
+
waiters: new Set,
|
|
25188
|
+
attemptTimer: null,
|
|
25189
|
+
graceTimer: null,
|
|
25190
|
+
nextAttemptAtPerf: null,
|
|
25191
|
+
anyLeftByDeadline: false,
|
|
25192
|
+
gaveUp: false
|
|
25193
|
+
};
|
|
25194
|
+
episodes.set(key, ep);
|
|
25195
|
+
byId.set(ep.episodeId, ep);
|
|
25196
|
+
logRecovery(`[Recovery] episode ${ep.episodeId} opened for ${ep.providerDisplayName} at ${ep.endpoint} ` + `(${ep.kind}/${ep.code ?? "?"})`);
|
|
25197
|
+
if (uiHooks) {
|
|
25198
|
+
try {
|
|
25199
|
+
uiHooks.onEpisodeOpened(ep.episodeId);
|
|
25200
|
+
} catch (err) {
|
|
25201
|
+
log(`[Recovery] UI could not open for episode ${ep.episodeId}: ${String(err)}`);
|
|
25202
|
+
}
|
|
25203
|
+
}
|
|
25204
|
+
}
|
|
25205
|
+
const episode = ep;
|
|
25206
|
+
const waiter = {
|
|
25207
|
+
deadlineAt: seed.deadlineAt,
|
|
25208
|
+
wake: null,
|
|
25209
|
+
fail: null,
|
|
25210
|
+
detach: null
|
|
25211
|
+
};
|
|
25212
|
+
episode.waiters.add(waiter);
|
|
25213
|
+
let left = false;
|
|
25214
|
+
return {
|
|
25215
|
+
episodeId: episode.episodeId,
|
|
25216
|
+
key: episode.key,
|
|
25217
|
+
attempts: () => episode.attempts,
|
|
25218
|
+
recoveryMs: () => Math.round(recoveryClock().now() - episode.startedAtPerf),
|
|
25219
|
+
clientRetries: () => episode.clientRetries,
|
|
25220
|
+
ladderIndex: () => episode.ladderIndex,
|
|
25221
|
+
uiLeaseValid: () => uiLeaseValid(episode.episodeId),
|
|
25222
|
+
waitForNextAttempt(signal, delayMs) {
|
|
25223
|
+
if (episode.gaveUp || episode.state === "abandoned") {
|
|
25224
|
+
return Promise.resolve({ kind: "gave_up" });
|
|
25225
|
+
}
|
|
25226
|
+
if (signal.aborted)
|
|
25227
|
+
return Promise.reject(signal.reason ?? new Error("aborted"));
|
|
25228
|
+
return new Promise((resolve, reject) => {
|
|
25229
|
+
const onAbort = () => {
|
|
25230
|
+
waiter.wake = null;
|
|
25231
|
+
waiter.fail = null;
|
|
25232
|
+
waiter.detach = null;
|
|
25233
|
+
signal.removeEventListener("abort", onAbort);
|
|
25234
|
+
reject(signal.reason ?? new Error("aborted"));
|
|
25235
|
+
};
|
|
25236
|
+
waiter.detach = () => signal.removeEventListener("abort", onAbort);
|
|
25237
|
+
waiter.wake = (outcome) => {
|
|
25238
|
+
waiter.detach?.();
|
|
25239
|
+
waiter.detach = null;
|
|
25240
|
+
waiter.wake = null;
|
|
25241
|
+
waiter.fail = null;
|
|
25242
|
+
resolve(outcome);
|
|
25243
|
+
};
|
|
25244
|
+
waiter.fail = (err) => {
|
|
25245
|
+
waiter.detach?.();
|
|
25246
|
+
waiter.detach = null;
|
|
25247
|
+
waiter.wake = null;
|
|
25248
|
+
waiter.fail = null;
|
|
25249
|
+
reject(err);
|
|
25250
|
+
};
|
|
25251
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
25252
|
+
armAttemptTimer(episode, delayMs);
|
|
25253
|
+
});
|
|
25254
|
+
},
|
|
25255
|
+
recordAttemptResult(outcome, ok) {
|
|
25256
|
+
episode.attempts++;
|
|
25257
|
+
episode.lastOutcome = outcome;
|
|
25258
|
+
if (ok) {
|
|
25259
|
+
episode.recoveredCount++;
|
|
25260
|
+
episode.ladderIndex = 0;
|
|
25261
|
+
episode.advancedForRound = episode.round;
|
|
25262
|
+
episode.state = "attempting";
|
|
25263
|
+
clearAttemptTimer(episode);
|
|
25264
|
+
for (const w of [...episode.waiters])
|
|
25265
|
+
w.wake?.({ kind: "attempt" });
|
|
25266
|
+
return;
|
|
25267
|
+
}
|
|
25268
|
+
if (episode.advancedForRound !== episode.round) {
|
|
25269
|
+
episode.advancedForRound = episode.round;
|
|
25270
|
+
episode.ladderIndex++;
|
|
25271
|
+
}
|
|
25272
|
+
logRecovery(`[Recovery] ${episode.providerDisplayName} attempt ${episode.attempts} failed: ${outcome} ` + `(episode ${episode.episodeId}, ladder ${episode.ladderIndex})`);
|
|
25273
|
+
},
|
|
25274
|
+
handoff() {
|
|
25275
|
+
episode.anyLeftByDeadline = true;
|
|
25276
|
+
},
|
|
25277
|
+
close(outcome) {
|
|
25278
|
+
closeEpisode(episode, outcome);
|
|
25279
|
+
},
|
|
25280
|
+
leave() {
|
|
25281
|
+
if (left)
|
|
25282
|
+
return;
|
|
25283
|
+
left = true;
|
|
25284
|
+
waiter.detach?.();
|
|
25285
|
+
waiter.detach = null;
|
|
25286
|
+
waiter.wake = null;
|
|
25287
|
+
waiter.fail = null;
|
|
25288
|
+
episode.waiters.delete(waiter);
|
|
25289
|
+
if (episode.waiters.size > 0)
|
|
25290
|
+
return;
|
|
25291
|
+
clearAttemptTimer(episode);
|
|
25292
|
+
if (episode.state === "recovered" || episode.state === "abandoned") {
|
|
25293
|
+
byId.delete(episode.episodeId);
|
|
25294
|
+
return;
|
|
25295
|
+
}
|
|
25296
|
+
if (episode.recoveredCount > 0 && !episode.anyLeftByDeadline) {
|
|
25297
|
+
closeEpisode(episode, "recovered");
|
|
25298
|
+
} else if (episode.anyLeftByDeadline) {
|
|
25299
|
+
episode.state = "handoff";
|
|
25300
|
+
const c = recoveryClock();
|
|
25301
|
+
const timer = c.setTimeout(() => {
|
|
25302
|
+
episode.graceTimer = null;
|
|
25303
|
+
closeEpisode(episode, "grace_expired");
|
|
25304
|
+
}, EPISODE_GRACE_MS);
|
|
25305
|
+
c.unref?.(timer);
|
|
25306
|
+
episode.graceTimer = timer;
|
|
25307
|
+
logRecovery(`[Recovery] episode ${episode.episodeId} handed off after ${Math.round(c.now() - episode.startedAtPerf)}ms, ${episode.attempts} attempts \u2014 holding ${EPISODE_GRACE_MS / 1000}s for a client retry`);
|
|
25308
|
+
} else {
|
|
25309
|
+
closeEpisode(episode, "client_gone");
|
|
25310
|
+
}
|
|
25311
|
+
}
|
|
25312
|
+
};
|
|
25313
|
+
}
|
|
25314
|
+
function armAttemptTimer(episode, delay) {
|
|
25315
|
+
if (episode.attemptTimer !== null)
|
|
25316
|
+
return;
|
|
25317
|
+
const clock = recoveryClock();
|
|
25318
|
+
episode.state = "waiting";
|
|
25319
|
+
episode.nextAttemptAtPerf = clock.now() + delay;
|
|
25320
|
+
episode.attemptTimer = clock.setTimeout(() => {
|
|
25321
|
+
episode.attemptTimer = null;
|
|
25322
|
+
episode.nextAttemptAtPerf = null;
|
|
25323
|
+
episode.state = "attempting";
|
|
25324
|
+
episode.round++;
|
|
25325
|
+
for (const w of [...episode.waiters])
|
|
25326
|
+
w.wake?.({ kind: "attempt" });
|
|
25327
|
+
}, delay);
|
|
25328
|
+
logRecovery(`[Recovery] ${episode.providerDisplayName} waiting ${delay / 1000}s before attempt ` + `${episode.attempts + 1} (episode ${episode.episodeId}, ${episode.waiters.size} waiting)`);
|
|
25329
|
+
}
|
|
25330
|
+
function clearAttemptTimer(episode) {
|
|
25331
|
+
if (episode.attemptTimer === null)
|
|
25332
|
+
return;
|
|
25333
|
+
recoveryClock().clearTimeout(episode.attemptTimer);
|
|
25334
|
+
episode.attemptTimer = null;
|
|
25335
|
+
episode.nextAttemptAtPerf = null;
|
|
25336
|
+
}
|
|
25337
|
+
function closeEpisode(episode, outcome) {
|
|
25338
|
+
if (episode.state === "recovered" || episode.state === "abandoned")
|
|
25339
|
+
return;
|
|
25340
|
+
const clock = recoveryClock();
|
|
25341
|
+
clearAttemptTimer(episode);
|
|
25342
|
+
if (episode.graceTimer !== null) {
|
|
25343
|
+
clock.clearTimeout(episode.graceTimer);
|
|
25344
|
+
episode.graceTimer = null;
|
|
25345
|
+
}
|
|
25346
|
+
episode.state = outcome === "recovered" ? "recovered" : "abandoned";
|
|
25347
|
+
if (episodes.get(episode.key) === episode)
|
|
25348
|
+
episodes.delete(episode.key);
|
|
25349
|
+
byId.delete(episode.episodeId);
|
|
25350
|
+
if (episode.state === "abandoned") {
|
|
25351
|
+
for (const w of [...episode.waiters])
|
|
25352
|
+
w.wake?.({ kind: "gave_up" });
|
|
25353
|
+
}
|
|
25354
|
+
logRecovery(`[Recovery] episode ${episode.episodeId} closed: ${outcome} after ` + `${Math.round(clock.now() - episode.startedAtPerf)}ms and ${episode.attempts} attempts`);
|
|
25355
|
+
if (uiHooks) {
|
|
25356
|
+
try {
|
|
25357
|
+
uiHooks.onEpisodeClosed(episode.episodeId, outcome, episode.attempts);
|
|
25358
|
+
} catch {}
|
|
25359
|
+
}
|
|
25360
|
+
}
|
|
25361
|
+
function noteTargetReachable(providerName, endpoint) {
|
|
25362
|
+
const exact = `${providerName}|${hostOf2(endpoint)}`;
|
|
25363
|
+
const prefix = `${providerName}|`;
|
|
25364
|
+
for (const [key, ep] of [...episodes]) {
|
|
25365
|
+
if (key !== exact && !key.startsWith(prefix))
|
|
25366
|
+
continue;
|
|
25367
|
+
if (ep.state !== "handoff")
|
|
25368
|
+
continue;
|
|
25369
|
+
logRecovery(`[Recovery] ${ep.providerDisplayName} answered on the client's own retry \u2014 ` + `closing episode ${ep.episodeId} instead of waiting out its grace`);
|
|
25370
|
+
closeEpisode(ep, "recovered");
|
|
25371
|
+
}
|
|
25372
|
+
}
|
|
25373
|
+
function episodeIsLive(episodeId) {
|
|
25374
|
+
const ep = byId.get(episodeId);
|
|
25375
|
+
if (!ep)
|
|
25376
|
+
return false;
|
|
25377
|
+
return ep.state === "attempting" || ep.state === "waiting" || ep.state === "handoff";
|
|
25378
|
+
}
|
|
25379
|
+
function closeAllEpisodes(outcome = "shutdown") {
|
|
25380
|
+
for (const ep of [...episodes.values()])
|
|
25381
|
+
closeEpisode(ep, outcome);
|
|
25382
|
+
for (const ep of [...byId.values()])
|
|
25383
|
+
closeEpisode(ep, outcome);
|
|
25384
|
+
episodes.clear();
|
|
25385
|
+
byId.clear();
|
|
25386
|
+
}
|
|
25387
|
+
var EPISODE_GRACE_MS = 120000, episodes, byId, uiHooks = null;
|
|
25388
|
+
var init_coordinator = __esm(() => {
|
|
25389
|
+
init_connection_error();
|
|
25390
|
+
init_logger();
|
|
25391
|
+
init_clock();
|
|
25392
|
+
episodes = new Map;
|
|
25393
|
+
byId = new Map;
|
|
25394
|
+
});
|
|
25395
|
+
|
|
25396
|
+
// src/config.ts
|
|
25397
|
+
var DEFAULT_PORT_RANGE, ENV;
|
|
25398
|
+
var init_config = __esm(() => {
|
|
25399
|
+
DEFAULT_PORT_RANGE = { start: 3000, end: 9000 };
|
|
25400
|
+
ENV = {
|
|
25401
|
+
OPENROUTER_API_KEY: "OPENROUTER_API_KEY",
|
|
25402
|
+
CLAUDISH_MODEL: "CLAUDISH_MODEL",
|
|
25403
|
+
CLAUDISH_PORT: "CLAUDISH_PORT",
|
|
25404
|
+
CLAUDISH_ACTIVE_MODEL_NAME: "CLAUDISH_ACTIVE_MODEL_NAME",
|
|
25405
|
+
CLAUDISH_TOKEN_FILE: "CLAUDISH_TOKEN_FILE",
|
|
25406
|
+
CLAUDISH_PROVIDER_NAME: "CLAUDISH_PROVIDER_NAME",
|
|
25407
|
+
ANTHROPIC_MODEL: "ANTHROPIC_MODEL",
|
|
25408
|
+
ANTHROPIC_SMALL_FAST_MODEL: "ANTHROPIC_SMALL_FAST_MODEL",
|
|
25409
|
+
CLAUDISH_MODEL_OPUS: "CLAUDISH_MODEL_OPUS",
|
|
25410
|
+
CLAUDISH_MODEL_SONNET: "CLAUDISH_MODEL_SONNET",
|
|
25411
|
+
CLAUDISH_MODEL_HAIKU: "CLAUDISH_MODEL_HAIKU",
|
|
25412
|
+
CLAUDISH_MODEL_SUBAGENT: "CLAUDISH_MODEL_SUBAGENT",
|
|
25413
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "ANTHROPIC_DEFAULT_OPUS_MODEL",
|
|
25414
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "ANTHROPIC_DEFAULT_SONNET_MODEL",
|
|
25415
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "ANTHROPIC_DEFAULT_HAIKU_MODEL",
|
|
25416
|
+
CLAUDE_CODE_SUBAGENT_MODEL: "CLAUDE_CODE_SUBAGENT_MODEL",
|
|
25417
|
+
CLAUDE_CODE_AUTO_COMPACT_WINDOW: "CLAUDE_CODE_AUTO_COMPACT_WINDOW",
|
|
25418
|
+
CLAUDE_CODE_MAX_CONTEXT_TOKENS: "CLAUDE_CODE_MAX_CONTEXT_TOKENS",
|
|
25419
|
+
OLLAMA_BASE_URL: "OLLAMA_BASE_URL",
|
|
25420
|
+
OLLAMA_HOST: "OLLAMA_HOST",
|
|
25421
|
+
LMSTUDIO_BASE_URL: "LMSTUDIO_BASE_URL",
|
|
25422
|
+
VLLM_BASE_URL: "VLLM_BASE_URL",
|
|
25423
|
+
GEMINI_API_KEY: "GEMINI_API_KEY",
|
|
25424
|
+
GEMINI_BASE_URL: "GEMINI_BASE_URL",
|
|
25425
|
+
OPENAI_API_KEY: "OPENAI_API_KEY",
|
|
25426
|
+
OPENAI_BASE_URL: "OPENAI_BASE_URL",
|
|
25427
|
+
CLAUDISH_SUMMARIZE_TOOLS: "CLAUDISH_SUMMARIZE_TOOLS",
|
|
25428
|
+
CLAUDISH_DIAG_MODE: "CLAUDISH_DIAG_MODE",
|
|
25429
|
+
CLAUDISH_DEBUG: "CLAUDISH_DEBUG",
|
|
25430
|
+
CLAUDISH_ANTHROPIC_API_BILLING: "CLAUDISH_ANTHROPIC_API_BILLING",
|
|
25431
|
+
CLAUDISH_CLASSIFIER_PROVIDER: "CLAUDISH_CLASSIFIER_PROVIDER",
|
|
25432
|
+
CLAUDISH_CLASSIFIER_MODEL: "CLAUDISH_CLASSIFIER_MODEL",
|
|
25433
|
+
CLAUDISH_RECOVERY: "CLAUDISH_RECOVERY",
|
|
25434
|
+
CLAUDISH_RECOVERY_UI: "CLAUDISH_RECOVERY_UI",
|
|
25435
|
+
API_TIMEOUT_MS: "API_TIMEOUT_MS"
|
|
25436
|
+
};
|
|
25437
|
+
});
|
|
25438
|
+
|
|
25439
|
+
// src/recovery/settings.ts
|
|
25440
|
+
function setRecoveryFlagOverrides(overrides) {
|
|
25441
|
+
flagOverrides = { ...flagOverrides, ...overrides };
|
|
25442
|
+
}
|
|
25443
|
+
function parseEnvBool(raw) {
|
|
25444
|
+
const v = raw?.trim().toLowerCase();
|
|
25445
|
+
if (v === "1" || v === "true")
|
|
25446
|
+
return true;
|
|
25447
|
+
if (v === "0" || v === "false")
|
|
25448
|
+
return false;
|
|
25449
|
+
return;
|
|
25450
|
+
}
|
|
25451
|
+
function resolveRecoveryEnabled() {
|
|
25452
|
+
if (flagOverrides.recovery !== undefined)
|
|
25453
|
+
return flagOverrides.recovery;
|
|
25454
|
+
const env = parseEnvBool(process.env[ENV.CLAUDISH_RECOVERY]);
|
|
25455
|
+
if (env !== undefined)
|
|
25456
|
+
return env;
|
|
25457
|
+
const scoped = readRecoveryEnabled();
|
|
25458
|
+
if (scoped !== undefined)
|
|
25459
|
+
return scoped;
|
|
25460
|
+
return true;
|
|
25461
|
+
}
|
|
25462
|
+
function resolveRecoveryUi() {
|
|
25463
|
+
if (flagOverrides.recoveryUi !== undefined)
|
|
25464
|
+
return flagOverrides.recoveryUi;
|
|
25465
|
+
const env = parseEnvBool(process.env[ENV.CLAUDISH_RECOVERY_UI]);
|
|
25466
|
+
if (env !== undefined)
|
|
25467
|
+
return env;
|
|
25468
|
+
const scoped = readRecoveryUi();
|
|
25469
|
+
if (scoped !== undefined)
|
|
25470
|
+
return scoped;
|
|
25471
|
+
return true;
|
|
25472
|
+
}
|
|
25473
|
+
function recoverySurfaceAllowed() {
|
|
25474
|
+
return resolveRecoveryEnabled() && resolveRecoveryUi();
|
|
25475
|
+
}
|
|
25476
|
+
function resolveTier1DeadlineMs(env = process.env) {
|
|
25477
|
+
const raw = env[ENV.API_TIMEOUT_MS];
|
|
25478
|
+
const parsed = raw === undefined ? Number.NaN : Number(raw);
|
|
25479
|
+
const clientCeiling = Number.isFinite(parsed) && parsed > 0 ? Math.min(parsed, CLIENT_CEILING_CLAMP_MS) : CLIENT_CEILING_CLAMP_MS;
|
|
25480
|
+
const derived = clientCeiling - DEADLINE_MARGIN_MS;
|
|
25481
|
+
return Math.max(TIER1_DEADLINE_FLOOR_MS, derived);
|
|
25482
|
+
}
|
|
25483
|
+
function logDeadlineIfShortened(deadlineMs) {
|
|
25484
|
+
if (deadlineNoticeLogged)
|
|
25485
|
+
return;
|
|
25486
|
+
if (deadlineMs >= DEFAULT_TIER1_DEADLINE_MS)
|
|
25487
|
+
return;
|
|
25488
|
+
deadlineNoticeLogged = true;
|
|
25489
|
+
const raw = process.env[ENV.API_TIMEOUT_MS];
|
|
25490
|
+
log(`[Recovery] tier-1 hold shortened to ${Math.round(deadlineMs / 1000)}s ` + `(API_TIMEOUT_MS=${raw ?? "unset"}; default would be ${Math.round(DEFAULT_TIER1_DEADLINE_MS / 1000)}s)`);
|
|
25491
|
+
}
|
|
25492
|
+
function retryWatchdogEnv(opts) {
|
|
25493
|
+
if (!opts.paneEligible)
|
|
25494
|
+
return {};
|
|
25495
|
+
return recoverySurfaceAllowed() ? { CLAUDE_CODE_RETRY_WATCHDOG: "1" } : {};
|
|
25496
|
+
}
|
|
25497
|
+
function applyRetryWatchdog(env, opts) {
|
|
25498
|
+
const grant = retryWatchdogEnv(opts);
|
|
25499
|
+
if (grant.CLAUDE_CODE_RETRY_WATCHDOG) {
|
|
25500
|
+
const usersOwn = env.CLAUDE_CODE_RETRY_WATCHDOG !== undefined && env[RETRY_WATCHDOG_OWNER_ENV] !== "1";
|
|
25501
|
+
Object.assign(env, grant);
|
|
25502
|
+
if (!usersOwn)
|
|
25503
|
+
env[RETRY_WATCHDOG_OWNER_ENV] = "1";
|
|
25504
|
+
return;
|
|
25505
|
+
}
|
|
25506
|
+
if (env[RETRY_WATCHDOG_OWNER_ENV] === "1") {
|
|
25507
|
+
delete env.CLAUDE_CODE_RETRY_WATCHDOG;
|
|
25508
|
+
delete env[RETRY_WATCHDOG_OWNER_ENV];
|
|
25509
|
+
}
|
|
25510
|
+
}
|
|
25511
|
+
var flagOverrides, CLIENT_CEILING_CLAMP_MS = 300000, DEADLINE_MARGIN_MS = 30000, TIER1_DEADLINE_FLOOR_MS = 15000, DEFAULT_TIER1_DEADLINE_MS, deadlineNoticeLogged = false, RETRY_WATCHDOG_OWNER_ENV = "CLAUDISH_SET_RETRY_WATCHDOG";
|
|
25512
|
+
var init_settings = __esm(() => {
|
|
25513
|
+
init_config();
|
|
25514
|
+
init_logger();
|
|
25515
|
+
init_profile_config();
|
|
25516
|
+
flagOverrides = {};
|
|
25517
|
+
DEFAULT_TIER1_DEADLINE_MS = CLIENT_CEILING_CLAMP_MS - DEADLINE_MARGIN_MS;
|
|
25518
|
+
});
|
|
25519
|
+
|
|
25520
|
+
// src/handlers/shared/transient-retry.ts
|
|
25521
|
+
function connectionRetryDelayMs(ladderIndex) {
|
|
25522
|
+
const i = Math.max(0, Math.min(ladderIndex, CONNECTION_RETRY_DELAYS_MS.length - 1));
|
|
25523
|
+
return CONNECTION_RETRY_DELAYS_MS[i];
|
|
25524
|
+
}
|
|
25525
|
+
function refreshDeadlineAt(deadlineAt) {
|
|
25526
|
+
const reserve = Math.min(PER_ATTEMPT_CONNECT_CAP_MS, resolveTier1DeadlineMs() / 2);
|
|
25527
|
+
return deadlineAt - reserve;
|
|
25528
|
+
}
|
|
25529
|
+
function inboundStartedAtPerf(c) {
|
|
25530
|
+
const key = c.req?.raw;
|
|
25531
|
+
if (!key)
|
|
25532
|
+
return recoveryClock().now();
|
|
25533
|
+
const seen = INBOUND_START.get(key);
|
|
25534
|
+
if (seen !== undefined)
|
|
25535
|
+
return seen;
|
|
25536
|
+
const t = recoveryClock().now();
|
|
25537
|
+
INBOUND_START.set(key, t);
|
|
25538
|
+
return t;
|
|
25539
|
+
}
|
|
25540
|
+
function tier1DeadlineAt(c) {
|
|
25541
|
+
return inboundStartedAtPerf(c) + resolveTier1DeadlineMs();
|
|
25542
|
+
}
|
|
25543
|
+
function noteDeadlineForEpisode() {
|
|
25544
|
+
logDeadlineIfShortened(resolveTier1DeadlineMs());
|
|
25545
|
+
}
|
|
25546
|
+
function deadlineClamp(deadlineAt) {
|
|
25547
|
+
const clock = recoveryClock();
|
|
25548
|
+
const remaining = deadlineAt - clock.now();
|
|
25549
|
+
if (!(remaining > 0))
|
|
25550
|
+
return { disarm: () => {} };
|
|
25551
|
+
const ac = new AbortController;
|
|
25552
|
+
const timer = clock.setTimeout(() => ac.abort(markOwnTimeout(new DOMException("request deadline", "TimeoutError"))), remaining);
|
|
25553
|
+
return {
|
|
25554
|
+
signal: ac.signal,
|
|
25555
|
+
disarm: () => clock.clearTimeout(timer)
|
|
25556
|
+
};
|
|
25557
|
+
}
|
|
25558
|
+
function mergeSignalIntoInit(init, signal) {
|
|
25559
|
+
if (!signal)
|
|
25560
|
+
return init;
|
|
25561
|
+
const own = init.signal;
|
|
25562
|
+
if (!own || own.aborted)
|
|
25563
|
+
return { ...init, signal };
|
|
25564
|
+
return { ...init, signal: AbortSignal.any([own, signal]) };
|
|
25565
|
+
}
|
|
25566
|
+
function isClientAbort(signal, err) {
|
|
25567
|
+
if (signal.aborted)
|
|
25568
|
+
return true;
|
|
25569
|
+
return err?.name === "AbortError";
|
|
25570
|
+
}
|
|
25571
|
+
function untilAborted(p, signal) {
|
|
25572
|
+
if (signal.aborted) {
|
|
25573
|
+
p.catch(() => {});
|
|
25574
|
+
return Promise.reject(signal.reason ?? new DOMException("aborted", "AbortError"));
|
|
25575
|
+
}
|
|
25576
|
+
return new Promise((resolve, reject) => {
|
|
25577
|
+
const onAbort = () => {
|
|
25578
|
+
reject(signal.reason ?? new DOMException("aborted", "AbortError"));
|
|
25579
|
+
};
|
|
25580
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
25581
|
+
p.then((v) => {
|
|
25582
|
+
signal.removeEventListener("abort", onAbort);
|
|
25583
|
+
resolve(v);
|
|
25584
|
+
}, (e) => {
|
|
25585
|
+
signal.removeEventListener("abort", onAbort);
|
|
25586
|
+
reject(e);
|
|
25587
|
+
});
|
|
25588
|
+
});
|
|
25589
|
+
}
|
|
25590
|
+
async function withConnectionRetry(op, first, ctx) {
|
|
25591
|
+
const clock = recoveryClock();
|
|
25592
|
+
const enteredAtPerf = clock.now();
|
|
25593
|
+
let requestRetries = 0;
|
|
25594
|
+
const firstConn = classifyConnectionError(first);
|
|
25595
|
+
if (!firstConn) {
|
|
25596
|
+
return {
|
|
25597
|
+
kind: "threw",
|
|
25598
|
+
error: first,
|
|
25599
|
+
attempts: 0,
|
|
25600
|
+
recoveryMs: 0,
|
|
25601
|
+
requestRetries: 0,
|
|
25602
|
+
requestRecoveryMs: 0,
|
|
25603
|
+
clientRetry: 0,
|
|
25604
|
+
episodeId: ""
|
|
25605
|
+
};
|
|
25606
|
+
}
|
|
25607
|
+
let conn = firstConn;
|
|
25608
|
+
let error = first;
|
|
25609
|
+
let endpoint = ctx.resolveEndpoint(first);
|
|
25610
|
+
noteDeadlineForEpisode();
|
|
25611
|
+
const handle = joinEpisode({
|
|
25612
|
+
providerName: ctx.providerName,
|
|
25613
|
+
providerDisplayName: ctx.providerDisplayName,
|
|
25614
|
+
endpoint,
|
|
25615
|
+
kind: conn.kind,
|
|
25616
|
+
code: conn.code,
|
|
25617
|
+
reason: buildConnectionErrorMessage(conn.kind, ctx.providerDisplayName, endpoint),
|
|
25618
|
+
deadlineAt: ctx.deadlineAt
|
|
25619
|
+
});
|
|
25620
|
+
const tally = () => ({
|
|
25621
|
+
attempts: handle.attempts(),
|
|
25622
|
+
recoveryMs: handle.recoveryMs(),
|
|
25623
|
+
requestRetries,
|
|
25624
|
+
requestRecoveryMs: Math.round(clock.now() - enteredAtPerf),
|
|
25625
|
+
clientRetry: handle.clientRetries(),
|
|
25626
|
+
episodeId: handle.episodeId
|
|
25627
|
+
});
|
|
25628
|
+
try {
|
|
25629
|
+
handle.recordAttemptResult(conn.code, false);
|
|
25630
|
+
for (;; ) {
|
|
25631
|
+
if (ctx.signal.aborted) {
|
|
25632
|
+
return { kind: "client_gone", ...tally() };
|
|
25633
|
+
}
|
|
25634
|
+
const waitBudget = ctx.deadlineAt - clock.now() - MIN_ATTEMPT_SLOT_MS;
|
|
25635
|
+
let delay = connectionRetryDelayMs(handle.ladderIndex());
|
|
25636
|
+
if (delay > waitBudget) {
|
|
25637
|
+
const mayTruncate = requestRetries === 0 && waitBudget >= MIN_ATTEMPT_SLOT_MS;
|
|
25638
|
+
if (!mayTruncate) {
|
|
25639
|
+
handle.handoff();
|
|
25640
|
+
return { kind: "exhausted", conn, error, endpoint, ...tally() };
|
|
25641
|
+
}
|
|
25642
|
+
delay = waitBudget;
|
|
25643
|
+
}
|
|
25644
|
+
let waited;
|
|
25645
|
+
try {
|
|
25646
|
+
waited = await handle.waitForNextAttempt(ctx.signal, delay);
|
|
25647
|
+
} catch (err) {
|
|
25648
|
+
if (isClientAbort(ctx.signal, err)) {
|
|
25649
|
+
log(`[Recovery] client gone during wait (episode ${handle.episodeId})`);
|
|
25650
|
+
return { kind: "client_gone", ...tally() };
|
|
25651
|
+
}
|
|
25652
|
+
throw err;
|
|
25653
|
+
}
|
|
25654
|
+
if (waited.kind === "gave_up") {
|
|
25655
|
+
return { kind: "gave_up", conn, error, endpoint, ...tally() };
|
|
25656
|
+
}
|
|
25657
|
+
const attemptAc = new AbortController;
|
|
25658
|
+
const remaining = ctx.deadlineAt - clock.now();
|
|
25659
|
+
const clampMs = Math.max(MIN_ATTEMPT_SLOT_MS, Math.min(PER_ATTEMPT_CONNECT_CAP_MS, remaining));
|
|
25660
|
+
const clampTimer = clock.setTimeout(() => attemptAc.abort(markOwnTimeout(new DOMException("attempt cap", "TimeoutError"))), clampMs);
|
|
25661
|
+
const merged = AbortSignal.any([ctx.signal, attemptAc.signal]);
|
|
25662
|
+
try {
|
|
25663
|
+
requestRetries++;
|
|
25664
|
+
const value = await untilAborted(op(merged), merged);
|
|
25665
|
+
handle.recordAttemptResult("ok", true);
|
|
25666
|
+
logRecovery(`[Recovery] ${ctx.providerDisplayName} recovered after ${handle.attempts()} attempts ` + `in ${handle.recoveryMs()}ms (episode ${handle.episodeId})`);
|
|
25667
|
+
return { kind: "ok", value, ...tally() };
|
|
25668
|
+
} catch (err) {
|
|
25669
|
+
if (isClientAbort(ctx.signal, err)) {
|
|
25670
|
+
log(`[Recovery] client gone during attempt (episode ${handle.episodeId})`);
|
|
25671
|
+
return { kind: "client_gone", ...tally() };
|
|
25672
|
+
}
|
|
25673
|
+
const next = classifyConnectionError(err);
|
|
25674
|
+
if (!next) {
|
|
25675
|
+
return { kind: "threw", error: err, ...tally() };
|
|
25676
|
+
}
|
|
25677
|
+
conn = next;
|
|
25678
|
+
error = err;
|
|
25679
|
+
endpoint = ctx.resolveEndpoint(err) || endpoint;
|
|
25680
|
+
handle.recordAttemptResult(next.code, false);
|
|
25681
|
+
} finally {
|
|
25682
|
+
clock.clearTimeout(clampTimer);
|
|
25683
|
+
}
|
|
25684
|
+
}
|
|
25685
|
+
} finally {
|
|
25686
|
+
handle.leave();
|
|
25687
|
+
}
|
|
25688
|
+
}
|
|
25689
|
+
var CONNECTION_RETRY_DELAYS_MS, PER_ATTEMPT_CONNECT_CAP_MS = 45000, MIN_ATTEMPT_SLOT_MS = 3000, TOKEN_REFRESH_TIMEOUT_MS = 20000, INBOUND_START;
|
|
25690
|
+
var init_transient_retry = __esm(() => {
|
|
25691
|
+
init_logger();
|
|
25692
|
+
init_clock();
|
|
25693
|
+
init_coordinator();
|
|
25694
|
+
init_settings();
|
|
25695
|
+
init_connection_error();
|
|
25696
|
+
init_coordinator();
|
|
25697
|
+
CONNECTION_RETRY_DELAYS_MS = [5000, 1e4, 30000, 60000];
|
|
25698
|
+
INBOUND_START = new WeakMap;
|
|
25699
|
+
});
|
|
25700
|
+
|
|
24929
25701
|
// src/providers/grok/grok-credentials.ts
|
|
24930
25702
|
import { existsSync as existsSync11, readFileSync as readFileSync10, renameSync, writeFileSync as writeFileSync7 } from "fs";
|
|
24931
25703
|
import { homedir as homedir13 } from "os";
|
|
@@ -25070,10 +25842,11 @@ async function performRefresh(cred) {
|
|
|
25070
25842
|
response = await fetch(tokenEndpoint, {
|
|
25071
25843
|
method: "POST",
|
|
25072
25844
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
25073
|
-
body
|
|
25845
|
+
body,
|
|
25846
|
+
signal: AbortSignal.timeout(TOKEN_REFRESH_TIMEOUT_MS)
|
|
25074
25847
|
});
|
|
25075
25848
|
} catch (error) {
|
|
25076
|
-
throw new Error(`Could not reach ${tokenEndpoint} to refresh the Grok token: ${error.message}
|
|
25849
|
+
throw Object.assign(new Error(`Could not reach ${tokenEndpoint} to refresh the Grok token: ${error.message}`, { cause: markOwnTimeout(error) }), { claudishEndpoint: tokenEndpoint });
|
|
25077
25850
|
}
|
|
25078
25851
|
const text = await response.text();
|
|
25079
25852
|
if (!response.ok) {
|
|
@@ -25137,6 +25910,8 @@ function refreshShared(cred) {
|
|
|
25137
25910
|
var GROK_PROXY_URL_ENV = "GROK_PROXY_URL", DEFAULT_GROK_PROXY_URL = "https://cli-chat-proxy.grok.com/v1", GROK_CLIENT_IDENTIFIER = "grok-shell", FALLBACK_GROK_CLIENT_VERSION = "1.0.4", LEGACY_SCOPE = "https://accounts.x.ai/sign-in", EXPIRY_SKEW_MS2, grokHomeOverride = null, claudishOAuthPathOverride = null, GROK_CHANNEL_URL = "https://x.ai/cli/stable", liveClientVersion = null, SIGN_IN_HINT, refreshInFlight = null;
|
|
25138
25911
|
var init_grok_credentials = __esm(() => {
|
|
25139
25912
|
init_grok_oauth();
|
|
25913
|
+
init_connection_error();
|
|
25914
|
+
init_transient_retry();
|
|
25140
25915
|
EXPIRY_SKEW_MS2 = 5 * 60 * 1000;
|
|
25141
25916
|
SIGN_IN_HINT = "Run `claudish login grok` (no Grok CLI required \u2014 SuperGrok or X Premium+ subscription). " + "An existing `grok login` (~/.grok/auth.json) is also picked up automatically.";
|
|
25142
25917
|
});
|
|
@@ -25461,7 +26236,7 @@ function globMatches(pattern, value) {
|
|
|
25461
26236
|
return new RegExp(`^${escaped}$`).test(value);
|
|
25462
26237
|
}
|
|
25463
26238
|
var SeveritySchema, BehaviorConfigSchema;
|
|
25464
|
-
var
|
|
26239
|
+
var init_config2 = __esm(() => {
|
|
25465
26240
|
init_zod();
|
|
25466
26241
|
init_logger();
|
|
25467
26242
|
SeveritySchema = _enum(["off", "warn", "fix"]);
|
|
@@ -26482,7 +27257,7 @@ class BehaviorEngine {
|
|
|
26482
27257
|
var MAX_OBSERVED_CHARS, MAX_OBSERVED_TOOLS = 200, MAX_TRACKED_CONVERSATIONS = 64;
|
|
26483
27258
|
var init_engine = __esm(() => {
|
|
26484
27259
|
init_logger();
|
|
26485
|
-
|
|
27260
|
+
init_config2();
|
|
26486
27261
|
init_harness();
|
|
26487
27262
|
init_journal();
|
|
26488
27263
|
init_aggregate();
|
|
@@ -26790,12 +27565,12 @@ function registerHookRules(rules) {
|
|
|
26790
27565
|
var BUILTIN_RULES, sharedEngine = null, hookRules;
|
|
26791
27566
|
var init_behavior = __esm(() => {
|
|
26792
27567
|
init_profile_config();
|
|
26793
|
-
|
|
27568
|
+
init_config2();
|
|
26794
27569
|
init_engine();
|
|
26795
27570
|
init_plan_mode();
|
|
26796
27571
|
init_session_context();
|
|
26797
27572
|
init_engine();
|
|
26798
|
-
|
|
27573
|
+
init_config2();
|
|
26799
27574
|
init_harness();
|
|
26800
27575
|
init_hooks();
|
|
26801
27576
|
init_digest();
|
|
@@ -27303,7 +28078,7 @@ var init_gemini_queue = __esm(() => {
|
|
|
27303
28078
|
});
|
|
27304
28079
|
|
|
27305
28080
|
// src/providers/transport/antigravity.ts
|
|
27306
|
-
import { randomUUID } from "crypto";
|
|
28081
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
27307
28082
|
function antigravityEndpoint() {
|
|
27308
28083
|
return `${antigravityHost()}/v1internal:streamGenerateContent?alt=sse`;
|
|
27309
28084
|
}
|
|
@@ -27481,7 +28256,7 @@ class AntigravityProviderTransport {
|
|
|
27481
28256
|
const envelope = {
|
|
27482
28257
|
model,
|
|
27483
28258
|
project: this.projectId,
|
|
27484
|
-
user_prompt_id:
|
|
28259
|
+
user_prompt_id: randomUUID2(),
|
|
27485
28260
|
request: innerPayload
|
|
27486
28261
|
};
|
|
27487
28262
|
if (this.tierId && this.tierId !== "free-tier") {
|
|
@@ -27735,7 +28510,7 @@ function redactSecrets(text) {
|
|
|
27735
28510
|
}
|
|
27736
28511
|
|
|
27737
28512
|
// src/auth/credentials/antigravity-credential.ts
|
|
27738
|
-
import { randomUUID as
|
|
28513
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
27739
28514
|
function createActivityRequestId2() {
|
|
27740
28515
|
return Math.random().toString(36).substring(7);
|
|
27741
28516
|
}
|
|
@@ -27770,7 +28545,7 @@ class AntigravityCredentialProvider {
|
|
|
27770
28545
|
const env = {
|
|
27771
28546
|
model: servedModel,
|
|
27772
28547
|
project: projectId,
|
|
27773
|
-
user_prompt_id:
|
|
28548
|
+
user_prompt_id: randomUUID3(),
|
|
27774
28549
|
request: inner
|
|
27775
28550
|
};
|
|
27776
28551
|
if (tierId && tierId !== "free-tier") {
|
|
@@ -28407,8 +29182,9 @@ class CodexOAuth {
|
|
|
28407
29182
|
throw new Error("No OpenAI Codex OAuth credentials found. Please run `claudish login codex` first.");
|
|
28408
29183
|
}
|
|
28409
29184
|
log("[CodexOAuth] Refreshing access token");
|
|
29185
|
+
let response;
|
|
28410
29186
|
try {
|
|
28411
|
-
|
|
29187
|
+
response = await fetch(OAUTH_CONFIG.tokenUrl, {
|
|
28412
29188
|
method: "POST",
|
|
28413
29189
|
headers: {
|
|
28414
29190
|
"Content-Type": "application/json"
|
|
@@ -28417,8 +29193,14 @@ class CodexOAuth {
|
|
|
28417
29193
|
grant_type: "refresh_token",
|
|
28418
29194
|
refresh_token: this.credentials.refresh_token,
|
|
28419
29195
|
client_id: OAUTH_CONFIG.clientId
|
|
28420
|
-
})
|
|
29196
|
+
}),
|
|
29197
|
+
signal: AbortSignal.timeout(TOKEN_REFRESH_TIMEOUT_MS)
|
|
28421
29198
|
});
|
|
29199
|
+
} catch (e) {
|
|
29200
|
+
log(`[CodexOAuth] Could not reach ${OAUTH_CONFIG.tokenUrl}: ${e.message}`);
|
|
29201
|
+
throw Object.assign(new Error(`Could not reach ${OAUTH_CONFIG.tokenUrl} to refresh the Codex token: ${e.message}`, { cause: markOwnTimeout(e) }), { claudishEndpoint: OAUTH_CONFIG.tokenUrl });
|
|
29202
|
+
}
|
|
29203
|
+
try {
|
|
28422
29204
|
if (!response.ok) {
|
|
28423
29205
|
const errorText = await response.text();
|
|
28424
29206
|
throw new Error(`Token refresh failed: ${response.status} - ${errorText}`);
|
|
@@ -28439,7 +29221,7 @@ class CodexOAuth {
|
|
|
28439
29221
|
log(`[CodexOAuth] Refresh failed: ${e.message}`);
|
|
28440
29222
|
throw new Error(`OAuth credentials invalid. Please run \`claudish login codex\` again.
|
|
28441
29223
|
|
|
28442
|
-
Details: ${e.message}
|
|
29224
|
+
Details: ${e.message}`, { cause: e });
|
|
28443
29225
|
}
|
|
28444
29226
|
}
|
|
28445
29227
|
loadCredentials() {
|
|
@@ -28666,6 +29448,8 @@ function getCodexOAuth() {
|
|
|
28666
29448
|
}
|
|
28667
29449
|
var execAsync3, OAUTH_CONFIG;
|
|
28668
29450
|
var init_codex_oauth = __esm(() => {
|
|
29451
|
+
init_connection_error();
|
|
29452
|
+
init_transient_retry();
|
|
28669
29453
|
init_logger();
|
|
28670
29454
|
execAsync3 = promisify3(exec3);
|
|
28671
29455
|
OAUTH_CONFIG = {
|
|
@@ -29804,14 +30588,14 @@ var init_proto_codec = __esm(() => {
|
|
|
29804
30588
|
});
|
|
29805
30589
|
|
|
29806
30590
|
// src/providers/devin/devin-request.ts
|
|
29807
|
-
import { randomUUID as
|
|
30591
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
29808
30592
|
function encodeChatMetadata(meta) {
|
|
29809
30593
|
const version = meta.clientVersion ?? DEVIN_CLI_VERSION;
|
|
29810
30594
|
return msg(bytes(1, "devin-cli"), bytes(2, version), bytes(3, meta.apiKey), bytes(4, "en"), bytes(5, meta.platform ?? process.platform), bytes(7, version), bytes(12, "chisel"), bytes(28, "chisel"));
|
|
29811
30595
|
}
|
|
29812
30596
|
function encodeMessage(message) {
|
|
29813
30597
|
const parts = [
|
|
29814
|
-
bytes(1, message.id ??
|
|
30598
|
+
bytes(1, message.id ?? randomUUID4()),
|
|
29815
30599
|
vint(2, DEVIN_ROLE[message.role])
|
|
29816
30600
|
];
|
|
29817
30601
|
if (message.text)
|
|
@@ -31266,11 +32050,11 @@ class OpenAIProviderTransport {
|
|
|
31266
32050
|
} catch (fetchError) {
|
|
31267
32051
|
if (fetchError.name === "AbortError") {
|
|
31268
32052
|
log(`[${this.displayName}] Request timed out after 30s`);
|
|
31269
|
-
throw new OpenAITimeoutError(this.provider.baseUrl);
|
|
32053
|
+
throw new OpenAITimeoutError(this.provider.baseUrl, fetchError);
|
|
31270
32054
|
}
|
|
31271
32055
|
if (fetchError.cause?.code === "UND_ERR_CONNECT_TIMEOUT") {
|
|
31272
32056
|
log(`[${this.displayName}] Connection timeout: ${fetchError.message}`);
|
|
31273
|
-
throw new OpenAIConnectionError(this.provider.baseUrl, fetchError.cause?.code);
|
|
32057
|
+
throw new OpenAIConnectionError(this.provider.baseUrl, fetchError.cause?.code, fetchError);
|
|
31274
32058
|
}
|
|
31275
32059
|
throw fetchError;
|
|
31276
32060
|
}
|
|
@@ -31305,15 +32089,20 @@ var init_openai = __esm(() => {
|
|
|
31305
32089
|
init_logger();
|
|
31306
32090
|
init_provider_model_discovery();
|
|
31307
32091
|
OpenAITimeoutError = class OpenAITimeoutError extends Error {
|
|
31308
|
-
|
|
31309
|
-
|
|
32092
|
+
code = "ETIMEDOUT";
|
|
32093
|
+
constructor(baseUrl, cause) {
|
|
32094
|
+
super(`Request to OpenAI API timed out. Check your network connection to ${baseUrl}`, {
|
|
32095
|
+
cause
|
|
32096
|
+
});
|
|
31310
32097
|
this.name = "OpenAITimeoutError";
|
|
31311
32098
|
}
|
|
31312
32099
|
};
|
|
31313
32100
|
OpenAIConnectionError = class OpenAIConnectionError extends Error {
|
|
31314
|
-
|
|
31315
|
-
|
|
32101
|
+
code;
|
|
32102
|
+
constructor(baseUrl, code, cause) {
|
|
32103
|
+
super(`Cannot connect to OpenAI API (${baseUrl}). This may be due to: network/firewall blocking, VPN interference, or regional restrictions. Error: ${code}`, { cause });
|
|
31316
32104
|
this.name = "OpenAIConnectionError";
|
|
32105
|
+
this.code = code;
|
|
31317
32106
|
}
|
|
31318
32107
|
};
|
|
31319
32108
|
});
|
|
@@ -32188,6 +32977,21 @@ function eventToLogRecord(event) {
|
|
|
32188
32977
|
if (event.fallback_attempts !== undefined) {
|
|
32189
32978
|
attributes.push(intAttr("llm.fallback_attempts", event.fallback_attempts));
|
|
32190
32979
|
}
|
|
32980
|
+
if (event.retry_attempts !== undefined) {
|
|
32981
|
+
attributes.push(intAttr("llm.retry_attempts", event.retry_attempts));
|
|
32982
|
+
}
|
|
32983
|
+
if (event.recovery_ms !== undefined) {
|
|
32984
|
+
attributes.push(intAttr("llm.recovery_ms", event.recovery_ms));
|
|
32985
|
+
}
|
|
32986
|
+
if (event.recovery_episode_id !== undefined) {
|
|
32987
|
+
attributes.push(stringAttr("llm.recovery_episode_id", event.recovery_episode_id));
|
|
32988
|
+
}
|
|
32989
|
+
if (event.recovery_client_retry !== undefined) {
|
|
32990
|
+
attributes.push(intAttr("llm.recovery_client_retry", event.recovery_client_retry));
|
|
32991
|
+
}
|
|
32992
|
+
if (event.recovery_outcome !== undefined) {
|
|
32993
|
+
attributes.push(stringAttr("llm.recovery_outcome", event.recovery_outcome));
|
|
32994
|
+
}
|
|
32191
32995
|
return {
|
|
32192
32996
|
timeUnixNano,
|
|
32193
32997
|
severityNumber: 9,
|
|
@@ -32726,6 +33530,16 @@ function recordStats(partial) {
|
|
|
32726
33530
|
event.fallback_chain = partial.fallback_chain;
|
|
32727
33531
|
if (partial.fallback_attempts !== undefined)
|
|
32728
33532
|
event.fallback_attempts = partial.fallback_attempts;
|
|
33533
|
+
if (partial.retry_attempts !== undefined)
|
|
33534
|
+
event.retry_attempts = partial.retry_attempts;
|
|
33535
|
+
if (partial.recovery_ms !== undefined)
|
|
33536
|
+
event.recovery_ms = partial.recovery_ms;
|
|
33537
|
+
if (partial.recovery_episode_id !== undefined)
|
|
33538
|
+
event.recovery_episode_id = partial.recovery_episode_id;
|
|
33539
|
+
if (partial.recovery_client_retry !== undefined)
|
|
33540
|
+
event.recovery_client_retry = partial.recovery_client_retry;
|
|
33541
|
+
if (partial.recovery_outcome !== undefined)
|
|
33542
|
+
event.recovery_outcome = partial.recovery_outcome;
|
|
32729
33543
|
appendEvent(event);
|
|
32730
33544
|
checkAndFlush();
|
|
32731
33545
|
} catch {}
|
|
@@ -33291,79 +34105,6 @@ var init_collect_sse_message = __esm(() => {
|
|
|
33291
34105
|
init_logger();
|
|
33292
34106
|
});
|
|
33293
34107
|
|
|
33294
|
-
// src/handlers/shared/connection-error.ts
|
|
33295
|
-
function findConnectionCode(error) {
|
|
33296
|
-
let e = error;
|
|
33297
|
-
const seen = new Set;
|
|
33298
|
-
for (let depth = 0;e && typeof e === "object" && depth < 8 && !seen.has(e); depth++) {
|
|
33299
|
-
seen.add(e);
|
|
33300
|
-
if (typeof e.code === "string" && e.code in CODE_KIND)
|
|
33301
|
-
return e.code;
|
|
33302
|
-
e = e.cause;
|
|
33303
|
-
}
|
|
33304
|
-
const msg = String(error?.message ?? error ?? "");
|
|
33305
|
-
if (/getaddrinfo|ENOTFOUND|EAI_AGAIN|nodename nor servname/i.test(msg))
|
|
33306
|
-
return "ENOTFOUND";
|
|
33307
|
-
if (BUN_CONNECT_MESSAGE.test(msg))
|
|
33308
|
-
return "ConnectionRefused";
|
|
33309
|
-
return null;
|
|
33310
|
-
}
|
|
33311
|
-
function isLoopback(endpoint) {
|
|
33312
|
-
try {
|
|
33313
|
-
const { hostname } = new URL(endpoint);
|
|
33314
|
-
return /^(localhost|127\.\d+\.\d+\.\d+|0\.0\.0\.0|\[?::1\]?)$/i.test(hostname);
|
|
33315
|
-
} catch {
|
|
33316
|
-
return false;
|
|
33317
|
-
}
|
|
33318
|
-
}
|
|
33319
|
-
function classifyConnectionError(error) {
|
|
33320
|
-
const code = findConnectionCode(error);
|
|
33321
|
-
if (!code)
|
|
33322
|
-
return null;
|
|
33323
|
-
return { kind: CODE_KIND[code] ?? "unreachable", code };
|
|
33324
|
-
}
|
|
33325
|
-
function hostOf(endpoint) {
|
|
33326
|
-
try {
|
|
33327
|
-
return new URL(endpoint).host || endpoint;
|
|
33328
|
-
} catch {
|
|
33329
|
-
return endpoint;
|
|
33330
|
-
}
|
|
33331
|
-
}
|
|
33332
|
-
function buildConnectionErrorMessage(kind, displayName, endpoint) {
|
|
33333
|
-
const host = hostOf(endpoint);
|
|
33334
|
-
switch (kind) {
|
|
33335
|
-
case "dns":
|
|
33336
|
-
return `Cannot resolve ${host} for ${displayName}. This is a DNS/network problem on your machine \u2014 check your internet connection, VPN, or DNS resolver (e.g. Tailscale MagicDNS) \u2014 not ${displayName}.`;
|
|
33337
|
-
case "refused":
|
|
33338
|
-
if (isLoopback(endpoint)) {
|
|
33339
|
-
return `Cannot connect to ${displayName} at ${endpoint}. Make sure the server is running.`;
|
|
33340
|
-
}
|
|
33341
|
-
return `Cannot reach ${host} for ${displayName}. This is a network problem on your machine \u2014 check your internet connection, VPN, or DNS resolver (e.g. Tailscale MagicDNS) \u2014 not ${displayName}.`;
|
|
33342
|
-
case "unreachable":
|
|
33343
|
-
return `Cannot reach ${displayName} at ${endpoint}. Check your network connection.`;
|
|
33344
|
-
}
|
|
33345
|
-
}
|
|
33346
|
-
var CODE_KIND, BUN_CONNECT_MESSAGE;
|
|
33347
|
-
var init_connection_error = __esm(() => {
|
|
33348
|
-
CODE_KIND = {
|
|
33349
|
-
ENOTFOUND: "dns",
|
|
33350
|
-
EAI_AGAIN: "dns",
|
|
33351
|
-
ECONNREFUSED: "refused",
|
|
33352
|
-
ETIMEDOUT: "unreachable",
|
|
33353
|
-
ECONNRESET: "unreachable",
|
|
33354
|
-
ENETUNREACH: "unreachable",
|
|
33355
|
-
EHOSTUNREACH: "unreachable",
|
|
33356
|
-
EPIPE: "unreachable",
|
|
33357
|
-
UND_ERR_CONNECT_TIMEOUT: "unreachable",
|
|
33358
|
-
UND_ERR_SOCKET: "unreachable",
|
|
33359
|
-
ConnectionRefused: "refused",
|
|
33360
|
-
ConnectionClosed: "unreachable",
|
|
33361
|
-
FailedToOpenSocket: "unreachable",
|
|
33362
|
-
ERR_SOCKET_CLOSED: "unreachable"
|
|
33363
|
-
};
|
|
33364
|
-
BUN_CONNECT_MESSAGE = /unable to connect\. is the computer able to access the url\?/i;
|
|
33365
|
-
});
|
|
33366
|
-
|
|
33367
34108
|
// src/handlers/shared/devin-stream-head-sniffer.ts
|
|
33368
34109
|
function classifyDevinStreamError(code, message) {
|
|
33369
34110
|
const lowerCode = code.toLowerCase();
|
|
@@ -33513,6 +34254,39 @@ var init_model_unsupported = __esm(() => {
|
|
|
33513
34254
|
PARAMETER_IS_UNSUPPORTED = /\b(thinking[ _-]?(level|budget)|reasoning[ _-]?effort|effort|temperature|top[_ ]?[pk]|tool[_ ]?choice|tools?|function[ _-]?calling|parameter|argument|field|image[s]?|audio|video|streaming|stream|json[ _-]?mode|response[ _-]?format|system[ _-]?(prompt|instruction)s?)\b[^.]{0,60}?\b(is|are|was|were)?\s*not supported\b/;
|
|
33514
34255
|
});
|
|
33515
34256
|
|
|
34257
|
+
// src/handlers/shared/recovery-marker.ts
|
|
34258
|
+
function recoveryHoldHeaders() {
|
|
34259
|
+
return {
|
|
34260
|
+
"content-type": "application/json",
|
|
34261
|
+
"x-should-retry": "true",
|
|
34262
|
+
[RECOVERY_MARKER_HEADER]: RECOVERY_MARKER_VALUE
|
|
34263
|
+
};
|
|
34264
|
+
}
|
|
34265
|
+
function connectionFaultHeaders() {
|
|
34266
|
+
return { [CONNECTION_FAULT_HEADER]: RECOVERY_MARKER_VALUE };
|
|
34267
|
+
}
|
|
34268
|
+
function hasRecoveryMarker(headers) {
|
|
34269
|
+
try {
|
|
34270
|
+
return headers?.get(RECOVERY_MARKER_HEADER) === RECOVERY_MARKER_VALUE;
|
|
34271
|
+
} catch {
|
|
34272
|
+
return false;
|
|
34273
|
+
}
|
|
34274
|
+
}
|
|
34275
|
+
function hasConnectionFaultMarker(headers) {
|
|
34276
|
+
try {
|
|
34277
|
+
return headers?.get(CONNECTION_FAULT_HEADER) === RECOVERY_MARKER_VALUE;
|
|
34278
|
+
} catch {
|
|
34279
|
+
return false;
|
|
34280
|
+
}
|
|
34281
|
+
}
|
|
34282
|
+
function isClaudishConnectionVerdict(headers) {
|
|
34283
|
+
return hasRecoveryMarker(headers) || hasConnectionFaultMarker(headers);
|
|
34284
|
+
}
|
|
34285
|
+
function isRecoveryHoldResponse(response) {
|
|
34286
|
+
return hasRecoveryMarker(response?.headers);
|
|
34287
|
+
}
|
|
34288
|
+
var RECOVERY_MARKER_HEADER = "x-claudish-recovery", RECOVERY_MARKER_VALUE = "1", CONNECTION_FAULT_HEADER = "x-claudish-connection-error";
|
|
34289
|
+
|
|
33516
34290
|
// src/handlers/shared/stream-head-sniffer.ts
|
|
33517
34291
|
function isRetryableStreamError(code, type, message) {
|
|
33518
34292
|
if (RETRYABLE_ERROR_CODES.has(code))
|
|
@@ -35628,11 +36402,204 @@ class ComposedHandler {
|
|
|
35628
36402
|
return this.modelAdapter.getName();
|
|
35629
36403
|
return this.getAdapter().getName();
|
|
35630
36404
|
}
|
|
36405
|
+
connectionEndpointFor(error, requestEndpoint) {
|
|
36406
|
+
const attached = error?.claudishEndpoint;
|
|
36407
|
+
if (typeof attached === "string" && attached.length > 0)
|
|
36408
|
+
return attached;
|
|
36409
|
+
if (requestEndpoint)
|
|
36410
|
+
return requestEndpoint;
|
|
36411
|
+
try {
|
|
36412
|
+
return this.provider.getEndpoint(this.targetModel);
|
|
36413
|
+
} catch {
|
|
36414
|
+
return this.provider.displayName;
|
|
36415
|
+
}
|
|
36416
|
+
}
|
|
36417
|
+
static newRecoveryTally() {
|
|
36418
|
+
return { retries: 0, recoveryMs: 0, clientRetry: 0 };
|
|
36419
|
+
}
|
|
36420
|
+
recoveryStats(tally) {
|
|
36421
|
+
if (!tally?.episodeId)
|
|
36422
|
+
return {};
|
|
36423
|
+
return {
|
|
36424
|
+
retry_attempts: tally.retries,
|
|
36425
|
+
recovery_ms: tally.recoveryMs,
|
|
36426
|
+
recovery_episode_id: tally.episodeId,
|
|
36427
|
+
recovery_client_retry: tally.clientRetry,
|
|
36428
|
+
...tally.outcome ? { recovery_outcome: tally.outcome } : {}
|
|
36429
|
+
};
|
|
36430
|
+
}
|
|
36431
|
+
respondConnectionError(c, error, conn, endpoint, ctx) {
|
|
36432
|
+
const msg = buildConnectionErrorMessage(conn.kind, this.provider.displayName, endpoint);
|
|
36433
|
+
log(`[${this.provider.displayName}] ${msg} (code=${conn.code}${ctx.site ? `, site=${ctx.site}` : ""})`);
|
|
36434
|
+
logStderr(`Error: ${msg}`);
|
|
36435
|
+
reportError({
|
|
36436
|
+
error,
|
|
36437
|
+
providerName: this.provider.name,
|
|
36438
|
+
providerDisplayName: this.provider.displayName,
|
|
36439
|
+
streamFormat: this.provider.streamFormat,
|
|
36440
|
+
modelId: this.targetModel,
|
|
36441
|
+
httpStatus: undefined,
|
|
36442
|
+
isStreaming: false,
|
|
36443
|
+
retryAttempted: (ctx.retriedBeforeLadder ?? false) || (ctx.recovery?.retries ?? 0) > 0,
|
|
36444
|
+
isInteractive: this.isInteractive,
|
|
36445
|
+
...ctx.authType ? { authType: ctx.authType } : {}
|
|
36446
|
+
});
|
|
36447
|
+
try {
|
|
36448
|
+
const { error_class, error_code } = classifyError(error, undefined);
|
|
36449
|
+
recordStats({
|
|
36450
|
+
model_id: this.targetModel,
|
|
36451
|
+
provider_name: this.provider.name,
|
|
36452
|
+
stream_format: this.provider.streamFormat,
|
|
36453
|
+
latency_ms: Math.round(performance.now() - ctx.startTime),
|
|
36454
|
+
success: false,
|
|
36455
|
+
http_status: 0,
|
|
36456
|
+
error_class,
|
|
36457
|
+
error_code,
|
|
36458
|
+
token_strategy: this.options.tokenStrategy ?? "standard",
|
|
36459
|
+
adapter_name: this.getActiveAdapterName(),
|
|
36460
|
+
middleware_names: this.middlewareManager.getActiveNames(this.bareModelName),
|
|
36461
|
+
fallback_used: ctx.fallbackMeta !== undefined,
|
|
36462
|
+
fallback_chain: ctx.fallbackMeta?.chain,
|
|
36463
|
+
fallback_attempts: ctx.fallbackMeta?.attempts,
|
|
36464
|
+
invocation_mode: this.options.invocationMode ?? "auto-route",
|
|
36465
|
+
...this.recoveryStats(ctx.recovery)
|
|
36466
|
+
});
|
|
36467
|
+
} catch {}
|
|
36468
|
+
return c.json(wrapAnthropicError(400, msg, "connection_error"), 400, connectionFaultHeaders());
|
|
36469
|
+
}
|
|
36470
|
+
respondRecoveryHold(error, conn, endpoint, ctx) {
|
|
36471
|
+
const reason = buildConnectionErrorMessage(conn.kind, this.provider.displayName, endpoint);
|
|
36472
|
+
const msg = buildRecoveryHoldMessage(reason, ctx.attempts, ctx.recoveryMs);
|
|
36473
|
+
try {
|
|
36474
|
+
const { error_class, error_code } = classifyError(error, undefined);
|
|
36475
|
+
recordStats({
|
|
36476
|
+
model_id: this.targetModel,
|
|
36477
|
+
provider_name: this.provider.name,
|
|
36478
|
+
stream_format: this.provider.streamFormat,
|
|
36479
|
+
latency_ms: Math.round(performance.now() - ctx.startTime),
|
|
36480
|
+
success: false,
|
|
36481
|
+
http_status: 503,
|
|
36482
|
+
error_class,
|
|
36483
|
+
error_code,
|
|
36484
|
+
token_strategy: this.options.tokenStrategy ?? "standard",
|
|
36485
|
+
adapter_name: this.getActiveAdapterName(),
|
|
36486
|
+
middleware_names: this.middlewareManager.getActiveNames(this.bareModelName),
|
|
36487
|
+
fallback_used: ctx.fallbackMeta !== undefined,
|
|
36488
|
+
fallback_chain: ctx.fallbackMeta?.chain,
|
|
36489
|
+
fallback_attempts: ctx.fallbackMeta?.attempts,
|
|
36490
|
+
invocation_mode: this.options.invocationMode ?? "auto-route",
|
|
36491
|
+
...this.recoveryStats(ctx.recovery)
|
|
36492
|
+
});
|
|
36493
|
+
} catch {}
|
|
36494
|
+
return new Response(JSON.stringify(wrapAnthropicError(503, msg, "overloaded_error")), {
|
|
36495
|
+
status: 503,
|
|
36496
|
+
headers: recoveryHoldHeaders()
|
|
36497
|
+
});
|
|
36498
|
+
}
|
|
36499
|
+
clientSignal(c) {
|
|
36500
|
+
try {
|
|
36501
|
+
const signal = c.req?.raw?.signal;
|
|
36502
|
+
if (signal)
|
|
36503
|
+
return signal;
|
|
36504
|
+
} catch {}
|
|
36505
|
+
return new AbortController().signal;
|
|
36506
|
+
}
|
|
36507
|
+
shouldSkipTier1(c, deadlineAt) {
|
|
36508
|
+
if (c.req.header("x-claudish-no-recovery") === "1")
|
|
36509
|
+
return "probe";
|
|
36510
|
+
if (!resolveRecoveryEnabled())
|
|
36511
|
+
return "recovery-disabled";
|
|
36512
|
+
if (recoveryClock().now() + MIN_ATTEMPT_SLOT_MS > deadlineAt)
|
|
36513
|
+
return "no-budget";
|
|
36514
|
+
return null;
|
|
36515
|
+
}
|
|
36516
|
+
async recoverConnection(c, first, conn, endpoint, op, ctx) {
|
|
36517
|
+
const skip = this.shouldSkipTier1(c, ctx.deadlineAt);
|
|
36518
|
+
if (skip) {
|
|
36519
|
+
log(`[Recovery] skipped (${skip}) \u2014 ${this.provider.displayName} at ${endpoint}, site=${ctx.site}`);
|
|
36520
|
+
return {
|
|
36521
|
+
kind: "respond",
|
|
36522
|
+
response: this.respondConnectionError(c, first, conn, endpoint, ctx)
|
|
36523
|
+
};
|
|
36524
|
+
}
|
|
36525
|
+
try {
|
|
36526
|
+
c.env?.timeout?.(c.req.raw, 0);
|
|
36527
|
+
} catch {}
|
|
36528
|
+
const result = await withConnectionRetry(op, first, {
|
|
36529
|
+
providerName: this.provider.name,
|
|
36530
|
+
providerDisplayName: this.provider.displayName,
|
|
36531
|
+
resolveEndpoint: (err) => this.connectionEndpointFor(err, endpoint),
|
|
36532
|
+
deadlineAt: ctx.deadlineAt,
|
|
36533
|
+
signal: this.clientSignal(c)
|
|
36534
|
+
});
|
|
36535
|
+
ctx.recovery.retries += result.requestRetries;
|
|
36536
|
+
ctx.recovery.recoveryMs += result.requestRecoveryMs;
|
|
36537
|
+
ctx.recovery.episodeId = result.episodeId;
|
|
36538
|
+
ctx.recovery.clientRetry = result.clientRetry;
|
|
36539
|
+
switch (result.kind) {
|
|
36540
|
+
case "ok":
|
|
36541
|
+
ctx.recovery.outcome = "recovered";
|
|
36542
|
+
return { kind: "value", value: result.value };
|
|
36543
|
+
case "threw":
|
|
36544
|
+
throw result.error;
|
|
36545
|
+
case "client_gone":
|
|
36546
|
+
ctx.recovery.outcome = "client_gone";
|
|
36547
|
+
this.recordClientGone(ctx.startTime, ctx.fallbackMeta, result.episodeId, result.attempts, ctx.recovery);
|
|
36548
|
+
return { kind: "respond", response: new Response(null, { status: 499 }) };
|
|
36549
|
+
default: {
|
|
36550
|
+
const leased = uiLeaseValid(result.episodeId);
|
|
36551
|
+
ctx.recovery.outcome = result.kind === "exhausted" ? "handoff" : "gave_up";
|
|
36552
|
+
if (result.kind === "exhausted" && leased) {
|
|
36553
|
+
logRecovery(`[Recovery] ${this.provider.displayName} exhausted after ${result.attempts} attempts ` + `in ${result.recoveryMs}ms (episode ${result.episodeId}, ui_lease=true) \u2014 ` + "handing the retry back to the client (503, tier 2)");
|
|
36554
|
+
return {
|
|
36555
|
+
kind: "respond",
|
|
36556
|
+
response: this.respondRecoveryHold(result.error, result.conn, result.endpoint, {
|
|
36557
|
+
startTime: ctx.startTime,
|
|
36558
|
+
attempts: result.attempts,
|
|
36559
|
+
recoveryMs: result.recoveryMs,
|
|
36560
|
+
fallbackMeta: ctx.fallbackMeta,
|
|
36561
|
+
recovery: ctx.recovery
|
|
36562
|
+
})
|
|
36563
|
+
};
|
|
36564
|
+
}
|
|
36565
|
+
logRecovery(`[Recovery] ${this.provider.displayName} exhausted after ${result.attempts} attempts ` + `in ${result.recoveryMs}ms (episode ${result.episodeId}, outcome ${result.kind}, ` + `ui_lease=${leased}) \u2014 answering connection_error`);
|
|
36566
|
+
return {
|
|
36567
|
+
kind: "respond",
|
|
36568
|
+
response: this.respondConnectionError(c, result.error, result.conn, result.endpoint, ctx)
|
|
36569
|
+
};
|
|
36570
|
+
}
|
|
36571
|
+
}
|
|
36572
|
+
}
|
|
36573
|
+
recordClientGone(startTime, fallbackMeta, episodeId, attempts, recovery) {
|
|
36574
|
+
log(`[Recovery] client disconnected after ${attempts} attempts (episode ${episodeId}) \u2014 ` + "releasing the request");
|
|
36575
|
+
try {
|
|
36576
|
+
recordStats({
|
|
36577
|
+
model_id: this.targetModel,
|
|
36578
|
+
provider_name: this.provider.name,
|
|
36579
|
+
stream_format: this.provider.streamFormat,
|
|
36580
|
+
latency_ms: Math.round(performance.now() - startTime),
|
|
36581
|
+
success: false,
|
|
36582
|
+
http_status: 0,
|
|
36583
|
+
error_class: "network",
|
|
36584
|
+
error_code: "client_disconnected",
|
|
36585
|
+
token_strategy: this.options.tokenStrategy ?? "standard",
|
|
36586
|
+
adapter_name: this.getActiveAdapterName(),
|
|
36587
|
+
middleware_names: this.middlewareManager.getActiveNames(this.bareModelName),
|
|
36588
|
+
fallback_used: fallbackMeta !== undefined,
|
|
36589
|
+
fallback_chain: fallbackMeta?.chain,
|
|
36590
|
+
fallback_attempts: fallbackMeta?.attempts,
|
|
36591
|
+
invocation_mode: this.options.invocationMode ?? "auto-route",
|
|
36592
|
+
...this.recoveryStats(recovery)
|
|
36593
|
+
});
|
|
36594
|
+
} catch {}
|
|
36595
|
+
}
|
|
35631
36596
|
async handle(c, payload) {
|
|
35632
36597
|
const startTime = performance.now();
|
|
36598
|
+
inboundStartedAtPerf(c);
|
|
35633
36599
|
let latencyMs = 0;
|
|
35634
36600
|
const fallbackMeta = this.pendingFallbackMeta;
|
|
35635
36601
|
this.pendingFallbackMeta = undefined;
|
|
36602
|
+
const recoveryTally = ComposedHandler.newRecoveryTally();
|
|
35636
36603
|
const { claudeRequest, droppedParams } = transformOpenAIToClaude(payload);
|
|
35637
36604
|
const adapter = this.getAdapter();
|
|
35638
36605
|
if (typeof adapter.reset === "function")
|
|
@@ -35764,28 +36731,42 @@ class ComposedHandler {
|
|
|
35764
36731
|
if (this.provider.refreshAuth) {
|
|
35765
36732
|
try {
|
|
35766
36733
|
await this.provider.refreshAuth();
|
|
35767
|
-
if (this.provider.displayName) {
|
|
35768
|
-
this.tokenTracker.setProviderDisplayName(this.provider.displayName);
|
|
35769
|
-
}
|
|
35770
36734
|
} catch (err) {
|
|
35771
|
-
|
|
35772
|
-
|
|
35773
|
-
|
|
35774
|
-
|
|
35775
|
-
|
|
35776
|
-
|
|
35777
|
-
|
|
35778
|
-
|
|
35779
|
-
|
|
35780
|
-
|
|
35781
|
-
|
|
35782
|
-
|
|
35783
|
-
|
|
35784
|
-
|
|
35785
|
-
|
|
35786
|
-
|
|
36735
|
+
const conn = classifyConnectionError(err);
|
|
36736
|
+
if (conn) {
|
|
36737
|
+
const outcome = await this.recoverConnection(c, err, conn, this.connectionEndpointFor(err), () => this.provider.refreshAuth(), {
|
|
36738
|
+
startTime,
|
|
36739
|
+
deadlineAt: refreshDeadlineAt(tier1DeadlineAt(c)),
|
|
36740
|
+
fallbackMeta,
|
|
36741
|
+
authType: "oauth",
|
|
36742
|
+
site: "refreshAuth",
|
|
36743
|
+
recovery: recoveryTally
|
|
36744
|
+
});
|
|
36745
|
+
if (outcome.kind === "respond")
|
|
36746
|
+
return outcome.response;
|
|
36747
|
+
} else {
|
|
36748
|
+
log(`[${this.provider.displayName}] Auth/health check failed: ${err.message}`);
|
|
36749
|
+
logStderr(`Error [${this.provider.displayName}]: Auth/health check failed \u2014 ${err.message}. Check credentials and server.`);
|
|
36750
|
+
reportError({
|
|
36751
|
+
error: err,
|
|
36752
|
+
providerName: this.provider.name,
|
|
36753
|
+
providerDisplayName: this.provider.displayName,
|
|
36754
|
+
streamFormat: this.provider.streamFormat,
|
|
36755
|
+
modelId: this.targetModel,
|
|
36756
|
+
httpStatus: 401,
|
|
36757
|
+
isStreaming: false,
|
|
36758
|
+
retryAttempted: false,
|
|
36759
|
+
isInteractive: this.isInteractive,
|
|
36760
|
+
authType: "oauth"
|
|
36761
|
+
});
|
|
36762
|
+
if (err?.terminal) {
|
|
36763
|
+
return c.json(wrapAnthropicError(400, err.message, "invalid_request_error"), 400);
|
|
36764
|
+
}
|
|
36765
|
+
return c.json(wrapAnthropicError(401, err.message, "authentication_error"), 401);
|
|
35787
36766
|
}
|
|
35788
|
-
|
|
36767
|
+
}
|
|
36768
|
+
if (this.provider.displayName) {
|
|
36769
|
+
this.tokenTracker.setProviderDisplayName(this.provider.displayName);
|
|
35789
36770
|
}
|
|
35790
36771
|
}
|
|
35791
36772
|
if (this.provider.getContextWindow) {
|
|
@@ -35798,61 +36779,72 @@ class ComposedHandler {
|
|
|
35798
36779
|
requestPayload = this.provider.transformPayload(requestPayload, claudeRequest);
|
|
35799
36780
|
}
|
|
35800
36781
|
const endpoint = this.provider.getEndpoint(this.targetModel);
|
|
35801
|
-
|
|
36782
|
+
let headers;
|
|
36783
|
+
try {
|
|
36784
|
+
headers = await this.provider.getHeaders(payload);
|
|
36785
|
+
} catch (err) {
|
|
36786
|
+
const conn = classifyConnectionError(err);
|
|
36787
|
+
if (!conn) {
|
|
36788
|
+
throw err;
|
|
36789
|
+
}
|
|
36790
|
+
const outcome = await this.recoverConnection(c, err, conn, this.connectionEndpointFor(err, endpoint), () => this.provider.getHeaders(), {
|
|
36791
|
+
startTime,
|
|
36792
|
+
deadlineAt: refreshDeadlineAt(tier1DeadlineAt(c)),
|
|
36793
|
+
fallbackMeta,
|
|
36794
|
+
authType: "oauth",
|
|
36795
|
+
site: "getHeaders",
|
|
36796
|
+
recovery: recoveryTally
|
|
36797
|
+
});
|
|
36798
|
+
if (outcome.kind === "respond")
|
|
36799
|
+
return outcome.response;
|
|
36800
|
+
headers = outcome.value;
|
|
36801
|
+
}
|
|
35802
36802
|
const serialized = this.provider.serializeBody?.(requestPayload);
|
|
35803
36803
|
headers["Content-Type"] = serialized?.contentType ?? "application/json";
|
|
35804
36804
|
log(`[${this.provider.displayName}] Calling API: ${endpoint}`);
|
|
35805
36805
|
const requestInit = this.provider.getRequestInit?.() || {};
|
|
35806
|
-
const
|
|
36806
|
+
const deadlineAt = tier1DeadlineAt(c);
|
|
36807
|
+
const clamp = deadlineClamp(deadlineAt);
|
|
36808
|
+
const doFetch = () => fetch(endpoint, mergeSignalIntoInit({
|
|
35807
36809
|
method: "POST",
|
|
35808
36810
|
headers,
|
|
35809
36811
|
body: serialized?.body ?? JSON.stringify(requestPayload),
|
|
35810
36812
|
...requestInit
|
|
35811
|
-
});
|
|
36813
|
+
}, clamp.signal));
|
|
35812
36814
|
let response;
|
|
35813
36815
|
try {
|
|
35814
|
-
|
|
36816
|
+
try {
|
|
36817
|
+
response = this.provider.enqueueRequest ? await this.provider.enqueueRequest(doFetch) : await doFetch();
|
|
36818
|
+
} finally {
|
|
36819
|
+
clamp.disarm();
|
|
36820
|
+
}
|
|
35815
36821
|
} catch (error) {
|
|
35816
36822
|
const conn = classifyConnectionError(error);
|
|
35817
|
-
if (conn)
|
|
35818
|
-
|
|
35819
|
-
|
|
35820
|
-
|
|
35821
|
-
|
|
35822
|
-
|
|
35823
|
-
|
|
35824
|
-
|
|
35825
|
-
|
|
35826
|
-
|
|
35827
|
-
|
|
35828
|
-
|
|
35829
|
-
|
|
35830
|
-
|
|
35831
|
-
|
|
35832
|
-
|
|
35833
|
-
|
|
35834
|
-
|
|
35835
|
-
|
|
35836
|
-
|
|
35837
|
-
|
|
35838
|
-
|
|
35839
|
-
success: false,
|
|
35840
|
-
http_status: 0,
|
|
35841
|
-
error_class,
|
|
35842
|
-
error_code,
|
|
35843
|
-
token_strategy: this.options.tokenStrategy ?? "standard",
|
|
35844
|
-
adapter_name: this.getActiveAdapterName(),
|
|
35845
|
-
middleware_names: this.middlewareManager.getActiveNames(this.bareModelName),
|
|
35846
|
-
fallback_used: fallbackMeta !== undefined,
|
|
35847
|
-
fallback_chain: fallbackMeta?.chain,
|
|
35848
|
-
fallback_attempts: fallbackMeta?.attempts,
|
|
35849
|
-
invocation_mode: this.options.invocationMode ?? "auto-route"
|
|
35850
|
-
});
|
|
35851
|
-
} catch {}
|
|
35852
|
-
return c.json(wrapAnthropicError(400, msg, "connection_error"), 400);
|
|
35853
|
-
}
|
|
35854
|
-
throw error;
|
|
36823
|
+
if (!conn)
|
|
36824
|
+
throw error;
|
|
36825
|
+
const doFetchWith = (sig) => fetch(endpoint, mergeSignalIntoInit({
|
|
36826
|
+
method: "POST",
|
|
36827
|
+
headers,
|
|
36828
|
+
body: serialized?.body ?? JSON.stringify(requestPayload),
|
|
36829
|
+
...this.provider.getRequestInit?.() || {}
|
|
36830
|
+
}, sig));
|
|
36831
|
+
const reissue = (sig) => {
|
|
36832
|
+
const attempt = () => doFetchWith(sig);
|
|
36833
|
+
return this.provider.enqueueRequest ? this.provider.enqueueRequest(attempt, { signal: sig }) : attempt();
|
|
36834
|
+
};
|
|
36835
|
+
const outcome = await this.recoverConnection(c, error, conn, endpoint, reissue, {
|
|
36836
|
+
startTime,
|
|
36837
|
+
deadlineAt,
|
|
36838
|
+
fallbackMeta,
|
|
36839
|
+
site: "fetch",
|
|
36840
|
+
recovery: recoveryTally
|
|
36841
|
+
});
|
|
36842
|
+
if (outcome.kind === "respond")
|
|
36843
|
+
return outcome.response;
|
|
36844
|
+
response = outcome.value;
|
|
35855
36845
|
}
|
|
36846
|
+
if (episodeCount() > 0)
|
|
36847
|
+
noteTargetReachable(this.provider.name, endpoint);
|
|
35856
36848
|
if (this.provider.getActiveModelName?.()) {
|
|
35857
36849
|
const activeModel = this.provider.getActiveModelName();
|
|
35858
36850
|
this.tokenTracker.setActiveModelName(activeModel);
|
|
@@ -35874,14 +36866,35 @@ class ComposedHandler {
|
|
|
35874
36866
|
log(`[${this.provider.displayName}] Parameter rejected \u2014 retrying: ${recovery.note}`);
|
|
35875
36867
|
requestPayload = recovery.payload;
|
|
35876
36868
|
const retrySerialized = this.provider.serializeBody?.(requestPayload);
|
|
35877
|
-
const
|
|
35878
|
-
|
|
35879
|
-
|
|
35880
|
-
|
|
35881
|
-
|
|
35882
|
-
|
|
35883
|
-
|
|
35884
|
-
|
|
36869
|
+
const doParamRetry = async (sig) => {
|
|
36870
|
+
const retryHeaders = await this.provider.getHeaders(payload);
|
|
36871
|
+
retryHeaders["Content-Type"] = retrySerialized?.contentType ?? "application/json";
|
|
36872
|
+
return fetch(endpoint, mergeSignalIntoInit({
|
|
36873
|
+
method: "POST",
|
|
36874
|
+
headers: retryHeaders,
|
|
36875
|
+
body: retrySerialized?.body ?? JSON.stringify(requestPayload),
|
|
36876
|
+
...this.provider.getRequestInit?.() || {}
|
|
36877
|
+
}, sig));
|
|
36878
|
+
};
|
|
36879
|
+
let retryResp;
|
|
36880
|
+
try {
|
|
36881
|
+
retryResp = await doParamRetry();
|
|
36882
|
+
} catch (err) {
|
|
36883
|
+
const conn = classifyConnectionError(err);
|
|
36884
|
+
if (!conn)
|
|
36885
|
+
throw err;
|
|
36886
|
+
const outcome = await this.recoverConnection(c, err, conn, this.connectionEndpointFor(err, endpoint), doParamRetry, {
|
|
36887
|
+
startTime,
|
|
36888
|
+
deadlineAt: tier1DeadlineAt(c),
|
|
36889
|
+
fallbackMeta,
|
|
36890
|
+
retriedBeforeLadder: true,
|
|
36891
|
+
site: "parameter-recovery",
|
|
36892
|
+
recovery: recoveryTally
|
|
36893
|
+
});
|
|
36894
|
+
if (outcome.kind === "respond")
|
|
36895
|
+
return outcome.response;
|
|
36896
|
+
retryResp = outcome.value;
|
|
36897
|
+
}
|
|
35885
36898
|
if (retryResp.ok) {
|
|
35886
36899
|
response = retryResp;
|
|
35887
36900
|
} else {
|
|
@@ -35893,17 +36906,19 @@ class ComposedHandler {
|
|
|
35893
36906
|
if (!response.ok) {
|
|
35894
36907
|
if (response.status === 401 && this.provider.forceRefreshAuth) {
|
|
35895
36908
|
log(`[${this.provider.displayName}] Got 401, forcing auth refresh and retrying`);
|
|
35896
|
-
|
|
36909
|
+
const doAuthRetry = async (sig) => {
|
|
35897
36910
|
await this.provider.forceRefreshAuth();
|
|
35898
36911
|
const retryHeaders = await this.provider.getHeaders(payload);
|
|
35899
36912
|
retryHeaders["Content-Type"] = serialized?.contentType ?? "application/json";
|
|
35900
36913
|
const retryInit = this.provider.getRequestInit?.() || {};
|
|
35901
|
-
|
|
36914
|
+
return fetch(endpoint, mergeSignalIntoInit({
|
|
35902
36915
|
method: "POST",
|
|
35903
36916
|
headers: retryHeaders,
|
|
35904
36917
|
body: serialized?.body ?? JSON.stringify(requestPayload),
|
|
35905
36918
|
...retryInit
|
|
35906
|
-
});
|
|
36919
|
+
}, sig));
|
|
36920
|
+
};
|
|
36921
|
+
const settleAuthRetry = async (retryResp) => {
|
|
35907
36922
|
if (retryResp.ok) {
|
|
35908
36923
|
response = retryResp;
|
|
35909
36924
|
} else {
|
|
@@ -35939,47 +36954,73 @@ class ComposedHandler {
|
|
|
35939
36954
|
fallback_used: fallbackMeta !== undefined,
|
|
35940
36955
|
fallback_chain: fallbackMeta?.chain,
|
|
35941
36956
|
fallback_attempts: fallbackMeta?.attempts,
|
|
35942
|
-
invocation_mode: this.options.invocationMode ?? "auto-route"
|
|
36957
|
+
invocation_mode: this.options.invocationMode ?? "auto-route",
|
|
36958
|
+
...this.recoveryStats(recoveryTally)
|
|
35943
36959
|
});
|
|
35944
36960
|
} catch {}
|
|
35945
36961
|
return c.json(wrapAnthropicError(retryResp.status, errorText), retryResp.status);
|
|
35946
36962
|
}
|
|
36963
|
+
return null;
|
|
36964
|
+
};
|
|
36965
|
+
try {
|
|
36966
|
+
const answered = await settleAuthRetry(await doAuthRetry());
|
|
36967
|
+
if (answered)
|
|
36968
|
+
return answered;
|
|
35947
36969
|
} catch (err) {
|
|
35948
|
-
|
|
35949
|
-
|
|
35950
|
-
|
|
35951
|
-
|
|
35952
|
-
|
|
35953
|
-
|
|
35954
|
-
|
|
35955
|
-
|
|
35956
|
-
|
|
35957
|
-
|
|
35958
|
-
retryAttempted: true,
|
|
35959
|
-
isInteractive: this.isInteractive,
|
|
35960
|
-
authType: "oauth"
|
|
35961
|
-
});
|
|
35962
|
-
try {
|
|
35963
|
-
const { error_class, error_code } = classifyError(err, 401, err.message);
|
|
35964
|
-
recordStats({
|
|
35965
|
-
model_id: this.targetModel,
|
|
35966
|
-
provider_name: this.provider.name,
|
|
35967
|
-
stream_format: this.provider.streamFormat,
|
|
35968
|
-
latency_ms: Math.round(performance.now() - startTime),
|
|
35969
|
-
success: false,
|
|
35970
|
-
http_status: 401,
|
|
35971
|
-
error_class,
|
|
35972
|
-
error_code,
|
|
35973
|
-
token_strategy: this.options.tokenStrategy ?? "standard",
|
|
35974
|
-
adapter_name: this.getActiveAdapterName(),
|
|
35975
|
-
middleware_names: this.middlewareManager.getActiveNames(this.bareModelName),
|
|
35976
|
-
fallback_used: fallbackMeta !== undefined,
|
|
35977
|
-
fallback_chain: fallbackMeta?.chain,
|
|
35978
|
-
fallback_attempts: fallbackMeta?.attempts,
|
|
35979
|
-
invocation_mode: this.options.invocationMode ?? "auto-route"
|
|
36970
|
+
const conn = classifyConnectionError(err);
|
|
36971
|
+
if (conn) {
|
|
36972
|
+
const outcome = await this.recoverConnection(c, err, conn, this.connectionEndpointFor(err, endpoint), doAuthRetry, {
|
|
36973
|
+
startTime,
|
|
36974
|
+
deadlineAt: tier1DeadlineAt(c),
|
|
36975
|
+
fallbackMeta,
|
|
36976
|
+
retriedBeforeLadder: true,
|
|
36977
|
+
authType: "oauth",
|
|
36978
|
+
site: "forceRefreshAuth",
|
|
36979
|
+
recovery: recoveryTally
|
|
35980
36980
|
});
|
|
35981
|
-
|
|
35982
|
-
|
|
36981
|
+
if (outcome.kind === "respond")
|
|
36982
|
+
return outcome.response;
|
|
36983
|
+
const answered = await settleAuthRetry(outcome.value);
|
|
36984
|
+
if (answered)
|
|
36985
|
+
return answered;
|
|
36986
|
+
} else {
|
|
36987
|
+
log(`[${this.provider.displayName}] Auth refresh failed: ${err.message}`);
|
|
36988
|
+
logStderr(`Error [${this.provider.displayName}]: Authentication failed \u2014 ${err.message}. Check API key.`);
|
|
36989
|
+
reportError({
|
|
36990
|
+
error: err,
|
|
36991
|
+
providerName: this.provider.name,
|
|
36992
|
+
providerDisplayName: this.provider.displayName,
|
|
36993
|
+
streamFormat: this.provider.streamFormat,
|
|
36994
|
+
modelId: this.targetModel,
|
|
36995
|
+
httpStatus: 401,
|
|
36996
|
+
isStreaming: false,
|
|
36997
|
+
retryAttempted: true,
|
|
36998
|
+
isInteractive: this.isInteractive,
|
|
36999
|
+
authType: "oauth"
|
|
37000
|
+
});
|
|
37001
|
+
try {
|
|
37002
|
+
const { error_class, error_code } = classifyError(err, 401, err.message);
|
|
37003
|
+
recordStats({
|
|
37004
|
+
model_id: this.targetModel,
|
|
37005
|
+
provider_name: this.provider.name,
|
|
37006
|
+
stream_format: this.provider.streamFormat,
|
|
37007
|
+
latency_ms: Math.round(performance.now() - startTime),
|
|
37008
|
+
success: false,
|
|
37009
|
+
http_status: 401,
|
|
37010
|
+
error_class,
|
|
37011
|
+
error_code,
|
|
37012
|
+
token_strategy: this.options.tokenStrategy ?? "standard",
|
|
37013
|
+
adapter_name: this.getActiveAdapterName(),
|
|
37014
|
+
middleware_names: this.middlewareManager.getActiveNames(this.bareModelName),
|
|
37015
|
+
fallback_used: fallbackMeta !== undefined,
|
|
37016
|
+
fallback_chain: fallbackMeta?.chain,
|
|
37017
|
+
fallback_attempts: fallbackMeta?.attempts,
|
|
37018
|
+
invocation_mode: this.options.invocationMode ?? "auto-route",
|
|
37019
|
+
...this.recoveryStats(recoveryTally)
|
|
37020
|
+
});
|
|
37021
|
+
} catch {}
|
|
37022
|
+
return c.json(wrapAnthropicError(401, err.message, "authentication_error"), 401);
|
|
37023
|
+
}
|
|
35983
37024
|
}
|
|
35984
37025
|
} else {
|
|
35985
37026
|
const errorText = await response.text();
|
|
@@ -36039,7 +37080,8 @@ class ComposedHandler {
|
|
|
36039
37080
|
fallback_used: fallbackMeta !== undefined,
|
|
36040
37081
|
fallback_chain: fallbackMeta?.chain,
|
|
36041
37082
|
fallback_attempts: fallbackMeta?.attempts,
|
|
36042
|
-
invocation_mode: this.options.invocationMode ?? "auto-route"
|
|
37083
|
+
invocation_mode: this.options.invocationMode ?? "auto-route",
|
|
37084
|
+
...this.recoveryStats(recoveryTally)
|
|
36043
37085
|
});
|
|
36044
37086
|
} catch {}
|
|
36045
37087
|
const errorBody = parsedErrorBody ?? {
|
|
@@ -36083,7 +37125,8 @@ class ComposedHandler {
|
|
|
36083
37125
|
fallback_used: fallbackMeta !== undefined,
|
|
36084
37126
|
fallback_chain: fallbackMeta?.chain,
|
|
36085
37127
|
fallback_attempts: fallbackMeta?.attempts,
|
|
36086
|
-
invocation_mode: this.options.invocationMode ?? "auto-route"
|
|
37128
|
+
invocation_mode: this.options.invocationMode ?? "auto-route",
|
|
37129
|
+
...this.recoveryStats(recoveryTally)
|
|
36087
37130
|
});
|
|
36088
37131
|
} catch {}
|
|
36089
37132
|
return c.json(wrapAnthropicError(503, surfaced, "overloaded_error", undefined, settled.message), 503);
|
|
@@ -36125,7 +37168,8 @@ class ComposedHandler {
|
|
|
36125
37168
|
fallback_used: fallbackMeta !== undefined,
|
|
36126
37169
|
fallback_chain: fallbackMeta?.chain,
|
|
36127
37170
|
fallback_attempts: fallbackMeta?.attempts,
|
|
36128
|
-
invocation_mode: this.options.invocationMode ?? "auto-route"
|
|
37171
|
+
invocation_mode: this.options.invocationMode ?? "auto-route",
|
|
37172
|
+
...this.recoveryStats(recoveryTally)
|
|
36129
37173
|
});
|
|
36130
37174
|
} catch {}
|
|
36131
37175
|
return isTerminal ? c.json(wrapAnthropicError(400, surfaced, "invalid_request_error", undefined, settled.message), 400) : c.json(wrapAnthropicError(503, surfaced, "overloaded_error", undefined, settled.message), 503);
|
|
@@ -36156,7 +37200,8 @@ class ComposedHandler {
|
|
|
36156
37200
|
fallback_used: fallbackMeta !== undefined,
|
|
36157
37201
|
fallback_chain: fallbackMeta?.chain,
|
|
36158
37202
|
fallback_attempts: fallbackMeta?.attempts,
|
|
36159
|
-
invocation_mode: this.options.invocationMode ?? "auto-route"
|
|
37203
|
+
invocation_mode: this.options.invocationMode ?? "auto-route",
|
|
37204
|
+
...this.recoveryStats(recoveryTally)
|
|
36160
37205
|
});
|
|
36161
37206
|
} catch {}
|
|
36162
37207
|
try {
|
|
@@ -36501,6 +37546,9 @@ var init_composed_handler = __esm(() => {
|
|
|
36501
37546
|
init_middleware();
|
|
36502
37547
|
init_provider_definitions();
|
|
36503
37548
|
init_openai();
|
|
37549
|
+
init_clock();
|
|
37550
|
+
init_coordinator();
|
|
37551
|
+
init_settings();
|
|
36504
37552
|
init_vision_proxy();
|
|
36505
37553
|
init_session_events();
|
|
36506
37554
|
init_pro_injection();
|
|
@@ -36522,6 +37570,7 @@ var init_composed_handler = __esm(() => {
|
|
|
36522
37570
|
init_openai_responses_sse();
|
|
36523
37571
|
init_openai_sse();
|
|
36524
37572
|
init_token_tracker();
|
|
37573
|
+
init_transient_retry();
|
|
36525
37574
|
init_upstream_error_capture();
|
|
36526
37575
|
STREAM_RETRY_DELAYS_MS = [3000, 15000, 30000];
|
|
36527
37576
|
});
|
|
@@ -37085,6 +38134,7 @@ var init_openai_codex = __esm(() => {
|
|
|
37085
38134
|
init_model_catalog();
|
|
37086
38135
|
init_authority();
|
|
37087
38136
|
init_billing_probe();
|
|
38137
|
+
init_connection_error();
|
|
37088
38138
|
init_conversation_key();
|
|
37089
38139
|
init_openai();
|
|
37090
38140
|
OpenAICodexTransport = class OpenAICodexTransport extends OpenAIProviderTransport {
|
|
@@ -37092,7 +38142,9 @@ var init_openai_codex = __esm(() => {
|
|
|
37092
38142
|
async refreshAuth() {
|
|
37093
38143
|
try {
|
|
37094
38144
|
this.cachedAuth = await credentials.getRequestAuth("openai-codex", { model: "" });
|
|
37095
|
-
} catch {
|
|
38145
|
+
} catch (err) {
|
|
38146
|
+
if (classifyConnectionError(err))
|
|
38147
|
+
throw err;
|
|
37096
38148
|
this.cachedAuth = null;
|
|
37097
38149
|
}
|
|
37098
38150
|
const signedWithOAuth = this.cachedAuth?.arm === "oauth";
|
|
@@ -37381,7 +38433,7 @@ class VertexProviderTransport {
|
|
|
37381
38433
|
try {
|
|
37382
38434
|
this.cachedAuth = await credentials.getRequestAuth("vertex", { model: this.parsed.model });
|
|
37383
38435
|
} catch (e) {
|
|
37384
|
-
throw new Error(`Vertex AI auth failed: ${e.message}
|
|
38436
|
+
throw new Error(`Vertex AI auth failed: ${e.message}`, { cause: e });
|
|
37385
38437
|
}
|
|
37386
38438
|
}
|
|
37387
38439
|
async forceRefreshAuth() {
|
|
@@ -41849,46 +42901,6 @@ var init_stream_json_reducer = __esm(() => {
|
|
|
41849
42901
|
};
|
|
41850
42902
|
});
|
|
41851
42903
|
|
|
41852
|
-
// src/config.ts
|
|
41853
|
-
var DEFAULT_PORT_RANGE, ENV;
|
|
41854
|
-
var init_config2 = __esm(() => {
|
|
41855
|
-
DEFAULT_PORT_RANGE = { start: 3000, end: 9000 };
|
|
41856
|
-
ENV = {
|
|
41857
|
-
OPENROUTER_API_KEY: "OPENROUTER_API_KEY",
|
|
41858
|
-
CLAUDISH_MODEL: "CLAUDISH_MODEL",
|
|
41859
|
-
CLAUDISH_PORT: "CLAUDISH_PORT",
|
|
41860
|
-
CLAUDISH_ACTIVE_MODEL_NAME: "CLAUDISH_ACTIVE_MODEL_NAME",
|
|
41861
|
-
CLAUDISH_TOKEN_FILE: "CLAUDISH_TOKEN_FILE",
|
|
41862
|
-
CLAUDISH_PROVIDER_NAME: "CLAUDISH_PROVIDER_NAME",
|
|
41863
|
-
ANTHROPIC_MODEL: "ANTHROPIC_MODEL",
|
|
41864
|
-
ANTHROPIC_SMALL_FAST_MODEL: "ANTHROPIC_SMALL_FAST_MODEL",
|
|
41865
|
-
CLAUDISH_MODEL_OPUS: "CLAUDISH_MODEL_OPUS",
|
|
41866
|
-
CLAUDISH_MODEL_SONNET: "CLAUDISH_MODEL_SONNET",
|
|
41867
|
-
CLAUDISH_MODEL_HAIKU: "CLAUDISH_MODEL_HAIKU",
|
|
41868
|
-
CLAUDISH_MODEL_SUBAGENT: "CLAUDISH_MODEL_SUBAGENT",
|
|
41869
|
-
ANTHROPIC_DEFAULT_OPUS_MODEL: "ANTHROPIC_DEFAULT_OPUS_MODEL",
|
|
41870
|
-
ANTHROPIC_DEFAULT_SONNET_MODEL: "ANTHROPIC_DEFAULT_SONNET_MODEL",
|
|
41871
|
-
ANTHROPIC_DEFAULT_HAIKU_MODEL: "ANTHROPIC_DEFAULT_HAIKU_MODEL",
|
|
41872
|
-
CLAUDE_CODE_SUBAGENT_MODEL: "CLAUDE_CODE_SUBAGENT_MODEL",
|
|
41873
|
-
CLAUDE_CODE_AUTO_COMPACT_WINDOW: "CLAUDE_CODE_AUTO_COMPACT_WINDOW",
|
|
41874
|
-
CLAUDE_CODE_MAX_CONTEXT_TOKENS: "CLAUDE_CODE_MAX_CONTEXT_TOKENS",
|
|
41875
|
-
OLLAMA_BASE_URL: "OLLAMA_BASE_URL",
|
|
41876
|
-
OLLAMA_HOST: "OLLAMA_HOST",
|
|
41877
|
-
LMSTUDIO_BASE_URL: "LMSTUDIO_BASE_URL",
|
|
41878
|
-
VLLM_BASE_URL: "VLLM_BASE_URL",
|
|
41879
|
-
GEMINI_API_KEY: "GEMINI_API_KEY",
|
|
41880
|
-
GEMINI_BASE_URL: "GEMINI_BASE_URL",
|
|
41881
|
-
OPENAI_API_KEY: "OPENAI_API_KEY",
|
|
41882
|
-
OPENAI_BASE_URL: "OPENAI_BASE_URL",
|
|
41883
|
-
CLAUDISH_SUMMARIZE_TOOLS: "CLAUDISH_SUMMARIZE_TOOLS",
|
|
41884
|
-
CLAUDISH_DIAG_MODE: "CLAUDISH_DIAG_MODE",
|
|
41885
|
-
CLAUDISH_DEBUG: "CLAUDISH_DEBUG",
|
|
41886
|
-
CLAUDISH_ANTHROPIC_API_BILLING: "CLAUDISH_ANTHROPIC_API_BILLING",
|
|
41887
|
-
CLAUDISH_CLASSIFIER_PROVIDER: "CLAUDISH_CLASSIFIER_PROVIDER",
|
|
41888
|
-
CLAUDISH_CLASSIFIER_MODEL: "CLAUDISH_CLASSIFIER_MODEL"
|
|
41889
|
-
};
|
|
41890
|
-
});
|
|
41891
|
-
|
|
41892
42904
|
// src/process-tree.ts
|
|
41893
42905
|
function signalProcessTree(proc, signal) {
|
|
41894
42906
|
if (KILL_PROCESS_GROUP && proc.pid) {
|
|
@@ -43323,7 +44335,7 @@ var TEAM_CAPTURE_ENV_VAR = "CLAUDISH_TEAM_CAPTURE", liveTeamRuns, STDOUT_TAIL_LI
|
|
|
43323
44335
|
var init_team_orchestrator = __esm(() => {
|
|
43324
44336
|
init_prehydrate();
|
|
43325
44337
|
init_stream_json_reducer();
|
|
43326
|
-
|
|
44338
|
+
init_config();
|
|
43327
44339
|
init_upstream_error_capture();
|
|
43328
44340
|
init_process_tree();
|
|
43329
44341
|
init_redact();
|
|
@@ -43337,7 +44349,7 @@ var init_team_orchestrator = __esm(() => {
|
|
|
43337
44349
|
|
|
43338
44350
|
// src/channel/session-manager.ts
|
|
43339
44351
|
import { spawn as spawn3 } from "child_process";
|
|
43340
|
-
import { randomUUID as
|
|
44352
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
43341
44353
|
import {
|
|
43342
44354
|
appendFileSync as appendFileSync6,
|
|
43343
44355
|
closeSync as closeSync7,
|
|
@@ -43493,8 +44505,8 @@ class SessionManager {
|
|
|
43493
44505
|
if (opts.sessionId !== undefined && this.sessions.has(opts.sessionId)) {
|
|
43494
44506
|
throw new Error(`Session id already in use: ${opts.sessionId}`);
|
|
43495
44507
|
}
|
|
43496
|
-
const sessionId = opts.sessionId ??
|
|
43497
|
-
const claudeSessionId =
|
|
44508
|
+
const sessionId = opts.sessionId ?? randomUUID5().slice(0, 8);
|
|
44509
|
+
const claudeSessionId = randomUUID5();
|
|
43498
44510
|
const timeout = Math.min(opts.timeoutSeconds ?? DEFAULT_TIMEOUT, MAX_TIMEOUT);
|
|
43499
44511
|
const startedAt = new Date().toISOString();
|
|
43500
44512
|
const sessionDir = opts.sessionDir ?? join31(this.sessionsDir, sessionId);
|
|
@@ -44224,7 +45236,7 @@ ${STDERR_TRUNCATION_MARKER} ${STDERR_SIDE_LIMIT} bytes per end \u2026
|
|
|
44224
45236
|
}
|
|
44225
45237
|
var DEFAULT_MAX_SESSIONS = 20, DRAIN_TIMEOUT_MS = 1e4, DEFAULT_SCROLLBACK = 2000, DEFAULT_TIMEOUT = 600, MAX_TIMEOUT = 3600, KILL_GRACE_MS = 5000, TERMINAL_RETENTION_MS, MAX_TERMINAL_SESSIONS = 50, STDERR_SIDE_LIMIT, EVENT_LOG_LIMIT, EVENT_RING_SIZE = 200, EVENT_PREVIEW_CHARS = 800, DEFAULT_EVENT_LIMIT = 40, UPSTREAM_ERROR_TAIL_BYTES, STDERR_TRUNCATION_MARKER = "[claudish] \u2026 stderr truncated to", TERMINAL_STATUSES, KNOWN_STATUSES, SESSION_ID_RE, META_READ_LIMIT, OUTPUT_TAIL_BYTES, STDERR_READ_BYTES, EVENT_TAIL_BYTES, NO_TERMINAL_RECORD = "claudish_no_terminal_record", RESERVED_FLAG_ALIASES, TRANSPORT_BREAKING_FLAGS, RESERVED_CHILD_FLAGS, metaString = (v) => typeof v === "string" && v.length > 0 ? v : null, metaNumber = (v) => typeof v === "number" && Number.isFinite(v) ? v : null, metaStatus = (v) => typeof v === "string" && KNOWN_STATUSES.includes(v) ? v : null, CLAUDISH_NOTE_PREFIX = "[claudish] ";
|
|
44226
45238
|
var init_session_manager = __esm(() => {
|
|
44227
|
-
|
|
45239
|
+
init_config();
|
|
44228
45240
|
init_upstream_error_capture();
|
|
44229
45241
|
init_process_tree();
|
|
44230
45242
|
init_redact();
|
|
@@ -44849,7 +45861,8 @@ async function probeLink(proxyUrl, link, timeoutMs) {
|
|
|
44849
45861
|
response = await fetch(`${proxyUrl}/v1/messages`, {
|
|
44850
45862
|
method: "POST",
|
|
44851
45863
|
headers: {
|
|
44852
|
-
"Content-Type": "application/json"
|
|
45864
|
+
"Content-Type": "application/json",
|
|
45865
|
+
"x-claudish-no-recovery": "1"
|
|
44853
45866
|
},
|
|
44854
45867
|
body: JSON.stringify({
|
|
44855
45868
|
model: link.modelSpec,
|
|
@@ -48774,8 +49787,12 @@ class FallbackHandler {
|
|
|
48774
49787
|
}
|
|
48775
49788
|
return response;
|
|
48776
49789
|
}
|
|
49790
|
+
if (isRecoveryHoldResponse(response)) {
|
|
49791
|
+
logStderr(`[Fallback] ${name} is unreachable and claudish is still retrying it \u2014 ` + "holding the chain here rather than switching providers mid-outage.");
|
|
49792
|
+
return response;
|
|
49793
|
+
}
|
|
48777
49794
|
const errorBody = await response.clone().text();
|
|
48778
|
-
if (!isRetryableError(response.status, errorBody, name)) {
|
|
49795
|
+
if (!isRetryableError(response.status, errorBody, name, response.headers)) {
|
|
48779
49796
|
if (errors.length > 0) {
|
|
48780
49797
|
errors.push({ provider: name, status: response.status, message: errorBody });
|
|
48781
49798
|
return this.formatCombinedError(c, errors, payload.model);
|
|
@@ -48824,7 +49841,9 @@ ${summary}`);
|
|
|
48824
49841
|
}
|
|
48825
49842
|
}
|
|
48826
49843
|
}
|
|
48827
|
-
function isRetryableError(status, errorBody, provider) {
|
|
49844
|
+
function isRetryableError(status, errorBody, provider, headers) {
|
|
49845
|
+
if (isClaudishConnectionVerdict(headers))
|
|
49846
|
+
return false;
|
|
48828
49847
|
if (hasQuotaExhaustionWording(errorBody))
|
|
48829
49848
|
return true;
|
|
48830
49849
|
const upstream = status === 400 ? extractUpstreamStatus(errorBody) : undefined;
|
|
@@ -49071,6 +50090,14 @@ var init_native_handler = __esm(() => {
|
|
|
49071
50090
|
});
|
|
49072
50091
|
|
|
49073
50092
|
// src/handlers/shared/local-queue.ts
|
|
50093
|
+
function toAbortError(reason) {
|
|
50094
|
+
if (reason instanceof Error)
|
|
50095
|
+
return reason;
|
|
50096
|
+
const err = new Error("Request aborted while queued");
|
|
50097
|
+
err.name = "AbortError";
|
|
50098
|
+
return err;
|
|
50099
|
+
}
|
|
50100
|
+
|
|
49074
50101
|
class LocalModelQueue {
|
|
49075
50102
|
static instance = null;
|
|
49076
50103
|
queue = [];
|
|
@@ -49101,7 +50128,10 @@ class LocalModelQueue {
|
|
|
49101
50128
|
return true;
|
|
49102
50129
|
return enabled !== "false" && enabled !== "0";
|
|
49103
50130
|
}
|
|
49104
|
-
async enqueue(fetchFn, providerId, concurrencyOverride) {
|
|
50131
|
+
async enqueue(fetchFn, providerId, concurrencyOverride, signal) {
|
|
50132
|
+
if (signal?.aborted) {
|
|
50133
|
+
throw toAbortError(signal.reason);
|
|
50134
|
+
}
|
|
49105
50135
|
if (concurrencyOverride !== undefined) {
|
|
49106
50136
|
if (concurrencyOverride === 0) {
|
|
49107
50137
|
if (getLogLevel() === "debug") {
|
|
@@ -49128,8 +50158,22 @@ class LocalModelQueue {
|
|
|
49128
50158
|
fetchFn,
|
|
49129
50159
|
resolve,
|
|
49130
50160
|
reject,
|
|
49131
|
-
providerId
|
|
50161
|
+
providerId,
|
|
50162
|
+
signal
|
|
49132
50163
|
};
|
|
50164
|
+
if (signal) {
|
|
50165
|
+
const onAbort = () => {
|
|
50166
|
+
queuedRequest.abandoned = true;
|
|
50167
|
+
queuedRequest.detach?.();
|
|
50168
|
+
queuedRequest.detach = undefined;
|
|
50169
|
+
const i = this.queue.indexOf(queuedRequest);
|
|
50170
|
+
if (i !== -1)
|
|
50171
|
+
this.queue.splice(i, 1);
|
|
50172
|
+
reject(toAbortError(signal.reason));
|
|
50173
|
+
};
|
|
50174
|
+
queuedRequest.detach = () => signal.removeEventListener("abort", onAbort);
|
|
50175
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
50176
|
+
}
|
|
49133
50177
|
this.queue.push(queuedRequest);
|
|
49134
50178
|
if (getLogLevel() === "debug") {
|
|
49135
50179
|
log(`[LocalQueue] Request enqueued for ${providerId} (queue length: ${this.queue.length}, active: ${this.activeRequests}/${this.maxParallel})`);
|
|
@@ -49142,6 +50186,11 @@ class LocalModelQueue {
|
|
|
49142
50186
|
const request = this.queue.shift();
|
|
49143
50187
|
if (!request)
|
|
49144
50188
|
break;
|
|
50189
|
+
if (request.abandoned || request.signal?.aborted) {
|
|
50190
|
+
request.detach?.();
|
|
50191
|
+
request.detach = undefined;
|
|
50192
|
+
continue;
|
|
50193
|
+
}
|
|
49145
50194
|
if (getLogLevel() === "debug") {
|
|
49146
50195
|
log(`[LocalQueue] Processing request for ${request.providerId} (${this.queue.length} remaining in queue, ${this.activeRequests + 1}/${this.maxParallel} active)`);
|
|
49147
50196
|
}
|
|
@@ -49155,6 +50204,8 @@ class LocalModelQueue {
|
|
|
49155
50204
|
}
|
|
49156
50205
|
async executeRequest(request) {
|
|
49157
50206
|
this.activeRequests++;
|
|
50207
|
+
request.detach?.();
|
|
50208
|
+
request.detach = undefined;
|
|
49158
50209
|
try {
|
|
49159
50210
|
const response = await request.fetchFn();
|
|
49160
50211
|
if (response.status === 500) {
|
|
@@ -49250,6 +50301,8 @@ class LocalTransport {
|
|
|
49250
50301
|
concurrency;
|
|
49251
50302
|
healthChecked = false;
|
|
49252
50303
|
isHealthy = false;
|
|
50304
|
+
lastProbeError;
|
|
50305
|
+
lastProbeUrl;
|
|
49253
50306
|
_contextWindow = 32768;
|
|
49254
50307
|
constructor(config, modelName, options) {
|
|
49255
50308
|
this.config = config;
|
|
@@ -49317,17 +50370,19 @@ class LocalTransport {
|
|
|
49317
50370
|
}
|
|
49318
50371
|
return {};
|
|
49319
50372
|
}
|
|
49320
|
-
async enqueueRequest(fetchFn) {
|
|
50373
|
+
async enqueueRequest(fetchFn, opts) {
|
|
49321
50374
|
if (!LocalModelQueue.isEnabled())
|
|
49322
50375
|
return fetchFn();
|
|
49323
|
-
return LocalModelQueue.getInstance().enqueue(fetchFn, this.name, this.concurrency);
|
|
50376
|
+
return LocalModelQueue.getInstance().enqueue(fetchFn, this.name, this.concurrency, opts?.signal);
|
|
49324
50377
|
}
|
|
49325
50378
|
async refreshAuth() {
|
|
49326
50379
|
if (this.healthChecked)
|
|
49327
50380
|
return;
|
|
49328
50381
|
const healthy = await this.checkHealth();
|
|
49329
50382
|
if (!healthy) {
|
|
49330
|
-
throw new Error(this.getConnectionErrorMessage()
|
|
50383
|
+
throw Object.assign(new Error(this.getConnectionErrorMessage(), {
|
|
50384
|
+
cause: this.lastProbeError
|
|
50385
|
+
}), { claudishEndpoint: this.lastProbeUrl ?? this.config.baseUrl });
|
|
49331
50386
|
}
|
|
49332
50387
|
await this.fetchContextWindow();
|
|
49333
50388
|
}
|
|
@@ -49340,8 +50395,10 @@ class LocalTransport {
|
|
|
49340
50395
|
async checkHealth() {
|
|
49341
50396
|
if (this.healthChecked)
|
|
49342
50397
|
return this.isHealthy;
|
|
50398
|
+
this.lastProbeError = undefined;
|
|
50399
|
+
this.lastProbeUrl = undefined;
|
|
50400
|
+
const healthUrl = `${this.config.baseUrl}/api/tags`;
|
|
49343
50401
|
try {
|
|
49344
|
-
const healthUrl = `${this.config.baseUrl}/api/tags`;
|
|
49345
50402
|
log(`[${this.displayName}] Trying health check: ${healthUrl}`);
|
|
49346
50403
|
const response = await fetch(healthUrl, {
|
|
49347
50404
|
method: "GET",
|
|
@@ -49355,10 +50412,12 @@ class LocalTransport {
|
|
|
49355
50412
|
}
|
|
49356
50413
|
log(`[${this.displayName}] /api/tags returned ${response.status}, trying /v1/models`);
|
|
49357
50414
|
} catch (e) {
|
|
50415
|
+
this.lastProbeError = markOwnTimeout(e);
|
|
50416
|
+
this.lastProbeUrl = healthUrl;
|
|
49358
50417
|
log(`[${this.displayName}] /api/tags failed: ${e?.message || e}, trying /v1/models`);
|
|
49359
50418
|
}
|
|
50419
|
+
const modelsUrl = `${this.config.baseUrl}/v1/models`;
|
|
49360
50420
|
try {
|
|
49361
|
-
const modelsUrl = `${this.config.baseUrl}/v1/models`;
|
|
49362
50421
|
log(`[${this.displayName}] Trying health check: ${modelsUrl}`);
|
|
49363
50422
|
const response = await fetch(modelsUrl, {
|
|
49364
50423
|
method: "GET",
|
|
@@ -49372,9 +50431,10 @@ class LocalTransport {
|
|
|
49372
50431
|
}
|
|
49373
50432
|
log(`[${this.displayName}] /v1/models returned ${response.status}`);
|
|
49374
50433
|
} catch (e) {
|
|
50434
|
+
this.lastProbeError = markOwnTimeout(e);
|
|
50435
|
+
this.lastProbeUrl = modelsUrl;
|
|
49375
50436
|
log(`[${this.displayName}] /v1/models failed: ${e?.message || e}`);
|
|
49376
50437
|
}
|
|
49377
|
-
this.healthChecked = true;
|
|
49378
50438
|
this.isHealthy = false;
|
|
49379
50439
|
log(`[${this.displayName}] Health check FAILED - provider not available`);
|
|
49380
50440
|
return false;
|
|
@@ -49467,6 +50527,7 @@ class LocalTransport {
|
|
|
49467
50527
|
var localProviderAgent, DISPLAY_NAMES2;
|
|
49468
50528
|
var init_local = __esm(() => {
|
|
49469
50529
|
init_authority();
|
|
50530
|
+
init_connection_error();
|
|
49470
50531
|
init_local_queue();
|
|
49471
50532
|
init_logger();
|
|
49472
50533
|
init_probe_discovery();
|
|
@@ -50337,6 +51398,7 @@ ${plan.hint}` : `[Route] ${plan.reason}`;
|
|
|
50337
51398
|
modelRequestCount: () => modelRequestCount,
|
|
50338
51399
|
shutdown: async () => {
|
|
50339
51400
|
await server.stop(true);
|
|
51401
|
+
closeAllEpisodes();
|
|
50340
51402
|
},
|
|
50341
51403
|
invalidateHandlerCache: (providerSlug) => {
|
|
50342
51404
|
if (!providerSlug) {
|
|
@@ -50389,6 +51451,7 @@ var init_proxy_server = __esm(() => {
|
|
|
50389
51451
|
init_local();
|
|
50390
51452
|
init_openrouter();
|
|
50391
51453
|
init_poe();
|
|
51454
|
+
init_coordinator();
|
|
50392
51455
|
init_pricing_cache();
|
|
50393
51456
|
RoutingError = class RoutingError extends Error {
|
|
50394
51457
|
constructor(message) {
|
|
@@ -52322,14 +53385,57 @@ var init_behavior_command = __esm(() => {
|
|
|
52322
53385
|
init_ansi();
|
|
52323
53386
|
});
|
|
52324
53387
|
|
|
53388
|
+
// src/launcher/magmux-binary.ts
|
|
53389
|
+
import { execSync } from "child_process";
|
|
53390
|
+
import { existsSync as existsSync27 } from "fs";
|
|
53391
|
+
import { dirname as dirname11, join as join36 } from "path";
|
|
53392
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
53393
|
+
function findMagmuxBinaryOrNull() {
|
|
53394
|
+
const thisFile = fileURLToPath2(import.meta.url);
|
|
53395
|
+
const thisDir = dirname11(thisFile);
|
|
53396
|
+
const pkgRoots = [join36(thisDir, ".."), join36(thisDir, "..", "..")];
|
|
53397
|
+
const platform = process.platform;
|
|
53398
|
+
const arch = process.arch;
|
|
53399
|
+
for (const pkgRoot of pkgRoots) {
|
|
53400
|
+
const bundled = join36(pkgRoot, "native", `magmux-${platform}-${arch}`);
|
|
53401
|
+
if (existsSync27(bundled))
|
|
53402
|
+
return bundled;
|
|
53403
|
+
}
|
|
53404
|
+
try {
|
|
53405
|
+
const pkgName = `@claudish/magmux-${platform}-${arch}`;
|
|
53406
|
+
let searchDir = pkgRoots[0];
|
|
53407
|
+
for (let i = 0;i < 5; i++) {
|
|
53408
|
+
const candidate = join36(searchDir, "node_modules", pkgName, "bin", "magmux");
|
|
53409
|
+
if (existsSync27(candidate))
|
|
53410
|
+
return candidate;
|
|
53411
|
+
const parent = dirname11(searchDir);
|
|
53412
|
+
if (parent === searchDir)
|
|
53413
|
+
break;
|
|
53414
|
+
searchDir = parent;
|
|
53415
|
+
}
|
|
53416
|
+
} catch {}
|
|
53417
|
+
try {
|
|
53418
|
+
const result = execSync("which magmux", { encoding: "utf-8" }).trim();
|
|
53419
|
+
if (result)
|
|
53420
|
+
return result;
|
|
53421
|
+
} catch {}
|
|
53422
|
+
return null;
|
|
53423
|
+
}
|
|
53424
|
+
function findMagmuxBinary() {
|
|
53425
|
+
const found = findMagmuxBinaryOrNull();
|
|
53426
|
+
if (found)
|
|
53427
|
+
return found;
|
|
53428
|
+
throw new Error(`magmux not found. Install it:
|
|
53429
|
+
brew install MadAppGang/tap/magmux`);
|
|
53430
|
+
}
|
|
53431
|
+
var init_magmux_binary = () => {};
|
|
53432
|
+
|
|
52325
53433
|
// src/team-grid.ts
|
|
52326
53434
|
import { spawn as spawn4 } from "child_process";
|
|
52327
|
-
import {
|
|
52328
|
-
import { existsSync as existsSync27, readFileSync as readFileSync28, writeFileSync as writeFileSync17 } from "fs";
|
|
53435
|
+
import { existsSync as existsSync28, readFileSync as readFileSync28, writeFileSync as writeFileSync17 } from "fs";
|
|
52329
53436
|
import { connect as netConnect } from "net";
|
|
52330
|
-
import {
|
|
53437
|
+
import { join as join37 } from "path";
|
|
52331
53438
|
import { setTimeout as wait } from "timers/promises";
|
|
52332
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
52333
53439
|
function resolveRouteInfo(modelId) {
|
|
52334
53440
|
const parsed = parseModelSpec(modelId);
|
|
52335
53441
|
if (parsed.isExplicitProvider) {
|
|
@@ -52418,36 +53524,6 @@ function buildPaneHeader(model, prompt, bg) {
|
|
|
52418
53524
|
lines.push(`printf '\\033[2m %s\\033[0m\\n\\n' '\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500';`);
|
|
52419
53525
|
return lines.join(" ");
|
|
52420
53526
|
}
|
|
52421
|
-
function findMagmuxBinary() {
|
|
52422
|
-
const thisFile = fileURLToPath2(import.meta.url);
|
|
52423
|
-
const thisDir = dirname11(thisFile);
|
|
52424
|
-
const pkgRoot = join36(thisDir, "..");
|
|
52425
|
-
const platform = process.platform;
|
|
52426
|
-
const arch = process.arch;
|
|
52427
|
-
const bundledMagmux = join36(pkgRoot, "native", `magmux-${platform}-${arch}`);
|
|
52428
|
-
if (existsSync27(bundledMagmux))
|
|
52429
|
-
return bundledMagmux;
|
|
52430
|
-
try {
|
|
52431
|
-
const pkgName = `@claudish/magmux-${platform}-${arch}`;
|
|
52432
|
-
let searchDir = pkgRoot;
|
|
52433
|
-
for (let i = 0;i < 5; i++) {
|
|
52434
|
-
const candidate = join36(searchDir, "node_modules", pkgName, "bin", "magmux");
|
|
52435
|
-
if (existsSync27(candidate))
|
|
52436
|
-
return candidate;
|
|
52437
|
-
const parent = dirname11(searchDir);
|
|
52438
|
-
if (parent === searchDir)
|
|
52439
|
-
break;
|
|
52440
|
-
searchDir = parent;
|
|
52441
|
-
}
|
|
52442
|
-
} catch {}
|
|
52443
|
-
try {
|
|
52444
|
-
const result = execSync("which magmux", { encoding: "utf-8" }).trim();
|
|
52445
|
-
if (result)
|
|
52446
|
-
return result;
|
|
52447
|
-
} catch {}
|
|
52448
|
-
throw new Error(`magmux not found. Install it:
|
|
52449
|
-
brew install MadAppGang/tap/magmux`);
|
|
52450
|
-
}
|
|
52451
53527
|
function withoutControlPanes(evt) {
|
|
52452
53528
|
if (!Array.isArray(evt.panes))
|
|
52453
53529
|
return evt;
|
|
@@ -52456,7 +53532,7 @@ function withoutControlPanes(evt) {
|
|
|
52456
53532
|
async function subscribeToMagmux(sockPath, onEvent) {
|
|
52457
53533
|
let client = null;
|
|
52458
53534
|
for (let attempt = 0;attempt < 40; attempt++) {
|
|
52459
|
-
if (
|
|
53535
|
+
if (existsSync28(sockPath)) {
|
|
52460
53536
|
try {
|
|
52461
53537
|
client = await new Promise((resolve, reject) => {
|
|
52462
53538
|
const s = netConnect(sockPath);
|
|
@@ -52543,9 +53619,9 @@ async function runWithGrid(sessionPath, models, input, opts) {
|
|
|
52543
53619
|
const keep = opts?.keep ?? false;
|
|
52544
53620
|
const manifest = setupSession(sessionPath, models, input);
|
|
52545
53621
|
const startedAt = new Date().toISOString();
|
|
52546
|
-
const gridfilePath =
|
|
52547
|
-
const prompt = readFileSync28(
|
|
52548
|
-
const rawPrompt = readFileSync28(
|
|
53622
|
+
const gridfilePath = join37(sessionPath, "gridfile.txt");
|
|
53623
|
+
const prompt = readFileSync28(join37(sessionPath, "input.md"), "utf-8").replace(/'/g, "'\\''").replace(/\n/g, " ");
|
|
53624
|
+
const rawPrompt = readFileSync28(join37(sessionPath, "input.md"), "utf-8");
|
|
52549
53625
|
const usedBannerColors = new Set;
|
|
52550
53626
|
const gridLines = Object.entries(manifest.models).map(([anonId]) => {
|
|
52551
53627
|
const model = manifest.models[anonId].model;
|
|
@@ -52576,12 +53652,13 @@ async function runWithGrid(sessionPath, models, input, opts) {
|
|
|
52576
53652
|
});
|
|
52577
53653
|
const [{ results }] = await Promise.all([subscription, procExit]);
|
|
52578
53654
|
const status = buildTeamStatus(manifest, startedAt, results?.panes ?? null);
|
|
52579
|
-
const statusPath =
|
|
53655
|
+
const statusPath = join37(sessionPath, "status.json");
|
|
52580
53656
|
writeFileSync17(statusPath, JSON.stringify(status, null, 2), "utf-8");
|
|
52581
53657
|
return status;
|
|
52582
53658
|
}
|
|
52583
53659
|
var BANNER_BG_COLORS;
|
|
52584
53660
|
var init_team_grid = __esm(() => {
|
|
53661
|
+
init_magmux_binary();
|
|
52585
53662
|
init_profile_config();
|
|
52586
53663
|
init_model_parser();
|
|
52587
53664
|
init_routing_rules();
|
|
@@ -52598,7 +53675,7 @@ var init_team_grid = __esm(() => {
|
|
|
52598
53675
|
|
|
52599
53676
|
// src/team-cli.ts
|
|
52600
53677
|
import { readFileSync as readFileSync29 } from "fs";
|
|
52601
|
-
import { join as
|
|
53678
|
+
import { join as join38 } from "path";
|
|
52602
53679
|
function getFlag(args, flag) {
|
|
52603
53680
|
const idx = args.indexOf(flag);
|
|
52604
53681
|
if (idx === -1 || idx + 1 >= args.length)
|
|
@@ -52721,7 +53798,7 @@ async function teamCommand(args) {
|
|
|
52721
53798
|
}
|
|
52722
53799
|
case "judge": {
|
|
52723
53800
|
await judgeResponses(sessionPath, { judges });
|
|
52724
|
-
console.log(readFileSync29(
|
|
53801
|
+
console.log(readFileSync29(join38(sessionPath, "verdict.md"), "utf-8"));
|
|
52725
53802
|
break;
|
|
52726
53803
|
}
|
|
52727
53804
|
case "run-and-judge": {
|
|
@@ -52738,7 +53815,7 @@ async function teamCommand(args) {
|
|
|
52738
53815
|
});
|
|
52739
53816
|
printStatus(status);
|
|
52740
53817
|
await judgeResponses(sessionPath, { judges });
|
|
52741
|
-
console.log(readFileSync29(
|
|
53818
|
+
console.log(readFileSync29(join38(sessionPath, "verdict.md"), "utf-8"));
|
|
52742
53819
|
break;
|
|
52743
53820
|
}
|
|
52744
53821
|
case "status": {
|
|
@@ -55578,9 +56655,9 @@ var init_keychain_command = __esm(() => {
|
|
|
55578
56655
|
|
|
55579
56656
|
// src/auth/antigravity-oauth.ts
|
|
55580
56657
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
55581
|
-
import { existsSync as
|
|
56658
|
+
import { existsSync as existsSync29, unlinkSync as unlinkSync6 } from "fs";
|
|
55582
56659
|
import { homedir as homedir33 } from "os";
|
|
55583
|
-
import { join as
|
|
56660
|
+
import { join as join39 } from "path";
|
|
55584
56661
|
async function defaultSuggestModel() {
|
|
55585
56662
|
try {
|
|
55586
56663
|
const tok = readSharedAntigravityToken();
|
|
@@ -55701,8 +56778,8 @@ No session detected yet. Starting the Antigravity CLI interactively \u2014
|
|
|
55701
56778
|
async logout(deps) {
|
|
55702
56779
|
deleteSharedAntigravityToken(deps);
|
|
55703
56780
|
try {
|
|
55704
|
-
const tokenFile =
|
|
55705
|
-
if (
|
|
56781
|
+
const tokenFile = join39(homedir33(), ".gemini", "antigravity-cli", "antigravity-oauth-token");
|
|
56782
|
+
if (existsSync29(tokenFile))
|
|
55706
56783
|
unlinkSync6(tokenFile);
|
|
55707
56784
|
} catch {}
|
|
55708
56785
|
log("[AntigravityOAuth] Antigravity session cleared (keychain + agy token file)");
|
|
@@ -57211,9 +58288,9 @@ var init_DiscoveryNotice = __esm(() => {
|
|
|
57211
58288
|
});
|
|
57212
58289
|
|
|
57213
58290
|
// src/providers/model-descriptions.ts
|
|
57214
|
-
import { existsSync as
|
|
58291
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync17, readFileSync as readFileSync30, writeFileSync as writeFileSync18 } from "fs";
|
|
57215
58292
|
import { homedir as homedir34 } from "os";
|
|
57216
|
-
import { dirname as dirname12, join as
|
|
58293
|
+
import { dirname as dirname12, join as join40 } from "path";
|
|
57217
58294
|
function emptyDescriptionIndex() {
|
|
57218
58295
|
return EMPTY_INDEX;
|
|
57219
58296
|
}
|
|
@@ -57232,7 +58309,7 @@ function indexFrom(cache) {
|
|
|
57232
58309
|
};
|
|
57233
58310
|
}
|
|
57234
58311
|
function readCache(path) {
|
|
57235
|
-
if (!
|
|
58312
|
+
if (!existsSync30(path))
|
|
57236
58313
|
return null;
|
|
57237
58314
|
try {
|
|
57238
58315
|
const raw = JSON.parse(readFileSync30(path, "utf-8"));
|
|
@@ -57306,7 +58383,7 @@ var init_model_descriptions = __esm(() => {
|
|
|
57306
58383
|
init_logger();
|
|
57307
58384
|
init_model_loader();
|
|
57308
58385
|
init_cache_ttl();
|
|
57309
|
-
MODEL_DESCRIPTIONS_CACHE_PATH =
|
|
58386
|
+
MODEL_DESCRIPTIONS_CACHE_PATH = join40(homedir34(), ".claudish", "model-descriptions.json");
|
|
57310
58387
|
EMPTY_INDEX = { get: () => {
|
|
57311
58388
|
return;
|
|
57312
58389
|
}, size: 0 };
|
|
@@ -62462,30 +63539,30 @@ var init_branding = __esm(() => {
|
|
|
62462
63539
|
});
|
|
62463
63540
|
|
|
62464
63541
|
// src/update-checker.ts
|
|
62465
|
-
import { existsSync as
|
|
63542
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync18, readFileSync as readFileSync31, unlinkSync as unlinkSync7, writeFileSync as writeFileSync19 } from "fs";
|
|
62466
63543
|
import { homedir as homedir35, platform as platform2, tmpdir } from "os";
|
|
62467
|
-
import { join as
|
|
63544
|
+
import { join as join41 } from "path";
|
|
62468
63545
|
function getCacheFilePath() {
|
|
62469
63546
|
let cacheDir;
|
|
62470
63547
|
if (isWindows) {
|
|
62471
|
-
const localAppData = process.env.LOCALAPPDATA ||
|
|
62472
|
-
cacheDir =
|
|
63548
|
+
const localAppData = process.env.LOCALAPPDATA || join41(homedir35(), "AppData", "Local");
|
|
63549
|
+
cacheDir = join41(localAppData, "claudish");
|
|
62473
63550
|
} else {
|
|
62474
|
-
cacheDir =
|
|
63551
|
+
cacheDir = join41(homedir35(), ".cache", "claudish");
|
|
62475
63552
|
}
|
|
62476
63553
|
try {
|
|
62477
|
-
if (!
|
|
63554
|
+
if (!existsSync31(cacheDir)) {
|
|
62478
63555
|
mkdirSync18(cacheDir, { recursive: true });
|
|
62479
63556
|
}
|
|
62480
|
-
return
|
|
63557
|
+
return join41(cacheDir, "update-check.json");
|
|
62481
63558
|
} catch {
|
|
62482
|
-
return
|
|
63559
|
+
return join41(tmpdir(), "claudish-update-check.json");
|
|
62483
63560
|
}
|
|
62484
63561
|
}
|
|
62485
63562
|
function readCache2() {
|
|
62486
63563
|
try {
|
|
62487
63564
|
const cachePath = getCacheFilePath();
|
|
62488
|
-
if (!
|
|
63565
|
+
if (!existsSync31(cachePath)) {
|
|
62489
63566
|
return null;
|
|
62490
63567
|
}
|
|
62491
63568
|
const data = JSON.parse(readFileSync31(cachePath, "utf-8"));
|
|
@@ -62511,7 +63588,7 @@ function isCacheValid(cache) {
|
|
|
62511
63588
|
function clearCache() {
|
|
62512
63589
|
try {
|
|
62513
63590
|
const cachePath = getCacheFilePath();
|
|
62514
|
-
if (
|
|
63591
|
+
if (existsSync31(cachePath)) {
|
|
62515
63592
|
unlinkSync7(cachePath);
|
|
62516
63593
|
}
|
|
62517
63594
|
} catch {}
|
|
@@ -62596,7 +63673,7 @@ var init_update_checker = __esm(() => {
|
|
|
62596
63673
|
// src/cli.ts
|
|
62597
63674
|
import {
|
|
62598
63675
|
copyFileSync as copyFileSync2,
|
|
62599
|
-
existsSync as
|
|
63676
|
+
existsSync as existsSync32,
|
|
62600
63677
|
mkdirSync as mkdirSync19,
|
|
62601
63678
|
readFileSync as readFileSync32,
|
|
62602
63679
|
readdirSync as readdirSync7,
|
|
@@ -62604,14 +63681,14 @@ import {
|
|
|
62604
63681
|
writeFileSync as writeFileSync20
|
|
62605
63682
|
} from "fs";
|
|
62606
63683
|
import { homedir as homedir36 } from "os";
|
|
62607
|
-
import { dirname as dirname13, join as
|
|
63684
|
+
import { dirname as dirname13, join as join42 } from "path";
|
|
62608
63685
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
62609
63686
|
function getVersion3() {
|
|
62610
63687
|
return VERSION;
|
|
62611
63688
|
}
|
|
62612
63689
|
function clearAllModelCaches() {
|
|
62613
|
-
const cacheDir =
|
|
62614
|
-
if (!
|
|
63690
|
+
const cacheDir = join42(homedir36(), ".claudish");
|
|
63691
|
+
if (!existsSync32(cacheDir))
|
|
62615
63692
|
return;
|
|
62616
63693
|
const cachePatterns = ["pricing-cache.json", "recommended-models-cache.json"];
|
|
62617
63694
|
let cleared = 0;
|
|
@@ -62619,7 +63696,7 @@ function clearAllModelCaches() {
|
|
|
62619
63696
|
const files = readdirSync7(cacheDir);
|
|
62620
63697
|
for (const file of files) {
|
|
62621
63698
|
if (cachePatterns.includes(file)) {
|
|
62622
|
-
unlinkSync8(
|
|
63699
|
+
unlinkSync8(join42(cacheDir, file));
|
|
62623
63700
|
cleared++;
|
|
62624
63701
|
}
|
|
62625
63702
|
}
|
|
@@ -62774,6 +63851,10 @@ async function parseArgs(args) {
|
|
|
62774
63851
|
if (config.logLevel === "info") {
|
|
62775
63852
|
config.logLevel = "debug";
|
|
62776
63853
|
}
|
|
63854
|
+
} else if (arg === "--recovery-ui" || arg === "--no-recovery-ui") {
|
|
63855
|
+
setRecoveryFlagOverrides({ recoveryUi: arg === "--recovery-ui" });
|
|
63856
|
+
} else if (arg === "--recovery" || arg === "--no-recovery") {
|
|
63857
|
+
setRecoveryFlagOverrides({ recovery: arg === "--recovery" });
|
|
62777
63858
|
} else if (arg === "--no-debug-claudish") {
|
|
62778
63859
|
config.debug = false;
|
|
62779
63860
|
} else if (arg === "--log-debug") {
|
|
@@ -63093,8 +64174,8 @@ Usage: claudish --models --provider <slug>`);
|
|
|
63093
64174
|
});
|
|
63094
64175
|
config.resolvedDefaultProvider = resolved;
|
|
63095
64176
|
if (resolved.legacyAutoPromoted && !config.quiet) {
|
|
63096
|
-
const markerFile =
|
|
63097
|
-
if (!
|
|
64177
|
+
const markerFile = join42(homedir36(), ".claudish", ".legacy-litellm-hint-shown");
|
|
64178
|
+
if (!existsSync32(markerFile)) {
|
|
63098
64179
|
const hint = buildLegacyHint(resolved);
|
|
63099
64180
|
if (hint) {
|
|
63100
64181
|
console.error(hint);
|
|
@@ -64302,7 +65383,7 @@ ${h("MORE INFO")}
|
|
|
64302
65383
|
}
|
|
64303
65384
|
function printAIAgentGuide() {
|
|
64304
65385
|
try {
|
|
64305
|
-
const guidePath =
|
|
65386
|
+
const guidePath = join42(__dirname3, "../AI_AGENT_GUIDE.md");
|
|
64306
65387
|
const guideContent = readFileSync32(guidePath, "utf-8");
|
|
64307
65388
|
console.log(guideContent);
|
|
64308
65389
|
} catch (error) {
|
|
@@ -64319,19 +65400,19 @@ async function initializeClaudishSkill() {
|
|
|
64319
65400
|
console.log(`\uD83D\uDD27 Initializing Claudish skill in current project...
|
|
64320
65401
|
`);
|
|
64321
65402
|
const cwd = process.cwd();
|
|
64322
|
-
const claudeDir =
|
|
64323
|
-
const skillsDir =
|
|
64324
|
-
const claudishSkillDir =
|
|
64325
|
-
const skillFile =
|
|
64326
|
-
if (
|
|
65403
|
+
const claudeDir = join42(cwd, ".claude");
|
|
65404
|
+
const skillsDir = join42(claudeDir, "skills");
|
|
65405
|
+
const claudishSkillDir = join42(skillsDir, "claudish-usage");
|
|
65406
|
+
const skillFile = join42(claudishSkillDir, "SKILL.md");
|
|
65407
|
+
if (existsSync32(skillFile)) {
|
|
64327
65408
|
console.log("\u2705 Claudish skill already installed at:");
|
|
64328
65409
|
console.log(` ${skillFile}
|
|
64329
65410
|
`);
|
|
64330
65411
|
console.log("\uD83D\uDCA1 To reinstall, delete the file and run 'claudish --init' again.");
|
|
64331
65412
|
return;
|
|
64332
65413
|
}
|
|
64333
|
-
const sourceSkillPath =
|
|
64334
|
-
if (!
|
|
65414
|
+
const sourceSkillPath = join42(__dirname3, "../skills/claudish-usage/SKILL.md");
|
|
65415
|
+
if (!existsSync32(sourceSkillPath)) {
|
|
64335
65416
|
console.error("\u274C Error: Claudish skill file not found in installation.");
|
|
64336
65417
|
console.error(` Expected at: ${sourceSkillPath}`);
|
|
64337
65418
|
console.error(`
|
|
@@ -64340,15 +65421,15 @@ async function initializeClaudishSkill() {
|
|
|
64340
65421
|
process.exit(1);
|
|
64341
65422
|
}
|
|
64342
65423
|
try {
|
|
64343
|
-
if (!
|
|
65424
|
+
if (!existsSync32(claudeDir)) {
|
|
64344
65425
|
mkdirSync19(claudeDir, { recursive: true });
|
|
64345
65426
|
console.log("\uD83D\uDCC1 Created .claude/ directory");
|
|
64346
65427
|
}
|
|
64347
|
-
if (!
|
|
65428
|
+
if (!existsSync32(skillsDir)) {
|
|
64348
65429
|
mkdirSync19(skillsDir, { recursive: true });
|
|
64349
65430
|
console.log("\uD83D\uDCC1 Created .claude/skills/ directory");
|
|
64350
65431
|
}
|
|
64351
|
-
if (!
|
|
65432
|
+
if (!existsSync32(claudishSkillDir)) {
|
|
64352
65433
|
mkdirSync19(claudishSkillDir, { recursive: true });
|
|
64353
65434
|
console.log("\uD83D\uDCC1 Created .claude/skills/claudish-usage/ directory");
|
|
64354
65435
|
}
|
|
@@ -64407,7 +65488,7 @@ function printAvailableModels() {
|
|
|
64407
65488
|
var __filename3, __dirname3;
|
|
64408
65489
|
var init_cli = __esm(() => {
|
|
64409
65490
|
init_base_api_format();
|
|
64410
|
-
|
|
65491
|
+
init_config();
|
|
64411
65492
|
init_logger();
|
|
64412
65493
|
init_model_loader();
|
|
64413
65494
|
init_model_selector();
|
|
@@ -64425,6 +65506,7 @@ var init_cli = __esm(() => {
|
|
|
64425
65506
|
init_provider_definitions();
|
|
64426
65507
|
init_provider_slug_resolve();
|
|
64427
65508
|
init_routing_rules();
|
|
65509
|
+
init_settings();
|
|
64428
65510
|
init_ansi();
|
|
64429
65511
|
init_provider_resolver();
|
|
64430
65512
|
__filename3 = fileURLToPath3(import.meta.url);
|
|
@@ -71760,12 +72842,592 @@ var init_tui = __esm(() => {
|
|
|
71760
72842
|
}
|
|
71761
72843
|
});
|
|
71762
72844
|
|
|
72845
|
+
// src/launcher/magmux-wrapper.ts
|
|
72846
|
+
import {
|
|
72847
|
+
chmodSync,
|
|
72848
|
+
closeSync as closeSync8,
|
|
72849
|
+
existsSync as existsSync33,
|
|
72850
|
+
mkdirSync as mkdirSync20,
|
|
72851
|
+
mkdtempSync,
|
|
72852
|
+
openSync as openSync8,
|
|
72853
|
+
rmSync as rmSync2,
|
|
72854
|
+
writeFileSync as writeFileSync21
|
|
72855
|
+
} from "fs";
|
|
72856
|
+
import { connect as netConnect2 } from "net";
|
|
72857
|
+
import { join as join43 } from "path";
|
|
72858
|
+
function envDeltas(parentEnv, childEnv) {
|
|
72859
|
+
const set = [];
|
|
72860
|
+
const unset = [];
|
|
72861
|
+
for (const [k, v] of Object.entries(childEnv)) {
|
|
72862
|
+
if (STRIPPED_CHILD_VARS.includes(k))
|
|
72863
|
+
continue;
|
|
72864
|
+
if (parentEnv[k] !== v)
|
|
72865
|
+
set.push([k, v]);
|
|
72866
|
+
}
|
|
72867
|
+
for (const k of Object.keys(parentEnv)) {
|
|
72868
|
+
if (!(k in childEnv))
|
|
72869
|
+
unset.push(k);
|
|
72870
|
+
}
|
|
72871
|
+
for (const k of STRIPPED_CHILD_VARS) {
|
|
72872
|
+
if (!unset.includes(k))
|
|
72873
|
+
unset.push(k);
|
|
72874
|
+
}
|
|
72875
|
+
return { set, unset };
|
|
72876
|
+
}
|
|
72877
|
+
function shq(s) {
|
|
72878
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
72879
|
+
}
|
|
72880
|
+
function buildLauncherScript(claudeBinary, claudeArgs, deltas) {
|
|
72881
|
+
const lines = [
|
|
72882
|
+
"#!/bin/sh",
|
|
72883
|
+
"# Generated by claudish. Re-asserts claudish's OWN environment deltas after",
|
|
72884
|
+
"# the login shell has run the user's profile, then hands the terminal to",
|
|
72885
|
+
"# Claude Code. Only deltas are written here: the full environment would put",
|
|
72886
|
+
"# every exported secret into a file."
|
|
72887
|
+
];
|
|
72888
|
+
for (const k of deltas.unset)
|
|
72889
|
+
lines.push(`unset ${k}`);
|
|
72890
|
+
for (const [k, v] of deltas.set)
|
|
72891
|
+
lines.push(`export ${k}=${shq(v)}`);
|
|
72892
|
+
lines.push(`exec ${shq(claudeBinary)} ${claudeArgs.map(shq).join(" ")}`);
|
|
72893
|
+
return `${lines.join(`
|
|
72894
|
+
`)}
|
|
72895
|
+
`;
|
|
72896
|
+
}
|
|
72897
|
+
function magmuxPaneCapability(input) {
|
|
72898
|
+
const parentEnv = input.parentEnv ?? process.env;
|
|
72899
|
+
if (!input.interactive)
|
|
72900
|
+
return { kind: "none", reason: "not-interactive" };
|
|
72901
|
+
const ambient = parentEnv.MAGMUX_SOCK;
|
|
72902
|
+
if (ambient)
|
|
72903
|
+
return { kind: "ambient", sock: ambient };
|
|
72904
|
+
if (!input.stdoutIsTty)
|
|
72905
|
+
return { kind: "none", reason: "no-tty" };
|
|
72906
|
+
const magmux = input.magmuxBinary === undefined ? findMagmuxBinaryOrNull() : input.magmuxBinary;
|
|
72907
|
+
if (!magmux)
|
|
72908
|
+
return { kind: "none", reason: "no-magmux" };
|
|
72909
|
+
return { kind: "wrap", magmux };
|
|
72910
|
+
}
|
|
72911
|
+
function planMagmuxWrap(input) {
|
|
72912
|
+
const capability = magmuxPaneCapability(input);
|
|
72913
|
+
if (capability.kind !== "wrap")
|
|
72914
|
+
return null;
|
|
72915
|
+
const magmux = capability.magmux;
|
|
72916
|
+
const pid = input.pid ?? process.pid;
|
|
72917
|
+
const id = `claudish-${pid}`;
|
|
72918
|
+
const controlSocket = `/tmp/magmux-${id}.sock`;
|
|
72919
|
+
const tmpRoot = input.tmpRoot ?? "/tmp";
|
|
72920
|
+
mkdirSync20(tmpRoot, { recursive: true });
|
|
72921
|
+
const scriptDir = mkdtempSync(join43(tmpRoot, "claudish-launch-"));
|
|
72922
|
+
chmodSync(scriptDir, 448);
|
|
72923
|
+
const scriptPath = join43(scriptDir, "claude-launch.sh");
|
|
72924
|
+
const parentEnv = input.parentEnv ?? process.env;
|
|
72925
|
+
const deltas = envDeltas(parentEnv, input.childEnv);
|
|
72926
|
+
const fd = openSync8(scriptPath, "wx", 384);
|
|
72927
|
+
try {
|
|
72928
|
+
writeFileSync21(fd, buildLauncherScript(input.claudeBinary, input.claudeArgs, deltas));
|
|
72929
|
+
} finally {
|
|
72930
|
+
closeSync8(fd);
|
|
72931
|
+
}
|
|
72932
|
+
const env = { ...input.childEnv };
|
|
72933
|
+
for (const k of STRIPPED_CHILD_VARS)
|
|
72934
|
+
delete env[k];
|
|
72935
|
+
if (!env.MAGMUX_SCROLLBACK)
|
|
72936
|
+
env.MAGMUX_SCROLLBACK = "10000";
|
|
72937
|
+
const args = [
|
|
72938
|
+
"--id",
|
|
72939
|
+
id,
|
|
72940
|
+
"--no-idle-done",
|
|
72941
|
+
"-w",
|
|
72942
|
+
"--no-status",
|
|
72943
|
+
"-e",
|
|
72944
|
+
`. ${shq(scriptPath)}`
|
|
72945
|
+
];
|
|
72946
|
+
let paneExit = null;
|
|
72947
|
+
const exitListeners = [];
|
|
72948
|
+
let control = null;
|
|
72949
|
+
let cleaned = false;
|
|
72950
|
+
const cleanup = () => {
|
|
72951
|
+
if (cleaned)
|
|
72952
|
+
return;
|
|
72953
|
+
cleaned = true;
|
|
72954
|
+
process.removeListener("exit", cleanup);
|
|
72955
|
+
try {
|
|
72956
|
+
control?.destroy();
|
|
72957
|
+
} catch {}
|
|
72958
|
+
control = null;
|
|
72959
|
+
try {
|
|
72960
|
+
rmSync2(scriptDir, { recursive: true, force: true });
|
|
72961
|
+
} catch {}
|
|
72962
|
+
try {
|
|
72963
|
+
if (existsSync33(controlSocket))
|
|
72964
|
+
rmSync2(controlSocket, { force: true });
|
|
72965
|
+
} catch {}
|
|
72966
|
+
};
|
|
72967
|
+
process.on("exit", cleanup);
|
|
72968
|
+
return {
|
|
72969
|
+
command: magmux,
|
|
72970
|
+
args,
|
|
72971
|
+
env,
|
|
72972
|
+
controlSocket,
|
|
72973
|
+
scriptPath,
|
|
72974
|
+
paneExitCode: () => paneExit,
|
|
72975
|
+
onClaudeExit(fn) {
|
|
72976
|
+
exitListeners.push(fn);
|
|
72977
|
+
},
|
|
72978
|
+
watch(proc) {
|
|
72979
|
+
let stopped = false;
|
|
72980
|
+
const attempt = async () => {
|
|
72981
|
+
for (let i = 0;i < 60 && !stopped; i++) {
|
|
72982
|
+
if (existsSync33(controlSocket)) {
|
|
72983
|
+
try {
|
|
72984
|
+
const s = await new Promise((resolve, reject) => {
|
|
72985
|
+
const sock = netConnect2(controlSocket);
|
|
72986
|
+
sock.once("connect", () => resolve(sock));
|
|
72987
|
+
sock.once("error", reject);
|
|
72988
|
+
});
|
|
72989
|
+
control = s;
|
|
72990
|
+
let buf = "";
|
|
72991
|
+
s.on("data", (chunk) => {
|
|
72992
|
+
buf += chunk.toString("utf-8");
|
|
72993
|
+
let nl = buf.indexOf(`
|
|
72994
|
+
`);
|
|
72995
|
+
while (nl >= 0) {
|
|
72996
|
+
const line = buf.slice(0, nl).trim();
|
|
72997
|
+
buf = buf.slice(nl + 1);
|
|
72998
|
+
nl = buf.indexOf(`
|
|
72999
|
+
`);
|
|
73000
|
+
if (!line)
|
|
73001
|
+
continue;
|
|
73002
|
+
try {
|
|
73003
|
+
const evt = JSON.parse(line);
|
|
73004
|
+
if (evt.type === "exit" && evt.pane === 0) {
|
|
73005
|
+
paneExit = typeof evt.exitCode === "number" ? evt.exitCode : null;
|
|
73006
|
+
for (const fn of exitListeners)
|
|
73007
|
+
fn(paneExit);
|
|
73008
|
+
}
|
|
73009
|
+
} catch {}
|
|
73010
|
+
}
|
|
73011
|
+
});
|
|
73012
|
+
s.on("error", () => {
|
|
73013
|
+
control = null;
|
|
73014
|
+
});
|
|
73015
|
+
return;
|
|
73016
|
+
} catch {}
|
|
73017
|
+
}
|
|
73018
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
73019
|
+
}
|
|
73020
|
+
};
|
|
73021
|
+
proc.once("exit", () => {
|
|
73022
|
+
stopped = true;
|
|
73023
|
+
cleanup();
|
|
73024
|
+
});
|
|
73025
|
+
attempt();
|
|
73026
|
+
},
|
|
73027
|
+
cleanup
|
|
73028
|
+
};
|
|
73029
|
+
}
|
|
73030
|
+
var STRIPPED_CHILD_VARS;
|
|
73031
|
+
var init_magmux_wrapper = __esm(() => {
|
|
73032
|
+
init_magmux_binary();
|
|
73033
|
+
STRIPPED_CHILD_VARS = ["CLAUDECODE", "CLAUDE_CODE_CHILD_SESSION"];
|
|
73034
|
+
});
|
|
73035
|
+
|
|
73036
|
+
// src/recovery/types.ts
|
|
73037
|
+
var UI_LEASE_MS = 1e4, FRAME_TICK_MS = 1000;
|
|
73038
|
+
|
|
73039
|
+
// src/recovery/magmux-ui.ts
|
|
73040
|
+
import { execFile as execFile2 } from "child_process";
|
|
73041
|
+
import { existsSync as existsSync34 } from "fs";
|
|
73042
|
+
import { connect as netConnect3 } from "net";
|
|
73043
|
+
import { promisify as promisify5 } from "util";
|
|
73044
|
+
function resolveControlEndpoint() {
|
|
73045
|
+
if (explicitControlSock && existsSync34(explicitControlSock)) {
|
|
73046
|
+
return { sock: explicitControlSock, wrapped: true };
|
|
73047
|
+
}
|
|
73048
|
+
const ambient = process.env.MAGMUX_SOCK;
|
|
73049
|
+
if (ambient && existsSync34(ambient))
|
|
73050
|
+
return { sock: ambient, wrapped: false };
|
|
73051
|
+
return null;
|
|
73052
|
+
}
|
|
73053
|
+
function noteUnavailable(message) {
|
|
73054
|
+
if (state.unavailableLogged)
|
|
73055
|
+
return;
|
|
73056
|
+
state.unavailableLogged = true;
|
|
73057
|
+
log(message);
|
|
73058
|
+
}
|
|
73059
|
+
function uiLeaseValid2(episodeId) {
|
|
73060
|
+
if (!state.control || state.targetPane === null)
|
|
73061
|
+
return false;
|
|
73062
|
+
const last = state.leases.get(episodeId);
|
|
73063
|
+
if (last === undefined)
|
|
73064
|
+
return false;
|
|
73065
|
+
return recoveryClock().now() - last < UI_LEASE_MS;
|
|
73066
|
+
}
|
|
73067
|
+
async function connectControl(sock) {
|
|
73068
|
+
try {
|
|
73069
|
+
return await new Promise((resolve, reject) => {
|
|
73070
|
+
const s = netConnect3(sock);
|
|
73071
|
+
s.once("connect", () => resolve(s));
|
|
73072
|
+
s.once("error", reject);
|
|
73073
|
+
});
|
|
73074
|
+
} catch {
|
|
73075
|
+
return null;
|
|
73076
|
+
}
|
|
73077
|
+
}
|
|
73078
|
+
function request(socket, msg, timeoutMs = CONTROL_TIMEOUT_MS) {
|
|
73079
|
+
const id = state.nextControlId++;
|
|
73080
|
+
return new Promise((resolve) => {
|
|
73081
|
+
let buf = "";
|
|
73082
|
+
let settled = false;
|
|
73083
|
+
const finish = (v) => {
|
|
73084
|
+
if (settled)
|
|
73085
|
+
return;
|
|
73086
|
+
settled = true;
|
|
73087
|
+
socket.removeListener("data", onData);
|
|
73088
|
+
clearTimeout(timer);
|
|
73089
|
+
resolve(v);
|
|
73090
|
+
};
|
|
73091
|
+
const onData = (chunk) => {
|
|
73092
|
+
buf += chunk.toString("utf-8");
|
|
73093
|
+
let nl = buf.indexOf(`
|
|
73094
|
+
`);
|
|
73095
|
+
while (nl >= 0) {
|
|
73096
|
+
const line = buf.slice(0, nl).trim();
|
|
73097
|
+
buf = buf.slice(nl + 1);
|
|
73098
|
+
nl = buf.indexOf(`
|
|
73099
|
+
`);
|
|
73100
|
+
if (!line)
|
|
73101
|
+
continue;
|
|
73102
|
+
try {
|
|
73103
|
+
const evt = JSON.parse(line);
|
|
73104
|
+
if (evt.type === "reply" && evt.id === id)
|
|
73105
|
+
finish(evt);
|
|
73106
|
+
} catch {}
|
|
73107
|
+
}
|
|
73108
|
+
};
|
|
73109
|
+
const timer = setTimeout(() => finish(null), timeoutMs);
|
|
73110
|
+
timer.unref?.();
|
|
73111
|
+
socket.on("data", onData);
|
|
73112
|
+
try {
|
|
73113
|
+
socket.write(`${JSON.stringify({ ...msg, id })}
|
|
73114
|
+
`);
|
|
73115
|
+
} catch {
|
|
73116
|
+
finish(null);
|
|
73117
|
+
}
|
|
73118
|
+
});
|
|
73119
|
+
}
|
|
73120
|
+
function dropConnection() {
|
|
73121
|
+
state.control = null;
|
|
73122
|
+
state.targetPane = null;
|
|
73123
|
+
state.leases.clear();
|
|
73124
|
+
}
|
|
73125
|
+
async function ancestorPids(maxDepth = 6) {
|
|
73126
|
+
const out = new Set([process.pid]);
|
|
73127
|
+
let pid = process.ppid;
|
|
73128
|
+
for (let i = 0;i < maxDepth && pid > 1; i++) {
|
|
73129
|
+
out.add(pid);
|
|
73130
|
+
try {
|
|
73131
|
+
const { stdout } = await execFileAsync("ps", ["-o", "ppid=", "-p", String(pid)], {
|
|
73132
|
+
timeout: 1000
|
|
73133
|
+
});
|
|
73134
|
+
const next = Number(stdout.trim());
|
|
73135
|
+
if (!Number.isFinite(next) || next <= 1)
|
|
73136
|
+
break;
|
|
73137
|
+
pid = next;
|
|
73138
|
+
} catch {
|
|
73139
|
+
break;
|
|
73140
|
+
}
|
|
73141
|
+
}
|
|
73142
|
+
return out;
|
|
73143
|
+
}
|
|
73144
|
+
async function findOwnPane(socket) {
|
|
73145
|
+
const reply = await request(socket, { type: "list" });
|
|
73146
|
+
const result = reply?.result;
|
|
73147
|
+
const panes = Array.isArray(result) ? result : Array.isArray(result?.panes) ? result.panes : [];
|
|
73148
|
+
const ancestors = await ancestorPids();
|
|
73149
|
+
for (const p of panes) {
|
|
73150
|
+
const pane = p.pane;
|
|
73151
|
+
const pid = p.pid;
|
|
73152
|
+
if (typeof pane === "number" && typeof pid === "number" && ancestors.has(pid))
|
|
73153
|
+
return pane;
|
|
73154
|
+
}
|
|
73155
|
+
return null;
|
|
73156
|
+
}
|
|
73157
|
+
function ensureConnected() {
|
|
73158
|
+
if (state.control && state.targetPane !== null)
|
|
73159
|
+
return Promise.resolve(true);
|
|
73160
|
+
if (state.paneNotFound)
|
|
73161
|
+
return Promise.resolve(false);
|
|
73162
|
+
if (state.connecting)
|
|
73163
|
+
return state.connecting;
|
|
73164
|
+
state.connecting = (async () => {
|
|
73165
|
+
const endpoint = resolveControlEndpoint();
|
|
73166
|
+
if (!endpoint) {
|
|
73167
|
+
noteUnavailable("[Recovery] no magmux control endpoint \u2014 no banner; recovery continues without one");
|
|
73168
|
+
return false;
|
|
73169
|
+
}
|
|
73170
|
+
const socket = await connectControl(endpoint.sock);
|
|
73171
|
+
if (!socket) {
|
|
73172
|
+
noteUnavailable(`[Recovery] could not reach magmux at ${endpoint.sock} \u2014 no banner`);
|
|
73173
|
+
return false;
|
|
73174
|
+
}
|
|
73175
|
+
const onGone = () => {
|
|
73176
|
+
if (state.control === socket)
|
|
73177
|
+
dropConnection();
|
|
73178
|
+
};
|
|
73179
|
+
socket.on("error", onGone);
|
|
73180
|
+
socket.on("close", onGone);
|
|
73181
|
+
state.control = socket;
|
|
73182
|
+
const pane = endpoint.wrapped ? 0 : await findOwnPane(socket);
|
|
73183
|
+
if (pane === null) {
|
|
73184
|
+
state.paneNotFound = true;
|
|
73185
|
+
log("[Recovery] this claudish's pane was not found in magmux \u2014 no banner");
|
|
73186
|
+
try {
|
|
73187
|
+
socket.end();
|
|
73188
|
+
} catch {}
|
|
73189
|
+
dropConnection();
|
|
73190
|
+
return false;
|
|
73191
|
+
}
|
|
73192
|
+
state.targetPane = pane;
|
|
73193
|
+
state.unavailableLogged = false;
|
|
73194
|
+
log(`[Recovery] banner will draw on magmux pane ${pane} ` + `(${endpoint.wrapped ? "magmux launched by claudish" : "ambient magmux"})`);
|
|
73195
|
+
return true;
|
|
73196
|
+
})().finally(() => {
|
|
73197
|
+
state.connecting = null;
|
|
73198
|
+
});
|
|
73199
|
+
return state.connecting;
|
|
73200
|
+
}
|
|
73201
|
+
function formatElapsed2(ms) {
|
|
73202
|
+
const s = Math.max(0, Math.round(ms / 1000));
|
|
73203
|
+
if (s < 60)
|
|
73204
|
+
return `${s}s`;
|
|
73205
|
+
const m = Math.floor(s / 60);
|
|
73206
|
+
if (m < 60)
|
|
73207
|
+
return `${m}m ${String(s % 60).padStart(2, "0")}s`;
|
|
73208
|
+
return `${Math.floor(m / 60)}h ${m % 60}m`;
|
|
73209
|
+
}
|
|
73210
|
+
function wrap(text, cols) {
|
|
73211
|
+
const out = [];
|
|
73212
|
+
let line = "";
|
|
73213
|
+
for (const word of text.split(/\s+/).filter(Boolean)) {
|
|
73214
|
+
if (line && line.length + 1 + word.length > cols) {
|
|
73215
|
+
out.push(line);
|
|
73216
|
+
line = word;
|
|
73217
|
+
} else {
|
|
73218
|
+
line = line ? `${line} ${word}` : word;
|
|
73219
|
+
}
|
|
73220
|
+
}
|
|
73221
|
+
if (line)
|
|
73222
|
+
out.push(line);
|
|
73223
|
+
return out;
|
|
73224
|
+
}
|
|
73225
|
+
function bannerText(frame, nowMs) {
|
|
73226
|
+
const lines = [`\u2717 NETWORK \xB7 ${frame.providerDisplayName} ${KIND_WORD[frame.kind]}`];
|
|
73227
|
+
lines.push(...wrap(frame.reason, OVERLAY_WRAP_COLS));
|
|
73228
|
+
lines.push(`${frame.host} \xB7 ${frame.code ?? frame.kind}${frame.loopback ? " \xB7 local" : ""}`);
|
|
73229
|
+
const elapsed = formatElapsed2(nowMs - frame.startedAtMs);
|
|
73230
|
+
const attempt = Math.max(1, frame.attempts);
|
|
73231
|
+
if (frame.state === "handoff") {
|
|
73232
|
+
lines.push(`handed back to Claude Code \xB7 waiting for its retry \xB7 ${elapsed} in recovery`);
|
|
73233
|
+
} else if (frame.nextAttemptAtMs === null) {
|
|
73234
|
+
lines.push(`connecting to ${frame.host}\u2026 \xB7 attempt ${attempt} \xB7 ${elapsed} in recovery`);
|
|
73235
|
+
} else {
|
|
73236
|
+
const secs = Math.max(0, Math.ceil((frame.nextAttemptAtMs - nowMs) / 1000));
|
|
73237
|
+
lines.push(`next attempt in ${secs}s \xB7 attempt ${attempt} \xB7 ${elapsed} in recovery`);
|
|
73238
|
+
}
|
|
73239
|
+
const counts = [];
|
|
73240
|
+
if (frame.waiters > 0)
|
|
73241
|
+
counts.push(`${frame.waiters} request${frame.waiters === 1 ? "" : "s"} held`);
|
|
73242
|
+
if (frame.otherEpisodes > 0) {
|
|
73243
|
+
counts.push(`+${frame.otherEpisodes} more outage${frame.otherEpisodes === 1 ? "" : "s"}`);
|
|
73244
|
+
}
|
|
73245
|
+
if (counts.length > 0)
|
|
73246
|
+
lines.push(counts.join(" \xB7 "));
|
|
73247
|
+
lines.push("Esc in Claude Code stops the turn");
|
|
73248
|
+
return lines.join(`
|
|
73249
|
+
`);
|
|
73250
|
+
}
|
|
73251
|
+
function recoveredText(frame, nowMs) {
|
|
73252
|
+
return [
|
|
73253
|
+
`\u2713 NETWORK \xB7 ${frame.providerDisplayName} recovered`,
|
|
73254
|
+
`${frame.attempts} attempt${frame.attempts === 1 ? "" : "s"} over ${formatElapsed2(nowMs - frame.startedAtMs)}`
|
|
73255
|
+
].join(`
|
|
73256
|
+
`);
|
|
73257
|
+
}
|
|
73258
|
+
async function paint2(frame) {
|
|
73259
|
+
if (state.painting)
|
|
73260
|
+
return;
|
|
73261
|
+
state.painting = true;
|
|
73262
|
+
try {
|
|
73263
|
+
if (!await ensureConnected())
|
|
73264
|
+
return;
|
|
73265
|
+
const socket = state.control;
|
|
73266
|
+
const pane = state.targetPane;
|
|
73267
|
+
if (!socket || pane === null)
|
|
73268
|
+
return;
|
|
73269
|
+
const reply = await request(socket, {
|
|
73270
|
+
type: "overlay",
|
|
73271
|
+
pane,
|
|
73272
|
+
text: bannerText(frame, Date.now()),
|
|
73273
|
+
style: "error"
|
|
73274
|
+
});
|
|
73275
|
+
const ok = reply?.ok === true;
|
|
73276
|
+
if (ok && !episodeIsLive(frame.episodeId)) {
|
|
73277
|
+
state.lastPaintOk = ok;
|
|
73278
|
+
return;
|
|
73279
|
+
}
|
|
73280
|
+
if (ok) {
|
|
73281
|
+
const first = !state.leases.has(frame.episodeId);
|
|
73282
|
+
state.leases.set(frame.episodeId, recoveryClock().now());
|
|
73283
|
+
state.lastFrame = frame;
|
|
73284
|
+
if (first) {
|
|
73285
|
+
log(`[Recovery] banner painted for episode ${frame.episodeId} on magmux pane ${pane} ` + "\u2014 lease granted");
|
|
73286
|
+
}
|
|
73287
|
+
request(socket, { type: "tint", pane, color: "red" });
|
|
73288
|
+
} else if (state.lastPaintOk !== false) {
|
|
73289
|
+
log(`[Recovery] magmux did not acknowledge the banner: ${JSON.stringify(reply)}`);
|
|
73290
|
+
}
|
|
73291
|
+
state.lastPaintOk = ok;
|
|
73292
|
+
} finally {
|
|
73293
|
+
state.painting = false;
|
|
73294
|
+
}
|
|
73295
|
+
}
|
|
73296
|
+
async function writeOverlay(text, style, tint) {
|
|
73297
|
+
const socket = state.control;
|
|
73298
|
+
const pane = state.targetPane;
|
|
73299
|
+
if (!socket || pane === null)
|
|
73300
|
+
return;
|
|
73301
|
+
await request(socket, { type: "overlay", pane, text, style });
|
|
73302
|
+
await request(socket, { type: "tint", pane, color: tint });
|
|
73303
|
+
}
|
|
73304
|
+
function clearOverlay() {
|
|
73305
|
+
return writeOverlay("", "", "reset");
|
|
73306
|
+
}
|
|
73307
|
+
function stopTick() {
|
|
73308
|
+
if (state.tickTimer !== null) {
|
|
73309
|
+
recoveryClock().clearTimeout(state.tickTimer);
|
|
73310
|
+
state.tickTimer = null;
|
|
73311
|
+
}
|
|
73312
|
+
}
|
|
73313
|
+
function cancelLinger() {
|
|
73314
|
+
if (state.lingerTimer !== null) {
|
|
73315
|
+
recoveryClock().clearTimeout(state.lingerTimer);
|
|
73316
|
+
state.lingerTimer = null;
|
|
73317
|
+
}
|
|
73318
|
+
}
|
|
73319
|
+
function startTick() {
|
|
73320
|
+
if (state.tickTimer !== null)
|
|
73321
|
+
return;
|
|
73322
|
+
const clock = recoveryClock();
|
|
73323
|
+
const loop = () => {
|
|
73324
|
+
state.tickTimer = null;
|
|
73325
|
+
const frame = renderableEpisodeFrame();
|
|
73326
|
+
if (!frame)
|
|
73327
|
+
return;
|
|
73328
|
+
paint2(frame);
|
|
73329
|
+
const t = clock.setTimeout(loop, FRAME_TICK_MS);
|
|
73330
|
+
clock.unref?.(t);
|
|
73331
|
+
state.tickTimer = t;
|
|
73332
|
+
};
|
|
73333
|
+
loop();
|
|
73334
|
+
}
|
|
73335
|
+
function onEpisodeOpened(episodeId) {
|
|
73336
|
+
markPaneRequested(episodeId);
|
|
73337
|
+
cancelLinger();
|
|
73338
|
+
startTick();
|
|
73339
|
+
}
|
|
73340
|
+
function onEpisodeClosed(episodeId, outcome, attempts) {
|
|
73341
|
+
state.leases.delete(episodeId);
|
|
73342
|
+
if (renderableEpisodeFrame() !== null)
|
|
73343
|
+
return;
|
|
73344
|
+
stopTick();
|
|
73345
|
+
const last = state.lastFrame?.episodeId === episodeId ? state.lastFrame : null;
|
|
73346
|
+
state.lastFrame = null;
|
|
73347
|
+
if (outcome !== "recovered" || !last) {
|
|
73348
|
+
clearOverlay();
|
|
73349
|
+
return;
|
|
73350
|
+
}
|
|
73351
|
+
writeOverlay(recoveredText({ ...last, attempts }, Date.now()), "success", "green");
|
|
73352
|
+
cancelLinger();
|
|
73353
|
+
const clock = recoveryClock();
|
|
73354
|
+
const timer = clock.setTimeout(() => {
|
|
73355
|
+
state.lingerTimer = null;
|
|
73356
|
+
if (renderableEpisodeFrame() !== null)
|
|
73357
|
+
return;
|
|
73358
|
+
clearOverlay();
|
|
73359
|
+
}, OUTCOME_LINGER_MS);
|
|
73360
|
+
clock.unref?.(timer);
|
|
73361
|
+
state.lingerTimer = timer;
|
|
73362
|
+
}
|
|
73363
|
+
function installRecoveryUi(controlSock) {
|
|
73364
|
+
explicitControlSock = controlSock;
|
|
73365
|
+
if (installed)
|
|
73366
|
+
return;
|
|
73367
|
+
installed = true;
|
|
73368
|
+
registerRecoveryUi(hooks);
|
|
73369
|
+
}
|
|
73370
|
+
async function shutdownRecoveryUi() {
|
|
73371
|
+
stopTick();
|
|
73372
|
+
cancelLinger();
|
|
73373
|
+
if (state.control && state.targetPane !== null) {
|
|
73374
|
+
const socket = state.control;
|
|
73375
|
+
const pane = state.targetPane;
|
|
73376
|
+
await request(socket, { type: "overlay", pane, text: "", style: "" }, 500);
|
|
73377
|
+
await request(socket, { type: "tint", pane, color: "reset" }, 500);
|
|
73378
|
+
}
|
|
73379
|
+
if (state.control) {
|
|
73380
|
+
try {
|
|
73381
|
+
state.control.end();
|
|
73382
|
+
} catch {}
|
|
73383
|
+
}
|
|
73384
|
+
dropConnection();
|
|
73385
|
+
state.lastFrame = null;
|
|
73386
|
+
state.lastPaintOk = null;
|
|
73387
|
+
state.unavailableLogged = false;
|
|
73388
|
+
state.paneNotFound = false;
|
|
73389
|
+
registerRecoveryUi(null);
|
|
73390
|
+
installed = false;
|
|
73391
|
+
explicitControlSock = null;
|
|
73392
|
+
}
|
|
73393
|
+
var execFileAsync, OUTCOME_LINGER_MS = 4000, OVERLAY_WRAP_COLS = 60, CONTROL_TIMEOUT_MS = 3000, explicitControlSock = null, state, installed = false, KIND_WORD, hooks;
|
|
73394
|
+
var init_magmux_ui = __esm(() => {
|
|
73395
|
+
init_logger();
|
|
73396
|
+
init_clock();
|
|
73397
|
+
init_coordinator();
|
|
73398
|
+
execFileAsync = promisify5(execFile2);
|
|
73399
|
+
state = {
|
|
73400
|
+
control: null,
|
|
73401
|
+
connecting: null,
|
|
73402
|
+
targetPane: null,
|
|
73403
|
+
leases: new Map,
|
|
73404
|
+
lastFrame: null,
|
|
73405
|
+
tickTimer: null,
|
|
73406
|
+
lingerTimer: null,
|
|
73407
|
+
painting: false,
|
|
73408
|
+
lastPaintOk: null,
|
|
73409
|
+
unavailableLogged: false,
|
|
73410
|
+
paneNotFound: false,
|
|
73411
|
+
nextControlId: 1
|
|
73412
|
+
};
|
|
73413
|
+
KIND_WORD = {
|
|
73414
|
+
refused: "refused",
|
|
73415
|
+
dns: "not found",
|
|
73416
|
+
unreachable: "unreachable"
|
|
73417
|
+
};
|
|
73418
|
+
hooks = {
|
|
73419
|
+
onEpisodeOpened,
|
|
73420
|
+
onEpisodeClosed,
|
|
73421
|
+
leaseValid: (episodeId) => uiLeaseValid2(episodeId)
|
|
73422
|
+
};
|
|
73423
|
+
});
|
|
73424
|
+
|
|
71763
73425
|
// src/terminal-isolation.ts
|
|
71764
73426
|
import { format } from "util";
|
|
71765
73427
|
function beginTerminalIsolation(onSuppressed) {
|
|
71766
|
-
if (
|
|
73428
|
+
if (active2)
|
|
71767
73429
|
return () => {};
|
|
71768
|
-
|
|
73430
|
+
active2 = true;
|
|
71769
73431
|
const emit = (source, text) => {
|
|
71770
73432
|
if (routing)
|
|
71771
73433
|
return;
|
|
@@ -71795,17 +73457,17 @@ function beginTerminalIsolation(onSuppressed) {
|
|
|
71795
73457
|
process.stdout.write = makeWrite("stdout");
|
|
71796
73458
|
process.stderr.write = makeWrite("stderr");
|
|
71797
73459
|
return function restore() {
|
|
71798
|
-
if (!
|
|
73460
|
+
if (!active2)
|
|
71799
73461
|
return;
|
|
71800
73462
|
for (const method of CONSOLE_METHODS) {
|
|
71801
73463
|
console[method] = originalConsole[method];
|
|
71802
73464
|
}
|
|
71803
73465
|
process.stdout.write = originalStdoutWrite;
|
|
71804
73466
|
process.stderr.write = originalStderrWrite;
|
|
71805
|
-
|
|
73467
|
+
active2 = false;
|
|
71806
73468
|
};
|
|
71807
73469
|
}
|
|
71808
|
-
var CONSOLE_METHODS,
|
|
73470
|
+
var CONSOLE_METHODS, active2 = false, routing = false;
|
|
71809
73471
|
var init_terminal_isolation = __esm(() => {
|
|
71810
73472
|
CONSOLE_METHODS = ["log", "error", "warn", "info", "debug", "trace", "dir"];
|
|
71811
73473
|
});
|
|
@@ -71813,18 +73475,18 @@ var init_terminal_isolation = __esm(() => {
|
|
|
71813
73475
|
// src/claude-runner.ts
|
|
71814
73476
|
import { spawn as spawn5, spawnSync as spawnSync4 } from "child_process";
|
|
71815
73477
|
import {
|
|
71816
|
-
closeSync as
|
|
71817
|
-
existsSync as
|
|
71818
|
-
mkdirSync as
|
|
71819
|
-
openSync as
|
|
73478
|
+
closeSync as closeSync9,
|
|
73479
|
+
existsSync as existsSync35,
|
|
73480
|
+
mkdirSync as mkdirSync21,
|
|
73481
|
+
openSync as openSync9,
|
|
71820
73482
|
readFileSync as readFileSync33,
|
|
71821
73483
|
readdirSync as readdirSync8,
|
|
71822
73484
|
statSync as statSync8,
|
|
71823
73485
|
unlinkSync as unlinkSync9,
|
|
71824
|
-
writeFileSync as
|
|
73486
|
+
writeFileSync as writeFileSync22
|
|
71825
73487
|
} from "fs";
|
|
71826
73488
|
import { homedir as homedir37, tmpdir as tmpdir2 } from "os";
|
|
71827
|
-
import { dirname as dirname14, join as
|
|
73489
|
+
import { dirname as dirname14, join as join44 } from "path";
|
|
71828
73490
|
import { isatty } from "tty";
|
|
71829
73491
|
function releaseTerminalIsolation() {
|
|
71830
73492
|
if (!restoreTerminal)
|
|
@@ -71884,12 +73546,12 @@ function isRealAnthropicEnvCredential(env, name) {
|
|
|
71884
73546
|
}
|
|
71885
73547
|
function hasResolvableAnthropicAuth(deps = {}) {
|
|
71886
73548
|
const env = deps.env ?? process.env;
|
|
71887
|
-
const fileExists = deps.fileExists ??
|
|
73549
|
+
const fileExists = deps.fileExists ?? existsSync35;
|
|
71888
73550
|
const keychainProbe = deps.keychainProbe ?? defaultKeychainAnthropicProbe;
|
|
71889
73551
|
if (isRealAnthropicEnvCredential(env, "ANTHROPIC_API_KEY") || isRealAnthropicEnvCredential(env, "ANTHROPIC_AUTH_TOKEN")) {
|
|
71890
73552
|
return true;
|
|
71891
73553
|
}
|
|
71892
|
-
if (fileExists(
|
|
73554
|
+
if (fileExists(join44(homedir37(), ".claude", ".credentials.json")))
|
|
71893
73555
|
return true;
|
|
71894
73556
|
return keychainProbe();
|
|
71895
73557
|
}
|
|
@@ -71904,7 +73566,7 @@ function isProxyAuthMode(config) {
|
|
|
71904
73566
|
}
|
|
71905
73567
|
function managedSettingsPath() {
|
|
71906
73568
|
if (isWindows2()) {
|
|
71907
|
-
return
|
|
73569
|
+
return join44(process.env.PROGRAMDATA || "C:\\ProgramData", "ClaudeCode", "managed-settings.json");
|
|
71908
73570
|
}
|
|
71909
73571
|
if (process.platform === "darwin") {
|
|
71910
73572
|
return "/Library/Application Support/ClaudeCode/managed-settings.json";
|
|
@@ -71925,9 +73587,9 @@ function isWindows2() {
|
|
|
71925
73587
|
}
|
|
71926
73588
|
function createStatusLineScript(tokenFilePath) {
|
|
71927
73589
|
const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir2();
|
|
71928
|
-
const claudishDir =
|
|
73590
|
+
const claudishDir = join44(homeDir, ".claudish");
|
|
71929
73591
|
const timestamp = Date.now();
|
|
71930
|
-
const scriptPath =
|
|
73592
|
+
const scriptPath = join44(claudishDir, `status-${timestamp}.js`);
|
|
71931
73593
|
const escapedTokenPath = tokenFilePath.replace(/\\/g, "\\\\");
|
|
71932
73594
|
const light = getThemeMode() === "light";
|
|
71933
73595
|
const cyanCode = light ? "38;2;14;116;144" : "96";
|
|
@@ -72060,13 +73722,13 @@ process.stdin.on('end', () => {
|
|
|
72060
73722
|
}
|
|
72061
73723
|
});
|
|
72062
73724
|
`;
|
|
72063
|
-
|
|
73725
|
+
writeFileSync22(scriptPath, script, "utf-8");
|
|
72064
73726
|
return scriptPath;
|
|
72065
73727
|
}
|
|
72066
73728
|
function initializeTokenFile(tokenFilePath) {
|
|
72067
73729
|
try {
|
|
72068
|
-
|
|
72069
|
-
|
|
73730
|
+
mkdirSync21(dirname14(tokenFilePath), { recursive: true });
|
|
73731
|
+
writeFileSync22(tokenFilePath, JSON.stringify({
|
|
72070
73732
|
input_tokens: 0,
|
|
72071
73733
|
output_tokens: 0,
|
|
72072
73734
|
total_tokens: 0,
|
|
@@ -72097,7 +73759,7 @@ function cleanupStaleTokenFiles(dir, now = Date.now(), maxAgeMs = STALE_TOKEN_FI
|
|
|
72097
73759
|
if (!name.startsWith("tokens-") || !name.endsWith(".json"))
|
|
72098
73760
|
continue;
|
|
72099
73761
|
scanned++;
|
|
72100
|
-
const full =
|
|
73762
|
+
const full = join44(dir, name);
|
|
72101
73763
|
try {
|
|
72102
73764
|
if (statSync8(full).mtimeMs >= cutoff)
|
|
72103
73765
|
continue;
|
|
@@ -72126,13 +73788,13 @@ function parseSettingsArgSafe(value) {
|
|
|
72126
73788
|
}
|
|
72127
73789
|
function userSettingsFileCandidates(cwd) {
|
|
72128
73790
|
return [
|
|
72129
|
-
|
|
72130
|
-
|
|
72131
|
-
|
|
73791
|
+
join44(homedir37(), ".claude", "settings.json"),
|
|
73792
|
+
join44(cwd, ".claude", "settings.json"),
|
|
73793
|
+
join44(cwd, ".claude", "settings.local.json")
|
|
72132
73794
|
];
|
|
72133
73795
|
}
|
|
72134
73796
|
function discoverUserStatusLineCommand(claudeArgs = [], cwd = process.cwd()) {
|
|
72135
|
-
const sources = userSettingsFileCandidates(cwd).filter((file) =>
|
|
73797
|
+
const sources = userSettingsFileCandidates(cwd).filter((file) => existsSync35(file));
|
|
72136
73798
|
const idx = claudeArgs.indexOf("--settings");
|
|
72137
73799
|
const settingsArg = idx === -1 ? undefined : claudeArgs[idx + 1];
|
|
72138
73800
|
if (settingsArg)
|
|
@@ -72169,13 +73831,13 @@ function buildChainedStatusCommand(userCommand, claudishBody, claudishSegment) {
|
|
|
72169
73831
|
}
|
|
72170
73832
|
function createTempSettingsFile(_modelDisplay, port, proxyAuthMode, userStatusLineCommand) {
|
|
72171
73833
|
const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir2();
|
|
72172
|
-
const claudishDir =
|
|
73834
|
+
const claudishDir = join44(homeDir, ".claudish");
|
|
72173
73835
|
try {
|
|
72174
|
-
|
|
73836
|
+
mkdirSync21(claudishDir, { recursive: true });
|
|
72175
73837
|
} catch {}
|
|
72176
73838
|
const timestamp = Date.now();
|
|
72177
|
-
const tempPath =
|
|
72178
|
-
const tokenFilePath =
|
|
73839
|
+
const tempPath = join44(claudishDir, `settings-${timestamp}.json`);
|
|
73840
|
+
const tokenFilePath = join44(claudishDir, `tokens-${port}.json`);
|
|
72179
73841
|
cleanupStaleTokenFiles(claudishDir);
|
|
72180
73842
|
initializeTokenFile(tokenFilePath);
|
|
72181
73843
|
let statusCommand;
|
|
@@ -72209,7 +73871,7 @@ function createTempSettingsFile(_modelDisplay, port, proxyAuthMode, userStatusLi
|
|
|
72209
73871
|
padding: 0
|
|
72210
73872
|
};
|
|
72211
73873
|
const settings = buildClaudishSettingsOverlay(statusLine, proxyAuthMode);
|
|
72212
|
-
|
|
73874
|
+
writeFileSync22(tempPath, JSON.stringify(settings, null, 2), "utf-8");
|
|
72213
73875
|
return { path: tempPath, statusLine, tokenFilePath };
|
|
72214
73876
|
}
|
|
72215
73877
|
function buildClaudishSettingsOverlay(statusLine, proxyAuthMode) {
|
|
@@ -72234,7 +73896,7 @@ function mergeUserSettingsIfPresent(config, tempSettingsPath, statusLine, proxyA
|
|
|
72234
73896
|
if (proxyAuthMode && !("forceLoginMethod" in userSettings)) {
|
|
72235
73897
|
userSettings.forceLoginMethod = "console";
|
|
72236
73898
|
}
|
|
72237
|
-
|
|
73899
|
+
writeFileSync22(tempSettingsPath, JSON.stringify(userSettings, null, 2), "utf-8");
|
|
72238
73900
|
} catch {
|
|
72239
73901
|
if (!config.quiet) {
|
|
72240
73902
|
console.warn(`[claudish] Warning: could not merge user settings: ${userSettingsValue}`);
|
|
@@ -72305,13 +73967,13 @@ function resolveAdvisorToolEnv(config, processEnv = process.env) {
|
|
|
72305
73967
|
return { vars: { [ADVISOR_TOOL_ENV_VAR]: "1" }, source: "claudish" };
|
|
72306
73968
|
}
|
|
72307
73969
|
function discoverUserAdvisorModel(claudeArgs = [], cwd = process.cwd()) {
|
|
72308
|
-
const sources = userSettingsFileCandidates(cwd).filter((file) =>
|
|
73970
|
+
const sources = userSettingsFileCandidates(cwd).filter((file) => existsSync35(file));
|
|
72309
73971
|
const idx = claudeArgs.indexOf("--settings");
|
|
72310
73972
|
const settingsArg = idx === -1 ? undefined : claudeArgs[idx + 1];
|
|
72311
73973
|
if (settingsArg)
|
|
72312
73974
|
sources.push(settingsArg);
|
|
72313
73975
|
const managed = managedSettingsPath();
|
|
72314
|
-
if (
|
|
73976
|
+
if (existsSync35(managed))
|
|
72315
73977
|
sources.push(managed);
|
|
72316
73978
|
let effective;
|
|
72317
73979
|
for (const source of sources) {
|
|
@@ -72434,6 +74096,11 @@ async function runClaudeWithProxy(config, proxyUrl, onCleanup) {
|
|
|
72434
74096
|
[ENV.CLAUDISH_TOKEN_FILE]: tokenFilePath,
|
|
72435
74097
|
...advisorToolEnv.vars
|
|
72436
74098
|
};
|
|
74099
|
+
const paneCapability = magmuxPaneCapability({
|
|
74100
|
+
interactive: Boolean(config.interactive),
|
|
74101
|
+
stdoutIsTty: Boolean(process.stdout.isTTY)
|
|
74102
|
+
});
|
|
74103
|
+
applyRetryWatchdog(env, { paneEligible: paneCapability.kind !== "none" });
|
|
72437
74104
|
if (modelId) {
|
|
72438
74105
|
const parsedSpec = parseModelSpec(modelId);
|
|
72439
74106
|
const providerDisplayName = parsedSpec.isExplicitProvider ? getProviderByName(parsedSpec.provider)?.displayName : undefined;
|
|
@@ -72526,7 +74193,7 @@ async function runClaudeWithProxy(config, proxyUrl, onCleanup) {
|
|
|
72526
74193
|
console.error(`
|
|
72527
74194
|
Or set CLAUDE_PATH to your custom installation:`);
|
|
72528
74195
|
const home = homedir37();
|
|
72529
|
-
const localPath = isWindows2() ?
|
|
74196
|
+
const localPath = isWindows2() ? join44(home, ".claude", "local", "claude.exe") : join44(home, ".claude", "local", "claude");
|
|
72530
74197
|
console.error(` export CLAUDE_PATH=${localPath}`);
|
|
72531
74198
|
process.exit(1);
|
|
72532
74199
|
}
|
|
@@ -72537,11 +74204,11 @@ Or set CLAUDE_PATH to your custom installation:`);
|
|
|
72537
74204
|
const childWantsTty = config.interactive && !process.stdout.isTTY && Boolean(process.stdin.isTTY);
|
|
72538
74205
|
if (childWantsTty) {
|
|
72539
74206
|
try {
|
|
72540
|
-
const fd =
|
|
74207
|
+
const fd = openSync9("/dev/fd/0", "r+");
|
|
72541
74208
|
if (isatty(fd)) {
|
|
72542
74209
|
ttyFd = fd;
|
|
72543
74210
|
} else {
|
|
72544
|
-
|
|
74211
|
+
closeSync9(fd);
|
|
72545
74212
|
}
|
|
72546
74213
|
} catch {
|
|
72547
74214
|
ttyFd = undefined;
|
|
@@ -72551,11 +74218,29 @@ Or set CLAUDE_PATH to your custom installation:`);
|
|
|
72551
74218
|
}
|
|
72552
74219
|
const filterChildStderr = !config.interactive && ttyFd === undefined;
|
|
72553
74220
|
const stdio = ttyFd !== undefined ? [0, ttyFd, ttyFd] : filterChildStderr ? ["inherit", "inherit", "pipe"] : "inherit";
|
|
72554
|
-
const
|
|
74221
|
+
const wrap = recoverySurfaceAllowed() && paneCapability.kind === "wrap" ? planMagmuxWrap({
|
|
74222
|
+
claudeBinary,
|
|
74223
|
+
claudeArgs,
|
|
74224
|
+
childEnv: env,
|
|
74225
|
+
cwd: process.cwd(),
|
|
74226
|
+
interactive: Boolean(config.interactive),
|
|
74227
|
+
stdoutIsTty: Boolean(process.stdout.isTTY),
|
|
74228
|
+
magmuxBinary: paneCapability.magmux
|
|
74229
|
+
}) : null;
|
|
74230
|
+
const proc = wrap ? spawn5(wrap.command, wrap.args, { env: wrap.env, stdio, shell: false }) : spawn5(spawnCommand, claudeArgs, {
|
|
72555
74231
|
env,
|
|
72556
74232
|
stdio,
|
|
72557
74233
|
shell: needsShell
|
|
72558
74234
|
});
|
|
74235
|
+
if (wrap) {
|
|
74236
|
+
wrap.watch(proc);
|
|
74237
|
+
installRecoveryUi(wrap.controlSocket);
|
|
74238
|
+
wrap.onClaudeExit(() => {
|
|
74239
|
+
shutdownRecoveryUi();
|
|
74240
|
+
});
|
|
74241
|
+
} else if (recoverySurfaceAllowed() && config.interactive && process.env.MAGMUX_SOCK) {
|
|
74242
|
+
installRecoveryUi(null);
|
|
74243
|
+
}
|
|
72559
74244
|
if (filterChildStderr && proc.stderr) {
|
|
72560
74245
|
relayChildStderr(proc.stderr);
|
|
72561
74246
|
}
|
|
@@ -72568,7 +74253,7 @@ Or set CLAUDE_PATH to your custom installation:`);
|
|
|
72568
74253
|
const fdToClose = ttyFd;
|
|
72569
74254
|
proc.on("spawn", () => {
|
|
72570
74255
|
try {
|
|
72571
|
-
|
|
74256
|
+
closeSync9(fdToClose);
|
|
72572
74257
|
} catch {}
|
|
72573
74258
|
});
|
|
72574
74259
|
}
|
|
@@ -72576,12 +74261,14 @@ Or set CLAUDE_PATH to your custom installation:`);
|
|
|
72576
74261
|
const { exitCode, exitSignal } = await new Promise((resolve) => {
|
|
72577
74262
|
proc.on("exit", (code, signal) => {
|
|
72578
74263
|
setClaudeCodeRunning(false);
|
|
74264
|
+
const paneExit = wrap?.paneExitCode() ?? null;
|
|
72579
74265
|
resolve({
|
|
72580
|
-
exitCode: signal ? 128 + (SIGNAL_EXIT_NUMBERS[signal] ?? 0) : code ?? 1,
|
|
74266
|
+
exitCode: paneExit !== null ? paneExit : signal ? 128 + (SIGNAL_EXIT_NUMBERS[signal] ?? 0) : code ?? 1,
|
|
72581
74267
|
exitSignal: signal
|
|
72582
74268
|
});
|
|
72583
74269
|
});
|
|
72584
74270
|
});
|
|
74271
|
+
wrap?.cleanup();
|
|
72585
74272
|
log(exitSignal ? `[Claude Code] Exited from ${exitSignal} (exit code ${exitCode})` : `[Claude Code] Exited with code ${exitCode}`);
|
|
72586
74273
|
releaseTerminalIsolation();
|
|
72587
74274
|
try {
|
|
@@ -72614,23 +74301,23 @@ function setupSignalHandlers(proc, tempSettingsPath, quiet, onCleanup) {
|
|
|
72614
74301
|
async function findClaudeBinary() {
|
|
72615
74302
|
const isWindows = process.platform === "win32";
|
|
72616
74303
|
if (process.env.CLAUDE_PATH) {
|
|
72617
|
-
if (
|
|
74304
|
+
if (existsSync35(process.env.CLAUDE_PATH)) {
|
|
72618
74305
|
return process.env.CLAUDE_PATH;
|
|
72619
74306
|
}
|
|
72620
74307
|
}
|
|
72621
74308
|
const home = homedir37();
|
|
72622
|
-
const localPath = isWindows ?
|
|
72623
|
-
if (
|
|
74309
|
+
const localPath = isWindows ? join44(home, ".claude", "local", "claude.exe") : join44(home, ".claude", "local", "claude");
|
|
74310
|
+
if (existsSync35(localPath)) {
|
|
72624
74311
|
return localPath;
|
|
72625
74312
|
}
|
|
72626
74313
|
if (isWindows) {
|
|
72627
74314
|
const windowsPaths = [
|
|
72628
|
-
|
|
72629
|
-
|
|
72630
|
-
|
|
74315
|
+
join44(home, "AppData", "Roaming", "npm", "claude.cmd"),
|
|
74316
|
+
join44(home, ".npm-global", "claude.cmd"),
|
|
74317
|
+
join44(home, "node_modules", ".bin", "claude.cmd")
|
|
72631
74318
|
];
|
|
72632
74319
|
for (const path of windowsPaths) {
|
|
72633
|
-
if (
|
|
74320
|
+
if (existsSync35(path)) {
|
|
72634
74321
|
return path;
|
|
72635
74322
|
}
|
|
72636
74323
|
}
|
|
@@ -72638,14 +74325,14 @@ async function findClaudeBinary() {
|
|
|
72638
74325
|
const commonPaths = [
|
|
72639
74326
|
"/usr/local/bin/claude",
|
|
72640
74327
|
"/opt/homebrew/bin/claude",
|
|
72641
|
-
|
|
72642
|
-
|
|
72643
|
-
|
|
74328
|
+
join44(home, ".npm-global/bin/claude"),
|
|
74329
|
+
join44(home, ".local/bin/claude"),
|
|
74330
|
+
join44(home, "node_modules/.bin/claude"),
|
|
72644
74331
|
"/data/data/com.termux/files/usr/bin/claude",
|
|
72645
|
-
|
|
74332
|
+
join44(home, "../usr/bin/claude")
|
|
72646
74333
|
];
|
|
72647
74334
|
for (const path of commonPaths) {
|
|
72648
|
-
if (
|
|
74335
|
+
if (existsSync35(path)) {
|
|
72649
74336
|
return path;
|
|
72650
74337
|
}
|
|
72651
74338
|
}
|
|
@@ -72699,13 +74386,16 @@ var restoreTerminal = null, macosKeychainAnthropicResult, defaultKeychainAnthrop
|
|
|
72699
74386
|
}, CLAUDISH_PLACEHOLDER_API_KEY = "sk-ant-api03-placeholder-not-used-proxy-handles-auth-with-openrouter-key-xxxxxxxxxxxxxxxxxxxxx", CLAUDISH_PLACEHOLDER_AUTH_TOKEN = "placeholder-token-not-used-proxy-handles-auth", STALE_TOKEN_FILE_MS, MAX_TOKEN_FILES_SCANNED = 4000, CLAUDE_CODE_DEFAULT_MAX_CONTEXT = 200000, USER_STATUS_LINE_TIMEOUT_SECONDS = 3, MIN_AUTO_COMPACT_WINDOW = 200000, ADVISOR_TOOL_ENV_VAR = "CLAUDE_CODE_ENABLE_EXPERIMENTAL_ADVISOR_TOOL", CLAUDISH_CHILD_ADVISOR_MODEL = "sonnet", CHILD_STDERR_NOISE, SIGNAL_EXIT_NUMBERS;
|
|
72700
74387
|
var init_claude_runner = __esm(() => {
|
|
72701
74388
|
init_model_catalog();
|
|
72702
|
-
|
|
74389
|
+
init_config();
|
|
74390
|
+
init_magmux_wrapper();
|
|
72703
74391
|
init_logger();
|
|
72704
74392
|
init_profile_config();
|
|
72705
74393
|
init_model_discovery();
|
|
72706
74394
|
init_model_parser();
|
|
72707
74395
|
init_provider_definitions();
|
|
72708
74396
|
init_routing_rules();
|
|
74397
|
+
init_magmux_ui();
|
|
74398
|
+
init_settings();
|
|
72709
74399
|
init_telemetry();
|
|
72710
74400
|
init_terminal_isolation();
|
|
72711
74401
|
init_theme_mode();
|
|
@@ -72720,18 +74410,18 @@ var init_claude_runner = __esm(() => {
|
|
|
72720
74410
|
});
|
|
72721
74411
|
|
|
72722
74412
|
// src/diag-output.ts
|
|
72723
|
-
import { createWriteStream as createWriteStream3, mkdirSync as
|
|
74413
|
+
import { createWriteStream as createWriteStream3, mkdirSync as mkdirSync22, unlinkSync as unlinkSync10, writeFileSync as writeFileSync23 } from "fs";
|
|
72724
74414
|
import { homedir as homedir38 } from "os";
|
|
72725
|
-
import { join as
|
|
74415
|
+
import { join as join45 } from "path";
|
|
72726
74416
|
function getClaudishDir() {
|
|
72727
|
-
const dir =
|
|
74417
|
+
const dir = join45(homedir38(), ".claudish");
|
|
72728
74418
|
try {
|
|
72729
|
-
|
|
74419
|
+
mkdirSync22(dir, { recursive: true });
|
|
72730
74420
|
} catch {}
|
|
72731
74421
|
return dir;
|
|
72732
74422
|
}
|
|
72733
74423
|
function getDiagLogPath() {
|
|
72734
|
-
return
|
|
74424
|
+
return join45(getClaudishDir(), `diag-${process.pid}.log`);
|
|
72735
74425
|
}
|
|
72736
74426
|
|
|
72737
74427
|
class LogFileDiagOutput {
|
|
@@ -72740,7 +74430,7 @@ class LogFileDiagOutput {
|
|
|
72740
74430
|
constructor() {
|
|
72741
74431
|
this.logPath = getDiagLogPath();
|
|
72742
74432
|
try {
|
|
72743
|
-
|
|
74433
|
+
writeFileSync23(this.logPath, `--- claudish diag session ${new Date().toISOString()} ---
|
|
72744
74434
|
`);
|
|
72745
74435
|
} catch {}
|
|
72746
74436
|
this.stream = createWriteStream3(this.logPath, { flags: "a" });
|
|
@@ -73221,7 +74911,7 @@ var init_advisor_startup = __esm(() => {
|
|
|
73221
74911
|
});
|
|
73222
74912
|
|
|
73223
74913
|
// src/session/conversation.ts
|
|
73224
|
-
import { closeSync as
|
|
74914
|
+
import { closeSync as closeSync10, openSync as openSync10, readSync as readSync4, statSync as statSync9 } from "fs";
|
|
73225
74915
|
import { StringDecoder as StringDecoder3 } from "string_decoder";
|
|
73226
74916
|
function looksLikeTurn(line) {
|
|
73227
74917
|
const assistant = line.includes('"type":"assistant"');
|
|
@@ -73277,7 +74967,7 @@ function readConversation(file, opts = {}) {
|
|
|
73277
74967
|
let fd = null;
|
|
73278
74968
|
try {
|
|
73279
74969
|
const size = statSync9(file).size;
|
|
73280
|
-
fd =
|
|
74970
|
+
fd = openSync10(file, "r");
|
|
73281
74971
|
const buf = Buffer.allocUnsafe(chunkBytes);
|
|
73282
74972
|
const decoder = new StringDecoder3("utf-8");
|
|
73283
74973
|
let pending = "";
|
|
@@ -73333,7 +75023,7 @@ function readConversation(file, opts = {}) {
|
|
|
73333
75023
|
} catch {} finally {
|
|
73334
75024
|
if (fd !== null) {
|
|
73335
75025
|
try {
|
|
73336
|
-
|
|
75026
|
+
closeSync10(fd);
|
|
73337
75027
|
} catch {}
|
|
73338
75028
|
}
|
|
73339
75029
|
}
|
|
@@ -74937,9 +76627,9 @@ __export(exports_session_stats, {
|
|
|
74937
76627
|
});
|
|
74938
76628
|
import { readFileSync as readFileSync34 } from "fs";
|
|
74939
76629
|
import { homedir as homedir39 } from "os";
|
|
74940
|
-
import { join as
|
|
76630
|
+
import { join as join46 } from "path";
|
|
74941
76631
|
function tokenFilePath(port) {
|
|
74942
|
-
return process.env.CLAUDISH_TOKEN_FILE ||
|
|
76632
|
+
return process.env.CLAUDISH_TOKEN_FILE || join46(homedir39(), ".claudish", `tokens-${port}.json`);
|
|
74943
76633
|
}
|
|
74944
76634
|
function readSessionStats(port, opts) {
|
|
74945
76635
|
let raw;
|
|
@@ -75024,7 +76714,7 @@ function bg(hex) {
|
|
|
75024
76714
|
const [r, g, b] = rgb(hex);
|
|
75025
76715
|
return `\x1B[48;2;${r};${g};${b}m`;
|
|
75026
76716
|
}
|
|
75027
|
-
function
|
|
76717
|
+
function paint3(text, hex, bold = false) {
|
|
75028
76718
|
return `${bold ? BOLD4 : ""}${fg(hex)}${text}${RESET3}`;
|
|
75029
76719
|
}
|
|
75030
76720
|
function meter(pct, width, ramp = ramps.load) {
|
|
@@ -75032,7 +76722,7 @@ function meter(pct, width, ramp = ramps.load) {
|
|
|
75032
76722
|
if (!Number.isFinite(cells) || cells <= 0)
|
|
75033
76723
|
return "";
|
|
75034
76724
|
if (Number.isNaN(pct))
|
|
75035
|
-
return
|
|
76725
|
+
return paint3(NODATA2.repeat(cells), tokens.dead);
|
|
75036
76726
|
const cols = rampFor(cells, ramp);
|
|
75037
76727
|
const filled = fillCells(pct, cells);
|
|
75038
76728
|
let out = "";
|
|
@@ -75053,7 +76743,7 @@ function stackedBar(segments, width) {
|
|
|
75053
76743
|
return "";
|
|
75054
76744
|
const split = splitCells(segments.map((s) => s.value), cells);
|
|
75055
76745
|
if (split.reduce((a, b) => a + b, 0) === 0)
|
|
75056
|
-
return
|
|
76746
|
+
return paint3(TRACK2.repeat(cells), tokens.border);
|
|
75057
76747
|
let out = "";
|
|
75058
76748
|
for (let i = 0;i < split.length; i++) {
|
|
75059
76749
|
const n = split[i];
|
|
@@ -75178,14 +76868,14 @@ function renderSessionSummary(input) {
|
|
|
75178
76868
|
const W = cardWidth();
|
|
75179
76869
|
const inner = W - CHROME;
|
|
75180
76870
|
const out = [];
|
|
75181
|
-
const dim = (s) =>
|
|
75182
|
-
const body = (s) =>
|
|
76871
|
+
const dim = (s) => paint3(s, tokens.subtle);
|
|
76872
|
+
const body = (s) => paint3(s, tokens.text);
|
|
75183
76873
|
const titleText = exitCode === 0 ? " session " : " session \xB7 failed ";
|
|
75184
76874
|
const titleHex = exitCode === 0 ? tokens.accent : tokens.error;
|
|
75185
76875
|
const rule = "\u2500".repeat(Math.max(0, W - 2 - visibleWidth(titleText) - 1));
|
|
75186
|
-
out.push(
|
|
76876
|
+
out.push(paint3("\u256D\u2500", tokens.border) + paint3(titleText, titleHex) + paint3(rule + "\u256E", tokens.border));
|
|
75187
76877
|
const row = (s) => {
|
|
75188
|
-
out.push(`${
|
|
76878
|
+
out.push(`${paint3("\u2502", tokens.border)} ${padVisible2(s, inner)} ${paint3("\u2502", tokens.border)}`);
|
|
75189
76879
|
};
|
|
75190
76880
|
const blank = () => row("");
|
|
75191
76881
|
const chips = [badge(truncate2(modelSpec, 34), tokens.accent)];
|
|
@@ -75235,13 +76925,13 @@ function renderSessionSummary(input) {
|
|
|
75235
76925
|
if (rest > 0)
|
|
75236
76926
|
segs.push({ value: rest, color: area(other) });
|
|
75237
76927
|
dataRow("tools", stackedBar(segs, barW), body(padStartTo(String(stats.toolCallTotal), 4)) + dim(" calls"));
|
|
75238
|
-
const legend = shown.map((t, i) =>
|
|
76928
|
+
const legend = shown.map((t, i) => paint3(`${t.name} ${t.count}`, toolCols[i])).concat(rest > 0 ? [paint3(`other ${rest}`, other)] : []);
|
|
75239
76929
|
for (const line of wrapStyled(legend, dim(" \xB7 "), inner - LABEL_W)) {
|
|
75240
76930
|
row(" ".repeat(LABEL_W) + line);
|
|
75241
76931
|
}
|
|
75242
76932
|
}
|
|
75243
76933
|
blank();
|
|
75244
|
-
row(dim(padTo("cost", LABEL_W)) +
|
|
76934
|
+
row(dim(padTo("cost", LABEL_W)) + paint3(stats.isFree ? "free" : usd(stats.costUsd), tokens.text, true) + (stats.isEstimated && !stats.isFree ? dim(" estimated") : ""));
|
|
75245
76935
|
const dearest = Math.max(0, ...stats.savings.map((s) => s.baselineUsd));
|
|
75246
76936
|
for (const s of stats.savings) {
|
|
75247
76937
|
const label = padTo(`vs ${s.label}`, LABEL_W);
|
|
@@ -75250,10 +76940,10 @@ function renderSessionSummary(input) {
|
|
|
75250
76940
|
const share = dearest > 0 ? s.savedUsd / dearest * 100 : 0;
|
|
75251
76941
|
dataRow(label, meter(share, barW, areaRamp(ramps.volume)), dim(`${padStartTo(`${Math.round(pct)}%`, 4)} saved `) + body(usd(s.savedUsd)));
|
|
75252
76942
|
} else {
|
|
75253
|
-
dataRow(label, meter(0, barW, areaRamp(ramps.volume)), dim("over by ") +
|
|
76943
|
+
dataRow(label, meter(0, barW, areaRamp(ramps.volume)), dim("over by ") + paint3(usd(-s.savedUsd), tokens.error));
|
|
75254
76944
|
}
|
|
75255
76945
|
}
|
|
75256
|
-
out.push(
|
|
76946
|
+
out.push(paint3(`\u2570${"\u2500".repeat(W - 2)}\u256F`, tokens.border));
|
|
75257
76947
|
if (resumeId) {
|
|
75258
76948
|
out.push("");
|
|
75259
76949
|
out.push(dim("Resume this session with:"));
|
|
@@ -75302,8 +76992,8 @@ init_op_source();
|
|
|
75302
76992
|
init_startup_trace();
|
|
75303
76993
|
init_picker_cancelled();
|
|
75304
76994
|
var import_dotenv3 = __toESM(require_main(), 1);
|
|
75305
|
-
import { existsSync as
|
|
75306
|
-
import { join as
|
|
76995
|
+
import { existsSync as existsSync36, readFileSync as readFileSync35 } from "fs";
|
|
76996
|
+
import { join as join47, resolve as resolve6 } from "path";
|
|
75307
76997
|
|
|
75308
76998
|
// src/tui/runtime/should-open-picker.ts
|
|
75309
76999
|
function noModelConfigured(gate) {
|
|
@@ -75416,7 +77106,7 @@ async function applyConfigOverride() {
|
|
|
75416
77106
|
await Promise.resolve();
|
|
75417
77107
|
const plan = planConfigOverride(process.argv.slice(2), process.env, {
|
|
75418
77108
|
resolve: resolve6,
|
|
75419
|
-
exists:
|
|
77109
|
+
exists: existsSync36
|
|
75420
77110
|
});
|
|
75421
77111
|
if (plan.kind === "none")
|
|
75422
77112
|
return;
|
|
@@ -75558,7 +77248,7 @@ async function runCli() {
|
|
|
75558
77248
|
const endImports = beginSpan("startup:cli-imports");
|
|
75559
77249
|
await Promise.resolve().then(() => init_claude_runner());
|
|
75560
77250
|
await Promise.resolve().then(() => init_cli());
|
|
75561
|
-
await Promise.resolve().then(() =>
|
|
77251
|
+
await Promise.resolve().then(() => init_config());
|
|
75562
77252
|
await Promise.resolve().then(() => init_model_selector());
|
|
75563
77253
|
await Promise.resolve().then(() => init_provider_resolver());
|
|
75564
77254
|
await Promise.resolve().then(() => init_logger());
|
|
@@ -75596,7 +77286,7 @@ async function runCli() {
|
|
|
75596
77286
|
process.exit(1);
|
|
75597
77287
|
}
|
|
75598
77288
|
const mode = cliConfig.teamMode ?? "default";
|
|
75599
|
-
const sessionPath =
|
|
77289
|
+
const sessionPath = join47(process.cwd(), `.claudish-team-${Date.now()}`);
|
|
75600
77290
|
if (mode === "json") {
|
|
75601
77291
|
await Promise.resolve().then(() => init_team_orchestrator());
|
|
75602
77292
|
setupSession(sessionPath, cliConfig.team, prompt);
|
|
@@ -75605,7 +77295,7 @@ async function runCli() {
|
|
|
75605
77295
|
});
|
|
75606
77296
|
const result = { ...status, responses: {} };
|
|
75607
77297
|
for (const anonId of Object.keys(status.models)) {
|
|
75608
|
-
const responsePath =
|
|
77298
|
+
const responsePath = join47(sessionPath, `response-${anonId}.md`);
|
|
75609
77299
|
try {
|
|
75610
77300
|
const raw = readFileSync35(responsePath, "utf-8").trim();
|
|
75611
77301
|
try {
|