claudish 7.45.0 → 7.47.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 +3467 -403
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -729,7 +729,7 @@ var init_onepassword_config = __esm(() => {
|
|
|
729
729
|
});
|
|
730
730
|
|
|
731
731
|
// src/version.ts
|
|
732
|
-
var VERSION = "7.
|
|
732
|
+
var VERSION = "7.47.0";
|
|
733
733
|
|
|
734
734
|
// src/logger.ts
|
|
735
735
|
var exports_logger = {};
|
|
@@ -1219,11 +1219,6 @@ function writeJsonlCapped(payload) {
|
|
|
1219
1219
|
`);
|
|
1220
1220
|
}
|
|
1221
1221
|
}
|
|
1222
|
-
function printSlowLine(payload) {
|
|
1223
|
-
const top = [...payload.spans].sort((a, b) => b.durMs - a.durMs).slice(0, 3).map((s) => `${s.name} ${fmtDur(s.durMs)}${fmtWaitExec(s.meta)}`);
|
|
1224
|
-
const detail = top.length > 0 ? ` \u2014 ${top.join(", ")}` : "";
|
|
1225
|
-
seams.stderr(`[claudish] slow start ${fmtDur(payload.totalMs)}${detail} \u2026 full data: ` + `${displayPath(seams.outPath)} (CLAUDISH_STARTUP_TRACE=1 for live detail)`);
|
|
1226
|
-
}
|
|
1227
1222
|
function printTable(payload) {
|
|
1228
1223
|
seams.stderr(`[claudish] startup trace (${payload.argvKind}) \u2014 total ${fmtDur(payload.totalMs)}` + ` \xB7 auth ${payload.authKind} \xB7 v${payload.version}`);
|
|
1229
1224
|
seams.stderr(" start dur span");
|
|
@@ -1234,7 +1229,7 @@ function printTable(payload) {
|
|
|
1234
1229
|
}
|
|
1235
1230
|
seams.stderr(` metrics: ${displayPath(seams.outPath)}`);
|
|
1236
1231
|
}
|
|
1237
|
-
function finalizeStartupTrace(context,
|
|
1232
|
+
function finalizeStartupTrace(context, _opts = {}) {
|
|
1238
1233
|
try {
|
|
1239
1234
|
if (finalized)
|
|
1240
1235
|
return;
|
|
@@ -1254,8 +1249,6 @@ function finalizeStartupTrace(context, opts = {}) {
|
|
|
1254
1249
|
try {
|
|
1255
1250
|
if (terminalSuppressed) {} else if (traceModeOn()) {
|
|
1256
1251
|
printTable(payload);
|
|
1257
|
-
} else if (totalMs > seams.slowThresholdMs && !opts.quiet) {
|
|
1258
|
-
printSlowLine(payload);
|
|
1259
1252
|
}
|
|
1260
1253
|
} catch {}
|
|
1261
1254
|
} catch {}
|
|
@@ -1345,6 +1338,20 @@ function isAbandoned(path) {
|
|
|
1345
1338
|
return true;
|
|
1346
1339
|
return Date.now() - holder.at > timing.staleMs;
|
|
1347
1340
|
}
|
|
1341
|
+
function peerHoldsHandshakeLock() {
|
|
1342
|
+
if (lastHandshake?.heldByUs)
|
|
1343
|
+
return false;
|
|
1344
|
+
const holder = readHolder(currentLockPath());
|
|
1345
|
+
if (!holder || holder.pid === process.pid)
|
|
1346
|
+
return false;
|
|
1347
|
+
if (!holderAlive(holder.pid))
|
|
1348
|
+
return false;
|
|
1349
|
+
if (Date.now() - holder.at > timing.staleMs)
|
|
1350
|
+
return false;
|
|
1351
|
+
if (lastHandshake && holder.at > lastHandshake.startedAt)
|
|
1352
|
+
return false;
|
|
1353
|
+
return true;
|
|
1354
|
+
}
|
|
1348
1355
|
async function acquire(path) {
|
|
1349
1356
|
const deadline = Date.now() + timing.timeoutMs;
|
|
1350
1357
|
let madeDir = false;
|
|
@@ -1385,6 +1392,7 @@ function release(path) {
|
|
|
1385
1392
|
async function withHandshakeLock(handshake) {
|
|
1386
1393
|
if (process.env.CLAUDISH_NO_OP_HANDSHAKE_LOCK === "1") {
|
|
1387
1394
|
trace("bypassed (CLAUDISH_NO_OP_HANDSHAKE_LOCK=1)");
|
|
1395
|
+
lastHandshake = { heldByUs: false, startedAt: Date.now() };
|
|
1388
1396
|
return handshake();
|
|
1389
1397
|
}
|
|
1390
1398
|
const path = currentLockPath();
|
|
@@ -1396,6 +1404,7 @@ async function withHandshakeLock(handshake) {
|
|
|
1396
1404
|
held = false;
|
|
1397
1405
|
}
|
|
1398
1406
|
trace(`${held ? "acquired" : "NOT held (timeout or unwritable)"} after ${Date.now() - t0}ms`);
|
|
1407
|
+
lastHandshake = { heldByUs: held, startedAt: t0 };
|
|
1399
1408
|
try {
|
|
1400
1409
|
return await handshake();
|
|
1401
1410
|
} finally {
|
|
@@ -1404,7 +1413,7 @@ async function withHandshakeLock(handshake) {
|
|
|
1404
1413
|
trace(`handshake done after ${Date.now() - t0}ms${held ? ", released" : ""}`);
|
|
1405
1414
|
}
|
|
1406
1415
|
}
|
|
1407
|
-
var DEFAULT_STALE_MS = 120000, DEFAULT_TIMEOUT_MS = 45000, DEFAULT_POLL_MS = 60, timing, lockPath, sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
1416
|
+
var DEFAULT_STALE_MS = 120000, DEFAULT_TIMEOUT_MS = 45000, DEFAULT_POLL_MS = 60, timing, lockPath, sleep = (ms) => new Promise((r) => setTimeout(r, ms)), lastHandshake;
|
|
1408
1417
|
var init_onepassword_handshake_lock = __esm(() => {
|
|
1409
1418
|
timing = {
|
|
1410
1419
|
staleMs: DEFAULT_STALE_MS,
|
|
@@ -3897,6 +3906,7 @@ __export(exports_onepassword, {
|
|
|
3897
3906
|
wasOpAuthorizationDenied: () => wasOpAuthorizationDenied,
|
|
3898
3907
|
valueTail: () => valueTail,
|
|
3899
3908
|
setScreenLockProbe: () => setScreenLockProbe,
|
|
3909
|
+
setPeerLockProbe: () => setPeerLockProbe,
|
|
3900
3910
|
setLockRetryTiming: () => setLockRetryTiming,
|
|
3901
3911
|
setAppLockProbe: () => setAppLockProbe,
|
|
3902
3912
|
resolveSecretsPartial: () => resolveSecretsPartial,
|
|
@@ -3920,11 +3930,13 @@ __export(exports_onepassword, {
|
|
|
3920
3930
|
listItems: () => listItems,
|
|
3921
3931
|
isTransientSdkError: () => isTransientSdkError,
|
|
3922
3932
|
isScreenLocked: () => isScreenLocked,
|
|
3933
|
+
isPeerHoldingPrompt: () => isPeerHoldingPrompt,
|
|
3923
3934
|
isOpReference: () => isOpReference,
|
|
3924
3935
|
isOpHydratedVar: () => isOpHydratedVar,
|
|
3925
3936
|
isLockedDenial: () => isLockedDenial,
|
|
3926
3937
|
isGlobImport: () => isGlobImport,
|
|
3927
3938
|
isAppLocked: () => isAppLocked,
|
|
3939
|
+
humanizeOpError: () => humanizeOpError,
|
|
3928
3940
|
globToRegExp: () => globToRegExp,
|
|
3929
3941
|
getOpFailures: () => getOpFailures,
|
|
3930
3942
|
filterGlobFields: () => filterGlobFields,
|
|
@@ -3938,6 +3950,7 @@ __export(exports_onepassword, {
|
|
|
3938
3950
|
defaultAppLockProbe: () => defaultAppLockProbe,
|
|
3939
3951
|
currentLockCause: () => currentLockCause,
|
|
3940
3952
|
collectConfigImports: () => collectConfigImports,
|
|
3953
|
+
clearOpSkip: () => clearOpSkip,
|
|
3941
3954
|
classifyLockedDenial: () => classifyLockedDenial,
|
|
3942
3955
|
buildAuthError: () => buildAuthError,
|
|
3943
3956
|
appLockedFromSettings: () => appLockedFromSettings,
|
|
@@ -3971,6 +3984,21 @@ function resetOpFailures() {
|
|
|
3971
3984
|
function wasOpAuthorizationDenied() {
|
|
3972
3985
|
return opSourceFailures.some((f) => /denied authorization/i.test(f.message));
|
|
3973
3986
|
}
|
|
3987
|
+
function humanizeOpError(err) {
|
|
3988
|
+
const raw = (err instanceof Error ? err.message : String(err)).trim();
|
|
3989
|
+
if (/denied authorization/i.test(raw)) {
|
|
3990
|
+
const cause = currentLockCause();
|
|
3991
|
+
if (cause === "screen")
|
|
3992
|
+
return "your Mac is locked, so the approval prompt couldn't be shown";
|
|
3993
|
+
if (cause === "app")
|
|
3994
|
+
return "the 1Password app is locked, so it declined without prompting";
|
|
3995
|
+
if (cause === "peer")
|
|
3996
|
+
return "another claudish process is holding the 1Password prompt";
|
|
3997
|
+
return "the 1Password approval prompt was dismissed (or never answered)";
|
|
3998
|
+
}
|
|
3999
|
+
const unwrapped = raw.match(/Error\s*\{\s*msg:\s*(.*?)(?:,\s*inner:|\s*\})/s);
|
|
4000
|
+
return (unwrapped?.[1] ?? raw).trim();
|
|
4001
|
+
}
|
|
3974
4002
|
function renderOpFailureNotice(envVar) {
|
|
3975
4003
|
return renderOpFailureBlock(`for ${envVar} this run`);
|
|
3976
4004
|
}
|
|
@@ -3997,6 +4025,12 @@ function renderOpFailureBlock(subject) {
|
|
|
3997
4025
|
lines.push("");
|
|
3998
4026
|
lines.push(" Fix: unlock 1Password (Touch ID is enough) and re-run.");
|
|
3999
4027
|
lines.push(" To stop it re-locking mid-session, raise Settings \u2192 Security \u2192 auto-lock.");
|
|
4028
|
+
} else if (cause === "peer") {
|
|
4029
|
+
lines.push(" Another claudish process is at the 1Password prompt right now. 1Password");
|
|
4030
|
+
lines.push(" authorizes ONE client at a time and denies every peer instantly, so this");
|
|
4031
|
+
lines.push(" run lost a race rather than being refused.");
|
|
4032
|
+
lines.push("");
|
|
4033
|
+
lines.push(" Fix: approve the prompt in the other window, then re-run this one.");
|
|
4000
4034
|
} else {
|
|
4001
4035
|
lines.push(" The 1Password desktop app declined to release secrets. The approval prompt");
|
|
4002
4036
|
lines.push(" was most likely dismissed.");
|
|
@@ -4373,11 +4407,19 @@ function setAppLockProbe(probe) {
|
|
|
4373
4407
|
function isAppLocked() {
|
|
4374
4408
|
return appLockProbe();
|
|
4375
4409
|
}
|
|
4410
|
+
function setPeerLockProbe(probe) {
|
|
4411
|
+
peerLockProbe = probe ?? peerHoldsHandshakeLock;
|
|
4412
|
+
}
|
|
4413
|
+
function isPeerHoldingPrompt() {
|
|
4414
|
+
return peerLockProbe();
|
|
4415
|
+
}
|
|
4376
4416
|
function currentLockCause() {
|
|
4377
4417
|
if (isScreenLocked())
|
|
4378
4418
|
return "screen";
|
|
4379
4419
|
if (isAppLocked())
|
|
4380
4420
|
return "app";
|
|
4421
|
+
if (isPeerHoldingPrompt())
|
|
4422
|
+
return "peer";
|
|
4381
4423
|
return null;
|
|
4382
4424
|
}
|
|
4383
4425
|
function classifyLockedDenial(err, env = process.env) {
|
|
@@ -4395,23 +4437,28 @@ async function countdownForUnlock(round, rounds, cause) {
|
|
|
4395
4437
|
const ttyOut = process.stderr.isTTY === true;
|
|
4396
4438
|
const ttyIn = process.stdin.isTTY === true;
|
|
4397
4439
|
let cancelled = false;
|
|
4440
|
+
let skipped = false;
|
|
4398
4441
|
if (round === 1) {
|
|
4399
4442
|
const explain = cause === "screen" ? `${bold("\uD83D\uDD10 1Password needs your OK \u2014 but your Mac is locked, so it can't ask.")}
|
|
4400
|
-
Unlock your Mac and approve the popup. Claudish picks it up from there.` : `${bold("\uD83D\uDD10 1Password is locked, so it turned claudish away without asking you.")}
|
|
4443
|
+
Unlock your Mac and approve the popup. Claudish picks it up from there.` : cause === "app" ? `${bold("\uD83D\uDD10 1Password is locked, so it turned claudish away without asking you.")}
|
|
4401
4444
|
Unlock 1Password (Touch ID is enough). Claudish retries on its own \u2014
|
|
4402
|
-
no popup will appear until it's unlocked
|
|
4445
|
+
no popup will appear until it's unlocked.` : `${bold("\uD83D\uDD10 Another claudish is already at the 1Password prompt.")}
|
|
4446
|
+
1Password only lets one through at a time, so this run has to queue.
|
|
4447
|
+
Approve it in the other window (or wait \u2014 it usually takes a second).`;
|
|
4403
4448
|
process.stderr.write(`
|
|
4404
4449
|
${explain}
|
|
4405
4450
|
|
|
4406
4451
|
`);
|
|
4407
4452
|
}
|
|
4408
|
-
const stillLocked = () => cause === "screen" ? isScreenLocked() : isAppLocked();
|
|
4453
|
+
const stillLocked = () => cause === "screen" ? isScreenLocked() : cause === "app" ? isAppLocked() : isPeerHoldingPrompt();
|
|
4409
4454
|
let restoreInput = () => {};
|
|
4410
4455
|
if (ttyIn) {
|
|
4411
4456
|
const onKey = (buf) => {
|
|
4412
4457
|
const k = buf.toString();
|
|
4413
4458
|
if (k === "\x1B" || k === "q" || k === "\x03")
|
|
4414
4459
|
cancelled = true;
|
|
4460
|
+
if (k === "s" || k === "S")
|
|
4461
|
+
skipped = true;
|
|
4415
4462
|
};
|
|
4416
4463
|
const wasRaw = process.stdin.isRaw === true;
|
|
4417
4464
|
try {
|
|
@@ -4427,14 +4474,20 @@ ${explain}
|
|
|
4427
4474
|
restoreInput = () => {};
|
|
4428
4475
|
}
|
|
4429
4476
|
}
|
|
4430
|
-
const stop = ttyIn ? "Esc
|
|
4431
|
-
const
|
|
4477
|
+
const stop = ttyIn ? "Esc stop waiting" : "Ctrl-C stop waiting";
|
|
4478
|
+
const skip = ttyIn ? " \xB7 s skip 1Password" : "";
|
|
4479
|
+
const bar = (secs) => {
|
|
4480
|
+
const total = Math.max(1, lockRetrySeconds);
|
|
4481
|
+
const filled = Math.round((total - secs) / total * 10);
|
|
4482
|
+
return `${"\u2501".repeat(filled)}${"\u2500".repeat(Math.max(0, 10 - filled))}`;
|
|
4483
|
+
};
|
|
4484
|
+
const line = (secs) => dim(` ${bar(secs)} ${secs}s \xB7 try ${round}/${rounds} \xB7 ${stop}${skip}`);
|
|
4432
4485
|
if (!ttyOut)
|
|
4433
4486
|
process.stderr.write(`${line(lockRetrySeconds)}
|
|
4434
4487
|
`);
|
|
4435
4488
|
try {
|
|
4436
4489
|
for (let remaining = lockRetrySeconds;remaining > 0; remaining--) {
|
|
4437
|
-
if (cancelled)
|
|
4490
|
+
if (cancelled || skipped)
|
|
4438
4491
|
break;
|
|
4439
4492
|
if (!stillLocked())
|
|
4440
4493
|
break;
|
|
@@ -4447,6 +4500,12 @@ ${explain}
|
|
|
4447
4500
|
if (ttyOut)
|
|
4448
4501
|
process.stderr.write("\r\x1B[2K");
|
|
4449
4502
|
}
|
|
4503
|
+
if (skipped) {
|
|
4504
|
+
process.stderr.write(` Skipping 1Password for this run. Keys already in your environment still work;
|
|
4505
|
+
` + ` anything only 1Password has will report as missing.
|
|
4506
|
+
`);
|
|
4507
|
+
return "skip";
|
|
4508
|
+
}
|
|
4450
4509
|
if (cancelled) {
|
|
4451
4510
|
process.stderr.write(` OK, not waiting. You can unlock and re-run any time.
|
|
4452
4511
|
`);
|
|
@@ -4454,6 +4513,17 @@ ${explain}
|
|
|
4454
4513
|
}
|
|
4455
4514
|
return "retry";
|
|
4456
4515
|
}
|
|
4516
|
+
function latchOpSkip() {
|
|
4517
|
+
opSkipLatchedByPrompt = true;
|
|
4518
|
+
process.env.CLAUDISH_DISABLE_OP = "1";
|
|
4519
|
+
}
|
|
4520
|
+
function clearOpSkip() {
|
|
4521
|
+
if (!opSkipLatchedByPrompt)
|
|
4522
|
+
return false;
|
|
4523
|
+
opSkipLatchedByPrompt = false;
|
|
4524
|
+
delete process.env.CLAUDISH_DISABLE_OP;
|
|
4525
|
+
return true;
|
|
4526
|
+
}
|
|
4457
4527
|
async function withSdkRetry(op, label = "op:sdk-op") {
|
|
4458
4528
|
for (let round = 1;; round++) {
|
|
4459
4529
|
try {
|
|
@@ -4462,7 +4532,12 @@ async function withSdkRetry(op, label = "op:sdk-op") {
|
|
|
4462
4532
|
const cause = classifyLockedDenial(err);
|
|
4463
4533
|
if (round > LOCK_RETRY_ROUNDS || cause === null)
|
|
4464
4534
|
throw err;
|
|
4465
|
-
|
|
4535
|
+
const choice = await countdownForUnlock(round, LOCK_RETRY_ROUNDS, cause);
|
|
4536
|
+
if (choice === "skip") {
|
|
4537
|
+
latchOpSkip();
|
|
4538
|
+
throw err;
|
|
4539
|
+
}
|
|
4540
|
+
if (choice === "cancel")
|
|
4466
4541
|
throw err;
|
|
4467
4542
|
resetSdkClientCache();
|
|
4468
4543
|
}
|
|
@@ -4680,7 +4755,7 @@ var OP_REF_RE, opHydratedVars, opSourceFailures, ENV_VAR_NAME_RE, sdkClientCache
|
|
|
4680
4755
|
} catch {
|
|
4681
4756
|
return false;
|
|
4682
4757
|
}
|
|
4683
|
-
}, screenLockProbe, defaultAppLockProbe = () => false, appLockProbe, OP_PROBE_TIMEOUT_MS = 5000, defaultOpAccountLister = () => {
|
|
4758
|
+
}, screenLockProbe, defaultAppLockProbe = () => false, appLockProbe, peerLockProbe, opSkipLatchedByPrompt = false, OP_PROBE_TIMEOUT_MS = 5000, defaultOpAccountLister = () => {
|
|
4684
4759
|
try {
|
|
4685
4760
|
const res = spawnSync("op", ["--cache=false", "account", "list", "--format=json"], {
|
|
4686
4761
|
encoding: "utf-8",
|
|
@@ -4723,6 +4798,7 @@ var init_onepassword = __esm(() => {
|
|
|
4723
4798
|
lockRetrySeconds = LOCK_RETRY_SECONDS;
|
|
4724
4799
|
screenLockProbe = defaultScreenLockProbe;
|
|
4725
4800
|
appLockProbe = defaultAppLockProbe;
|
|
4801
|
+
peerLockProbe = peerHoldsHandshakeLock;
|
|
4726
4802
|
});
|
|
4727
4803
|
|
|
4728
4804
|
// src/auth/credentials/op-source.ts
|
|
@@ -4859,6 +4935,8 @@ function readConfigRaw() {
|
|
|
4859
4935
|
}
|
|
4860
4936
|
}
|
|
4861
4937
|
function hasOpSources() {
|
|
4938
|
+
if (process.env.CLAUDISH_DISABLE_OP === "1")
|
|
4939
|
+
return false;
|
|
4862
4940
|
if (sniffed !== undefined)
|
|
4863
4941
|
return sniffed;
|
|
4864
4942
|
sniffed = computeHasOpSources();
|
|
@@ -5137,7 +5215,8 @@ async function resolveOpKeyForEnvVarsInner(wanted, opts = {}, span) {
|
|
|
5137
5215
|
resolveSecrets: resolveSecrets2,
|
|
5138
5216
|
recordOpHydratedVars: recordOpHydratedVars2,
|
|
5139
5217
|
recordOpFailure: recordOpFailure2,
|
|
5140
|
-
withSdkRetry: withSdkRetry2
|
|
5218
|
+
withSdkRetry: withSdkRetry2,
|
|
5219
|
+
humanizeOpError: humanizeOpError2
|
|
5141
5220
|
} = await Promise.resolve().then(() => (init_onepassword(), exports_onepassword));
|
|
5142
5221
|
const cfg = readConfigRaw();
|
|
5143
5222
|
const out = {};
|
|
@@ -5198,7 +5277,7 @@ async function resolveOpKeyForEnvVarsInner(wanted, opts = {}, span) {
|
|
|
5198
5277
|
}
|
|
5199
5278
|
} catch (globErr) {
|
|
5200
5279
|
const m = globErr instanceof Error ? globErr.message : String(globErr);
|
|
5201
|
-
warnOnce(`[claudish] 1Password import skipped: ${
|
|
5280
|
+
warnOnce(`[claudish] 1Password import skipped: ${humanizeOpError2(globErr)}`);
|
|
5202
5281
|
recordOpFailure2({ kind: "import", source: globPath, message: m });
|
|
5203
5282
|
}
|
|
5204
5283
|
}
|
|
@@ -5239,7 +5318,7 @@ async function resolveOpKeyForEnvVarsInner(wanted, opts = {}, span) {
|
|
|
5239
5318
|
}
|
|
5240
5319
|
} catch (envErr) {
|
|
5241
5320
|
const m = envErr instanceof Error ? envErr.message : String(envErr);
|
|
5242
|
-
warnOnce(`[claudish] 1Password environment skipped: ${
|
|
5321
|
+
warnOnce(`[claudish] 1Password environment skipped: ${humanizeOpError2(envErr)}`);
|
|
5243
5322
|
recordOpFailure2({ kind: "environment", source: envId, message: m });
|
|
5244
5323
|
}
|
|
5245
5324
|
}
|
|
@@ -5251,7 +5330,7 @@ async function resolveOpKeyForEnvVarsInner(wanted, opts = {}, span) {
|
|
|
5251
5330
|
return out;
|
|
5252
5331
|
}
|
|
5253
5332
|
const message = err instanceof Error ? err.message : String(err);
|
|
5254
|
-
warnOnce(`[claudish] 1Password secret resolution failed: ${
|
|
5333
|
+
warnOnce(`[claudish] 1Password secret resolution failed: ${humanizeOpError2(err)}`);
|
|
5255
5334
|
recordOpFailure2({ kind: "reference", message });
|
|
5256
5335
|
if (onAuthFailure === "throw")
|
|
5257
5336
|
throw err;
|
|
@@ -28811,7 +28890,9 @@ var init_remote_provider_types = __esm(() => {
|
|
|
28811
28890
|
"kimi-coding",
|
|
28812
28891
|
"glm-coding",
|
|
28813
28892
|
"qwen-cloud",
|
|
28814
|
-
"devin"
|
|
28893
|
+
"devin",
|
|
28894
|
+
"antigravity",
|
|
28895
|
+
"sakana-subscription"
|
|
28815
28896
|
]);
|
|
28816
28897
|
PROVIDER_ALIAS = {
|
|
28817
28898
|
google: "gemini",
|
|
@@ -29136,6 +29217,11 @@ function removeUriFormat(schema) {
|
|
|
29136
29217
|
}
|
|
29137
29218
|
return result;
|
|
29138
29219
|
}
|
|
29220
|
+
function stripBillingHeader(text) {
|
|
29221
|
+
if (!text || !text.includes("x-anthropic-billing-header"))
|
|
29222
|
+
return text;
|
|
29223
|
+
return text.replace(BILLING_HEADER_RE, "");
|
|
29224
|
+
}
|
|
29139
29225
|
function transformOpenAIToClaude(claudeRequestInput) {
|
|
29140
29226
|
const req = JSON.parse(JSON.stringify(claudeRequestInput));
|
|
29141
29227
|
const isO3Model = typeof req.model === "string" && (req.model.includes("o3") || req.model.includes("o1"));
|
|
@@ -29159,9 +29245,11 @@ function transformOpenAIToClaude(claudeRequestInput) {
|
|
|
29159
29245
|
}
|
|
29160
29246
|
}
|
|
29161
29247
|
return JSON.stringify(item);
|
|
29162
|
-
}).filter((text) => text && text.trim() !== "").join(`
|
|
29248
|
+
}).map((text) => stripBillingHeader(text)).filter((text) => text && text.trim() !== "").join(`
|
|
29163
29249
|
|
|
29164
29250
|
`);
|
|
29251
|
+
} else if (typeof req.system === "string") {
|
|
29252
|
+
req.system = stripBillingHeader(req.system);
|
|
29165
29253
|
}
|
|
29166
29254
|
if (!Array.isArray(req.messages)) {
|
|
29167
29255
|
if (req.messages == null)
|
|
@@ -29183,7 +29271,10 @@ function transformOpenAIToClaude(claudeRequestInput) {
|
|
|
29183
29271
|
isO3Model
|
|
29184
29272
|
};
|
|
29185
29273
|
}
|
|
29186
|
-
var
|
|
29274
|
+
var BILLING_HEADER_RE;
|
|
29275
|
+
var init_transform = __esm(() => {
|
|
29276
|
+
BILLING_HEADER_RE = /x-anthropic-billing-header:[^\n]*\n?/gi;
|
|
29277
|
+
});
|
|
29187
29278
|
|
|
29188
29279
|
// src/handlers/shared/format/openai-tools.ts
|
|
29189
29280
|
function emptyParamsSchema() {
|
|
@@ -29911,23 +30002,33 @@ import { execFileSync } from "child_process";
|
|
|
29911
30002
|
import { existsSync as existsSync7 } from "fs";
|
|
29912
30003
|
import { homedir as homedir9 } from "os";
|
|
29913
30004
|
import { join as join9 } from "path";
|
|
30005
|
+
function invalidateReadStoreMemo() {
|
|
30006
|
+
cachedRawStore = null;
|
|
30007
|
+
}
|
|
29914
30008
|
function defaultReadStore() {
|
|
29915
30009
|
if (process.platform !== "darwin") {
|
|
29916
30010
|
logStderr("[Antigravity] Shared token store is macOS-only for now (other keyring backends are a follow-up).");
|
|
29917
30011
|
return null;
|
|
29918
30012
|
}
|
|
30013
|
+
const now = Date.now();
|
|
30014
|
+
if (cachedRawStore && now - cachedRawStore.at < READ_STORE_TTL_MS)
|
|
30015
|
+
return cachedRawStore.value;
|
|
30016
|
+
let value = null;
|
|
29919
30017
|
try {
|
|
29920
30018
|
const out = execFileSync("security", ["find-generic-password", "-s", KC_SERVICE, "-a", KC_ACCOUNT, "-w"], { encoding: "utf8" });
|
|
29921
30019
|
const trimmed2 = out.trim();
|
|
29922
|
-
|
|
30020
|
+
value = trimmed2.length > 0 ? trimmed2 : null;
|
|
29923
30021
|
} catch {
|
|
29924
|
-
|
|
30022
|
+
value = null;
|
|
29925
30023
|
}
|
|
30024
|
+
cachedRawStore = { at: now, value };
|
|
30025
|
+
return value;
|
|
29926
30026
|
}
|
|
29927
30027
|
function defaultWriteStore(rawValue) {
|
|
29928
30028
|
if (process.platform !== "darwin") {
|
|
29929
30029
|
throw new Error("[Antigravity] Cannot write the shared token store on a non-macOS platform.");
|
|
29930
30030
|
}
|
|
30031
|
+
invalidateReadStoreMemo();
|
|
29931
30032
|
execFileSync("security", ["add-generic-password", "-U", "-s", KC_SERVICE, "-a", KC_ACCOUNT, "-w", rawValue], { stdio: ["ignore", "ignore", "ignore"] });
|
|
29932
30033
|
}
|
|
29933
30034
|
function locateAgyBinary() {
|
|
@@ -29942,6 +30043,7 @@ function locateAgyBinary() {
|
|
|
29942
30043
|
function defaultDeleteStore() {
|
|
29943
30044
|
if (process.platform !== "darwin")
|
|
29944
30045
|
return;
|
|
30046
|
+
invalidateReadStoreMemo();
|
|
29945
30047
|
try {
|
|
29946
30048
|
execFileSync("security", ["delete-generic-password", "-s", KC_SERVICE, "-a", KC_ACCOUNT], {
|
|
29947
30049
|
stdio: ["ignore", "ignore", "ignore"]
|
|
@@ -29957,7 +30059,9 @@ function defaultRunAgyRefresh() {
|
|
|
29957
30059
|
stdio: ["ignore", "ignore", "ignore"],
|
|
29958
30060
|
timeout: AGY_REFRESH_TIMEOUT_MS
|
|
29959
30061
|
});
|
|
29960
|
-
} catch {}
|
|
30062
|
+
} catch {} finally {
|
|
30063
|
+
invalidateReadStoreMemo();
|
|
30064
|
+
}
|
|
29961
30065
|
}
|
|
29962
30066
|
function parseRecord(raw) {
|
|
29963
30067
|
if (!raw)
|
|
@@ -30035,8 +30139,9 @@ function getValidAntigravityAccessToken(deps = defaultDeps) {
|
|
|
30035
30139
|
function _resetAntigravityTokenState() {
|
|
30036
30140
|
inFlight = null;
|
|
30037
30141
|
cachedHasToken = null;
|
|
30142
|
+
invalidateReadStoreMemo();
|
|
30038
30143
|
}
|
|
30039
|
-
var KC_SERVICE = "gemini", KC_ACCOUNT = "antigravity", PREFIX = "go-keyring-base64:", EXPIRY_SKEW_MS = 120000, AGY_REFRESH_TIMEOUT_MS = 40000, defaultDeps, cachedHasToken = null, HAS_TOKEN_TTL_MS = 5000, inFlight = null;
|
|
30144
|
+
var KC_SERVICE = "gemini", KC_ACCOUNT = "antigravity", PREFIX = "go-keyring-base64:", EXPIRY_SKEW_MS = 120000, AGY_REFRESH_TIMEOUT_MS = 40000, READ_STORE_TTL_MS = 3000, cachedRawStore = null, defaultDeps, cachedHasToken = null, HAS_TOKEN_TTL_MS = 5000, inFlight = null;
|
|
30040
30145
|
var init_antigravity_token = __esm(() => {
|
|
30041
30146
|
init_logger();
|
|
30042
30147
|
defaultDeps = {
|
|
@@ -33042,6 +33147,13 @@ function createStreamingResponseHandler(c, response, adapter, target, middleware
|
|
|
33042
33147
|
async start(controller) {
|
|
33043
33148
|
const send = (e, d) => {
|
|
33044
33149
|
if (!isClosed) {
|
|
33150
|
+
if (e === "content_block_start" && d?.content_block?.type === "tool_use") {
|
|
33151
|
+
try {
|
|
33152
|
+
behavior?.onToolCallObserved?.(String(d.content_block.name ?? ""));
|
|
33153
|
+
} catch (err) {
|
|
33154
|
+
log(`[Streaming] onToolCallObserved threw: ${err}`);
|
|
33155
|
+
}
|
|
33156
|
+
}
|
|
33045
33157
|
controller.enqueue(encoder.encode(`event: ${e}
|
|
33046
33158
|
data: ${JSON.stringify(d)}
|
|
33047
33159
|
|
|
@@ -33069,54 +33181,98 @@ data: ${JSON.stringify(d)}
|
|
|
33069
33181
|
send("ping", { type: "ping" });
|
|
33070
33182
|
}
|
|
33071
33183
|
}, 1000);
|
|
33184
|
+
const teardown = () => {
|
|
33185
|
+
if (!isClosed) {
|
|
33186
|
+
try {
|
|
33187
|
+
controller.enqueue(encoder.encode(`data: [DONE]
|
|
33188
|
+
|
|
33189
|
+
|
|
33190
|
+
`));
|
|
33191
|
+
} catch {}
|
|
33192
|
+
try {
|
|
33193
|
+
controller.close();
|
|
33194
|
+
} catch {}
|
|
33195
|
+
isClosed = true;
|
|
33196
|
+
}
|
|
33197
|
+
if (ping) {
|
|
33198
|
+
clearInterval(ping);
|
|
33199
|
+
ping = null;
|
|
33200
|
+
}
|
|
33201
|
+
};
|
|
33072
33202
|
const finalize = async (reason, err) => {
|
|
33073
|
-
if (state.finalized)
|
|
33203
|
+
if (state.finalized) {
|
|
33204
|
+
teardown();
|
|
33074
33205
|
return;
|
|
33206
|
+
}
|
|
33075
33207
|
state.finalized = true;
|
|
33076
|
-
|
|
33077
|
-
|
|
33078
|
-
|
|
33079
|
-
|
|
33080
|
-
|
|
33081
|
-
|
|
33082
|
-
|
|
33083
|
-
|
|
33208
|
+
try {
|
|
33209
|
+
if (state.accumulatedText.length > 0) {
|
|
33210
|
+
const preview = state.accumulatedText.slice(0, 500).replace(/\n/g, "\\n");
|
|
33211
|
+
log(`[Streaming] Accumulated text (${state.accumulatedText.length} chars): ${preview}...`);
|
|
33212
|
+
}
|
|
33213
|
+
const textToolCalls = extractToolCallsFromText(state.accumulatedText);
|
|
33214
|
+
log(`[Streaming] Text-based tool calls found: ${textToolCalls.length}`);
|
|
33215
|
+
if (textToolCalls.length > 0) {
|
|
33216
|
+
log(`[Streaming] Found ${textToolCalls.length} text-based tool call(s), converting to structured format`);
|
|
33217
|
+
if (state.textStarted) {
|
|
33218
|
+
send("content_block_stop", { type: "content_block_stop", index: state.textIdx });
|
|
33219
|
+
state.textStarted = false;
|
|
33220
|
+
}
|
|
33221
|
+
for (const tc of textToolCalls) {
|
|
33222
|
+
const toolIdx = state.curIdx++;
|
|
33223
|
+
const toolId = `tool_${Date.now()}_${toolIdx}`;
|
|
33224
|
+
send("content_block_start", {
|
|
33225
|
+
type: "content_block_start",
|
|
33226
|
+
index: toolIdx,
|
|
33227
|
+
content_block: { type: "tool_use", id: toolId, name: tc.name }
|
|
33228
|
+
});
|
|
33229
|
+
send("content_block_delta", {
|
|
33230
|
+
type: "content_block_delta",
|
|
33231
|
+
index: toolIdx,
|
|
33232
|
+
delta: {
|
|
33233
|
+
type: "input_json_delta",
|
|
33234
|
+
partial_json: repairArgs(tc.name, JSON.stringify(tc.arguments))
|
|
33235
|
+
}
|
|
33236
|
+
});
|
|
33237
|
+
send("content_block_stop", { type: "content_block_stop", index: toolIdx });
|
|
33238
|
+
}
|
|
33239
|
+
}
|
|
33240
|
+
if (state.reasoningStarted) {
|
|
33241
|
+
send("content_block_stop", { type: "content_block_stop", index: state.reasoningIdx });
|
|
33242
|
+
}
|
|
33084
33243
|
if (state.textStarted) {
|
|
33085
33244
|
send("content_block_stop", { type: "content_block_stop", index: state.textIdx });
|
|
33086
|
-
state.textStarted = false;
|
|
33087
33245
|
}
|
|
33088
|
-
for (const
|
|
33089
|
-
|
|
33090
|
-
|
|
33091
|
-
|
|
33092
|
-
|
|
33093
|
-
|
|
33094
|
-
|
|
33095
|
-
|
|
33096
|
-
|
|
33097
|
-
|
|
33098
|
-
|
|
33099
|
-
|
|
33100
|
-
|
|
33101
|
-
|
|
33102
|
-
|
|
33103
|
-
|
|
33104
|
-
|
|
33105
|
-
|
|
33106
|
-
|
|
33107
|
-
|
|
33108
|
-
|
|
33109
|
-
|
|
33110
|
-
|
|
33111
|
-
|
|
33112
|
-
|
|
33113
|
-
|
|
33114
|
-
|
|
33115
|
-
|
|
33116
|
-
|
|
33117
|
-
|
|
33118
|
-
const argsJson = repairArgs(t.name, JSON.stringify(validation.repaired ? validation.repairedArgs : validation.parsedArgs));
|
|
33119
|
-
log(`[Streaming] Sending buffered tool call (finish_reason!=tool_calls): ${t.name} with args: ${argsJson}`);
|
|
33246
|
+
for (const t of Array.from(state.tools.values())) {
|
|
33247
|
+
if (!t.closed && t.buffered && !t.started) {
|
|
33248
|
+
if (toolSchemas && toolSchemas.length > 0) {
|
|
33249
|
+
const validation = validateToolArguments(t.name, t.arguments, toolSchemas, state.accumulatedText);
|
|
33250
|
+
if (validation.valid || validation.repaired && validation.repairedArgs) {
|
|
33251
|
+
const argsJson = repairArgs(t.name, JSON.stringify(validation.repaired ? validation.repairedArgs : validation.parsedArgs));
|
|
33252
|
+
log(`[Streaming] Sending buffered tool call (finish_reason!=tool_calls): ${t.name} with args: ${argsJson}`);
|
|
33253
|
+
send("content_block_start", {
|
|
33254
|
+
type: "content_block_start",
|
|
33255
|
+
index: t.blockIndex,
|
|
33256
|
+
content_block: { type: "tool_use", id: t.id, name: t.name }
|
|
33257
|
+
});
|
|
33258
|
+
send("content_block_delta", {
|
|
33259
|
+
type: "content_block_delta",
|
|
33260
|
+
index: t.blockIndex,
|
|
33261
|
+
delta: { type: "input_json_delta", partial_json: argsJson }
|
|
33262
|
+
});
|
|
33263
|
+
send("content_block_stop", {
|
|
33264
|
+
type: "content_block_stop",
|
|
33265
|
+
index: t.blockIndex
|
|
33266
|
+
});
|
|
33267
|
+
t.started = true;
|
|
33268
|
+
t.closed = true;
|
|
33269
|
+
} else {
|
|
33270
|
+
log(`[Streaming] Buffered tool call ${t.name} failed validation, skipping: ${validation.missingParams.join(", ")}`);
|
|
33271
|
+
t.closed = true;
|
|
33272
|
+
}
|
|
33273
|
+
} else {
|
|
33274
|
+
const argsJson = repairArgs(t.name, t.arguments || "{}");
|
|
33275
|
+
log(`[Streaming] Sending buffered tool call (no validation): ${t.name} with args: ${argsJson}`);
|
|
33120
33276
|
send("content_block_start", {
|
|
33121
33277
|
type: "content_block_start",
|
|
33122
33278
|
index: t.blockIndex,
|
|
@@ -33133,83 +33289,51 @@ data: ${JSON.stringify(d)}
|
|
|
33133
33289
|
});
|
|
33134
33290
|
t.started = true;
|
|
33135
33291
|
t.closed = true;
|
|
33136
|
-
} else {
|
|
33137
|
-
log(`[Streaming] Buffered tool call ${t.name} failed validation, skipping: ${validation.missingParams.join(", ")}`);
|
|
33138
|
-
t.closed = true;
|
|
33139
33292
|
}
|
|
33140
|
-
}
|
|
33141
|
-
|
|
33142
|
-
|
|
33143
|
-
|
|
33144
|
-
|
|
33145
|
-
index: t.blockIndex,
|
|
33146
|
-
content_block: { type: "tool_use", id: t.id, name: t.name }
|
|
33147
|
-
});
|
|
33148
|
-
send("content_block_delta", {
|
|
33149
|
-
type: "content_block_delta",
|
|
33150
|
-
index: t.blockIndex,
|
|
33151
|
-
delta: { type: "input_json_delta", partial_json: argsJson }
|
|
33152
|
-
});
|
|
33153
|
-
send("content_block_stop", {
|
|
33154
|
-
type: "content_block_stop",
|
|
33155
|
-
index: t.blockIndex
|
|
33156
|
-
});
|
|
33157
|
-
t.started = true;
|
|
33293
|
+
}
|
|
33294
|
+
}
|
|
33295
|
+
for (const t of Array.from(state.tools.values())) {
|
|
33296
|
+
if (t.started && !t.closed) {
|
|
33297
|
+
send("content_block_stop", { type: "content_block_stop", index: t.blockIndex });
|
|
33158
33298
|
t.closed = true;
|
|
33159
33299
|
}
|
|
33160
33300
|
}
|
|
33161
|
-
|
|
33162
|
-
|
|
33163
|
-
if (t.started && !t.closed) {
|
|
33164
|
-
send("content_block_stop", { type: "content_block_stop", index: t.blockIndex });
|
|
33165
|
-
t.closed = true;
|
|
33301
|
+
if (middlewareManager) {
|
|
33302
|
+
await middlewareManager.afterStreamComplete(target, streamMetadata);
|
|
33166
33303
|
}
|
|
33167
|
-
|
|
33168
|
-
|
|
33169
|
-
|
|
33170
|
-
|
|
33171
|
-
|
|
33172
|
-
|
|
33173
|
-
|
|
33174
|
-
|
|
33175
|
-
|
|
33176
|
-
|
|
33177
|
-
|
|
33178
|
-
|
|
33179
|
-
|
|
33304
|
+
if (reason === "error") {
|
|
33305
|
+
send("error", { type: "error", error: { type: "api_error", message: err } });
|
|
33306
|
+
} else {
|
|
33307
|
+
const hasStructuredTools = Array.from(state.tools.values()).some((t) => t.started);
|
|
33308
|
+
const truncated = state.finishReason === "length";
|
|
33309
|
+
const refused = state.finishReason === "content_filter";
|
|
33310
|
+
const stopReason = refused ? "refusal" : truncated ? "max_tokens" : textToolCalls.length > 0 || hasStructuredTools ? "tool_use" : "end_turn";
|
|
33311
|
+
if (truncated || refused) {
|
|
33312
|
+
log(`[Streaming] Upstream finish_reason=${state.finishReason} \u2192 stop_reason=${stopReason} (${state.accumulatedText.length} chars produced)`);
|
|
33313
|
+
}
|
|
33314
|
+
send("message_delta", {
|
|
33315
|
+
type: "message_delta",
|
|
33316
|
+
delta: { stop_reason: stopReason, stop_sequence: null },
|
|
33317
|
+
usage: {
|
|
33318
|
+
...state.usage?.prompt_tokens ? { input_tokens: state.usage.prompt_tokens } : {},
|
|
33319
|
+
output_tokens: state.usage?.completion_tokens || 0
|
|
33320
|
+
}
|
|
33321
|
+
});
|
|
33322
|
+
behavior?.onTurnEnd?.();
|
|
33323
|
+
send("message_stop", { type: "message_stop" });
|
|
33180
33324
|
}
|
|
33181
|
-
|
|
33182
|
-
|
|
33183
|
-
|
|
33184
|
-
|
|
33185
|
-
|
|
33186
|
-
|
|
33325
|
+
if (onTokenUpdate) {
|
|
33326
|
+
if (state.usage) {
|
|
33327
|
+
log(`[Streaming] Final usage: prompt=${state.usage.prompt_tokens || 0}, completion=${state.usage.completion_tokens || 0}`);
|
|
33328
|
+
onTokenUpdate(state.usage.prompt_tokens || 0, state.usage.completion_tokens || 0);
|
|
33329
|
+
} else {
|
|
33330
|
+
const estimatedOutputTokens = Math.ceil(state.accumulatedText.length / 4);
|
|
33331
|
+
log(`[Streaming] No usage data from provider, estimating: ~${estimatedOutputTokens} output tokens`);
|
|
33332
|
+
onTokenUpdate(priorInputTokens || 100, estimatedOutputTokens);
|
|
33187
33333
|
}
|
|
33188
|
-
});
|
|
33189
|
-
behavior?.onTurnEnd?.();
|
|
33190
|
-
send("message_stop", { type: "message_stop" });
|
|
33191
|
-
}
|
|
33192
|
-
if (onTokenUpdate) {
|
|
33193
|
-
if (state.usage) {
|
|
33194
|
-
log(`[Streaming] Final usage: prompt=${state.usage.prompt_tokens || 0}, completion=${state.usage.completion_tokens || 0}`);
|
|
33195
|
-
onTokenUpdate(state.usage.prompt_tokens || 0, state.usage.completion_tokens || 0);
|
|
33196
|
-
} else {
|
|
33197
|
-
const estimatedOutputTokens = Math.ceil(state.accumulatedText.length / 4);
|
|
33198
|
-
log(`[Streaming] No usage data from provider, estimating: ~${estimatedOutputTokens} output tokens`);
|
|
33199
|
-
onTokenUpdate(priorInputTokens || 100, estimatedOutputTokens);
|
|
33200
33334
|
}
|
|
33201
|
-
}
|
|
33202
|
-
|
|
33203
|
-
try {
|
|
33204
|
-
controller.enqueue(encoder.encode(`data: [DONE]
|
|
33205
|
-
|
|
33206
|
-
|
|
33207
|
-
`));
|
|
33208
|
-
} catch (e) {}
|
|
33209
|
-
controller.close();
|
|
33210
|
-
isClosed = true;
|
|
33211
|
-
if (ping)
|
|
33212
|
-
clearInterval(ping);
|
|
33335
|
+
} finally {
|
|
33336
|
+
teardown();
|
|
33213
33337
|
}
|
|
33214
33338
|
};
|
|
33215
33339
|
try {
|
|
@@ -39089,62 +39213,70 @@ data: ${JSON.stringify(data)}
|
|
|
39089
39213
|
send("ping", { type: "ping" });
|
|
39090
39214
|
}
|
|
39091
39215
|
}, 1000);
|
|
39216
|
+
const teardown = () => {
|
|
39217
|
+
if (!isClosed) {
|
|
39218
|
+
isClosed = true;
|
|
39219
|
+
try {
|
|
39220
|
+
controller.close();
|
|
39221
|
+
} catch {}
|
|
39222
|
+
}
|
|
39223
|
+
if (pingInterval) {
|
|
39224
|
+
clearInterval(pingInterval);
|
|
39225
|
+
pingInterval = null;
|
|
39226
|
+
}
|
|
39227
|
+
};
|
|
39092
39228
|
const finalize = async (reason, err) => {
|
|
39093
|
-
if (finalized2)
|
|
39229
|
+
if (finalized2) {
|
|
39230
|
+
teardown();
|
|
39094
39231
|
return;
|
|
39095
|
-
finalized2 = true;
|
|
39096
|
-
if (thinkingStarted) {
|
|
39097
|
-
send("content_block_stop", { type: "content_block_stop", index: thinkingIdx });
|
|
39098
39232
|
}
|
|
39099
|
-
|
|
39100
|
-
|
|
39101
|
-
|
|
39102
|
-
|
|
39103
|
-
if (t.started && !t.closed) {
|
|
39104
|
-
send("content_block_stop", { type: "content_block_stop", index: t.blockIndex });
|
|
39105
|
-
t.closed = true;
|
|
39233
|
+
finalized2 = true;
|
|
39234
|
+
try {
|
|
39235
|
+
if (thinkingStarted) {
|
|
39236
|
+
send("content_block_stop", { type: "content_block_stop", index: thinkingIdx });
|
|
39106
39237
|
}
|
|
39107
|
-
|
|
39108
|
-
|
|
39109
|
-
await opts.middlewareManager.afterStreamComplete(opts.modelName, new Map);
|
|
39110
|
-
}
|
|
39111
|
-
const inputTokens = usage?.promptTokenCount || 0;
|
|
39112
|
-
const outputTokens = usage?.candidatesTokenCount || 0;
|
|
39113
|
-
if (usage) {
|
|
39114
|
-
log(`[GeminiSSE] Usage: prompt=${inputTokens}, completion=${outputTokens}`);
|
|
39115
|
-
}
|
|
39116
|
-
if (opts.onTokenUpdate) {
|
|
39117
|
-
opts.onTokenUpdate(inputTokens, outputTokens);
|
|
39118
|
-
}
|
|
39119
|
-
if (reason === "error") {
|
|
39120
|
-
log(`[GeminiSSE] Stream error: ${err}`);
|
|
39121
|
-
send("error", { type: "error", error: { type: "api_error", message: err } });
|
|
39122
|
-
} else {
|
|
39123
|
-
const hasToolCalls = toolCalls.size > 0;
|
|
39124
|
-
const stopReason = truncated ? "max_tokens" : hasToolCalls ? "tool_use" : "end_turn";
|
|
39125
|
-
if (truncated) {
|
|
39126
|
-
log("[GeminiSSE] finishReason=MAX_TOKENS \u2192 stop_reason=max_tokens");
|
|
39238
|
+
if (textStarted) {
|
|
39239
|
+
send("content_block_stop", { type: "content_block_stop", index: textIdx });
|
|
39127
39240
|
}
|
|
39128
|
-
|
|
39129
|
-
|
|
39130
|
-
|
|
39131
|
-
|
|
39132
|
-
...inputTokens > 0 ? { input_tokens: inputTokens } : {},
|
|
39133
|
-
output_tokens: outputTokens
|
|
39241
|
+
for (const t of toolCalls.values()) {
|
|
39242
|
+
if (t.started && !t.closed) {
|
|
39243
|
+
send("content_block_stop", { type: "content_block_stop", index: t.blockIndex });
|
|
39244
|
+
t.closed = true;
|
|
39134
39245
|
}
|
|
39135
|
-
});
|
|
39136
|
-
opts.onTurnEnd?.();
|
|
39137
|
-
send("message_stop", { type: "message_stop" });
|
|
39138
|
-
}
|
|
39139
|
-
if (!isClosed) {
|
|
39140
|
-
isClosed = true;
|
|
39141
|
-
if (pingInterval) {
|
|
39142
|
-
clearInterval(pingInterval);
|
|
39143
|
-
pingInterval = null;
|
|
39144
39246
|
}
|
|
39145
|
-
|
|
39146
|
-
|
|
39147
|
-
}
|
|
39247
|
+
if (opts.middlewareManager) {
|
|
39248
|
+
await opts.middlewareManager.afterStreamComplete(opts.modelName, new Map);
|
|
39249
|
+
}
|
|
39250
|
+
const inputTokens = usage?.promptTokenCount || 0;
|
|
39251
|
+
const outputTokens = usage?.candidatesTokenCount || 0;
|
|
39252
|
+
if (usage) {
|
|
39253
|
+
log(`[GeminiSSE] Usage: prompt=${inputTokens}, completion=${outputTokens}`);
|
|
39254
|
+
}
|
|
39255
|
+
if (opts.onTokenUpdate) {
|
|
39256
|
+
opts.onTokenUpdate(inputTokens, outputTokens);
|
|
39257
|
+
}
|
|
39258
|
+
if (reason === "error") {
|
|
39259
|
+
log(`[GeminiSSE] Stream error: ${err}`);
|
|
39260
|
+
send("error", { type: "error", error: { type: "api_error", message: err } });
|
|
39261
|
+
} else {
|
|
39262
|
+
const hasToolCalls = toolCalls.size > 0;
|
|
39263
|
+
const stopReason = truncated ? "max_tokens" : hasToolCalls ? "tool_use" : "end_turn";
|
|
39264
|
+
if (truncated) {
|
|
39265
|
+
log("[GeminiSSE] finishReason=MAX_TOKENS \u2192 stop_reason=max_tokens");
|
|
39266
|
+
}
|
|
39267
|
+
send("message_delta", {
|
|
39268
|
+
type: "message_delta",
|
|
39269
|
+
delta: { stop_reason: stopReason, stop_sequence: null },
|
|
39270
|
+
usage: {
|
|
39271
|
+
...inputTokens > 0 ? { input_tokens: inputTokens } : {},
|
|
39272
|
+
output_tokens: outputTokens
|
|
39273
|
+
}
|
|
39274
|
+
});
|
|
39275
|
+
opts.onTurnEnd?.();
|
|
39276
|
+
send("message_stop", { type: "message_stop" });
|
|
39277
|
+
}
|
|
39278
|
+
} finally {
|
|
39279
|
+
teardown();
|
|
39148
39280
|
}
|
|
39149
39281
|
};
|
|
39150
39282
|
try {
|
|
@@ -39362,38 +39494,48 @@ data: ${JSON.stringify(data)}
|
|
|
39362
39494
|
send("ping", { type: "ping" });
|
|
39363
39495
|
}
|
|
39364
39496
|
}, 1000);
|
|
39365
|
-
|
|
39366
|
-
|
|
39367
|
-
return;
|
|
39368
|
-
if (textStarted) {
|
|
39369
|
-
send("content_block_stop", { type: "content_block_stop", index: 0 });
|
|
39370
|
-
}
|
|
39371
|
-
if (reason === "error") {
|
|
39372
|
-
send("error", { type: "error", error: { type: "api_error", message: err } });
|
|
39373
|
-
} else {
|
|
39374
|
-
send("message_delta", {
|
|
39375
|
-
type: "message_delta",
|
|
39376
|
-
delta: { stop_reason: "end_turn", stop_sequence: null },
|
|
39377
|
-
usage: {
|
|
39378
|
-
...promptTokens > 0 ? { input_tokens: promptTokens } : {},
|
|
39379
|
-
output_tokens: completionTokens
|
|
39380
|
-
}
|
|
39381
|
-
});
|
|
39382
|
-
send("message_stop", { type: "message_stop" });
|
|
39383
|
-
}
|
|
39384
|
-
if (opts.onTokenUpdate) {
|
|
39385
|
-
opts.onTokenUpdate(promptTokens, completionTokens);
|
|
39386
|
-
}
|
|
39497
|
+
let finalized2 = false;
|
|
39498
|
+
const teardown = () => {
|
|
39387
39499
|
if (!isClosed) {
|
|
39388
39500
|
isClosed = true;
|
|
39389
|
-
if (pingInterval) {
|
|
39390
|
-
clearInterval(pingInterval);
|
|
39391
|
-
pingInterval = null;
|
|
39392
|
-
}
|
|
39393
39501
|
try {
|
|
39394
39502
|
controller.close();
|
|
39395
39503
|
} catch {}
|
|
39396
39504
|
}
|
|
39505
|
+
if (pingInterval) {
|
|
39506
|
+
clearInterval(pingInterval);
|
|
39507
|
+
pingInterval = null;
|
|
39508
|
+
}
|
|
39509
|
+
};
|
|
39510
|
+
const finalize = (reason, err) => {
|
|
39511
|
+
if (finalized2) {
|
|
39512
|
+
teardown();
|
|
39513
|
+
return;
|
|
39514
|
+
}
|
|
39515
|
+
finalized2 = true;
|
|
39516
|
+
try {
|
|
39517
|
+
if (textStarted) {
|
|
39518
|
+
send("content_block_stop", { type: "content_block_stop", index: 0 });
|
|
39519
|
+
}
|
|
39520
|
+
if (reason === "error") {
|
|
39521
|
+
send("error", { type: "error", error: { type: "api_error", message: err } });
|
|
39522
|
+
} else {
|
|
39523
|
+
send("message_delta", {
|
|
39524
|
+
type: "message_delta",
|
|
39525
|
+
delta: { stop_reason: "end_turn", stop_sequence: null },
|
|
39526
|
+
usage: {
|
|
39527
|
+
...promptTokens > 0 ? { input_tokens: promptTokens } : {},
|
|
39528
|
+
output_tokens: completionTokens
|
|
39529
|
+
}
|
|
39530
|
+
});
|
|
39531
|
+
send("message_stop", { type: "message_stop" });
|
|
39532
|
+
}
|
|
39533
|
+
if (opts.onTokenUpdate) {
|
|
39534
|
+
opts.onTokenUpdate(promptTokens, completionTokens);
|
|
39535
|
+
}
|
|
39536
|
+
} finally {
|
|
39537
|
+
teardown();
|
|
39538
|
+
}
|
|
39397
39539
|
};
|
|
39398
39540
|
try {
|
|
39399
39541
|
const reader = response.body.getReader();
|
|
@@ -39880,10 +40022,26 @@ class TokenTracker {
|
|
|
39880
40022
|
modelNameOverride;
|
|
39881
40023
|
planUsage;
|
|
39882
40024
|
lastPlanSerialized = "";
|
|
40025
|
+
toolCallsByName = new Map;
|
|
40026
|
+
startedAt = Date.now();
|
|
40027
|
+
sessionBilledInputTokens = 0;
|
|
39883
40028
|
constructor(port, config2) {
|
|
39884
40029
|
this.port = port;
|
|
39885
40030
|
this.config = config2;
|
|
39886
40031
|
}
|
|
40032
|
+
recordToolUse(name) {
|
|
40033
|
+
const key = name.trim() || "unknown";
|
|
40034
|
+
this.toolCallsByName.set(key, (this.toolCallsByName.get(key) ?? 0) + 1);
|
|
40035
|
+
}
|
|
40036
|
+
getToolCallCount() {
|
|
40037
|
+
let n = 0;
|
|
40038
|
+
for (const v of this.toolCallsByName.values())
|
|
40039
|
+
n += v;
|
|
40040
|
+
return n;
|
|
40041
|
+
}
|
|
40042
|
+
getToolCalls() {
|
|
40043
|
+
return [...this.toolCallsByName].map(([name, count]) => ({ name, count })).sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));
|
|
40044
|
+
}
|
|
39887
40045
|
setActiveModelName(name) {
|
|
39888
40046
|
this.modelNameOverride = name;
|
|
39889
40047
|
}
|
|
@@ -39905,6 +40063,7 @@ class TokenTracker {
|
|
|
39905
40063
|
this.sessionInputTokens = inputTokens;
|
|
39906
40064
|
this.lastInputTokens = inputTokens;
|
|
39907
40065
|
this.sessionOutputTokens += outputTokens;
|
|
40066
|
+
this.sessionBilledInputTokens += inputTokens;
|
|
39908
40067
|
const pricing = this.getPricing();
|
|
39909
40068
|
const cost = inputTokens / 1e6 * pricing.inputCostPer1M + outputTokens / 1e6 * pricing.outputCostPer1M;
|
|
39910
40069
|
this.sessionTotalCost += cost;
|
|
@@ -39917,6 +40076,7 @@ class TokenTracker {
|
|
|
39917
40076
|
const pricing = this.getPricing();
|
|
39918
40077
|
const cost = this.sessionInputTokens / 1e6 * pricing.inputCostPer1M + this.sessionOutputTokens / 1e6 * pricing.outputCostPer1M;
|
|
39919
40078
|
this.sessionTotalCost = cost;
|
|
40079
|
+
this.sessionBilledInputTokens = this.sessionInputTokens;
|
|
39920
40080
|
this.writeFile(this.sessionInputTokens, this.sessionOutputTokens, pricing.isEstimate);
|
|
39921
40081
|
}
|
|
39922
40082
|
updateWithDelta(inputTokens, outputTokens) {
|
|
@@ -39935,6 +40095,7 @@ class TokenTracker {
|
|
|
39935
40095
|
}
|
|
39936
40096
|
this.sessionOutputTokens += outputTokens;
|
|
39937
40097
|
const pricing = this.getPricing();
|
|
40098
|
+
this.sessionBilledInputTokens += incrementalInputTokens;
|
|
39938
40099
|
const cost = incrementalInputTokens / 1e6 * pricing.inputCostPer1M + outputTokens / 1e6 * pricing.outputCostPer1M;
|
|
39939
40100
|
this.sessionTotalCost += cost;
|
|
39940
40101
|
this.writeFile(inputTokens, this.sessionOutputTokens, pricing.isEstimate);
|
|
@@ -39943,6 +40104,7 @@ class TokenTracker {
|
|
|
39943
40104
|
this.sessionInputTokens = inputTokens;
|
|
39944
40105
|
this.lastInputTokens = inputTokens;
|
|
39945
40106
|
this.sessionOutputTokens += outputTokens;
|
|
40107
|
+
this.sessionBilledInputTokens += inputTokens;
|
|
39946
40108
|
if (typeof actualCost === "number" && actualCost > 0) {
|
|
39947
40109
|
this.sessionTotalCost += actualCost;
|
|
39948
40110
|
log(`[TokenTracker] Actual cost from API: $${actualCost.toFixed(6)}`);
|
|
@@ -39960,6 +40122,7 @@ class TokenTracker {
|
|
|
39960
40122
|
this.lastInputTokens = inputTokens;
|
|
39961
40123
|
}
|
|
39962
40124
|
this.sessionOutputTokens += outputTokens;
|
|
40125
|
+
this.sessionBilledInputTokens += inputTokens;
|
|
39963
40126
|
this.writeFile(this.sessionInputTokens, this.sessionOutputTokens);
|
|
39964
40127
|
}
|
|
39965
40128
|
setContextWindow(contextWindow) {
|
|
@@ -40012,7 +40175,12 @@ class TokenTracker {
|
|
|
40012
40175
|
provider_name: this.getDisplayName(),
|
|
40013
40176
|
updated_at: Date.now(),
|
|
40014
40177
|
is_free: isFreeModel,
|
|
40015
|
-
is_estimated: isEstimate || false
|
|
40178
|
+
is_estimated: isEstimate || false,
|
|
40179
|
+
started_at: this.startedAt,
|
|
40180
|
+
tool_calls: this.getToolCalls(),
|
|
40181
|
+
billed_input_tokens: this.sessionBilledInputTokens,
|
|
40182
|
+
input_per_m: pricing.inputCostPer1M,
|
|
40183
|
+
output_per_m: pricing.outputCostPer1M
|
|
40016
40184
|
};
|
|
40017
40185
|
const displayModel = stripProviderPrefix(this.modelNameOverride || this.config.modelName || "");
|
|
40018
40186
|
if (displayModel) {
|
|
@@ -40718,14 +40886,18 @@ class ComposedHandler {
|
|
|
40718
40886
|
};
|
|
40719
40887
|
const streamFormat = this.resolveStreamFormat();
|
|
40720
40888
|
const priorInputTokens = this.tokenTracker.getLastInputTokens();
|
|
40889
|
+
const observeToolCall = (name) => {
|
|
40890
|
+
this.tokenTracker.recordToolUse(name);
|
|
40891
|
+
behaviorSession?.observeToolCall(name);
|
|
40892
|
+
};
|
|
40721
40893
|
switch (streamFormat) {
|
|
40722
40894
|
case "openai-sse":
|
|
40723
|
-
return createStreamingResponseHandler(c, response, adapter, this.bareModelName, this.middlewareManager, onTokenUpdate, claudeRequest.tools, toolNameMap, priorInputTokens,
|
|
40724
|
-
shouldBufferTool: (name) => behaviorSession
|
|
40725
|
-
onToolCall: (name, argsJson) => behaviorSession
|
|
40726
|
-
onAssistantText: (text, kind) => behaviorSession
|
|
40727
|
-
onToolCallObserved:
|
|
40728
|
-
onTurnEnd: () => behaviorSession
|
|
40895
|
+
return createStreamingResponseHandler(c, response, adapter, this.bareModelName, this.middlewareManager, onTokenUpdate, claudeRequest.tools, toolNameMap, priorInputTokens, {
|
|
40896
|
+
shouldBufferTool: (name) => behaviorSession?.interceptsTool(name) ?? false,
|
|
40897
|
+
onToolCall: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
|
|
40898
|
+
onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
|
|
40899
|
+
onToolCallObserved: observeToolCall,
|
|
40900
|
+
onTurnEnd: () => behaviorSession?.finishTurn()
|
|
40729
40901
|
});
|
|
40730
40902
|
case "openai-responses-sse":
|
|
40731
40903
|
return createResponsesStreamHandler(c, response, {
|
|
@@ -40739,7 +40911,7 @@ class ComposedHandler {
|
|
|
40739
40911
|
shouldBufferTool: (name) => behaviorSession?.interceptsTool(name) ?? false,
|
|
40740
40912
|
onToolCall: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
|
|
40741
40913
|
onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
|
|
40742
|
-
onToolCallObserved:
|
|
40914
|
+
onToolCallObserved: observeToolCall,
|
|
40743
40915
|
onTurnEnd: () => behaviorSession?.finishTurn()
|
|
40744
40916
|
});
|
|
40745
40917
|
case "anthropic-sse":
|
|
@@ -40750,7 +40922,7 @@ class ComposedHandler {
|
|
|
40750
40922
|
shouldBufferTool: (name) => behaviorSession?.interceptsTool(name) ?? false,
|
|
40751
40923
|
repairToolArgs: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
|
|
40752
40924
|
onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
|
|
40753
|
-
onToolCallObserved:
|
|
40925
|
+
onToolCallObserved: observeToolCall,
|
|
40754
40926
|
onTurnEnd: () => behaviorSession?.finishTurn()
|
|
40755
40927
|
});
|
|
40756
40928
|
case "gemini-sse": {
|
|
@@ -40767,7 +40939,7 @@ class ComposedHandler {
|
|
|
40767
40939
|
onToolCall,
|
|
40768
40940
|
repairToolArgs: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
|
|
40769
40941
|
onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
|
|
40770
|
-
onToolCallObserved:
|
|
40942
|
+
onToolCallObserved: observeToolCall,
|
|
40771
40943
|
onTurnEnd: () => behaviorSession?.finishTurn(),
|
|
40772
40944
|
unwrapResponse: this.options.unwrapGeminiResponse,
|
|
40773
40945
|
priorInputTokens
|
|
@@ -40787,7 +40959,7 @@ class ComposedHandler {
|
|
|
40787
40959
|
repairToolArgs: (name, argsJson) => behaviorSession?.repairToolCall(name, argsJson) ?? null,
|
|
40788
40960
|
shouldBufferTool: (name) => behaviorSession?.interceptsTool(name) ?? false,
|
|
40789
40961
|
onAssistantText: (text, kind) => behaviorSession?.observeText(text, kind),
|
|
40790
|
-
onToolCallObserved:
|
|
40962
|
+
onToolCallObserved: observeToolCall,
|
|
40791
40963
|
onTurnEnd: () => behaviorSession?.finishTurn()
|
|
40792
40964
|
});
|
|
40793
40965
|
case "ollama-jsonl":
|
|
@@ -42796,6 +42968,9 @@ async function routeBare(model, nativeProvider, rules, defaultProvider, cachePat
|
|
|
42796
42968
|
const [primary, ...fallbacks] = credentialed;
|
|
42797
42969
|
return { kind: "ok", primary, fallbacks };
|
|
42798
42970
|
}
|
|
42971
|
+
function normalizeGlmSlug(model) {
|
|
42972
|
+
return model.replace(/^glm-(\d+)-(\d+)(-.*)?$/i, (_m, major, minor, suffix) => `glm-${major}.${minor}${suffix ?? ""}`);
|
|
42973
|
+
}
|
|
42799
42974
|
async function route(modelSpec, rulesOverride, defaultProviderOverride, cachePath) {
|
|
42800
42975
|
const parsed = parseModelSpec(modelSpec);
|
|
42801
42976
|
if (parsed.isExplicitProvider) {
|
|
@@ -42803,7 +42978,7 @@ async function route(modelSpec, rulesOverride, defaultProviderOverride, cachePat
|
|
|
42803
42978
|
}
|
|
42804
42979
|
const rules = rulesOverride ?? loadRoutingRules();
|
|
42805
42980
|
const defaultProvider = defaultProviderOverride !== undefined ? defaultProviderOverride : rulesOverride !== undefined ? undefined : loadConfig().defaultProvider;
|
|
42806
|
-
return routeBare(parsed.model, parsed.provider, rules, defaultProvider, cachePath);
|
|
42981
|
+
return routeBare(normalizeGlmSlug(parsed.model), parsed.provider, rules, defaultProvider, cachePath);
|
|
42807
42982
|
}
|
|
42808
42983
|
var init_routing_rules = __esm(() => {
|
|
42809
42984
|
init_model_catalog();
|
|
@@ -64519,8 +64694,11 @@ __export(exports_model_selector, {
|
|
|
64519
64694
|
promptForApiKey: () => promptForApiKey,
|
|
64520
64695
|
pickerProviderToFirebaseSlug: () => pickerProviderToFirebaseSlug,
|
|
64521
64696
|
isUserDeployedProvider: () => isUserDeployedProvider,
|
|
64697
|
+
isPickableProvider: () => isPickableProvider,
|
|
64698
|
+
getProviderFilterAliases: () => getProviderFilterAliases,
|
|
64522
64699
|
confirmAction: () => confirmAction,
|
|
64523
64700
|
compareByReleaseDateDesc: () => compareByReleaseDateDesc,
|
|
64701
|
+
buildProviderChoices: () => buildProviderChoices,
|
|
64524
64702
|
buildExplicitModelSpec: () => buildExplicitModelSpec,
|
|
64525
64703
|
buildDiscoveredModelRows: () => buildDiscoveredModelRows
|
|
64526
64704
|
});
|
|
@@ -64679,6 +64857,18 @@ function dedupeModels(models) {
|
|
|
64679
64857
|
}
|
|
64680
64858
|
return deduped;
|
|
64681
64859
|
}
|
|
64860
|
+
function dedupeByProviderSpec(provider, models) {
|
|
64861
|
+
const seen = new Set;
|
|
64862
|
+
const deduped = [];
|
|
64863
|
+
for (const model of models) {
|
|
64864
|
+
const spec = buildExplicitModelSpec(provider, resolveProviderExternalId(provider, model));
|
|
64865
|
+
if (seen.has(spec))
|
|
64866
|
+
continue;
|
|
64867
|
+
seen.add(spec);
|
|
64868
|
+
deduped.push(model);
|
|
64869
|
+
}
|
|
64870
|
+
return deduped;
|
|
64871
|
+
}
|
|
64682
64872
|
function sortModelsNewestFirst(models) {
|
|
64683
64873
|
return [...models].sort(compareByReleaseDateDesc);
|
|
64684
64874
|
}
|
|
@@ -64729,6 +64919,16 @@ function formatModelChoiceAsSpec(model, spec, priceStr) {
|
|
|
64729
64919
|
const dateStr = model.releaseDate ? `, ${model.releaseDate.slice(0, 7)}` : "";
|
|
64730
64920
|
return `${spec} (${priceStr}, ${ctxStr}${capsStr}${dateStr})`;
|
|
64731
64921
|
}
|
|
64922
|
+
function getProviderFilterAliases() {
|
|
64923
|
+
const aliases = {};
|
|
64924
|
+
for (const def of pickableProvidersInPickerOrder()) {
|
|
64925
|
+
aliases[def.name.toLowerCase()] = def.name;
|
|
64926
|
+
for (const shortcut of def.shortcuts) {
|
|
64927
|
+
aliases[shortcut.toLowerCase()] = def.name;
|
|
64928
|
+
}
|
|
64929
|
+
}
|
|
64930
|
+
return { ...aliases, ...PROVIDER_FILTER_ALIAS_EXTRA };
|
|
64931
|
+
}
|
|
64732
64932
|
function parseProviderFilter(term, providers = []) {
|
|
64733
64933
|
if (!term.startsWith("@")) {
|
|
64734
64934
|
return { provider: null, searchTerm: term };
|
|
@@ -64744,7 +64944,7 @@ function parseProviderFilter(term, providers = []) {
|
|
|
64744
64944
|
prefix = withoutAt.slice(0, spaceIdx);
|
|
64745
64945
|
rest = withoutAt.slice(spaceIdx + 1).trim();
|
|
64746
64946
|
}
|
|
64747
|
-
const source =
|
|
64947
|
+
const source = getProviderFilterAliases()[prefix.toLowerCase()];
|
|
64748
64948
|
if (source) {
|
|
64749
64949
|
return { provider: source, searchTerm: rest };
|
|
64750
64950
|
}
|
|
@@ -64752,7 +64952,7 @@ function parseProviderFilter(term, providers = []) {
|
|
|
64752
64952
|
if (exactMatch) {
|
|
64753
64953
|
return { provider: exactMatch.slug, searchTerm: rest };
|
|
64754
64954
|
}
|
|
64755
|
-
const partialMatch = Object.entries(
|
|
64955
|
+
const partialMatch = Object.entries(getProviderFilterAliases()).find(([alias]) => alias.startsWith(prefix.toLowerCase()));
|
|
64756
64956
|
if (partialMatch) {
|
|
64757
64957
|
return { provider: partialMatch[1], searchTerm: rest };
|
|
64758
64958
|
}
|
|
@@ -64766,7 +64966,7 @@ async function fetchPickerModels(providerSlug, searchTerm, defaultModels, catalo
|
|
|
64766
64966
|
if (providerSlug) {
|
|
64767
64967
|
const firebaseSlug = pickerProviderToFirebaseSlug[providerSlug] ?? providerSlug;
|
|
64768
64968
|
const vendorModels = await catalog.modelsByVendor(firebaseSlug);
|
|
64769
|
-
const infos = sortModelsNewestFirst(dedupeModels(vendorModels.map(catalogModelToModelInfo)));
|
|
64969
|
+
const infos = dedupeByProviderSpec(providerSlug, sortModelsNewestFirst(dedupeModels(vendorModels.map(catalogModelToModelInfo))));
|
|
64770
64970
|
if (!searchTerm)
|
|
64771
64971
|
return infos;
|
|
64772
64972
|
const needle = searchTerm.toLowerCase();
|
|
@@ -64784,6 +64984,7 @@ async function selectModel(options = {}) {
|
|
|
64784
64984
|
let models;
|
|
64785
64985
|
let recommendedModels = [];
|
|
64786
64986
|
let pickerProviders = [];
|
|
64987
|
+
let interactiveProviderChoices = [];
|
|
64787
64988
|
const remoteQueryCache = new Map;
|
|
64788
64989
|
if (freeOnly) {
|
|
64789
64990
|
models = await getFreeModels();
|
|
@@ -64798,7 +64999,8 @@ async function selectModel(options = {}) {
|
|
|
64798
64999
|
const topModels = top100Result.status === "fulfilled" ? sortModelsNewestFirst(dedupeModels(top100Result.value.models.map(modelDocToModelInfo))) : [];
|
|
64799
65000
|
recommendedModels = recommendedResult.status === "fulfilled" ? recommendedResult.value : [];
|
|
64800
65001
|
models = topModels.length > 0 ? topModels : recommendedModels;
|
|
64801
|
-
|
|
65002
|
+
interactiveProviderChoices = await getInteractiveProviderChoices();
|
|
65003
|
+
pickerProviders = toPickerProviders(interactiveProviderChoices);
|
|
64802
65004
|
}
|
|
64803
65005
|
const loadRemoteModels = async (providerSlug, searchTerm) => {
|
|
64804
65006
|
const cacheKey = `${providerSlug || "__all__"}::${searchTerm}`;
|
|
@@ -64827,7 +65029,6 @@ async function selectModel(options = {}) {
|
|
|
64827
65029
|
const cleanupKeypress = () => process.stdin.removeListener("data", onData);
|
|
64828
65030
|
try {
|
|
64829
65031
|
if (!freeOnly && !message && pickerProviders.length > 1) {
|
|
64830
|
-
const interactiveProviderChoices = await getInteractiveProviderChoices();
|
|
64831
65032
|
const providerChoices = [
|
|
64832
65033
|
{
|
|
64833
65034
|
name: "All providers",
|
|
@@ -64860,11 +65061,14 @@ async function selectModel(options = {}) {
|
|
|
64860
65061
|
const { provider: filterProvider, searchTerm } = parseProviderFilter(normalizedTerm, pickerProviders);
|
|
64861
65062
|
const effectiveProvider = filterProvider;
|
|
64862
65063
|
const remoteModels = await loadRemoteModels(effectiveProvider, searchTerm);
|
|
64863
|
-
return remoteModels.slice(0, 100).map((model) =>
|
|
64864
|
-
|
|
64865
|
-
|
|
64866
|
-
|
|
64867
|
-
|
|
65064
|
+
return remoteModels.slice(0, 100).map((model) => {
|
|
65065
|
+
const spec = effectiveProvider ? buildExplicitModelSpec(effectiveProvider, resolveProviderExternalId(effectiveProvider, model)) : model.id;
|
|
65066
|
+
return {
|
|
65067
|
+
name: effectiveProvider ? formatModelChoiceAsSpec(model, spec, resolveProviderDisplayPrice(effectiveProvider, model)) : formatModelChoice(model, true),
|
|
65068
|
+
value: spec,
|
|
65069
|
+
description: model.description?.slice(0, 160)
|
|
65070
|
+
};
|
|
65071
|
+
});
|
|
64868
65072
|
}
|
|
64869
65073
|
}, { signal: ac.signal });
|
|
64870
65074
|
return selected;
|
|
@@ -64878,13 +65082,59 @@ async function selectModel(options = {}) {
|
|
|
64878
65082
|
cleanupKeypress();
|
|
64879
65083
|
}
|
|
64880
65084
|
}
|
|
65085
|
+
function isPickableProvider(def) {
|
|
65086
|
+
return def.shortcuts.length > 0;
|
|
65087
|
+
}
|
|
65088
|
+
function pickableProvidersInPickerOrder() {
|
|
65089
|
+
const rank = new Map(PICKER_ORDER.map((name, i) => [name, i]));
|
|
65090
|
+
return getAllProviders().filter(isPickableProvider).sort((a, b) => {
|
|
65091
|
+
const ra = rank.get(a.name) ?? Number.MAX_SAFE_INTEGER;
|
|
65092
|
+
const rb = rank.get(b.name) ?? Number.MAX_SAFE_INTEGER;
|
|
65093
|
+
return ra !== rb ? ra - rb : a.displayName.localeCompare(b.displayName);
|
|
65094
|
+
});
|
|
65095
|
+
}
|
|
65096
|
+
function buildProviderChoices() {
|
|
65097
|
+
const derived = pickableProvidersInPickerOrder().map((def) => {
|
|
65098
|
+
const copy = PICKER_COPY[def.name] ?? {};
|
|
65099
|
+
return {
|
|
65100
|
+
name: copy.name ?? def.displayName,
|
|
65101
|
+
value: def.name,
|
|
65102
|
+
description: copy.description ?? def.description ?? "",
|
|
65103
|
+
provider: def.name
|
|
65104
|
+
};
|
|
65105
|
+
});
|
|
65106
|
+
return [
|
|
65107
|
+
{
|
|
65108
|
+
name: "Skip (keep Claude default)",
|
|
65109
|
+
value: "skip",
|
|
65110
|
+
description: "Use native Claude model for this tier"
|
|
65111
|
+
},
|
|
65112
|
+
...derived,
|
|
65113
|
+
{
|
|
65114
|
+
name: "Enter custom model",
|
|
65115
|
+
value: "custom",
|
|
65116
|
+
description: "Type a provider@model specification"
|
|
65117
|
+
}
|
|
65118
|
+
];
|
|
65119
|
+
}
|
|
64881
65120
|
async function getProviderChoices() {
|
|
64882
|
-
const
|
|
65121
|
+
const all = buildProviderChoices();
|
|
65122
|
+
const checks4 = await Promise.all(all.map(async (choice) => {
|
|
64883
65123
|
if (!choice.provider)
|
|
64884
65124
|
return true;
|
|
64885
65125
|
return credentials.isAvailable(choice.provider);
|
|
64886
65126
|
}));
|
|
64887
|
-
return
|
|
65127
|
+
return all.filter((_, i) => checks4[i]);
|
|
65128
|
+
}
|
|
65129
|
+
function pickerModelPrefix(provider) {
|
|
65130
|
+
const override = PROVIDER_MODEL_PREFIX_OVERRIDE[provider];
|
|
65131
|
+
if (override)
|
|
65132
|
+
return override;
|
|
65133
|
+
const def = getProviderByName(provider);
|
|
65134
|
+
if (!def || !isPickableProvider(def))
|
|
65135
|
+
return;
|
|
65136
|
+
const prefix = def.shortestPrefix || def.shortcuts[0];
|
|
65137
|
+
return prefix ? `${prefix}@` : undefined;
|
|
64888
65138
|
}
|
|
64889
65139
|
async function getInteractiveProviderChoices() {
|
|
64890
65140
|
return (await getProviderChoices()).filter((choice) => choice.value !== "skip");
|
|
@@ -64897,7 +65147,7 @@ function toPickerProviders(choices) {
|
|
|
64897
65147
|
}));
|
|
64898
65148
|
}
|
|
64899
65149
|
function buildExplicitModelSpec(provider, modelId) {
|
|
64900
|
-
const prefix =
|
|
65150
|
+
const prefix = pickerModelPrefix(provider);
|
|
64901
65151
|
if (!prefix) {
|
|
64902
65152
|
return modelId;
|
|
64903
65153
|
}
|
|
@@ -64916,6 +65166,8 @@ function resolveProviderAggregatorEntry(provider, model) {
|
|
|
64916
65166
|
return model.aggregators.find((a) => a.provider.toLowerCase() === firebaseSlug.toLowerCase());
|
|
64917
65167
|
}
|
|
64918
65168
|
function resolveProviderDisplayPrice(provider, model) {
|
|
65169
|
+
if (isSubscriptionProvider(provider))
|
|
65170
|
+
return "SUB";
|
|
64919
65171
|
const entry = resolveProviderAggregatorEntry(provider, model);
|
|
64920
65172
|
const entryPrice = formatAveragePricing(entry?.pricing);
|
|
64921
65173
|
if (entryPrice?.average)
|
|
@@ -64923,7 +65175,7 @@ function resolveProviderDisplayPrice(provider, model) {
|
|
|
64923
65175
|
return model.pricing?.average || "N/A";
|
|
64924
65176
|
}
|
|
64925
65177
|
function getPickerDisplayName(providerValue) {
|
|
64926
|
-
const choice =
|
|
65178
|
+
const choice = buildProviderChoices().find((c) => c.value === providerValue);
|
|
64927
65179
|
if (choice)
|
|
64928
65180
|
return choice.name;
|
|
64929
65181
|
return getDisplayName(providerValue);
|
|
@@ -64932,7 +65184,7 @@ async function loadModelsForPickerProvider(providerValue, catalog) {
|
|
|
64932
65184
|
const firebaseSlug = pickerProviderToFirebaseSlug[providerValue] ?? providerValue;
|
|
64933
65185
|
try {
|
|
64934
65186
|
const vendorModels = await catalog.modelsByVendor(firebaseSlug);
|
|
64935
|
-
return sortModelsNewestFirst(dedupeModels(vendorModels.map(catalogModelToModelInfo)));
|
|
65187
|
+
return dedupeByProviderSpec(providerValue, sortModelsNewestFirst(dedupeModels(vendorModels.map(catalogModelToModelInfo))));
|
|
64936
65188
|
} catch {
|
|
64937
65189
|
return [];
|
|
64938
65190
|
}
|
|
@@ -65024,7 +65276,7 @@ async function buildDiscoveredModelRows(provider, displayName, catalog) {
|
|
|
65024
65276
|
return sortModelsNewestFirst(rows);
|
|
65025
65277
|
}
|
|
65026
65278
|
async function selectModelFromProvider(provider, tierName, recommendedModels, _forceUpdate, catalog) {
|
|
65027
|
-
const prefix =
|
|
65279
|
+
const prefix = pickerModelPrefix(provider) ?? `${provider}@`;
|
|
65028
65280
|
const displayName = getPickerDisplayName(provider);
|
|
65029
65281
|
const def = getProviderByName(provider);
|
|
65030
65282
|
if (def?.modelDiscovery) {
|
|
@@ -65189,7 +65441,7 @@ async function selectProfile(profiles) {
|
|
|
65189
65441
|
async function confirmAction(message) {
|
|
65190
65442
|
return dist_default4({ message, default: false });
|
|
65191
65443
|
}
|
|
65192
|
-
var pickerProviderToFirebaseSlug, LOCAL_OR_USER_DEPLOYED, SUBSCRIPTION_PRICING,
|
|
65444
|
+
var pickerProviderToFirebaseSlug, LOCAL_OR_USER_DEPLOYED, SUBSCRIPTION_PRICING, PROVIDER_FILTER_ALIAS_EXTRA, PICKER_COPY, PICKER_ORDER, PROVIDER_MODEL_PREFIX_OVERRIDE;
|
|
65193
65445
|
var init_model_selector = __esm(() => {
|
|
65194
65446
|
init_dist16();
|
|
65195
65447
|
init_model_catalog();
|
|
@@ -65227,160 +65479,73 @@ var init_model_selector = __esm(() => {
|
|
|
65227
65479
|
output: "SUB",
|
|
65228
65480
|
average: "SUB"
|
|
65229
65481
|
};
|
|
65230
|
-
|
|
65231
|
-
openrouter: "openrouter",
|
|
65232
|
-
or: "openrouter",
|
|
65233
|
-
google: "google",
|
|
65234
|
-
gemini: "google",
|
|
65482
|
+
PROVIDER_FILTER_ALIAS_EXTRA = {
|
|
65235
65483
|
gem: "google",
|
|
65236
|
-
|
|
65237
|
-
|
|
65238
|
-
|
|
65239
|
-
|
|
65240
|
-
"
|
|
65241
|
-
|
|
65242
|
-
|
|
65243
|
-
|
|
65244
|
-
|
|
65245
|
-
|
|
65246
|
-
|
|
65247
|
-
|
|
65248
|
-
|
|
65249
|
-
|
|
65250
|
-
"
|
|
65251
|
-
|
|
65252
|
-
|
|
65253
|
-
"
|
|
65254
|
-
|
|
65255
|
-
"
|
|
65256
|
-
|
|
65257
|
-
|
|
65258
|
-
|
|
65259
|
-
|
|
65260
|
-
|
|
65261
|
-
|
|
65262
|
-
|
|
65263
|
-
|
|
65264
|
-
|
|
65265
|
-
|
|
65266
|
-
|
|
65267
|
-
"
|
|
65268
|
-
|
|
65269
|
-
"
|
|
65270
|
-
|
|
65271
|
-
|
|
65272
|
-
|
|
65273
|
-
|
|
65274
|
-
|
|
65275
|
-
|
|
65276
|
-
|
|
65277
|
-
|
|
65278
|
-
|
|
65279
|
-
|
|
65280
|
-
|
|
65281
|
-
|
|
65282
|
-
|
|
65283
|
-
|
|
65284
|
-
|
|
65285
|
-
|
|
65286
|
-
|
|
65287
|
-
|
|
65288
|
-
|
|
65289
|
-
|
|
65290
|
-
|
|
65291
|
-
|
|
65292
|
-
|
|
65293
|
-
|
|
65294
|
-
value: "openai-codex",
|
|
65295
|
-
description: "ChatGPT Plus/Pro subscription (Responses API)",
|
|
65296
|
-
provider: "openai-codex"
|
|
65297
|
-
},
|
|
65298
|
-
{ name: "xAI / Grok", value: "x-ai", description: "Direct API", provider: "x-ai" },
|
|
65299
|
-
{ name: "DeepSeek", value: "deepseek", description: "Direct API", provider: "deepseek" },
|
|
65300
|
-
{ name: "Mistral", value: "mistralai", description: "Direct API", provider: "mistralai" },
|
|
65301
|
-
{ name: "Sakana Fugu", value: "sakana", description: "Direct API", provider: "sakana" },
|
|
65302
|
-
{
|
|
65303
|
-
name: "Sakana Fugu Subscription",
|
|
65304
|
-
value: "sakana-subscription",
|
|
65305
|
-
description: "Subscription plan",
|
|
65306
|
-
provider: "sakana-subscription"
|
|
65307
|
-
},
|
|
65308
|
-
{ name: "MiniMax", value: "minimax", description: "Direct API", provider: "minimax" },
|
|
65309
|
-
{
|
|
65310
|
-
name: "MiniMax Coding",
|
|
65311
|
-
value: "minimax-coding",
|
|
65312
|
-
description: "Coding subscription",
|
|
65313
|
-
provider: "minimax-coding"
|
|
65314
|
-
},
|
|
65315
|
-
{ name: "Kimi / Moonshot", value: "kimi", description: "Direct API", provider: "kimi" },
|
|
65316
|
-
{
|
|
65317
|
-
name: "Kimi Coding",
|
|
65318
|
-
value: "kimi-coding",
|
|
65319
|
-
description: "Coding subscription",
|
|
65320
|
-
provider: "kimi-coding"
|
|
65321
|
-
},
|
|
65322
|
-
{
|
|
65323
|
-
name: "Qwen Plan",
|
|
65324
|
-
value: "qwen-cloud",
|
|
65325
|
-
description: "Alibaba Model Studio subscription",
|
|
65326
|
-
provider: "qwen-cloud"
|
|
65327
|
-
},
|
|
65328
|
-
{ name: "GLM / Zhipu", value: "glm", description: "Direct API", provider: "glm" },
|
|
65329
|
-
{
|
|
65330
|
-
name: "GLM Coding Plan",
|
|
65331
|
-
value: "glm-coding",
|
|
65332
|
-
description: "Coding subscription",
|
|
65333
|
-
provider: "glm-coding"
|
|
65334
|
-
},
|
|
65335
|
-
{ name: "Z.AI", value: "z-ai", description: "Direct API", provider: "z-ai" },
|
|
65336
|
-
{
|
|
65337
|
-
name: "OllamaCloud",
|
|
65338
|
-
value: "ollamacloud",
|
|
65339
|
-
description: "Cloud models",
|
|
65340
|
-
provider: "ollamacloud"
|
|
65341
|
-
},
|
|
65342
|
-
{ name: "LiteLLM", value: "litellm", description: "Configured proxy", provider: "litellm" },
|
|
65343
|
-
{
|
|
65344
|
-
name: "Ollama (local)",
|
|
65345
|
-
value: "ollama",
|
|
65346
|
-
description: "Local Ollama instance",
|
|
65347
|
-
provider: "ollama"
|
|
65348
|
-
},
|
|
65349
|
-
{
|
|
65350
|
-
name: "LM Studio (local)",
|
|
65351
|
-
value: "lmstudio",
|
|
65352
|
-
description: "Local LM Studio instance",
|
|
65353
|
-
provider: "lmstudio"
|
|
65354
|
-
},
|
|
65355
|
-
{
|
|
65356
|
-
name: "Enter custom model",
|
|
65357
|
-
value: "custom",
|
|
65358
|
-
description: "Type a provider@model specification"
|
|
65359
|
-
}
|
|
65484
|
+
zen: "opencode-zen"
|
|
65485
|
+
};
|
|
65486
|
+
PICKER_COPY = {
|
|
65487
|
+
openrouter: { description: "580+ models via unified API" },
|
|
65488
|
+
"opencode-zen": { name: "OpenCode Zen", description: "Free models, no API key needed" },
|
|
65489
|
+
google: { name: "Google Gemini", description: "Direct API" },
|
|
65490
|
+
openai: { description: "Direct API" },
|
|
65491
|
+
"openai-codex": { description: "ChatGPT Plus/Pro subscription (Responses API)" },
|
|
65492
|
+
"x-ai": { name: "xAI / Grok", description: "Direct API" },
|
|
65493
|
+
deepseek: { description: "Direct API" },
|
|
65494
|
+
mistralai: { name: "Mistral", description: "Direct API" },
|
|
65495
|
+
sakana: { name: "Sakana Fugu", description: "Direct API" },
|
|
65496
|
+
"sakana-subscription": { name: "Sakana Fugu Subscription", description: "Subscription plan" },
|
|
65497
|
+
minimax: { description: "Direct API" },
|
|
65498
|
+
"minimax-coding": { name: "MiniMax Coding", description: "Coding subscription" },
|
|
65499
|
+
kimi: { name: "Kimi / Moonshot", description: "Direct API" },
|
|
65500
|
+
"kimi-coding": { name: "Kimi Coding", description: "Coding subscription" },
|
|
65501
|
+
"qwen-cloud": { name: "Qwen Plan", description: "Alibaba Model Studio subscription" },
|
|
65502
|
+
glm: { name: "GLM / Zhipu", description: "Direct API" },
|
|
65503
|
+
"glm-coding": { name: "GLM Coding Plan", description: "Coding subscription" },
|
|
65504
|
+
"z-ai": { name: "Z.AI", description: "Direct API" },
|
|
65505
|
+
ollamacloud: { name: "OllamaCloud", description: "Cloud models" },
|
|
65506
|
+
litellm: { description: "Configured proxy" },
|
|
65507
|
+
ollama: { name: "Ollama (local)", description: "Local Ollama instance" },
|
|
65508
|
+
lmstudio: { name: "LM Studio (local)", description: "Local LM Studio instance" },
|
|
65509
|
+
vllm: { name: "vLLM (local)", description: "Local vLLM server" },
|
|
65510
|
+
mlx: { name: "MLX (local)", description: "Local MLX server" }
|
|
65511
|
+
};
|
|
65512
|
+
PICKER_ORDER = [
|
|
65513
|
+
"openrouter",
|
|
65514
|
+
"opencode-zen",
|
|
65515
|
+
"opencode-zen-go",
|
|
65516
|
+
"google",
|
|
65517
|
+
"antigravity",
|
|
65518
|
+
"openai",
|
|
65519
|
+
"openai-codex",
|
|
65520
|
+
"devin",
|
|
65521
|
+
"x-ai",
|
|
65522
|
+
"deepseek",
|
|
65523
|
+
"mistralai",
|
|
65524
|
+
"sakana",
|
|
65525
|
+
"sakana-subscription",
|
|
65526
|
+
"minimax",
|
|
65527
|
+
"minimax-coding",
|
|
65528
|
+
"kimi",
|
|
65529
|
+
"kimi-coding",
|
|
65530
|
+
"qwen-cloud",
|
|
65531
|
+
"glm",
|
|
65532
|
+
"glm-coding",
|
|
65533
|
+
"z-ai",
|
|
65534
|
+
"ollamacloud",
|
|
65535
|
+
"poe",
|
|
65536
|
+
"vertex",
|
|
65537
|
+
"litellm",
|
|
65538
|
+
"ollama",
|
|
65539
|
+
"lmstudio",
|
|
65540
|
+
"vllm",
|
|
65541
|
+
"mlx"
|
|
65360
65542
|
];
|
|
65361
|
-
|
|
65543
|
+
PROVIDER_MODEL_PREFIX_OVERRIDE = {
|
|
65362
65544
|
google: "google@",
|
|
65363
|
-
|
|
65364
|
-
openai: "oai@",
|
|
65365
|
-
"openai-codex": "cx@",
|
|
65366
|
-
"x-ai": "x-ai@",
|
|
65367
|
-
deepseek: "ds@",
|
|
65368
|
-
mistralai: "mistral@",
|
|
65369
|
-
sakana: "sakana@",
|
|
65370
|
-
"sakana-subscription": "sc@",
|
|
65371
|
-
minimax: "mm@",
|
|
65372
|
-
kimi: "kimi@",
|
|
65373
|
-
"minimax-coding": "mmc@",
|
|
65374
|
-
"kimi-coding": "kc@",
|
|
65375
|
-
"qwen-cloud": "qc@",
|
|
65376
|
-
glm: "glm@",
|
|
65377
|
-
"glm-coding": "gc@",
|
|
65378
|
-
"z-ai": "z-ai@",
|
|
65379
|
-
ollamacloud: "oc@",
|
|
65380
|
-
ollama: "ollama@",
|
|
65545
|
+
openrouter: "openrouter@",
|
|
65381
65546
|
lmstudio: "lmstudio@",
|
|
65382
|
-
|
|
65383
|
-
|
|
65547
|
+
sakana: "sakana@",
|
|
65548
|
+
zen: "zen@"
|
|
65384
65549
|
};
|
|
65385
65550
|
});
|
|
65386
65551
|
|
|
@@ -68424,6 +68589,8 @@ Usage: claudish --models --provider <slug>`);
|
|
|
68424
68589
|
if (rest.length > 0)
|
|
68425
68590
|
config3._hasPositionalPrompt = true;
|
|
68426
68591
|
break;
|
|
68592
|
+
} else if (arg === "--resume" && (i + 1 >= args.length || args[i + 1].startsWith("-"))) {
|
|
68593
|
+
config3._resumePicker = true;
|
|
68427
68594
|
} else if (arg.startsWith("-")) {
|
|
68428
68595
|
config3.claudeArgs.push(arg);
|
|
68429
68596
|
if (arg === "-p" || arg === "--print") {
|
|
@@ -75521,6 +75688,7 @@ function App({ requestLogin } = {}) {
|
|
|
75521
75688
|
setOpFieldCursor(idx < 0 ? 0 : idx);
|
|
75522
75689
|
}, [opFieldOptionsFiltered, opFieldCursor, mode]);
|
|
75523
75690
|
const acquireOpAuth = useCallback3(async () => {
|
|
75691
|
+
clearOpSkip();
|
|
75524
75692
|
const auth = await resolveSdkAuth({
|
|
75525
75693
|
interactive: true,
|
|
75526
75694
|
configAccount: readOnepasswordAccount(),
|
|
@@ -77636,7 +77804,10 @@ async function runClaudeWithProxy(config3, proxyUrl, onCleanup) {
|
|
|
77636
77804
|
setSessionContextWindow2(realWindow);
|
|
77637
77805
|
} catch {}
|
|
77638
77806
|
if (contextEnv.notice && !config3.quiet) {
|
|
77639
|
-
|
|
77807
|
+
if (config3.interactive)
|
|
77808
|
+
console.log(contextEnv.notice);
|
|
77809
|
+
else
|
|
77810
|
+
console.error(contextEnv.notice);
|
|
77640
77811
|
}
|
|
77641
77812
|
}
|
|
77642
77813
|
}
|
|
@@ -78322,12 +78493,2864 @@ var init_team_grid = __esm(() => {
|
|
|
78322
78493
|
];
|
|
78323
78494
|
});
|
|
78324
78495
|
|
|
78496
|
+
// src/tui/viz/text.ts
|
|
78497
|
+
function columns(n, fn, arg = "width") {
|
|
78498
|
+
if (!Number.isFinite(n))
|
|
78499
|
+
throw new RangeError(`${fn}: ${arg} must be a finite number, got ${n}`);
|
|
78500
|
+
return Math.max(0, Math.floor(n));
|
|
78501
|
+
}
|
|
78502
|
+
function isWide2(cp) {
|
|
78503
|
+
let lo = 0;
|
|
78504
|
+
let hi = WIDE.length - 1;
|
|
78505
|
+
while (lo <= hi) {
|
|
78506
|
+
const mid = lo + hi >> 1;
|
|
78507
|
+
const [a, b] = WIDE[mid];
|
|
78508
|
+
if (cp < a)
|
|
78509
|
+
hi = mid - 1;
|
|
78510
|
+
else if (cp > b)
|
|
78511
|
+
lo = mid + 1;
|
|
78512
|
+
else
|
|
78513
|
+
return true;
|
|
78514
|
+
}
|
|
78515
|
+
return false;
|
|
78516
|
+
}
|
|
78517
|
+
function fallbackClusterWidth(cluster) {
|
|
78518
|
+
const cp = cluster.codePointAt(0) ?? 0;
|
|
78519
|
+
if (cp < 32 || cp >= 127 && cp <= 159 || LEADING_FORMAT.test(cluster))
|
|
78520
|
+
return 0;
|
|
78521
|
+
return isWide2(cp) || LEADING_EMOJI_PRESENTATION.test(cluster) || cluster.includes(VS16) ? 2 : 1;
|
|
78522
|
+
}
|
|
78523
|
+
function displayWidth(s) {
|
|
78524
|
+
if (NATIVE_WIDTH)
|
|
78525
|
+
return NATIVE_WIDTH(s);
|
|
78526
|
+
let w = 0;
|
|
78527
|
+
for (const { segment } of seg.segment(s))
|
|
78528
|
+
w += fallbackClusterWidth(segment);
|
|
78529
|
+
return w;
|
|
78530
|
+
}
|
|
78531
|
+
function truncate3(s, width) {
|
|
78532
|
+
const n = columns(width, "truncate");
|
|
78533
|
+
if (n === 0)
|
|
78534
|
+
return "";
|
|
78535
|
+
const clean = sanitize2(s);
|
|
78536
|
+
if (displayWidth(clean) <= n)
|
|
78537
|
+
return clean;
|
|
78538
|
+
let out = "";
|
|
78539
|
+
let w = 0;
|
|
78540
|
+
for (const { segment } of seg.segment(clean)) {
|
|
78541
|
+
const cw = displayWidth(segment);
|
|
78542
|
+
if (w + cw > n - 1)
|
|
78543
|
+
break;
|
|
78544
|
+
out += segment;
|
|
78545
|
+
w += cw;
|
|
78546
|
+
}
|
|
78547
|
+
return `${out}\u2026`;
|
|
78548
|
+
}
|
|
78549
|
+
function padTo(s, width) {
|
|
78550
|
+
const n = columns(width, "padTo");
|
|
78551
|
+
if (n === 0)
|
|
78552
|
+
return "";
|
|
78553
|
+
const clean = sanitize2(s);
|
|
78554
|
+
const clipped = displayWidth(clean) > n ? truncate3(clean, n) : clean;
|
|
78555
|
+
return clipped + " ".repeat(Math.max(0, n - displayWidth(clipped)));
|
|
78556
|
+
}
|
|
78557
|
+
function padStartTo(s, width) {
|
|
78558
|
+
const n = columns(width, "padStartTo");
|
|
78559
|
+
if (n === 0)
|
|
78560
|
+
return "";
|
|
78561
|
+
const clean = sanitize2(s);
|
|
78562
|
+
const clipped = displayWidth(clean) > n ? truncate3(clean, n) : clean;
|
|
78563
|
+
return " ".repeat(Math.max(0, n - displayWidth(clipped))) + clipped;
|
|
78564
|
+
}
|
|
78565
|
+
function splitCells(parts, cells) {
|
|
78566
|
+
const n = columns(cells, "splitCells", "cells");
|
|
78567
|
+
const finite = parts.map((p) => Number.isFinite(p) && p > 0 ? p : 0);
|
|
78568
|
+
const out = finite.map(() => 0);
|
|
78569
|
+
let safe = finite;
|
|
78570
|
+
let sum = finite.reduce((a, b) => a + b, 0);
|
|
78571
|
+
if (!Number.isFinite(sum)) {
|
|
78572
|
+
const max = finite.reduce((a, b) => b > a ? b : a, 0);
|
|
78573
|
+
safe = finite.map((p) => p / max);
|
|
78574
|
+
sum = safe.reduce((a, b) => a + b, 0);
|
|
78575
|
+
}
|
|
78576
|
+
if (n <= 0 || sum <= 0)
|
|
78577
|
+
return out;
|
|
78578
|
+
const byShare = safe.map((p, i) => ({ p, i })).sort((a, b) => b.p - a.p);
|
|
78579
|
+
if (n < byShare.filter(({ p }) => p > 0).length) {
|
|
78580
|
+
for (const { i } of byShare.slice(0, n))
|
|
78581
|
+
out[i] = 1;
|
|
78582
|
+
return out;
|
|
78583
|
+
}
|
|
78584
|
+
const exact = safe.map((p) => n * (p / sum));
|
|
78585
|
+
exact.forEach((e, i) => {
|
|
78586
|
+
out[i] = Math.floor(e);
|
|
78587
|
+
});
|
|
78588
|
+
const rank = exact.map((e, i) => ({ rem: e % 1, i })).sort((a, b) => b.rem - a.rem);
|
|
78589
|
+
for (let k = 0, left = n - out.reduce((a, b) => a + b, 0);left > 0; k++, left--)
|
|
78590
|
+
out[rank[k % rank.length].i] += 1;
|
|
78591
|
+
for (const { p, i } of byShare)
|
|
78592
|
+
if (p > 0 && out[i] === 0) {
|
|
78593
|
+
const d = byShare.reduce((m, c) => out[c.i] > out[m] ? c.i : m, 0);
|
|
78594
|
+
if (out[d] > 1) {
|
|
78595
|
+
out[d] -= 1;
|
|
78596
|
+
out[i] = 1;
|
|
78597
|
+
}
|
|
78598
|
+
}
|
|
78599
|
+
return out;
|
|
78600
|
+
}
|
|
78601
|
+
var NATIVE_WIDTH, WIDE, seg, VS16 = "\uFE0F", LEADING_FORMAT, LEADING_EMOJI_PRESENTATION, CONTROL, sanitize2 = (s) => s.replace(CONTROL, " ");
|
|
78602
|
+
var init_text = __esm(() => {
|
|
78603
|
+
NATIVE_WIDTH = (() => {
|
|
78604
|
+
const b = globalThis.Bun;
|
|
78605
|
+
return typeof b?.stringWidth === "function" ? b.stringWidth.bind(b) : null;
|
|
78606
|
+
})();
|
|
78607
|
+
WIDE = [
|
|
78608
|
+
[4352, 4447],
|
|
78609
|
+
[9001, 9002],
|
|
78610
|
+
[11904, 12350],
|
|
78611
|
+
[12353, 12771],
|
|
78612
|
+
[12774, 12871],
|
|
78613
|
+
[12880, 13311],
|
|
78614
|
+
[13312, 19903],
|
|
78615
|
+
[19968, 40959],
|
|
78616
|
+
[40960, 42191],
|
|
78617
|
+
[43360, 43391],
|
|
78618
|
+
[44032, 55203],
|
|
78619
|
+
[63744, 64255],
|
|
78620
|
+
[65040, 65049],
|
|
78621
|
+
[65072, 65135],
|
|
78622
|
+
[65280, 65376],
|
|
78623
|
+
[65504, 65510],
|
|
78624
|
+
[94176, 94180],
|
|
78625
|
+
[94192, 94193],
|
|
78626
|
+
[94208, 100343],
|
|
78627
|
+
[100352, 101589],
|
|
78628
|
+
[101632, 101640],
|
|
78629
|
+
[110576, 110579],
|
|
78630
|
+
[110581, 110587],
|
|
78631
|
+
[110589, 110590],
|
|
78632
|
+
[110592, 110882],
|
|
78633
|
+
[110898, 110898],
|
|
78634
|
+
[110928, 110930],
|
|
78635
|
+
[110933, 110933],
|
|
78636
|
+
[110948, 110951],
|
|
78637
|
+
[110960, 111355],
|
|
78638
|
+
[127488, 127490],
|
|
78639
|
+
[127504, 127547],
|
|
78640
|
+
[127552, 127560],
|
|
78641
|
+
[127568, 127569],
|
|
78642
|
+
[127584, 127589],
|
|
78643
|
+
[131072, 196605],
|
|
78644
|
+
[196608, 262141]
|
|
78645
|
+
];
|
|
78646
|
+
seg = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
78647
|
+
LEADING_FORMAT = /^\p{Cf}/u;
|
|
78648
|
+
LEADING_EMOJI_PRESENTATION = /^\p{Emoji_Presentation}/u;
|
|
78649
|
+
CONTROL = /[\u0000-\u001f\u007f-\u009f]/g;
|
|
78650
|
+
});
|
|
78651
|
+
|
|
78652
|
+
// src/tui/viz/tokens.ts
|
|
78653
|
+
var tokens, ramps;
|
|
78654
|
+
var init_tokens = __esm(() => {
|
|
78655
|
+
init_theme2();
|
|
78656
|
+
tokens = {
|
|
78657
|
+
fatal: C.red,
|
|
78658
|
+
error: C.red,
|
|
78659
|
+
warn: C.orange,
|
|
78660
|
+
info: C.cyan,
|
|
78661
|
+
debug: C.fgMuted,
|
|
78662
|
+
trace: C.dim,
|
|
78663
|
+
success: C.green,
|
|
78664
|
+
running: C.blue,
|
|
78665
|
+
idle: C.fgMuted,
|
|
78666
|
+
dead: C.dim,
|
|
78667
|
+
border: C.border,
|
|
78668
|
+
subtle: C.dim,
|
|
78669
|
+
text: C.fg,
|
|
78670
|
+
accent: C.focusBorder,
|
|
78671
|
+
bgPanel: C.bgAlt,
|
|
78672
|
+
ink: C.black
|
|
78673
|
+
};
|
|
78674
|
+
ramps = {
|
|
78675
|
+
load: [tokens.success, C.yellow, tokens.error],
|
|
78676
|
+
temperature: [tokens.running, tokens.success, C.orange, tokens.error],
|
|
78677
|
+
network: [tokens.success, C.yellow, tokens.error],
|
|
78678
|
+
savings: [tokens.error, C.yellow, tokens.success],
|
|
78679
|
+
volume: [C.border, C.blue, C.cyan]
|
|
78680
|
+
};
|
|
78681
|
+
});
|
|
78682
|
+
|
|
78683
|
+
// src/tui/viz/color.ts
|
|
78684
|
+
import { RGBA, parseColor, rgbToHex } from "@opentui/core";
|
|
78685
|
+
function mix(a, b, t) {
|
|
78686
|
+
const k = clamp01(t);
|
|
78687
|
+
return hex3(RGBA.fromValues(a.r + (b.r - a.r) * k, a.g + (b.g - a.g) * k, a.b + (b.b - a.b) * k, a.a + (b.a - a.a) * k));
|
|
78688
|
+
}
|
|
78689
|
+
function blend1D(steps, from, to) {
|
|
78690
|
+
if (!Number.isInteger(steps) || steps <= 0)
|
|
78691
|
+
return [];
|
|
78692
|
+
const a = rgba(from);
|
|
78693
|
+
if (steps === 1)
|
|
78694
|
+
return [hex3(a)];
|
|
78695
|
+
const b = rgba(to);
|
|
78696
|
+
return Array.from({ length: steps }, (_, i) => mix(a, b, i / (steps - 1)));
|
|
78697
|
+
}
|
|
78698
|
+
function blendStops(steps, ...stops) {
|
|
78699
|
+
if (!Number.isInteger(steps) || steps <= 0 || stops.length === 0)
|
|
78700
|
+
return [];
|
|
78701
|
+
if (stops.length === 1 || steps === 1)
|
|
78702
|
+
return blend1D(steps, stops[0], stops.at(-1));
|
|
78703
|
+
const pts = stops.map(rgba);
|
|
78704
|
+
const segs = pts.length - 1;
|
|
78705
|
+
return Array.from({ length: steps }, (_, i) => {
|
|
78706
|
+
const p = i / (steps - 1) * segs;
|
|
78707
|
+
const s = Math.min(segs - 1, Math.floor(p));
|
|
78708
|
+
return mix(pts[s], pts[s + 1], p - s);
|
|
78709
|
+
});
|
|
78710
|
+
}
|
|
78711
|
+
function luminance(c) {
|
|
78712
|
+
const f = (v) => v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
|
|
78713
|
+
return 0.2126 * f(clamp01(c.r)) + 0.7152 * f(clamp01(c.g)) + 0.0722 * f(clamp01(c.b));
|
|
78714
|
+
}
|
|
78715
|
+
function contrastRatio(a, b) {
|
|
78716
|
+
const [x, y] = [luminance(a), luminance(b)];
|
|
78717
|
+
return x >= y ? (x + 0.05) / (y + 0.05) : (y + 0.05) / (x + 0.05);
|
|
78718
|
+
}
|
|
78719
|
+
function over(fg, bg) {
|
|
78720
|
+
const a = clamp01(fg.a);
|
|
78721
|
+
if (a >= 1)
|
|
78722
|
+
return fg;
|
|
78723
|
+
return RGBA.fromValues(fg.r * a + bg.r * (1 - a), fg.g * a + bg.g * (1 - a), fg.b * a + bg.b * (1 - a), 1);
|
|
78724
|
+
}
|
|
78725
|
+
function pickInk(bg, dark = tokens.ink, light = tokens.text) {
|
|
78726
|
+
const surface = over(rgba(bg), rgba(tokens.bgPanel));
|
|
78727
|
+
const inkDark = over(rgba(dark), surface);
|
|
78728
|
+
const inkLight = over(rgba(light), surface);
|
|
78729
|
+
return hex3(contrastRatio(inkDark, surface) >= contrastRatio(inkLight, surface) ? inkDark : inkLight);
|
|
78730
|
+
}
|
|
78731
|
+
var rgba = (c) => parseColor(c), hex3 = (c) => rgbToHex(c), clamp01 = (n) => Number.isFinite(n) ? Math.min(1, Math.max(0, n)) : 0;
|
|
78732
|
+
var init_color = __esm(() => {
|
|
78733
|
+
init_tokens();
|
|
78734
|
+
});
|
|
78735
|
+
|
|
78736
|
+
// src/tui/viz/widgets.tsx
|
|
78737
|
+
import { createTextAttributes as createTextAttributes2 } from "@opentui/core";
|
|
78738
|
+
import { jsxDEV as jsxDEV18, Fragment as Fragment11 } from "@opentui/react/jsx-dev-runtime";
|
|
78739
|
+
function rampFor(width, stops) {
|
|
78740
|
+
const use = stops.length > 0 ? stops : ramps.load;
|
|
78741
|
+
const key = `${width}|${use.join(":")}`;
|
|
78742
|
+
const hit = RAMP_CACHE.get(key);
|
|
78743
|
+
if (hit)
|
|
78744
|
+
return hit;
|
|
78745
|
+
const built = blendStops(width, ...use);
|
|
78746
|
+
if (RAMP_CACHE.size >= RAMP_CACHE_MAX)
|
|
78747
|
+
RAMP_CACHE.delete(RAMP_CACHE.keys().next().value);
|
|
78748
|
+
RAMP_CACHE.set(key, built);
|
|
78749
|
+
return built;
|
|
78750
|
+
}
|
|
78751
|
+
function fillCells(pct, width) {
|
|
78752
|
+
const cells = Math.floor(width);
|
|
78753
|
+
if (!Number.isFinite(cells) || cells <= 0 || Number.isNaN(pct))
|
|
78754
|
+
return 0;
|
|
78755
|
+
return Math.round(Math.min(100, Math.max(0, pct)) / 100 * cells);
|
|
78756
|
+
}
|
|
78757
|
+
function MeterSpan({
|
|
78758
|
+
pct,
|
|
78759
|
+
width,
|
|
78760
|
+
ramp = ramps.load
|
|
78761
|
+
}) {
|
|
78762
|
+
const cells = Math.floor(width);
|
|
78763
|
+
if (!Number.isFinite(cells) || cells <= 0)
|
|
78764
|
+
return null;
|
|
78765
|
+
if (Number.isNaN(pct))
|
|
78766
|
+
return /* @__PURE__ */ jsxDEV18("span", {
|
|
78767
|
+
fg: tokens.dead,
|
|
78768
|
+
children: NODATA.repeat(cells)
|
|
78769
|
+
}, undefined, false, undefined, this);
|
|
78770
|
+
const cols = rampFor(cells, ramp);
|
|
78771
|
+
const filled = fillCells(pct, cells);
|
|
78772
|
+
return /* @__PURE__ */ jsxDEV18(Fragment11, {
|
|
78773
|
+
children: Array.from({ length: cells }, (_, i) => /* @__PURE__ */ jsxDEV18("span", {
|
|
78774
|
+
fg: i < filled ? cols[i] : tokens.border,
|
|
78775
|
+
children: i < filled ? FILL : TRACK
|
|
78776
|
+
}, i, false, undefined, this))
|
|
78777
|
+
}, undefined, false, undefined, this);
|
|
78778
|
+
}
|
|
78779
|
+
function Sparkline({
|
|
78780
|
+
values,
|
|
78781
|
+
fg = tokens.info,
|
|
78782
|
+
style,
|
|
78783
|
+
...layout
|
|
78784
|
+
}) {
|
|
78785
|
+
const row = sparkGlyphs(values);
|
|
78786
|
+
if (row === null)
|
|
78787
|
+
return null;
|
|
78788
|
+
return /* @__PURE__ */ jsxDEV18("text", {
|
|
78789
|
+
fg,
|
|
78790
|
+
flexShrink: 0,
|
|
78791
|
+
...layout,
|
|
78792
|
+
style,
|
|
78793
|
+
children: row
|
|
78794
|
+
}, undefined, false, undefined, this);
|
|
78795
|
+
}
|
|
78796
|
+
function sparkGlyphs(values) {
|
|
78797
|
+
if (values.length === 0)
|
|
78798
|
+
return null;
|
|
78799
|
+
let max = Number.NEGATIVE_INFINITY;
|
|
78800
|
+
let min = Number.POSITIVE_INFINITY;
|
|
78801
|
+
for (const v of values)
|
|
78802
|
+
if (Number.isFinite(v)) {
|
|
78803
|
+
max = Math.max(max, v);
|
|
78804
|
+
min = Math.min(min, v);
|
|
78805
|
+
}
|
|
78806
|
+
if (max === Number.NEGATIVE_INFINITY)
|
|
78807
|
+
return GAP.repeat(values.length);
|
|
78808
|
+
const half = max / 2 - min / 2;
|
|
78809
|
+
const mid = SPARK[Math.floor((SPARK.length - 1) / 2)];
|
|
78810
|
+
const top = SPARK.length - 1;
|
|
78811
|
+
const glyph = (v) => SPARK[Math.min(top, Math.max(0, Math.round((v / 2 - min / 2) / half * top)))];
|
|
78812
|
+
return values.map((v) => !Number.isFinite(v) ? GAP : half > 0 ? glyph(v) : mid).join("");
|
|
78813
|
+
}
|
|
78814
|
+
function SparklineSpan({
|
|
78815
|
+
values,
|
|
78816
|
+
fg = tokens.info
|
|
78817
|
+
}) {
|
|
78818
|
+
const row = sparkGlyphs(values);
|
|
78819
|
+
return row === null ? null : /* @__PURE__ */ jsxDEV18("span", {
|
|
78820
|
+
fg,
|
|
78821
|
+
children: row
|
|
78822
|
+
}, undefined, false, undefined, this);
|
|
78823
|
+
}
|
|
78824
|
+
function badgePad(label, width) {
|
|
78825
|
+
const pad2 = Math.max(0, (width ?? 0) - displayWidth(label) - 2);
|
|
78826
|
+
return pad2 > 0 ? /* @__PURE__ */ jsxDEV18("span", {
|
|
78827
|
+
children: " ".repeat(pad2)
|
|
78828
|
+
}, undefined, false, undefined, this) : null;
|
|
78829
|
+
}
|
|
78830
|
+
function BadgeSpan({ label, bg, width }) {
|
|
78831
|
+
return /* @__PURE__ */ jsxDEV18(Fragment11, {
|
|
78832
|
+
children: [
|
|
78833
|
+
/* @__PURE__ */ jsxDEV18("span", {
|
|
78834
|
+
fg: pickInk(bg),
|
|
78835
|
+
bg,
|
|
78836
|
+
attributes: BOLD4,
|
|
78837
|
+
children: ` ${label} `
|
|
78838
|
+
}, undefined, false, undefined, this),
|
|
78839
|
+
badgePad(label, width)
|
|
78840
|
+
]
|
|
78841
|
+
}, undefined, true, undefined, this);
|
|
78842
|
+
}
|
|
78843
|
+
function Panel({
|
|
78844
|
+
title,
|
|
78845
|
+
focused = false,
|
|
78846
|
+
flush = false,
|
|
78847
|
+
children,
|
|
78848
|
+
style,
|
|
78849
|
+
...layout
|
|
78850
|
+
}) {
|
|
78851
|
+
return /* @__PURE__ */ jsxDEV18("box", {
|
|
78852
|
+
border: true,
|
|
78853
|
+
borderStyle: "rounded",
|
|
78854
|
+
borderColor: focused ? tokens.accent : tokens.border,
|
|
78855
|
+
backgroundColor: tokens.bgPanel,
|
|
78856
|
+
title,
|
|
78857
|
+
titleAlignment: "left",
|
|
78858
|
+
flexDirection: "column",
|
|
78859
|
+
overflow: "hidden",
|
|
78860
|
+
paddingLeft: flush ? 0 : 1,
|
|
78861
|
+
paddingRight: flush ? 0 : 1,
|
|
78862
|
+
...layout,
|
|
78863
|
+
style,
|
|
78864
|
+
children
|
|
78865
|
+
}, undefined, false, undefined, this);
|
|
78866
|
+
}
|
|
78867
|
+
var BOLD4, FILL = "\u2588", TRACK = "\u2591", SPARK, GAP = " ", NODATA = "\u254C", RAMP_CACHE, RAMP_CACHE_MAX = 64;
|
|
78868
|
+
var init_widgets = __esm(() => {
|
|
78869
|
+
init_color();
|
|
78870
|
+
init_text();
|
|
78871
|
+
init_tokens();
|
|
78872
|
+
BOLD4 = createTextAttributes2({ bold: true });
|
|
78873
|
+
SPARK = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
|
|
78874
|
+
RAMP_CACHE = new Map;
|
|
78875
|
+
});
|
|
78876
|
+
|
|
78877
|
+
// src/session/session-discovery.ts
|
|
78878
|
+
var exports_session_discovery = {};
|
|
78879
|
+
__export(exports_session_discovery, {
|
|
78880
|
+
slugForPath: () => slugForPath,
|
|
78881
|
+
sessionLabel: () => sessionLabel,
|
|
78882
|
+
mainConversationTurn: () => mainConversationTurn,
|
|
78883
|
+
isHarnessNoise: () => isHarnessNoise,
|
|
78884
|
+
isAgentSession: () => isAgentSession,
|
|
78885
|
+
isActive: () => isActive,
|
|
78886
|
+
hydrateSession: () => hydrateSession,
|
|
78887
|
+
hydrateConversation: () => hydrateConversation,
|
|
78888
|
+
getRepoContext: () => getRepoContext,
|
|
78889
|
+
findLatestSessionId: () => findLatestSessionId,
|
|
78890
|
+
enrichWorktreeGit: () => enrichWorktreeGit,
|
|
78891
|
+
discoverWorktreeGroups: () => discoverWorktreeGroups,
|
|
78892
|
+
PROJECTS_DIR: () => PROJECTS_DIR,
|
|
78893
|
+
ACTIVE_WINDOW_MS: () => ACTIVE_WINDOW_MS
|
|
78894
|
+
});
|
|
78895
|
+
import { execFile, execFileSync as execFileSync2 } from "child_process";
|
|
78896
|
+
import { closeSync as closeSync5, openSync as openSync5, readSync, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
|
|
78897
|
+
import { homedir as homedir34 } from "os";
|
|
78898
|
+
import { basename, join as join37 } from "path";
|
|
78899
|
+
function slugForPath(absPath) {
|
|
78900
|
+
return absPath.replace(/[/.]/g, "-");
|
|
78901
|
+
}
|
|
78902
|
+
function isAgentSession(row) {
|
|
78903
|
+
return row.entrypoint !== undefined && row.entrypoint !== "cli";
|
|
78904
|
+
}
|
|
78905
|
+
function getRepoContext(cwd = process.cwd()) {
|
|
78906
|
+
const git = (args) => {
|
|
78907
|
+
try {
|
|
78908
|
+
return execFileSync2("git", args, {
|
|
78909
|
+
cwd,
|
|
78910
|
+
encoding: "utf-8",
|
|
78911
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
78912
|
+
}).trim();
|
|
78913
|
+
} catch {
|
|
78914
|
+
return null;
|
|
78915
|
+
}
|
|
78916
|
+
};
|
|
78917
|
+
const current = git(["rev-parse", "--show-toplevel"]);
|
|
78918
|
+
const commonDir = git(["rev-parse", "--git-common-dir"]);
|
|
78919
|
+
if (!current || !commonDir)
|
|
78920
|
+
return null;
|
|
78921
|
+
const root = commonDir.endsWith("/.git") ? commonDir.slice(0, -"/.git".length) : current;
|
|
78922
|
+
const liveWorktrees = [];
|
|
78923
|
+
const branchByPath = new Map;
|
|
78924
|
+
const porcelain = git(["worktree", "list", "--porcelain"]);
|
|
78925
|
+
if (porcelain) {
|
|
78926
|
+
let currentPath = null;
|
|
78927
|
+
for (const line of porcelain.split(`
|
|
78928
|
+
`)) {
|
|
78929
|
+
if (line.startsWith("worktree ")) {
|
|
78930
|
+
currentPath = line.slice("worktree ".length);
|
|
78931
|
+
liveWorktrees.push(currentPath);
|
|
78932
|
+
} else if (line.startsWith("branch ") && currentPath) {
|
|
78933
|
+
branchByPath.set(currentPath, line.slice("branch ".length).replace(/^refs\/heads\//, ""));
|
|
78934
|
+
}
|
|
78935
|
+
}
|
|
78936
|
+
}
|
|
78937
|
+
return { root, current, liveWorktrees, branchByPath };
|
|
78938
|
+
}
|
|
78939
|
+
function projectDirs() {
|
|
78940
|
+
try {
|
|
78941
|
+
return readdirSync7(PROJECTS_DIR, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
78942
|
+
} catch {
|
|
78943
|
+
return [];
|
|
78944
|
+
}
|
|
78945
|
+
}
|
|
78946
|
+
function sessionsIn(dirName) {
|
|
78947
|
+
const dir = join37(PROJECTS_DIR, dirName);
|
|
78948
|
+
let names;
|
|
78949
|
+
try {
|
|
78950
|
+
names = readdirSync7(dir).filter((n) => n.endsWith(".jsonl"));
|
|
78951
|
+
} catch {
|
|
78952
|
+
return [];
|
|
78953
|
+
}
|
|
78954
|
+
const rows = [];
|
|
78955
|
+
for (const n of names) {
|
|
78956
|
+
const file2 = join37(dir, n);
|
|
78957
|
+
try {
|
|
78958
|
+
const st = statSync6(file2);
|
|
78959
|
+
if (st.size === 0)
|
|
78960
|
+
continue;
|
|
78961
|
+
const row = {
|
|
78962
|
+
id: basename(n, ".jsonl"),
|
|
78963
|
+
file: file2,
|
|
78964
|
+
mtimeMs: st.mtimeMs,
|
|
78965
|
+
sizeBytes: st.size
|
|
78966
|
+
};
|
|
78967
|
+
const head = readChunk(file2, 0, Math.min(ENTRYPOINT_BYTES, st.size));
|
|
78968
|
+
const m = /"entrypoint":"([a-z-]+)"/.exec(head);
|
|
78969
|
+
if (m)
|
|
78970
|
+
row.entrypoint = m[1];
|
|
78971
|
+
rows.push(row);
|
|
78972
|
+
} catch {}
|
|
78973
|
+
}
|
|
78974
|
+
return rows;
|
|
78975
|
+
}
|
|
78976
|
+
async function enrichWorktreeGit(groups, repoRoot) {
|
|
78977
|
+
const run = (cwd, args) => new Promise((resolve5) => {
|
|
78978
|
+
execFile("git", args, { cwd, encoding: "utf-8" }, (err, stdout) => resolve5(err ? "" : stdout));
|
|
78979
|
+
});
|
|
78980
|
+
const trackByBranch = new Map;
|
|
78981
|
+
const refs = await run(repoRoot, [
|
|
78982
|
+
"for-each-ref",
|
|
78983
|
+
"--format=%(refname:short)\t%(upstream:track)",
|
|
78984
|
+
"refs/heads/"
|
|
78985
|
+
]);
|
|
78986
|
+
for (const line of refs.split(`
|
|
78987
|
+
`)) {
|
|
78988
|
+
const [name, track] = line.split("\t");
|
|
78989
|
+
if (!name || !track)
|
|
78990
|
+
continue;
|
|
78991
|
+
const ahead = /ahead (\d+)/.exec(track)?.[1];
|
|
78992
|
+
const behind = /behind (\d+)/.exec(track)?.[1];
|
|
78993
|
+
if (ahead || behind) {
|
|
78994
|
+
trackByBranch.set(name, {
|
|
78995
|
+
...ahead ? { ahead: Number(ahead) } : {},
|
|
78996
|
+
...behind ? { behind: Number(behind) } : {}
|
|
78997
|
+
});
|
|
78998
|
+
}
|
|
78999
|
+
}
|
|
79000
|
+
await Promise.all(groups.map(async (g) => {
|
|
79001
|
+
if (g.branch)
|
|
79002
|
+
Object.assign(g, trackByBranch.get(g.branch) ?? {});
|
|
79003
|
+
if (!g.path || !g.live)
|
|
79004
|
+
return;
|
|
79005
|
+
const out = await run(g.path, ["status", "--porcelain"]);
|
|
79006
|
+
g.dirty = out.split(`
|
|
79007
|
+
`).filter((l) => l.trim().length > 0).length;
|
|
79008
|
+
}));
|
|
79009
|
+
}
|
|
79010
|
+
function isUnder(p, root) {
|
|
79011
|
+
return p === root || p.startsWith(`${root}/`);
|
|
79012
|
+
}
|
|
79013
|
+
function readProjectCwd(dirName) {
|
|
79014
|
+
const rows = sessionsIn(dirName);
|
|
79015
|
+
if (rows.length === 0)
|
|
79016
|
+
return null;
|
|
79017
|
+
rows.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
79018
|
+
for (const row of rows.slice(0, 2)) {
|
|
79019
|
+
for (const r of parseRecords(readChunk(row.file, 0, Math.min(HEAD_BYTES, row.sizeBytes)), false)) {
|
|
79020
|
+
if (typeof r.cwd === "string" && r.cwd)
|
|
79021
|
+
return r.cwd;
|
|
79022
|
+
}
|
|
79023
|
+
}
|
|
79024
|
+
return null;
|
|
79025
|
+
}
|
|
79026
|
+
function isActive(row, now = Date.now()) {
|
|
79027
|
+
return now - row.mtimeMs < ACTIVE_WINDOW_MS;
|
|
79028
|
+
}
|
|
79029
|
+
function discoverWorktreeGroups(repo) {
|
|
79030
|
+
const rootSlug = slugForPath(repo.root);
|
|
79031
|
+
const mine = projectDirs();
|
|
79032
|
+
const known = [...repo.liveWorktrees].map((p) => ({ path: p, slug: slugForPath(p) })).sort((a, b) => b.slug.length - a.slug.length);
|
|
79033
|
+
const groups = new Map;
|
|
79034
|
+
const groupCwd = new Map;
|
|
79035
|
+
const upsert = (name, path2, live) => {
|
|
79036
|
+
let g = groups.get(name);
|
|
79037
|
+
if (!g) {
|
|
79038
|
+
g = {
|
|
79039
|
+
name,
|
|
79040
|
+
path: path2,
|
|
79041
|
+
live,
|
|
79042
|
+
current: path2 !== null && path2 === repo.current,
|
|
79043
|
+
sessions: [],
|
|
79044
|
+
lastActiveMs: 0,
|
|
79045
|
+
activeNow: false,
|
|
79046
|
+
...path2 ? { branch: repo.branchByPath.get(path2) } : {}
|
|
79047
|
+
};
|
|
79048
|
+
groups.set(name, g);
|
|
79049
|
+
}
|
|
79050
|
+
return g;
|
|
79051
|
+
};
|
|
79052
|
+
const WORKTREE_MARK = "--claude-worktrees-";
|
|
79053
|
+
const worktreeMatches = known.filter((k) => k.path !== repo.root);
|
|
79054
|
+
for (const dir of mine) {
|
|
79055
|
+
const at = dir.indexOf(WORKTREE_MARK);
|
|
79056
|
+
let name;
|
|
79057
|
+
let path2;
|
|
79058
|
+
let live;
|
|
79059
|
+
const hit = worktreeMatches.find((k) => dir === k.slug || dir.startsWith(`${k.slug}-`));
|
|
79060
|
+
if (hit) {
|
|
79061
|
+
path2 = hit.path;
|
|
79062
|
+
live = true;
|
|
79063
|
+
name = hit.path === repo.root ? "(root)" : basename(hit.path);
|
|
79064
|
+
} else if (at !== -1 && dir.startsWith(rootSlug)) {
|
|
79065
|
+
path2 = null;
|
|
79066
|
+
live = false;
|
|
79067
|
+
name = dir.slice(at + WORKTREE_MARK.length);
|
|
79068
|
+
} else if (dir === rootSlug) {
|
|
79069
|
+
name = "(root)";
|
|
79070
|
+
path2 = repo.root;
|
|
79071
|
+
live = true;
|
|
79072
|
+
} else if (dir.startsWith(`${rootSlug}-`) && at === -1) {
|
|
79073
|
+
const cwd = readProjectCwd(dir);
|
|
79074
|
+
if (!cwd || !isUnder(cwd, repo.root))
|
|
79075
|
+
continue;
|
|
79076
|
+
name = "(root)";
|
|
79077
|
+
path2 = repo.root;
|
|
79078
|
+
live = true;
|
|
79079
|
+
} else {
|
|
79080
|
+
continue;
|
|
79081
|
+
}
|
|
79082
|
+
const g = upsert(name, path2, live);
|
|
79083
|
+
if (path2)
|
|
79084
|
+
groupCwd.set(name, path2);
|
|
79085
|
+
else if (!groupCwd.has(name)) {
|
|
79086
|
+
const cwd = readProjectCwd(dir);
|
|
79087
|
+
if (cwd)
|
|
79088
|
+
groupCwd.set(name, cwd);
|
|
79089
|
+
}
|
|
79090
|
+
for (const s of sessionsIn(dir)) {
|
|
79091
|
+
g.sessions.push(s);
|
|
79092
|
+
if (s.mtimeMs > g.lastActiveMs)
|
|
79093
|
+
g.lastActiveMs = s.mtimeMs;
|
|
79094
|
+
}
|
|
79095
|
+
}
|
|
79096
|
+
const names = [...groups.keys()].sort((a, b) => b.length - a.length);
|
|
79097
|
+
for (const name of [...groups.keys()]) {
|
|
79098
|
+
const g = groups.get(name);
|
|
79099
|
+
if (!g || g.live)
|
|
79100
|
+
continue;
|
|
79101
|
+
const parent = names.find((n) => n !== name && name.startsWith(`${n}-`) && groups.has(n));
|
|
79102
|
+
if (!parent)
|
|
79103
|
+
continue;
|
|
79104
|
+
const into = groups.get(parent);
|
|
79105
|
+
const childCwd = groupCwd.get(name);
|
|
79106
|
+
const parentCwd = groupCwd.get(parent);
|
|
79107
|
+
if (!childCwd || !parentCwd || !isUnder(childCwd, parentCwd))
|
|
79108
|
+
continue;
|
|
79109
|
+
into.sessions.push(...g.sessions);
|
|
79110
|
+
into.lastActiveMs = Math.max(into.lastActiveMs, g.lastActiveMs);
|
|
79111
|
+
groups.delete(name);
|
|
79112
|
+
}
|
|
79113
|
+
for (const g of groups.values()) {
|
|
79114
|
+
g.sessions.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
79115
|
+
g.activeNow = g.sessions.some((s) => isActive(s));
|
|
79116
|
+
if (g.path) {
|
|
79117
|
+
try {
|
|
79118
|
+
g.createdMs = statSync6(g.path).birthtimeMs;
|
|
79119
|
+
} catch {}
|
|
79120
|
+
}
|
|
79121
|
+
if (!g.createdMs && g.sessions.length > 0) {
|
|
79122
|
+
g.createdMs = g.sessions.reduce((m, s) => Math.min(m, s.mtimeMs), Number.POSITIVE_INFINITY);
|
|
79123
|
+
}
|
|
79124
|
+
}
|
|
79125
|
+
return [...groups.values()].filter((g) => g.sessions.length > 0).sort((a, b) => {
|
|
79126
|
+
if (a.current !== b.current)
|
|
79127
|
+
return a.current ? -1 : 1;
|
|
79128
|
+
return b.lastActiveMs - a.lastActiveMs;
|
|
79129
|
+
});
|
|
79130
|
+
}
|
|
79131
|
+
function readChunk(file2, pos, len) {
|
|
79132
|
+
if (len <= 0)
|
|
79133
|
+
return "";
|
|
79134
|
+
let fd = null;
|
|
79135
|
+
try {
|
|
79136
|
+
fd = openSync5(file2, "r");
|
|
79137
|
+
const buf = Buffer.allocUnsafe(len);
|
|
79138
|
+
const n = readSync(fd, buf, 0, len, pos);
|
|
79139
|
+
return buf.subarray(0, n).toString("utf-8");
|
|
79140
|
+
} catch {
|
|
79141
|
+
return "";
|
|
79142
|
+
} finally {
|
|
79143
|
+
if (fd !== null) {
|
|
79144
|
+
try {
|
|
79145
|
+
closeSync5(fd);
|
|
79146
|
+
} catch {}
|
|
79147
|
+
}
|
|
79148
|
+
}
|
|
79149
|
+
}
|
|
79150
|
+
function parseRecords(chunk, dropFirstPartial) {
|
|
79151
|
+
const lines = chunk.split(`
|
|
79152
|
+
`);
|
|
79153
|
+
if (dropFirstPartial)
|
|
79154
|
+
lines.shift();
|
|
79155
|
+
else
|
|
79156
|
+
lines.pop();
|
|
79157
|
+
const out = [];
|
|
79158
|
+
for (const l of lines) {
|
|
79159
|
+
if (!l)
|
|
79160
|
+
continue;
|
|
79161
|
+
try {
|
|
79162
|
+
const o = JSON.parse(l);
|
|
79163
|
+
if (o && typeof o === "object")
|
|
79164
|
+
out.push(o);
|
|
79165
|
+
} catch {}
|
|
79166
|
+
}
|
|
79167
|
+
return out;
|
|
79168
|
+
}
|
|
79169
|
+
function contentText(content) {
|
|
79170
|
+
if (typeof content === "string")
|
|
79171
|
+
return content;
|
|
79172
|
+
if (!Array.isArray(content))
|
|
79173
|
+
return "";
|
|
79174
|
+
return content.map((b) => b && typeof b === "object" && typeof b.text === "string" ? b.text : "").join(`
|
|
79175
|
+
`);
|
|
79176
|
+
}
|
|
79177
|
+
function isHarnessNoise(raw2) {
|
|
79178
|
+
const t = raw2.trimStart();
|
|
79179
|
+
if (t.startsWith("[Request interrupted by user"))
|
|
79180
|
+
return true;
|
|
79181
|
+
return HARNESS_ENVELOPES.some((e) => t.startsWith(e));
|
|
79182
|
+
}
|
|
79183
|
+
function mainConversationTurn(r) {
|
|
79184
|
+
if (r.type !== "user" && r.type !== "assistant")
|
|
79185
|
+
return null;
|
|
79186
|
+
if (r.isMeta || r.isSidechain)
|
|
79187
|
+
return null;
|
|
79188
|
+
const content = r.message?.content;
|
|
79189
|
+
if (Array.isArray(content) && content.some((b) => b?.type === "tool_result")) {
|
|
79190
|
+
return null;
|
|
79191
|
+
}
|
|
79192
|
+
const raw2 = contentText(content);
|
|
79193
|
+
if (!raw2.trim() || isHarnessNoise(raw2))
|
|
79194
|
+
return null;
|
|
79195
|
+
return { role: r.type === "user" ? "user" : "assistant", raw: raw2 };
|
|
79196
|
+
}
|
|
79197
|
+
function cleanPrompt(text) {
|
|
79198
|
+
return text.replace(/<command-[a-z-]+>[\s\S]*?<\/command-[a-z-]+>/g, " ").replace(/<local-command-[a-z-]+>[\s\S]*?<\/local-command-[a-z-]+>/g, " ").replace(/<[^>]{1,40}>/g, " ").replace(/\s+/g, " ").trim();
|
|
79199
|
+
}
|
|
79200
|
+
function hydrateSession(row) {
|
|
79201
|
+
if (row.hydrated)
|
|
79202
|
+
return row;
|
|
79203
|
+
row.hydrated = true;
|
|
79204
|
+
if (isActive(row)) {
|
|
79205
|
+
try {
|
|
79206
|
+
row.sizeBytes = statSync6(row.file).size;
|
|
79207
|
+
} catch {}
|
|
79208
|
+
}
|
|
79209
|
+
const head = parseRecords(readChunk(row.file, 0, Math.min(HEAD_BYTES, row.sizeBytes)), false);
|
|
79210
|
+
for (const r of head) {
|
|
79211
|
+
if (!row.gitBranch && typeof r.gitBranch === "string")
|
|
79212
|
+
row.gitBranch = r.gitBranch;
|
|
79213
|
+
if (!row.firstPrompt && r.type === "user" && !r.isMeta) {
|
|
79214
|
+
const raw2 = contentText(r.message?.content);
|
|
79215
|
+
const t = isHarnessNoise(raw2) ? "" : cleanPrompt(raw2);
|
|
79216
|
+
if (t)
|
|
79217
|
+
row.firstPrompt = t;
|
|
79218
|
+
}
|
|
79219
|
+
if (row.gitBranch && row.firstPrompt)
|
|
79220
|
+
break;
|
|
79221
|
+
}
|
|
79222
|
+
const tailStart = Math.max(0, row.sizeBytes - TAIL_BYTES);
|
|
79223
|
+
const tail = parseRecords(readChunk(row.file, tailStart, row.sizeBytes - tailStart), tailStart > 0);
|
|
79224
|
+
for (let i = tail.length - 1;i >= 0; i--) {
|
|
79225
|
+
const r = tail[i];
|
|
79226
|
+
if (!row.title && r.type === "ai-title" && typeof r.aiTitle === "string" && r.aiTitle.trim()) {
|
|
79227
|
+
row.title = r.aiTitle.trim();
|
|
79228
|
+
}
|
|
79229
|
+
if (row.lastMessageChars === undefined && r.type === "user" && !r.isMeta) {
|
|
79230
|
+
const raw2 = contentText(r.message?.content);
|
|
79231
|
+
const t = isHarnessNoise(raw2) ? "" : cleanPrompt(raw2);
|
|
79232
|
+
if (t)
|
|
79233
|
+
row.lastMessageChars = t.length;
|
|
79234
|
+
}
|
|
79235
|
+
}
|
|
79236
|
+
row.recentTurns = extractRecentTurns(tail);
|
|
79237
|
+
return row;
|
|
79238
|
+
}
|
|
79239
|
+
function extractRecentTurns(records) {
|
|
79240
|
+
const out = [];
|
|
79241
|
+
let ai = 0;
|
|
79242
|
+
let user = 0;
|
|
79243
|
+
for (let i = records.length - 1;i >= 0; i--) {
|
|
79244
|
+
if (ai >= RECENT_AI_TURNS && user >= RECENT_USER_TURNS)
|
|
79245
|
+
break;
|
|
79246
|
+
const r = records[i];
|
|
79247
|
+
if (r.type !== "user" && r.type !== "assistant")
|
|
79248
|
+
continue;
|
|
79249
|
+
const role = r.type === "user" ? "user" : "assistant";
|
|
79250
|
+
if (role === "assistant" ? ai >= RECENT_AI_TURNS : user >= RECENT_USER_TURNS)
|
|
79251
|
+
continue;
|
|
79252
|
+
const turn = mainConversationTurn(r);
|
|
79253
|
+
if (!turn)
|
|
79254
|
+
continue;
|
|
79255
|
+
const text = cleanPrompt(turn.raw);
|
|
79256
|
+
if (!text)
|
|
79257
|
+
continue;
|
|
79258
|
+
out.push({ role, text });
|
|
79259
|
+
if (role === "assistant")
|
|
79260
|
+
ai++;
|
|
79261
|
+
else
|
|
79262
|
+
user++;
|
|
79263
|
+
}
|
|
79264
|
+
return out.reverse();
|
|
79265
|
+
}
|
|
79266
|
+
function hydrateConversation(row) {
|
|
79267
|
+
if (row.conversationDeepened)
|
|
79268
|
+
return row;
|
|
79269
|
+
row.conversationDeepened = true;
|
|
79270
|
+
const turns = row.recentTurns;
|
|
79271
|
+
if (!turns || turns.some((t) => t.role === "user"))
|
|
79272
|
+
return row;
|
|
79273
|
+
const start = Math.max(0, row.sizeBytes - DEEP_TAIL_BYTES);
|
|
79274
|
+
const chunk = readChunk(row.file, start, row.sizeBytes - start);
|
|
79275
|
+
if (!chunk)
|
|
79276
|
+
return row;
|
|
79277
|
+
const lines = chunk.split(`
|
|
79278
|
+
`);
|
|
79279
|
+
if (start > 0)
|
|
79280
|
+
lines.shift();
|
|
79281
|
+
for (let i = lines.length - 1;i >= 0; i--) {
|
|
79282
|
+
const line = lines[i];
|
|
79283
|
+
if (!line.includes('"type":"user"'))
|
|
79284
|
+
continue;
|
|
79285
|
+
let r;
|
|
79286
|
+
try {
|
|
79287
|
+
r = JSON.parse(line);
|
|
79288
|
+
} catch {
|
|
79289
|
+
continue;
|
|
79290
|
+
}
|
|
79291
|
+
const turn = mainConversationTurn(r);
|
|
79292
|
+
if (!turn || turn.role !== "user")
|
|
79293
|
+
continue;
|
|
79294
|
+
const text = cleanPrompt(turn.raw);
|
|
79295
|
+
if (!text)
|
|
79296
|
+
continue;
|
|
79297
|
+
turns.unshift({ role: "user", text });
|
|
79298
|
+
if (row.lastMessageChars === undefined)
|
|
79299
|
+
row.lastMessageChars = text.length;
|
|
79300
|
+
return row;
|
|
79301
|
+
}
|
|
79302
|
+
return row;
|
|
79303
|
+
}
|
|
79304
|
+
function sessionLabel(row) {
|
|
79305
|
+
return row.title || row.firstPrompt || row.id;
|
|
79306
|
+
}
|
|
79307
|
+
function findLatestSessionId(cwd = process.cwd(), sinceMs = 0) {
|
|
79308
|
+
const rows = sessionsIn(slugForPath(cwd)).filter((r) => r.mtimeMs >= sinceMs);
|
|
79309
|
+
if (rows.length === 0)
|
|
79310
|
+
return null;
|
|
79311
|
+
return rows.reduce((a, b) => b.mtimeMs > a.mtimeMs ? b : a).id;
|
|
79312
|
+
}
|
|
79313
|
+
var ENTRYPOINT_BYTES = 8192, PROJECTS_DIR, ACTIVE_WINDOW_MS = 120000, HEAD_BYTES, TAIL_BYTES, HARNESS_ENVELOPES, DEEP_TAIL_BYTES, RECENT_AI_TURNS = 5, RECENT_USER_TURNS = 1;
|
|
79314
|
+
var init_session_discovery = __esm(() => {
|
|
79315
|
+
PROJECTS_DIR = join37(homedir34(), ".claude", "projects");
|
|
79316
|
+
HEAD_BYTES = 64 * 1024;
|
|
79317
|
+
TAIL_BYTES = 128 * 1024;
|
|
79318
|
+
HARNESS_ENVELOPES = [
|
|
79319
|
+
"<task-notification>",
|
|
79320
|
+
"<system-reminder>",
|
|
79321
|
+
"<local-command-stdout>",
|
|
79322
|
+
"<user-prompt-submit-hook>"
|
|
79323
|
+
];
|
|
79324
|
+
DEEP_TAIL_BYTES = 4 * 1024 * 1024;
|
|
79325
|
+
});
|
|
79326
|
+
|
|
79327
|
+
// src/session/conversation.ts
|
|
79328
|
+
import { closeSync as closeSync6, openSync as openSync6, readSync as readSync2, statSync as statSync7 } from "fs";
|
|
79329
|
+
import { StringDecoder } from "string_decoder";
|
|
79330
|
+
function looksLikeTurn(line) {
|
|
79331
|
+
const assistant = line.includes('"type":"assistant"');
|
|
79332
|
+
if (!assistant && !line.includes('"type":"user"'))
|
|
79333
|
+
return false;
|
|
79334
|
+
if (line.includes('"isSidechain":true'))
|
|
79335
|
+
return false;
|
|
79336
|
+
if (line.includes('"isMeta":true'))
|
|
79337
|
+
return false;
|
|
79338
|
+
if (line.includes('"type":"tool_result"'))
|
|
79339
|
+
return false;
|
|
79340
|
+
if (assistant && !line.includes('"type":"text"'))
|
|
79341
|
+
return false;
|
|
79342
|
+
return true;
|
|
79343
|
+
}
|
|
79344
|
+
function cleanTurnText(raw2) {
|
|
79345
|
+
let t = raw2;
|
|
79346
|
+
for (const re of INLINE_ENVELOPES)
|
|
79347
|
+
t = t.replace(re, "");
|
|
79348
|
+
t = t.replace(/<command-name>([\s\S]*?)<\/command-name>/g, "/$1");
|
|
79349
|
+
return t.replace(/\t/g, " ").replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, "").replace(/\r\n?/g, `
|
|
79350
|
+
`).split(`
|
|
79351
|
+
`).map((l) => l.replace(/\s+$/, "")).join(`
|
|
79352
|
+
`).replace(/\n{3,}/g, `
|
|
79353
|
+
|
|
79354
|
+
`).trim();
|
|
79355
|
+
}
|
|
79356
|
+
function readConversation(file2, opts = {}) {
|
|
79357
|
+
const maxTurn = opts.maxTurnChars ?? MAX_TURN_CHARS;
|
|
79358
|
+
const maxTotal = opts.maxTotalChars ?? MAX_TOTAL_CHARS;
|
|
79359
|
+
const chunkBytes = opts.chunkBytes ?? CHUNK_BYTES;
|
|
79360
|
+
const started = Date.now();
|
|
79361
|
+
const turns = [];
|
|
79362
|
+
let chars = 0;
|
|
79363
|
+
let dropped = 0;
|
|
79364
|
+
let anyElided = false;
|
|
79365
|
+
let bytes2 = 0;
|
|
79366
|
+
const take = (turn) => {
|
|
79367
|
+
turns.push(turn);
|
|
79368
|
+
chars += turn.text.length;
|
|
79369
|
+
if (chars <= maxTotal)
|
|
79370
|
+
return;
|
|
79371
|
+
let freed = 0;
|
|
79372
|
+
let n = 0;
|
|
79373
|
+
while (n < turns.length - 1 && chars - freed > maxTotal) {
|
|
79374
|
+
freed += turns[n].text.length;
|
|
79375
|
+
n++;
|
|
79376
|
+
}
|
|
79377
|
+
turns.splice(0, n);
|
|
79378
|
+
chars -= freed;
|
|
79379
|
+
dropped += n;
|
|
79380
|
+
};
|
|
79381
|
+
let fd = null;
|
|
79382
|
+
try {
|
|
79383
|
+
const size = statSync7(file2).size;
|
|
79384
|
+
fd = openSync6(file2, "r");
|
|
79385
|
+
const buf = Buffer.allocUnsafe(chunkBytes);
|
|
79386
|
+
const decoder = new StringDecoder("utf-8");
|
|
79387
|
+
let pending = "";
|
|
79388
|
+
let pos = 0;
|
|
79389
|
+
const consume = (line) => {
|
|
79390
|
+
if (!line || !looksLikeTurn(line))
|
|
79391
|
+
return;
|
|
79392
|
+
let record4;
|
|
79393
|
+
try {
|
|
79394
|
+
record4 = JSON.parse(line);
|
|
79395
|
+
} catch {
|
|
79396
|
+
return;
|
|
79397
|
+
}
|
|
79398
|
+
if (!record4 || typeof record4 !== "object")
|
|
79399
|
+
return;
|
|
79400
|
+
const raw2 = mainConversationTurn(record4);
|
|
79401
|
+
if (!raw2)
|
|
79402
|
+
return;
|
|
79403
|
+
let text = cleanTurnText(raw2.raw);
|
|
79404
|
+
if (!text)
|
|
79405
|
+
return;
|
|
79406
|
+
const elided = text.length > maxTurn;
|
|
79407
|
+
if (elided) {
|
|
79408
|
+
anyElided = true;
|
|
79409
|
+
text = `${text.slice(0, maxTurn)}
|
|
79410
|
+
|
|
79411
|
+
\u2026 turn truncated at ${maxTurn.toLocaleString()} characters`;
|
|
79412
|
+
}
|
|
79413
|
+
take({ role: raw2.role, text, elided });
|
|
79414
|
+
};
|
|
79415
|
+
while (pos < size) {
|
|
79416
|
+
const n = readSync2(fd, buf, 0, Math.min(chunkBytes, size - pos), pos);
|
|
79417
|
+
if (n <= 0)
|
|
79418
|
+
break;
|
|
79419
|
+
pos += n;
|
|
79420
|
+
bytes2 += n;
|
|
79421
|
+
pending += decoder.write(buf.subarray(0, n));
|
|
79422
|
+
let from = 0;
|
|
79423
|
+
let nl = pending.indexOf(`
|
|
79424
|
+
`, from);
|
|
79425
|
+
while (nl !== -1) {
|
|
79426
|
+
consume(pending.slice(from, nl));
|
|
79427
|
+
from = nl + 1;
|
|
79428
|
+
nl = pending.indexOf(`
|
|
79429
|
+
`, from);
|
|
79430
|
+
}
|
|
79431
|
+
if (from > 0)
|
|
79432
|
+
pending = pending.slice(from);
|
|
79433
|
+
}
|
|
79434
|
+
pending += decoder.end();
|
|
79435
|
+
if (pending)
|
|
79436
|
+
consume(pending);
|
|
79437
|
+
} catch {} finally {
|
|
79438
|
+
if (fd !== null) {
|
|
79439
|
+
try {
|
|
79440
|
+
closeSync6(fd);
|
|
79441
|
+
} catch {}
|
|
79442
|
+
}
|
|
79443
|
+
}
|
|
79444
|
+
return { turns, bytes: bytes2, elapsedMs: Date.now() - started, chars, dropped, anyElided };
|
|
79445
|
+
}
|
|
79446
|
+
function cpWidth(cp) {
|
|
79447
|
+
if (cp >= 32 && cp < 127)
|
|
79448
|
+
return 1;
|
|
79449
|
+
return displayWidth(String.fromCodePoint(cp));
|
|
79450
|
+
}
|
|
79451
|
+
function wrapOffsets(text, width) {
|
|
79452
|
+
const out = [];
|
|
79453
|
+
const w = Math.max(1, Math.floor(width));
|
|
79454
|
+
const n = text.length;
|
|
79455
|
+
let paraStart = 0;
|
|
79456
|
+
for (;; ) {
|
|
79457
|
+
let nl = text.indexOf(`
|
|
79458
|
+
`, paraStart);
|
|
79459
|
+
if (nl === -1)
|
|
79460
|
+
nl = n;
|
|
79461
|
+
let s = paraStart;
|
|
79462
|
+
for (;; ) {
|
|
79463
|
+
if (s >= nl) {
|
|
79464
|
+
if (s === paraStart || paraStart === nl)
|
|
79465
|
+
out.push({ start: s, end: nl });
|
|
79466
|
+
break;
|
|
79467
|
+
}
|
|
79468
|
+
let cols = 0;
|
|
79469
|
+
let j = s;
|
|
79470
|
+
let lastSpace = -1;
|
|
79471
|
+
while (j < nl) {
|
|
79472
|
+
const cp = text.codePointAt(j);
|
|
79473
|
+
const size = cp > 65535 ? 2 : 1;
|
|
79474
|
+
const cw = cpWidth(cp);
|
|
79475
|
+
if (cols + cw > w)
|
|
79476
|
+
break;
|
|
79477
|
+
if (cp === 32 && j > s && text.charCodeAt(j - 1) !== 32)
|
|
79478
|
+
lastSpace = j;
|
|
79479
|
+
cols += cw;
|
|
79480
|
+
j += size;
|
|
79481
|
+
}
|
|
79482
|
+
if (j >= nl) {
|
|
79483
|
+
out.push({ start: s, end: nl });
|
|
79484
|
+
break;
|
|
79485
|
+
}
|
|
79486
|
+
const brk = text.charCodeAt(j) === 32 ? j : lastSpace > s ? lastSpace : j;
|
|
79487
|
+
out.push({ start: s, end: brk });
|
|
79488
|
+
s = brk === j && text.charCodeAt(j) !== 32 ? j : brk + 1;
|
|
79489
|
+
while (s < nl && text.charCodeAt(s) === 32)
|
|
79490
|
+
s++;
|
|
79491
|
+
}
|
|
79492
|
+
if (nl >= n)
|
|
79493
|
+
break;
|
|
79494
|
+
paraStart = nl + 1;
|
|
79495
|
+
if (paraStart > n)
|
|
79496
|
+
break;
|
|
79497
|
+
if (paraStart === n) {
|
|
79498
|
+
out.push({ start: n, end: n });
|
|
79499
|
+
break;
|
|
79500
|
+
}
|
|
79501
|
+
}
|
|
79502
|
+
return out;
|
|
79503
|
+
}
|
|
79504
|
+
var CHUNK_BYTES, MAX_TURN_CHARS = 32000, MAX_TOTAL_CHARS = 8000000, INLINE_ENVELOPES;
|
|
79505
|
+
var init_conversation = __esm(() => {
|
|
79506
|
+
init_text();
|
|
79507
|
+
init_session_discovery();
|
|
79508
|
+
CHUNK_BYTES = 1024 * 1024;
|
|
79509
|
+
INLINE_ENVELOPES = [
|
|
79510
|
+
/<system-reminder>[\s\S]*?<\/system-reminder>/g,
|
|
79511
|
+
/<command-message>[\s\S]*?<\/command-message>/g,
|
|
79512
|
+
/<command-args>[\s\S]*?<\/command-args>/g,
|
|
79513
|
+
/<local-command-stdout>[\s\S]*?<\/local-command-stdout>/g,
|
|
79514
|
+
/<user-prompt-submit-hook>[\s\S]*?<\/user-prompt-submit-hook>/g
|
|
79515
|
+
];
|
|
79516
|
+
});
|
|
79517
|
+
|
|
79518
|
+
// src/session/conversation-reader.tsx
|
|
79519
|
+
import { useKeyboard as useKeyboard3 } from "@opentui/react";
|
|
79520
|
+
import { useEffect as useEffect5, useMemo as useMemo3, useState as useState6 } from "react";
|
|
79521
|
+
import { jsxDEV as jsxDEV19, Fragment as Fragment12 } from "@opentui/react/jsx-dev-runtime";
|
|
79522
|
+
function layoutRows(turns, textWidth) {
|
|
79523
|
+
const rows = [];
|
|
79524
|
+
const turnStart = [];
|
|
79525
|
+
const turnEnd = [];
|
|
79526
|
+
for (let t = 0;t < turns.length; t++) {
|
|
79527
|
+
turnStart.push(rows.length);
|
|
79528
|
+
const slices = wrapOffsets(turns[t].text, textWidth);
|
|
79529
|
+
for (let i = 0;i < slices.length; i++) {
|
|
79530
|
+
rows.push({ turn: t, first: i === 0, start: slices[i].start, end: slices[i].end });
|
|
79531
|
+
}
|
|
79532
|
+
turnEnd.push(rows.length);
|
|
79533
|
+
if (t < turns.length - 1)
|
|
79534
|
+
rows.push({ turn: -1, first: false, start: 0, end: 0 });
|
|
79535
|
+
}
|
|
79536
|
+
return { rows, turnStart, turnEnd };
|
|
79537
|
+
}
|
|
79538
|
+
function buildSearch(turns, rows, turnStart, turnEnd, query) {
|
|
79539
|
+
if (!query)
|
|
79540
|
+
return EMPTY_SEARCH;
|
|
79541
|
+
const q = query.toLowerCase();
|
|
79542
|
+
const hits = [];
|
|
79543
|
+
const ranges = new Map;
|
|
79544
|
+
let capped = false;
|
|
79545
|
+
for (let t = 0;t < turns.length && !capped; t++) {
|
|
79546
|
+
const hay = turns[t].text.toLowerCase();
|
|
79547
|
+
let at = hay.indexOf(q);
|
|
79548
|
+
if (at === -1)
|
|
79549
|
+
continue;
|
|
79550
|
+
const lo = turnStart[t];
|
|
79551
|
+
const hi = turnEnd[t];
|
|
79552
|
+
let r = lo;
|
|
79553
|
+
while (at !== -1) {
|
|
79554
|
+
if (hits.length >= MAX_MATCHES) {
|
|
79555
|
+
capped = true;
|
|
79556
|
+
break;
|
|
79557
|
+
}
|
|
79558
|
+
const end = at + q.length;
|
|
79559
|
+
while (r < hi - 1 && rows[r].end <= at)
|
|
79560
|
+
r++;
|
|
79561
|
+
const hit = hits.length;
|
|
79562
|
+
hits.push(r);
|
|
79563
|
+
for (let k = r;k < hi; k++) {
|
|
79564
|
+
const row = rows[k];
|
|
79565
|
+
if (row.start >= end)
|
|
79566
|
+
break;
|
|
79567
|
+
const s = Math.max(at, row.start);
|
|
79568
|
+
const e = Math.min(end, row.end);
|
|
79569
|
+
if (e > s) {
|
|
79570
|
+
const list = ranges.get(k);
|
|
79571
|
+
if (list)
|
|
79572
|
+
list.push({ s: s - row.start, e: e - row.start, hit });
|
|
79573
|
+
else
|
|
79574
|
+
ranges.set(k, [{ s: s - row.start, e: e - row.start, hit }]);
|
|
79575
|
+
}
|
|
79576
|
+
}
|
|
79577
|
+
at = hay.indexOf(q, at + 1);
|
|
79578
|
+
}
|
|
79579
|
+
}
|
|
79580
|
+
return { hits, ranges, capped };
|
|
79581
|
+
}
|
|
79582
|
+
function ConversationReader({
|
|
79583
|
+
row,
|
|
79584
|
+
conv,
|
|
79585
|
+
width,
|
|
79586
|
+
height: height2,
|
|
79587
|
+
onClose,
|
|
79588
|
+
onResume,
|
|
79589
|
+
onCancel
|
|
79590
|
+
}) {
|
|
79591
|
+
const [top, setTop] = useState6(0);
|
|
79592
|
+
const [query, setQuery] = useState6("");
|
|
79593
|
+
const [typing, setTyping] = useState6(false);
|
|
79594
|
+
const [matchIdx, setMatchIdx] = useState6(0);
|
|
79595
|
+
const CHROME_ROWS = 5;
|
|
79596
|
+
const viewport = Math.max(1, height2 - CHROME_ROWS);
|
|
79597
|
+
const inner = Math.max(20, width - 2);
|
|
79598
|
+
const contentW = inner - BAR_W;
|
|
79599
|
+
const textW = Math.max(8, contentW - GUTTER - 1);
|
|
79600
|
+
const turns = useMemo3(() => conv?.turns ?? NO_TURNS, [conv]);
|
|
79601
|
+
const { rows, turnStart, turnEnd } = useMemo3(() => layoutRows(turns, textW), [turns, textW]);
|
|
79602
|
+
const search = useMemo3(() => buildSearch(turns, rows, turnStart, turnEnd, query), [turns, rows, turnStart, turnEnd, query]);
|
|
79603
|
+
const maxTop = Math.max(0, rows.length - viewport);
|
|
79604
|
+
const clampTop = (v) => Math.max(0, Math.min(v, maxTop));
|
|
79605
|
+
const reveal = (r) => setTop(clampTop(r - Math.floor(viewport / 3)));
|
|
79606
|
+
useEffect5(() => {
|
|
79607
|
+
setTop(Math.max(0, rows.length - viewport));
|
|
79608
|
+
}, [rows.length, viewport]);
|
|
79609
|
+
useEffect5(() => {
|
|
79610
|
+
if (search.hits.length === 0)
|
|
79611
|
+
return;
|
|
79612
|
+
let i = search.hits.findIndex((r) => r >= top);
|
|
79613
|
+
if (i === -1)
|
|
79614
|
+
i = 0;
|
|
79615
|
+
setMatchIdx(i);
|
|
79616
|
+
reveal(search.hits[i]);
|
|
79617
|
+
}, [search]);
|
|
79618
|
+
const step = (d) => {
|
|
79619
|
+
if (search.hits.length === 0)
|
|
79620
|
+
return;
|
|
79621
|
+
const i = (matchIdx + d + search.hits.length) % search.hits.length;
|
|
79622
|
+
setMatchIdx(i);
|
|
79623
|
+
reveal(search.hits[i]);
|
|
79624
|
+
};
|
|
79625
|
+
useKeyboard3((key) => {
|
|
79626
|
+
const name = key.name;
|
|
79627
|
+
if (key.ctrl && name === "c") {
|
|
79628
|
+
onCancel();
|
|
79629
|
+
return;
|
|
79630
|
+
}
|
|
79631
|
+
if (name === "escape") {
|
|
79632
|
+
if (typing || query) {
|
|
79633
|
+
setTyping(false);
|
|
79634
|
+
setQuery("");
|
|
79635
|
+
return;
|
|
79636
|
+
}
|
|
79637
|
+
onClose();
|
|
79638
|
+
return;
|
|
79639
|
+
}
|
|
79640
|
+
if (name === "return" || name === "enter") {
|
|
79641
|
+
if (typing) {
|
|
79642
|
+
setTyping(false);
|
|
79643
|
+
return;
|
|
79644
|
+
}
|
|
79645
|
+
onResume();
|
|
79646
|
+
return;
|
|
79647
|
+
}
|
|
79648
|
+
if (name === "up")
|
|
79649
|
+
return setTop((t) => clampTop(t - 1));
|
|
79650
|
+
if (name === "down")
|
|
79651
|
+
return setTop((t) => clampTop(t + 1));
|
|
79652
|
+
if (name === "pageup")
|
|
79653
|
+
return setTop((t) => clampTop(t - viewport));
|
|
79654
|
+
if (name === "pagedown")
|
|
79655
|
+
return setTop((t) => clampTop(t + viewport));
|
|
79656
|
+
if (name === "home")
|
|
79657
|
+
return setTop(0);
|
|
79658
|
+
if (name === "end")
|
|
79659
|
+
return setTop(maxTop);
|
|
79660
|
+
if (name === "backspace") {
|
|
79661
|
+
if (typing)
|
|
79662
|
+
setQuery((q) => q.slice(0, -1));
|
|
79663
|
+
return;
|
|
79664
|
+
}
|
|
79665
|
+
const ch = key.raw;
|
|
79666
|
+
const printable = ch && ch.length === 1 && ch >= " " && ch !== "\x7F";
|
|
79667
|
+
if (typing) {
|
|
79668
|
+
if (printable)
|
|
79669
|
+
setQuery((q) => q + ch);
|
|
79670
|
+
return;
|
|
79671
|
+
}
|
|
79672
|
+
if (name === "slash" || ch === "/") {
|
|
79673
|
+
setTyping(true);
|
|
79674
|
+
return;
|
|
79675
|
+
}
|
|
79676
|
+
if (name === "n")
|
|
79677
|
+
return step(key.shift ? -1 : 1);
|
|
79678
|
+
if (ch === "g")
|
|
79679
|
+
return setTop(0);
|
|
79680
|
+
if (ch === "G")
|
|
79681
|
+
return setTop(maxTop);
|
|
79682
|
+
if (name === "space")
|
|
79683
|
+
return setTop((t) => clampTop(t + viewport));
|
|
79684
|
+
});
|
|
79685
|
+
const label = sessionLabel(row);
|
|
79686
|
+
const mb = row.sizeBytes / 1048576;
|
|
79687
|
+
const size = mb >= 0.1 ? `${mb.toFixed(1)} MB` : `${Math.max(1, Math.round(row.sizeBytes / 1024))} KB`;
|
|
79688
|
+
const stats = conv ? `${turns.length} turns \xB7 ${rows.length} lines \xB7 ${size} transcript` : `reading ${size}\u2026`;
|
|
79689
|
+
const visible = rows.slice(top, top + viewport);
|
|
79690
|
+
const barCells = useMemo3(() => scrollbarCells(viewport, rows.length, top, search.hits), [viewport, rows.length, top, search.hits]);
|
|
79691
|
+
const pct = rows.length <= viewport ? 100 : top / maxTop * 100;
|
|
79692
|
+
const POSITION_COLS = 26;
|
|
79693
|
+
const matchLabel = search.hits.length ? `${matchIdx + 1} of ${search.hits.length}${search.capped ? "+" : ""} matches` : query ? "no matches" : "";
|
|
79694
|
+
return /* @__PURE__ */ jsxDEV19("box", {
|
|
79695
|
+
flexDirection: "column",
|
|
79696
|
+
height: height2,
|
|
79697
|
+
backgroundColor: C.bg,
|
|
79698
|
+
children: [
|
|
79699
|
+
/* @__PURE__ */ jsxDEV19("box", {
|
|
79700
|
+
flexDirection: "row",
|
|
79701
|
+
justifyContent: "space-between",
|
|
79702
|
+
height: 1,
|
|
79703
|
+
paddingX: 1,
|
|
79704
|
+
children: [
|
|
79705
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79706
|
+
children: [
|
|
79707
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79708
|
+
fg: tokens.accent,
|
|
79709
|
+
attributes: A.bold,
|
|
79710
|
+
children: "claudish"
|
|
79711
|
+
}, undefined, false, undefined, this),
|
|
79712
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79713
|
+
fg: tokens.subtle,
|
|
79714
|
+
children: " reader"
|
|
79715
|
+
}, undefined, false, undefined, this),
|
|
79716
|
+
conv && conv.dropped > 0 ? /* @__PURE__ */ jsxDEV19("span", {
|
|
79717
|
+
fg: tokens.warn,
|
|
79718
|
+
children: ` ${conv.dropped} older turns dropped (cap)`
|
|
79719
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV19("span", {}, undefined, false, undefined, this),
|
|
79720
|
+
conv?.anyElided ? /* @__PURE__ */ jsxDEV19("span", {
|
|
79721
|
+
fg: tokens.warn,
|
|
79722
|
+
children: " long turns truncated"
|
|
79723
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV19("span", {}, undefined, false, undefined, this)
|
|
79724
|
+
]
|
|
79725
|
+
}, undefined, true, undefined, this),
|
|
79726
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79727
|
+
children: /* @__PURE__ */ jsxDEV19("span", {
|
|
79728
|
+
fg: tokens.subtle,
|
|
79729
|
+
children: stats
|
|
79730
|
+
}, undefined, false, undefined, this)
|
|
79731
|
+
}, undefined, false, undefined, this)
|
|
79732
|
+
]
|
|
79733
|
+
}, undefined, true, undefined, this),
|
|
79734
|
+
/* @__PURE__ */ jsxDEV19(Panel, {
|
|
79735
|
+
title: `conversation \xB7 ${truncate3(label, Math.max(10, width - 20))}`,
|
|
79736
|
+
flush: true,
|
|
79737
|
+
flexGrow: 1,
|
|
79738
|
+
flexBasis: 0,
|
|
79739
|
+
children: !conv ? /* @__PURE__ */ jsxDEV19("text", {
|
|
79740
|
+
fg: tokens.subtle,
|
|
79741
|
+
children: " reading transcript\u2026"
|
|
79742
|
+
}, undefined, false, undefined, this) : rows.length === 0 ? /* @__PURE__ */ jsxDEV19("text", {
|
|
79743
|
+
fg: tokens.trace,
|
|
79744
|
+
children: " no conversation recorded \u2014 this transcript has no main-thread prose"
|
|
79745
|
+
}, undefined, false, undefined, this) : visible.map((r, i) => {
|
|
79746
|
+
const line = top + i;
|
|
79747
|
+
return /* @__PURE__ */ jsxDEV19(ReaderRow, {
|
|
79748
|
+
row: r,
|
|
79749
|
+
turn: r.turn >= 0 ? turns[r.turn] : undefined,
|
|
79750
|
+
hl: search.ranges.get(line),
|
|
79751
|
+
current: matchIdx,
|
|
79752
|
+
width: contentW,
|
|
79753
|
+
bar: barCells[i] ?? "track"
|
|
79754
|
+
}, line, false, undefined, this);
|
|
79755
|
+
})
|
|
79756
|
+
}, undefined, false, undefined, this),
|
|
79757
|
+
/* @__PURE__ */ jsxDEV19("box", {
|
|
79758
|
+
flexDirection: "row",
|
|
79759
|
+
justifyContent: "space-between",
|
|
79760
|
+
height: 1,
|
|
79761
|
+
paddingX: 1,
|
|
79762
|
+
children: [
|
|
79763
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79764
|
+
children: query || typing ? /* @__PURE__ */ jsxDEV19(Fragment12, {
|
|
79765
|
+
children: [
|
|
79766
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79767
|
+
fg: tokens.warn,
|
|
79768
|
+
attributes: A.bold,
|
|
79769
|
+
children: truncate3(`/${query}`, Math.max(4, width - POSITION_COLS - displayWidth(matchLabel) - 4))
|
|
79770
|
+
}, undefined, false, undefined, this),
|
|
79771
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79772
|
+
fg: typing ? tokens.warn : tokens.trace,
|
|
79773
|
+
children: typing ? "\u258C" : " "
|
|
79774
|
+
}, undefined, false, undefined, this),
|
|
79775
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79776
|
+
fg: search.hits.length ? tokens.subtle : tokens.trace,
|
|
79777
|
+
children: matchLabel ? ` ${matchLabel}` : ""
|
|
79778
|
+
}, undefined, false, undefined, this)
|
|
79779
|
+
]
|
|
79780
|
+
}, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV19("span", {
|
|
79781
|
+
fg: tokens.trace,
|
|
79782
|
+
children: "/ to search"
|
|
79783
|
+
}, undefined, false, undefined, this)
|
|
79784
|
+
}, undefined, false, undefined, this),
|
|
79785
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79786
|
+
children: [
|
|
79787
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79788
|
+
fg: tokens.subtle,
|
|
79789
|
+
children: `${Math.min(rows.length, top + viewport)}/${rows.length} `
|
|
79790
|
+
}, undefined, false, undefined, this),
|
|
79791
|
+
/* @__PURE__ */ jsxDEV19(MeterSpan, {
|
|
79792
|
+
pct,
|
|
79793
|
+
width: 12,
|
|
79794
|
+
ramp: ramps.volume
|
|
79795
|
+
}, undefined, false, undefined, this)
|
|
79796
|
+
]
|
|
79797
|
+
}, undefined, true, undefined, this)
|
|
79798
|
+
]
|
|
79799
|
+
}, undefined, true, undefined, this),
|
|
79800
|
+
/* @__PURE__ */ jsxDEV19("box", {
|
|
79801
|
+
flexDirection: "row",
|
|
79802
|
+
height: 1,
|
|
79803
|
+
paddingX: 1,
|
|
79804
|
+
gap: 2,
|
|
79805
|
+
children: [
|
|
79806
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79807
|
+
fg: tokens.subtle,
|
|
79808
|
+
children: "\u2191\u2193 \u21DE\u21DF scroll"
|
|
79809
|
+
}, undefined, false, undefined, this),
|
|
79810
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79811
|
+
fg: tokens.subtle,
|
|
79812
|
+
children: "g/G ends"
|
|
79813
|
+
}, undefined, false, undefined, this),
|
|
79814
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79815
|
+
fg: typing ? tokens.warn : tokens.subtle,
|
|
79816
|
+
children: "/ search"
|
|
79817
|
+
}, undefined, false, undefined, this),
|
|
79818
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79819
|
+
fg: search.hits.length ? tokens.subtle : tokens.trace,
|
|
79820
|
+
children: "n/N match"
|
|
79821
|
+
}, undefined, false, undefined, this),
|
|
79822
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79823
|
+
fg: tokens.accent,
|
|
79824
|
+
children: "\u23CE resume"
|
|
79825
|
+
}, undefined, false, undefined, this),
|
|
79826
|
+
/* @__PURE__ */ jsxDEV19("text", {
|
|
79827
|
+
fg: tokens.subtle,
|
|
79828
|
+
children: "esc back"
|
|
79829
|
+
}, undefined, false, undefined, this)
|
|
79830
|
+
]
|
|
79831
|
+
}, undefined, true, undefined, this)
|
|
79832
|
+
]
|
|
79833
|
+
}, undefined, true, undefined, this);
|
|
79834
|
+
}
|
|
79835
|
+
function scrollbarCells(viewport, total, top, hits) {
|
|
79836
|
+
const cells = new Array(viewport).fill("track");
|
|
79837
|
+
if (total <= 0)
|
|
79838
|
+
return cells;
|
|
79839
|
+
if (total > viewport) {
|
|
79840
|
+
const size = Math.max(1, Math.round(viewport / total * viewport));
|
|
79841
|
+
const span = Math.max(1, total - viewport);
|
|
79842
|
+
const start = Math.min(viewport - size, Math.round(top / span * (viewport - size)));
|
|
79843
|
+
for (let i = 0;i < size; i++)
|
|
79844
|
+
cells[start + i] = "thumb";
|
|
79845
|
+
} else {
|
|
79846
|
+
cells.fill("thumb");
|
|
79847
|
+
}
|
|
79848
|
+
for (const h of hits) {
|
|
79849
|
+
const i = Math.min(viewport - 1, Math.floor(h / total * viewport));
|
|
79850
|
+
if (i >= 0)
|
|
79851
|
+
cells[i] = "hit";
|
|
79852
|
+
}
|
|
79853
|
+
return cells;
|
|
79854
|
+
}
|
|
79855
|
+
function ReaderRow({
|
|
79856
|
+
row,
|
|
79857
|
+
turn,
|
|
79858
|
+
hl,
|
|
79859
|
+
current,
|
|
79860
|
+
width,
|
|
79861
|
+
bar
|
|
79862
|
+
}) {
|
|
79863
|
+
if (!turn) {
|
|
79864
|
+
return /* @__PURE__ */ jsxDEV19("text", {
|
|
79865
|
+
children: [
|
|
79866
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79867
|
+
children: " ".repeat(Math.max(0, width))
|
|
79868
|
+
}, undefined, false, undefined, this),
|
|
79869
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79870
|
+
bg: BAR_COLOR[bar],
|
|
79871
|
+
children: " "
|
|
79872
|
+
}, undefined, false, undefined, this)
|
|
79873
|
+
]
|
|
79874
|
+
}, undefined, true, undefined, this);
|
|
79875
|
+
}
|
|
79876
|
+
const text = turn.text.slice(row.start, row.end);
|
|
79877
|
+
const fg = turn.role === "user" ? tokens.text : C.fgMuted;
|
|
79878
|
+
const railFg = turn.role === "user" ? SPEAKER.you : SPEAKER.ai;
|
|
79879
|
+
const pad2 = Math.max(0, width - GUTTER - displayWidth(text));
|
|
79880
|
+
return /* @__PURE__ */ jsxDEV19("text", {
|
|
79881
|
+
children: [
|
|
79882
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79883
|
+
fg: railFg,
|
|
79884
|
+
children: RAIL
|
|
79885
|
+
}, undefined, false, undefined, this),
|
|
79886
|
+
row.first ? /* @__PURE__ */ jsxDEV19(BadgeSpan, {
|
|
79887
|
+
label: turn.role === "user" ? "you" : "ai",
|
|
79888
|
+
bg: turn.role === "user" ? SPEAKER.you : SPEAKER.ai,
|
|
79889
|
+
width: ROLE_W
|
|
79890
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV19("span", {
|
|
79891
|
+
children: " ".repeat(ROLE_W)
|
|
79892
|
+
}, undefined, false, undefined, this),
|
|
79893
|
+
hl && hl.length > 0 ? highlighted(text, hl, current, fg) : /* @__PURE__ */ jsxDEV19("span", {
|
|
79894
|
+
fg,
|
|
79895
|
+
children: text
|
|
79896
|
+
}, undefined, false, undefined, this),
|
|
79897
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79898
|
+
children: " ".repeat(pad2)
|
|
79899
|
+
}, undefined, false, undefined, this),
|
|
79900
|
+
/* @__PURE__ */ jsxDEV19("span", {
|
|
79901
|
+
bg: BAR_COLOR[bar],
|
|
79902
|
+
children: " "
|
|
79903
|
+
}, undefined, false, undefined, this)
|
|
79904
|
+
]
|
|
79905
|
+
}, undefined, true, undefined, this);
|
|
79906
|
+
}
|
|
79907
|
+
function highlighted(text, hl, current, fg) {
|
|
79908
|
+
const out = [];
|
|
79909
|
+
let from = 0;
|
|
79910
|
+
for (let i = 0;i < hl.length; i++) {
|
|
79911
|
+
const { s, e, hit } = hl[i];
|
|
79912
|
+
if (e <= from)
|
|
79913
|
+
continue;
|
|
79914
|
+
const start = Math.max(from, s);
|
|
79915
|
+
if (start > from)
|
|
79916
|
+
out.push(/* @__PURE__ */ jsxDEV19("span", {
|
|
79917
|
+
fg,
|
|
79918
|
+
children: text.slice(from, start)
|
|
79919
|
+
}, `p${i}`, false, undefined, this));
|
|
79920
|
+
const bg = hit === current ? tokens.accent : tokens.warn;
|
|
79921
|
+
out.push(/* @__PURE__ */ jsxDEV19("span", {
|
|
79922
|
+
fg: tokens.ink,
|
|
79923
|
+
bg,
|
|
79924
|
+
attributes: A.bold,
|
|
79925
|
+
children: text.slice(start, e)
|
|
79926
|
+
}, `h${i}`, false, undefined, this));
|
|
79927
|
+
from = e;
|
|
79928
|
+
}
|
|
79929
|
+
if (from < text.length)
|
|
79930
|
+
out.push(/* @__PURE__ */ jsxDEV19("span", {
|
|
79931
|
+
fg,
|
|
79932
|
+
children: text.slice(from)
|
|
79933
|
+
}, "tail", false, undefined, this));
|
|
79934
|
+
return /* @__PURE__ */ jsxDEV19(Fragment12, {
|
|
79935
|
+
children: out
|
|
79936
|
+
}, undefined, false, undefined, this);
|
|
79937
|
+
}
|
|
79938
|
+
var RAIL = "\u258D", RAIL_W = 2, ROLE_W = 6, GUTTER, BAR_W = 1, SPEAKER, MAX_MATCHES = 5000, EMPTY_SEARCH, NO_TURNS, BAR_COLOR;
|
|
79939
|
+
var init_conversation_reader = __esm(() => {
|
|
79940
|
+
init_theme2();
|
|
79941
|
+
init_text();
|
|
79942
|
+
init_tokens();
|
|
79943
|
+
init_widgets();
|
|
79944
|
+
init_conversation();
|
|
79945
|
+
init_session_discovery();
|
|
79946
|
+
GUTTER = RAIL_W + ROLE_W;
|
|
79947
|
+
SPEAKER = {
|
|
79948
|
+
you: "#39d353",
|
|
79949
|
+
ai: "#39c5cf"
|
|
79950
|
+
};
|
|
79951
|
+
EMPTY_SEARCH = { hits: [], ranges: new Map, capped: false };
|
|
79952
|
+
NO_TURNS = [];
|
|
79953
|
+
BAR_COLOR = {
|
|
79954
|
+
track: tokens.border,
|
|
79955
|
+
thumb: tokens.accent,
|
|
79956
|
+
hit: tokens.warn
|
|
79957
|
+
};
|
|
79958
|
+
});
|
|
79959
|
+
|
|
79960
|
+
// src/session/resume-picker.tsx
|
|
79961
|
+
import { useKeyboard as useKeyboard4, useTerminalDimensions as useTerminalDimensions3 } from "@opentui/react";
|
|
79962
|
+
import { useEffect as useEffect6, useMemo as useMemo4, useState as useState7 } from "react";
|
|
79963
|
+
import { jsxDEV as jsxDEV20 } from "@opentui/react/jsx-dev-runtime";
|
|
79964
|
+
function detailTurns(height2) {
|
|
79965
|
+
return height2 >= 40 ? 6 : height2 >= 30 ? 4 : 2;
|
|
79966
|
+
}
|
|
79967
|
+
function fuzzy(needle, hay) {
|
|
79968
|
+
if (!needle)
|
|
79969
|
+
return true;
|
|
79970
|
+
const n = needle.toLowerCase();
|
|
79971
|
+
const h = hay.toLowerCase();
|
|
79972
|
+
let i = 0;
|
|
79973
|
+
for (let j = 0;j < h.length && i < n.length; j++)
|
|
79974
|
+
if (h[j] === n[i])
|
|
79975
|
+
i++;
|
|
79976
|
+
return i === n.length;
|
|
79977
|
+
}
|
|
79978
|
+
function age(ms) {
|
|
79979
|
+
const s = Math.max(0, Math.round((Date.now() - ms) / 1000));
|
|
79980
|
+
if (s < 60)
|
|
79981
|
+
return `${s}s`;
|
|
79982
|
+
const m = Math.floor(s / 60);
|
|
79983
|
+
if (m < 60)
|
|
79984
|
+
return `${m}m`;
|
|
79985
|
+
const h = Math.floor(m / 60);
|
|
79986
|
+
if (h < 24)
|
|
79987
|
+
return `${h}h`;
|
|
79988
|
+
return `${Math.floor(h / 24)}d`;
|
|
79989
|
+
}
|
|
79990
|
+
function chipW(label) {
|
|
79991
|
+
return label + 2;
|
|
79992
|
+
}
|
|
79993
|
+
function blockWidth(c) {
|
|
79994
|
+
return LIVE_W + chipW(c.age) + chipW(c.count) + (c.dirty > 0 ? chipW(c.dirty + 1) : 0) + (c.sync > 0 ? 2 * chipW(c.sync + 1) : 0);
|
|
79995
|
+
}
|
|
79996
|
+
function sizePct(bytes2, max) {
|
|
79997
|
+
const lo = Math.log(SIZE_FLOOR_BYTES);
|
|
79998
|
+
const hi = Math.log(Math.max(max, SIZE_FLOOR_BYTES * 2));
|
|
79999
|
+
const v = Math.log(Math.max(bytes2, SIZE_FLOOR_BYTES));
|
|
80000
|
+
return Math.max(0, Math.min(100, (v - lo) / (hi - lo) * 100));
|
|
80001
|
+
}
|
|
80002
|
+
function Slot({
|
|
80003
|
+
label,
|
|
80004
|
+
bg,
|
|
80005
|
+
labelW
|
|
80006
|
+
}) {
|
|
80007
|
+
if (!label || !bg)
|
|
80008
|
+
return /* @__PURE__ */ jsxDEV20("span", {
|
|
80009
|
+
children: " ".repeat(chipW(labelW))
|
|
80010
|
+
}, undefined, false, undefined, this);
|
|
80011
|
+
return /* @__PURE__ */ jsxDEV20(BadgeSpan, {
|
|
80012
|
+
label,
|
|
80013
|
+
bg
|
|
80014
|
+
}, undefined, false, undefined, this);
|
|
80015
|
+
}
|
|
80016
|
+
function WorktreeRow({
|
|
80017
|
+
g,
|
|
80018
|
+
cursor,
|
|
80019
|
+
width,
|
|
80020
|
+
count,
|
|
80021
|
+
cols
|
|
80022
|
+
}) {
|
|
80023
|
+
const nameColor = !g.live ? tokens.dead : g.current ? tokens.success : tokens.text;
|
|
80024
|
+
const stale = !g.lastActiveMs || Date.now() - g.lastActiveMs >= STALE_MS;
|
|
80025
|
+
const room = Math.max(0, width - blockWidth(cols));
|
|
80026
|
+
const series = room >= SPARK_MIN_DAYS ? activitySeries(g.sessions, room) : null;
|
|
80027
|
+
const spark = series && hasActivity(series) ? series : null;
|
|
80028
|
+
const chips = [];
|
|
80029
|
+
if (cols.sync > 0 && g.ahead) {
|
|
80030
|
+
chips.push({
|
|
80031
|
+
label: `\u2191${padStartTo(String(g.ahead), cols.sync)}`,
|
|
80032
|
+
bg: CHIP.ahead,
|
|
80033
|
+
labelW: cols.sync + 1
|
|
80034
|
+
});
|
|
80035
|
+
}
|
|
80036
|
+
if (cols.sync > 0 && g.behind) {
|
|
80037
|
+
chips.push({
|
|
80038
|
+
label: `\u2193${padStartTo(String(g.behind), cols.sync)}`,
|
|
80039
|
+
bg: CHIP.behind,
|
|
80040
|
+
labelW: cols.sync + 1
|
|
80041
|
+
});
|
|
80042
|
+
}
|
|
80043
|
+
if (cols.dirty > 0 && g.dirty) {
|
|
80044
|
+
chips.push({
|
|
80045
|
+
label: `${DIRTY_GLYPH}${padStartTo(String(g.dirty), cols.dirty)}`,
|
|
80046
|
+
bg: CHIP.dirty,
|
|
80047
|
+
labelW: cols.dirty + 1
|
|
80048
|
+
});
|
|
80049
|
+
}
|
|
80050
|
+
chips.push({ label: padStartTo(String(count), cols.count), bg: CHIP.count, labelW: cols.count });
|
|
80051
|
+
chips.push({
|
|
80052
|
+
label: padStartTo(g.lastActiveMs ? age(g.lastActiveMs) : "\u2014", cols.age),
|
|
80053
|
+
bg: stale ? CHIP.stale : CHIP.fresh,
|
|
80054
|
+
labelW: cols.age
|
|
80055
|
+
});
|
|
80056
|
+
const used = chips.reduce((w, c) => w + c.labelW + 2, 0) + LIVE_W;
|
|
80057
|
+
const gap = Math.max(0, width - room - used);
|
|
80058
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80059
|
+
flexDirection: "column",
|
|
80060
|
+
height: 2,
|
|
80061
|
+
backgroundColor: cursor ? C.bgHighlight : undefined,
|
|
80062
|
+
children: [
|
|
80063
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80064
|
+
children: /* @__PURE__ */ jsxDEV20("span", {
|
|
80065
|
+
fg: nameColor,
|
|
80066
|
+
attributes: cursor || g.current ? A.bold : undefined,
|
|
80067
|
+
children: truncate3(g.name, width)
|
|
80068
|
+
}, undefined, false, undefined, this)
|
|
80069
|
+
}, undefined, false, undefined, this),
|
|
80070
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80071
|
+
height: 1,
|
|
80072
|
+
children: /* @__PURE__ */ jsxDEV20("text", {
|
|
80073
|
+
children: [
|
|
80074
|
+
spark ? /* @__PURE__ */ jsxDEV20(SparklineSpan, {
|
|
80075
|
+
values: spark,
|
|
80076
|
+
fg: SPARK_FG
|
|
80077
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV20("span", {
|
|
80078
|
+
children: " ".repeat(room)
|
|
80079
|
+
}, undefined, false, undefined, this),
|
|
80080
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80081
|
+
children: " ".repeat(gap)
|
|
80082
|
+
}, undefined, false, undefined, this),
|
|
80083
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80084
|
+
fg: tokens.success,
|
|
80085
|
+
children: g.activeNow ? "\u25CF " : " "
|
|
80086
|
+
}, undefined, false, undefined, this),
|
|
80087
|
+
chips.map((c) => /* @__PURE__ */ jsxDEV20(Slot, {
|
|
80088
|
+
label: c.label,
|
|
80089
|
+
bg: c.bg,
|
|
80090
|
+
labelW: c.labelW
|
|
80091
|
+
}, c.bg + c.label, false, undefined, this))
|
|
80092
|
+
]
|
|
80093
|
+
}, undefined, true, undefined, this)
|
|
80094
|
+
}, undefined, false, undefined, this)
|
|
80095
|
+
]
|
|
80096
|
+
}, undefined, true, undefined, this);
|
|
80097
|
+
}
|
|
80098
|
+
function dailyActivity(groups, days) {
|
|
80099
|
+
const day = 86400000;
|
|
80100
|
+
const today = Math.floor(Date.now() / day);
|
|
80101
|
+
const buckets = new Array(days).fill(0);
|
|
80102
|
+
for (const g of groups) {
|
|
80103
|
+
for (const s of g.sessions) {
|
|
80104
|
+
const idx = days - 1 - (today - Math.floor(s.mtimeMs / day));
|
|
80105
|
+
if (idx >= 0 && idx < days)
|
|
80106
|
+
buckets[idx] += 1;
|
|
80107
|
+
}
|
|
80108
|
+
}
|
|
80109
|
+
return buckets;
|
|
80110
|
+
}
|
|
80111
|
+
function activityLevels(days) {
|
|
80112
|
+
const nz = days.filter((v) => v > 0).sort((a, b) => a - b);
|
|
80113
|
+
if (nz.length === 0)
|
|
80114
|
+
return days.map(() => 0);
|
|
80115
|
+
const at = (p) => nz[Math.min(nz.length - 1, Math.floor(nz.length * p))];
|
|
80116
|
+
const q1 = at(0.25);
|
|
80117
|
+
const q2 = at(0.5);
|
|
80118
|
+
const q3 = at(0.75);
|
|
80119
|
+
return days.map((v) => v <= 0 ? 0 : v <= q1 ? 1 : v <= q2 ? 2 : v <= q3 ? 3 : 4);
|
|
80120
|
+
}
|
|
80121
|
+
function ActivityCalendar({
|
|
80122
|
+
days,
|
|
80123
|
+
width
|
|
80124
|
+
}) {
|
|
80125
|
+
const levels = activityLevels(days);
|
|
80126
|
+
const title = `activity \xB7 ${ACTIVITY_WEEKS}w `;
|
|
80127
|
+
const grid = Math.max(WEEK_DAYS, width - WEEK_LABEL_W);
|
|
80128
|
+
const base = Math.floor(grid / WEEK_DAYS);
|
|
80129
|
+
const extra = grid - base * WEEK_DAYS;
|
|
80130
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80131
|
+
flexDirection: "column",
|
|
80132
|
+
flexShrink: 0,
|
|
80133
|
+
paddingTop: 1,
|
|
80134
|
+
children: [
|
|
80135
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80136
|
+
children: [
|
|
80137
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80138
|
+
fg: tokens.subtle,
|
|
80139
|
+
attributes: A.bold,
|
|
80140
|
+
children: title
|
|
80141
|
+
}, undefined, false, undefined, this),
|
|
80142
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80143
|
+
fg: tokens.border,
|
|
80144
|
+
children: "\u2500".repeat(Math.max(0, width - title.length))
|
|
80145
|
+
}, undefined, false, undefined, this)
|
|
80146
|
+
]
|
|
80147
|
+
}, undefined, true, undefined, this),
|
|
80148
|
+
Array.from({ length: ACTIVITY_WEEKS }, (_, w) => {
|
|
80149
|
+
const ago = ACTIVITY_WEEKS - 1 - w;
|
|
80150
|
+
return /* @__PURE__ */ jsxDEV20("text", {
|
|
80151
|
+
children: [
|
|
80152
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80153
|
+
fg: tokens.trace,
|
|
80154
|
+
children: padTo(ago === 0 ? "now" : `-${ago}w`, WEEK_LABEL_W)
|
|
80155
|
+
}, undefined, false, undefined, this),
|
|
80156
|
+
Array.from({ length: WEEK_DAYS }, (_2, d) => /* @__PURE__ */ jsxDEV20("span", {
|
|
80157
|
+
bg: GH_LEVELS[levels[w * WEEK_DAYS + d] ?? 0],
|
|
80158
|
+
children: " ".repeat(base + (d < extra ? 1 : 0))
|
|
80159
|
+
}, d, false, undefined, this))
|
|
80160
|
+
]
|
|
80161
|
+
}, w, true, undefined, this);
|
|
80162
|
+
})
|
|
80163
|
+
]
|
|
80164
|
+
}, undefined, true, undefined, this);
|
|
80165
|
+
}
|
|
80166
|
+
function SectionHeader({ label, width }) {
|
|
80167
|
+
const text = `${label} `;
|
|
80168
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80169
|
+
flexDirection: "column",
|
|
80170
|
+
height: 2,
|
|
80171
|
+
children: [
|
|
80172
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80173
|
+
children: " "
|
|
80174
|
+
}, undefined, false, undefined, this),
|
|
80175
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80176
|
+
children: [
|
|
80177
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80178
|
+
fg: tokens.subtle,
|
|
80179
|
+
attributes: A.bold,
|
|
80180
|
+
children: text
|
|
80181
|
+
}, undefined, false, undefined, this),
|
|
80182
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80183
|
+
fg: tokens.border,
|
|
80184
|
+
children: "\u2500".repeat(Math.max(0, width - text.length))
|
|
80185
|
+
}, undefined, false, undefined, this)
|
|
80186
|
+
]
|
|
80187
|
+
}, undefined, true, undefined, this)
|
|
80188
|
+
]
|
|
80189
|
+
}, undefined, true, undefined, this);
|
|
80190
|
+
}
|
|
80191
|
+
function hasActivity(series) {
|
|
80192
|
+
return series.some((v) => v > 0);
|
|
80193
|
+
}
|
|
80194
|
+
function activitySeries(sessions2, days = WEEK_DAYS * 2) {
|
|
80195
|
+
const day = 86400000;
|
|
80196
|
+
const today = Math.floor(Date.now() / day);
|
|
80197
|
+
const buckets = new Array(days).fill(0);
|
|
80198
|
+
for (const s of sessions2) {
|
|
80199
|
+
const idx = days - 1 - (today - Math.floor(s.mtimeMs / day));
|
|
80200
|
+
if (idx >= 0 && idx < days)
|
|
80201
|
+
buckets[idx] += 1;
|
|
80202
|
+
}
|
|
80203
|
+
return buckets;
|
|
80204
|
+
}
|
|
80205
|
+
function SessionRowView({
|
|
80206
|
+
row,
|
|
80207
|
+
cursor,
|
|
80208
|
+
width,
|
|
80209
|
+
even,
|
|
80210
|
+
indent = 0,
|
|
80211
|
+
maxSize,
|
|
80212
|
+
meterW
|
|
80213
|
+
}) {
|
|
80214
|
+
const live = isActive(row);
|
|
80215
|
+
const pad2 = " ".repeat(indent);
|
|
80216
|
+
const mb = row.sizeBytes / 1048576;
|
|
80217
|
+
const size = mb >= 0.1 ? `${mb.toFixed(1)} MB` : `${Math.max(1, Math.round(row.sizeBytes / 1024))} KB`;
|
|
80218
|
+
const titleW = Math.max(10, width - indent - 2);
|
|
80219
|
+
const metaPad = `${pad2} `;
|
|
80220
|
+
const SIZE_COL = 8;
|
|
80221
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80222
|
+
flexDirection: "column",
|
|
80223
|
+
height: 2,
|
|
80224
|
+
backgroundColor: cursor ? C.bgHighlight : even ? undefined : C.bgAlt,
|
|
80225
|
+
children: [
|
|
80226
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80227
|
+
children: [
|
|
80228
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80229
|
+
fg: live ? tokens.success : tokens.border,
|
|
80230
|
+
children: `${pad2}${live ? "\u25CF" : "\xB7"} `
|
|
80231
|
+
}, undefined, false, undefined, this),
|
|
80232
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80233
|
+
fg: tokens.text,
|
|
80234
|
+
attributes: cursor ? A.bold : undefined,
|
|
80235
|
+
children: truncate3(sessionLabel(row), titleW)
|
|
80236
|
+
}, undefined, false, undefined, this)
|
|
80237
|
+
]
|
|
80238
|
+
}, undefined, true, undefined, this),
|
|
80239
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80240
|
+
children: [
|
|
80241
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80242
|
+
children: metaPad
|
|
80243
|
+
}, undefined, false, undefined, this),
|
|
80244
|
+
/* @__PURE__ */ jsxDEV20(BadgeSpan, {
|
|
80245
|
+
label: padStartTo(age(row.mtimeMs), SESSION_AGE_W),
|
|
80246
|
+
bg: Date.now() - row.mtimeMs < STALE_MS ? CHIP.fresh : CHIP.stale,
|
|
80247
|
+
width: SESSION_AGE_COL
|
|
80248
|
+
}, undefined, false, undefined, this),
|
|
80249
|
+
/* @__PURE__ */ jsxDEV20(MeterSpan, {
|
|
80250
|
+
pct: sizePct(row.sizeBytes, maxSize),
|
|
80251
|
+
width: meterW,
|
|
80252
|
+
ramp: ramps.volume
|
|
80253
|
+
}, undefined, false, undefined, this),
|
|
80254
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80255
|
+
fg: MUTED,
|
|
80256
|
+
children: padStartTo(size, SIZE_COL)
|
|
80257
|
+
}, undefined, false, undefined, this),
|
|
80258
|
+
row.gitBranch ? /* @__PURE__ */ jsxDEV20("span", {
|
|
80259
|
+
fg: tokens.trace,
|
|
80260
|
+
children: ` ${BRANCH_ICON} ${truncate3(row.gitBranch, Math.max(6, width - indent - 2 - SESSION_AGE_COL - meterW - SIZE_COL - BRANCH_LEAD_DENSE))}`
|
|
80261
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV20("span", {}, undefined, false, undefined, this)
|
|
80262
|
+
]
|
|
80263
|
+
}, undefined, true, undefined, this)
|
|
80264
|
+
]
|
|
80265
|
+
}, undefined, true, undefined, this);
|
|
80266
|
+
}
|
|
80267
|
+
function AgentNode({
|
|
80268
|
+
count,
|
|
80269
|
+
open,
|
|
80270
|
+
cursor,
|
|
80271
|
+
width
|
|
80272
|
+
}) {
|
|
80273
|
+
const label = `${count} agent session${count === 1 ? "" : "s"}`;
|
|
80274
|
+
const hint = open ? "enter to collapse" : "enter to expand \xB7 showing 3";
|
|
80275
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80276
|
+
height: 1,
|
|
80277
|
+
backgroundColor: cursor ? C.bgHighlight : undefined,
|
|
80278
|
+
children: /* @__PURE__ */ jsxDEV20("text", {
|
|
80279
|
+
children: [
|
|
80280
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80281
|
+
fg: cursor ? tokens.accent : tokens.trace,
|
|
80282
|
+
children: `${cursor ? "\u258D" : " "} `
|
|
80283
|
+
}, undefined, false, undefined, this),
|
|
80284
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80285
|
+
fg: tokens.warn,
|
|
80286
|
+
children: open ? "\u25BE " : "\u25B8 "
|
|
80287
|
+
}, undefined, false, undefined, this),
|
|
80288
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80289
|
+
fg: cursor ? tokens.text : tokens.subtle,
|
|
80290
|
+
children: label
|
|
80291
|
+
}, undefined, false, undefined, this),
|
|
80292
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80293
|
+
fg: tokens.trace,
|
|
80294
|
+
children: truncate3(` ${hint}`, Math.max(0, width - label.length - 6))
|
|
80295
|
+
}, undefined, false, undefined, this)
|
|
80296
|
+
]
|
|
80297
|
+
}, undefined, true, undefined, this)
|
|
80298
|
+
}, undefined, false, undefined, this);
|
|
80299
|
+
}
|
|
80300
|
+
function ResumePicker({ groups, onDone }) {
|
|
80301
|
+
const { width, height: height2 } = useTerminalDimensions3();
|
|
80302
|
+
const [pane, setPane] = useState7("worktrees");
|
|
80303
|
+
const [wtCursor, setWtCursor] = useState7(0);
|
|
80304
|
+
const [sessCursor, setSessCursor] = useState7(0);
|
|
80305
|
+
const [filter, setFilter] = useState7("");
|
|
80306
|
+
const [agentsOpen, setAgentsOpen] = useState7(false);
|
|
80307
|
+
const [reader, setReader] = useState7(null);
|
|
80308
|
+
const [, setTick] = useState7(0);
|
|
80309
|
+
const listed = useMemo4(() => {
|
|
80310
|
+
const m = new Map;
|
|
80311
|
+
for (const g of groups)
|
|
80312
|
+
m.set(g.name, g.sessions.filter((s) => !isAgentSession(s)));
|
|
80313
|
+
return m;
|
|
80314
|
+
}, [groups]);
|
|
80315
|
+
const { fresh, stale, visibleGroups } = useMemo4(() => {
|
|
80316
|
+
const withSessions = groups.filter((g) => (listed.get(g.name)?.length ?? 0) > 0);
|
|
80317
|
+
const matching = filter ? withSessions.filter((g) => fuzzy(filter, g.name)) : withSessions;
|
|
80318
|
+
const now = Date.now();
|
|
80319
|
+
const byRecency = (a, b) => b.lastActiveMs - a.lastActiveMs;
|
|
80320
|
+
const f = matching.filter((g) => g.current || now - g.lastActiveMs < STALE_MS).sort((a, b) => a.current !== b.current ? a.current ? -1 : 1 : byRecency(a, b));
|
|
80321
|
+
const st = matching.filter((g) => !g.current && now - g.lastActiveMs >= STALE_MS).sort(byRecency);
|
|
80322
|
+
return { fresh: f, stale: st, visibleGroups: [...f, ...st] };
|
|
80323
|
+
}, [groups, filter, listed]);
|
|
80324
|
+
const group = visibleGroups[Math.min(wtCursor, visibleGroups.length - 1)];
|
|
80325
|
+
const sessions2 = useMemo4(() => {
|
|
80326
|
+
if (!group)
|
|
80327
|
+
return [];
|
|
80328
|
+
const base = listed.get(group.name) ?? [];
|
|
80329
|
+
if (!filter)
|
|
80330
|
+
return base;
|
|
80331
|
+
if (fuzzy(filter, group.name))
|
|
80332
|
+
return base;
|
|
80333
|
+
return base.filter((s) => fuzzy(filter, sessionLabel(s)));
|
|
80334
|
+
}, [group, filter, listed]);
|
|
80335
|
+
const agentRows = useMemo4(() => group ? group.sessions.filter(isAgentSession) : [], [group]);
|
|
80336
|
+
const items = useMemo4(() => {
|
|
80337
|
+
const out = sessions2.map((row) => ({ kind: "session", row }));
|
|
80338
|
+
if (agentRows.length > 0 && !filter) {
|
|
80339
|
+
out.push({ kind: "agents", count: agentRows.length });
|
|
80340
|
+
for (const row of agentsOpen ? agentRows : agentRows.slice(0, 3)) {
|
|
80341
|
+
out.push({ kind: "agent", row });
|
|
80342
|
+
}
|
|
80343
|
+
}
|
|
80344
|
+
return out;
|
|
80345
|
+
}, [sessions2, agentRows, agentsOpen, filter]);
|
|
80346
|
+
const cursorItem = items[Math.min(sessCursor, items.length - 1)];
|
|
80347
|
+
const selected = cursorItem && cursorItem.kind !== "agents" ? cursorItem.row : undefined;
|
|
80348
|
+
const turns = detailTurns(height2);
|
|
80349
|
+
const sessionDetailH = DETAIL_CHROME + turns;
|
|
80350
|
+
const listRows = Math.max(3, height2 - sessionDetailH - WORKTREE_DETAIL_H - 4);
|
|
80351
|
+
useEffect6(() => {
|
|
80352
|
+
const start = Math.max(0, Math.min(sessCursor - 2, items.length - listRows));
|
|
80353
|
+
for (const it of items.slice(start, start + listRows + 2)) {
|
|
80354
|
+
if (it.kind !== "agents")
|
|
80355
|
+
hydrateSession(it.row);
|
|
80356
|
+
}
|
|
80357
|
+
if (selected) {
|
|
80358
|
+
hydrateSession(selected);
|
|
80359
|
+
hydrateConversation(selected);
|
|
80360
|
+
}
|
|
80361
|
+
setTick((t) => t + 1);
|
|
80362
|
+
}, [items, sessCursor, listRows, selected]);
|
|
80363
|
+
useEffect6(() => {
|
|
80364
|
+
if (!reader || reader.conv)
|
|
80365
|
+
return;
|
|
80366
|
+
const file2 = reader.row.file;
|
|
80367
|
+
const timer = setTimeout(() => {
|
|
80368
|
+
const conv = readConversation(file2);
|
|
80369
|
+
setReader((r) => r && r.row.file === file2 && !r.conv ? { row: r.row, conv } : r);
|
|
80370
|
+
}, 0);
|
|
80371
|
+
return () => clearTimeout(timer);
|
|
80372
|
+
}, [reader]);
|
|
80373
|
+
const clamp = (v, len) => Math.max(0, Math.min(v, len - 1));
|
|
80374
|
+
useKeyboard4((key) => {
|
|
80375
|
+
if (reader)
|
|
80376
|
+
return;
|
|
80377
|
+
const name = key.name;
|
|
80378
|
+
if (name === "escape") {
|
|
80379
|
+
if (filter) {
|
|
80380
|
+
setFilter("");
|
|
80381
|
+
return;
|
|
80382
|
+
}
|
|
80383
|
+
onDone(null);
|
|
80384
|
+
return;
|
|
80385
|
+
}
|
|
80386
|
+
if (key.ctrl && name === "c") {
|
|
80387
|
+
onDone(null);
|
|
80388
|
+
return;
|
|
80389
|
+
}
|
|
80390
|
+
if (name === "return" || name === "enter") {
|
|
80391
|
+
if (pane === "worktrees") {
|
|
80392
|
+
setPane("sessions");
|
|
80393
|
+
setSessCursor(0);
|
|
80394
|
+
return;
|
|
80395
|
+
}
|
|
80396
|
+
if (cursorItem?.kind === "agents") {
|
|
80397
|
+
setAgentsOpen((v) => !v);
|
|
80398
|
+
return;
|
|
80399
|
+
}
|
|
80400
|
+
if (selected)
|
|
80401
|
+
onDone(selected.id);
|
|
80402
|
+
return;
|
|
80403
|
+
}
|
|
80404
|
+
if (name === "tab") {
|
|
80405
|
+
setPane((p) => p === "worktrees" ? "sessions" : "worktrees");
|
|
80406
|
+
return;
|
|
80407
|
+
}
|
|
80408
|
+
if (name === "left") {
|
|
80409
|
+
setPane("worktrees");
|
|
80410
|
+
return;
|
|
80411
|
+
}
|
|
80412
|
+
if (name === "right") {
|
|
80413
|
+
setPane("sessions");
|
|
80414
|
+
return;
|
|
80415
|
+
}
|
|
80416
|
+
if (name === "up" || name === "down") {
|
|
80417
|
+
const d = name === "up" ? -1 : 1;
|
|
80418
|
+
if (pane === "worktrees") {
|
|
80419
|
+
setWtCursor((c) => clamp(c + d, visibleGroups.length));
|
|
80420
|
+
setSessCursor(0);
|
|
80421
|
+
} else {
|
|
80422
|
+
setSessCursor((c) => clamp(c + d, items.length));
|
|
80423
|
+
}
|
|
80424
|
+
return;
|
|
80425
|
+
}
|
|
80426
|
+
if (name === "a" && !key.ctrl && !key.meta) {
|
|
80427
|
+
setAgentsOpen((v) => !v);
|
|
80428
|
+
return;
|
|
80429
|
+
}
|
|
80430
|
+
if (name === "v" && !key.ctrl && !key.meta) {
|
|
80431
|
+
if (selected)
|
|
80432
|
+
setReader({ row: selected, conv: null });
|
|
80433
|
+
return;
|
|
80434
|
+
}
|
|
80435
|
+
if (name === "backspace") {
|
|
80436
|
+
setFilter((f) => f.slice(0, -1));
|
|
80437
|
+
setWtCursor(0);
|
|
80438
|
+
setSessCursor(0);
|
|
80439
|
+
return;
|
|
80440
|
+
}
|
|
80441
|
+
const ch = key.raw;
|
|
80442
|
+
if (ch && ch.length === 1 && ch >= " " && ch !== "\x7F" && ch !== "a" && ch !== "v") {
|
|
80443
|
+
setFilter((f) => f + ch);
|
|
80444
|
+
setWtCursor(0);
|
|
80445
|
+
setSessCursor(0);
|
|
80446
|
+
}
|
|
80447
|
+
});
|
|
80448
|
+
const bodyW = width;
|
|
80449
|
+
const sidebarW = Math.max(SIDEBAR_MIN, Math.min(SIDEBAR_MAX, Math.round(width * 0.34)));
|
|
80450
|
+
const rightW = Math.max(30, bodyW - sidebarW);
|
|
80451
|
+
const sessInner = rightW - PANEL_BORDER - SCROLL_CHROME;
|
|
80452
|
+
const sideInner = sidebarW - PANEL_BORDER - SCROLL_CHROME - 1;
|
|
80453
|
+
const sessionDetailInner = rightW - PANEL_CHROME;
|
|
80454
|
+
const worktreeDetailInner = width - PANEL_CHROME;
|
|
80455
|
+
const chipCols = useMemo4(() => {
|
|
80456
|
+
let cols = { age: 1, count: 1, dirty: 0, sync: 0 };
|
|
80457
|
+
for (const g of visibleGroups) {
|
|
80458
|
+
cols.age = Math.max(cols.age, displayWidth(g.lastActiveMs ? age(g.lastActiveMs) : "\u2014"));
|
|
80459
|
+
cols.count = Math.max(cols.count, String(listed.get(g.name)?.length ?? 0).length);
|
|
80460
|
+
if (g.dirty)
|
|
80461
|
+
cols.dirty = Math.max(cols.dirty, String(g.dirty).length);
|
|
80462
|
+
if (g.ahead)
|
|
80463
|
+
cols.sync = Math.max(cols.sync, String(g.ahead).length);
|
|
80464
|
+
if (g.behind)
|
|
80465
|
+
cols.sync = Math.max(cols.sync, String(g.behind).length);
|
|
80466
|
+
}
|
|
80467
|
+
if (blockWidth(cols) > sideInner)
|
|
80468
|
+
cols = { ...cols, sync: 0 };
|
|
80469
|
+
if (blockWidth(cols) > sideInner)
|
|
80470
|
+
cols = { ...cols, dirty: 0 };
|
|
80471
|
+
return cols;
|
|
80472
|
+
}, [visibleGroups, listed, sideInner]);
|
|
80473
|
+
const maxSize = Math.max(1, ...sessions2.slice(0, 400).map((s) => s.sizeBytes));
|
|
80474
|
+
const meterW = Math.max(6, Math.min(14, Math.round(sessInner * 0.16)));
|
|
80475
|
+
const sidebarInnerH = height2 - 1 - WORKTREE_DETAIL_H - 1 - PANEL_BORDER;
|
|
80476
|
+
const listContentH = visibleGroups.length * 2 + (stale.length > 0 ? 2 : 0);
|
|
80477
|
+
const showActivity = sidebarInnerH - listContentH >= ACTIVITY_H;
|
|
80478
|
+
const activityDays = useMemo4(() => dailyActivity(visibleGroups, ACTIVITY_WEEKS * WEEK_DAYS), [visibleGroups]);
|
|
80479
|
+
const shownSessions = [...listed.values()].reduce((a, v) => a + v.length, 0);
|
|
80480
|
+
if (reader) {
|
|
80481
|
+
return /* @__PURE__ */ jsxDEV20(ConversationReader, {
|
|
80482
|
+
row: reader.row,
|
|
80483
|
+
conv: reader.conv,
|
|
80484
|
+
width,
|
|
80485
|
+
height: height2,
|
|
80486
|
+
onClose: () => setReader(null),
|
|
80487
|
+
onResume: () => onDone(reader.row.id),
|
|
80488
|
+
onCancel: () => onDone(null)
|
|
80489
|
+
}, undefined, false, undefined, this);
|
|
80490
|
+
}
|
|
80491
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80492
|
+
flexDirection: "column",
|
|
80493
|
+
height: height2,
|
|
80494
|
+
backgroundColor: C.bg,
|
|
80495
|
+
children: [
|
|
80496
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80497
|
+
flexDirection: "row",
|
|
80498
|
+
justifyContent: "space-between",
|
|
80499
|
+
height: 1,
|
|
80500
|
+
paddingX: 1,
|
|
80501
|
+
children: [
|
|
80502
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80503
|
+
children: [
|
|
80504
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80505
|
+
fg: tokens.accent,
|
|
80506
|
+
attributes: 1,
|
|
80507
|
+
children: "claudish"
|
|
80508
|
+
}, undefined, false, undefined, this),
|
|
80509
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80510
|
+
fg: tokens.subtle,
|
|
80511
|
+
children: " resume"
|
|
80512
|
+
}, undefined, false, undefined, this)
|
|
80513
|
+
]
|
|
80514
|
+
}, undefined, true, undefined, this),
|
|
80515
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80516
|
+
children: [
|
|
80517
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80518
|
+
fg: tokens.subtle,
|
|
80519
|
+
children: `${visibleGroups.length} worktrees \xB7 ${shownSessions} sessions`
|
|
80520
|
+
}, undefined, false, undefined, this),
|
|
80521
|
+
filter ? /* @__PURE__ */ jsxDEV20("span", {
|
|
80522
|
+
fg: tokens.warn,
|
|
80523
|
+
children: ` /${filter}`
|
|
80524
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV20("span", {}, undefined, false, undefined, this)
|
|
80525
|
+
]
|
|
80526
|
+
}, undefined, true, undefined, this)
|
|
80527
|
+
]
|
|
80528
|
+
}, undefined, true, undefined, this),
|
|
80529
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80530
|
+
flexDirection: "row",
|
|
80531
|
+
flexGrow: 1,
|
|
80532
|
+
minHeight: 0,
|
|
80533
|
+
children: [
|
|
80534
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80535
|
+
width: sidebarW,
|
|
80536
|
+
flexDirection: "column",
|
|
80537
|
+
minHeight: 0,
|
|
80538
|
+
children: /* @__PURE__ */ jsxDEV20(Panel, {
|
|
80539
|
+
title: "worktrees",
|
|
80540
|
+
focused: pane === "worktrees",
|
|
80541
|
+
flush: true,
|
|
80542
|
+
flexGrow: 1,
|
|
80543
|
+
flexBasis: 0,
|
|
80544
|
+
children: [
|
|
80545
|
+
/* @__PURE__ */ jsxDEV20("scrollbox", {
|
|
80546
|
+
focused: false,
|
|
80547
|
+
flexGrow: 1,
|
|
80548
|
+
scrollbarOptions: SCROLLBAR,
|
|
80549
|
+
children: [
|
|
80550
|
+
fresh.map((g, i) => /* @__PURE__ */ jsxDEV20(WorktreeRow, {
|
|
80551
|
+
g,
|
|
80552
|
+
cursor: i === wtCursor,
|
|
80553
|
+
width: sideInner,
|
|
80554
|
+
count: listed.get(g.name)?.length ?? 0,
|
|
80555
|
+
cols: chipCols
|
|
80556
|
+
}, g.name, false, undefined, this)),
|
|
80557
|
+
stale.length > 0 ? /* @__PURE__ */ jsxDEV20(SectionHeader, {
|
|
80558
|
+
label: `stale \xB7 ${stale.length} \xB7 idle 3d+`,
|
|
80559
|
+
width: sideInner
|
|
80560
|
+
}, undefined, false, undefined, this) : null,
|
|
80561
|
+
stale.map((g, i) => /* @__PURE__ */ jsxDEV20(WorktreeRow, {
|
|
80562
|
+
g,
|
|
80563
|
+
cursor: fresh.length + i === wtCursor,
|
|
80564
|
+
width: sideInner,
|
|
80565
|
+
count: listed.get(g.name)?.length ?? 0,
|
|
80566
|
+
cols: chipCols
|
|
80567
|
+
}, g.name, false, undefined, this))
|
|
80568
|
+
]
|
|
80569
|
+
}, undefined, true, undefined, this),
|
|
80570
|
+
showActivity ? /* @__PURE__ */ jsxDEV20(ActivityCalendar, {
|
|
80571
|
+
days: activityDays,
|
|
80572
|
+
width: sideInner
|
|
80573
|
+
}, undefined, false, undefined, this) : null
|
|
80574
|
+
]
|
|
80575
|
+
}, undefined, true, undefined, this)
|
|
80576
|
+
}, undefined, false, undefined, this),
|
|
80577
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80578
|
+
flexDirection: "column",
|
|
80579
|
+
flexGrow: 1,
|
|
80580
|
+
minWidth: 0,
|
|
80581
|
+
minHeight: 0,
|
|
80582
|
+
children: [
|
|
80583
|
+
/* @__PURE__ */ jsxDEV20(Panel, {
|
|
80584
|
+
title: group ? `sessions \xB7 ${truncate3(group.name, 28)}` : "sessions",
|
|
80585
|
+
focused: pane === "sessions",
|
|
80586
|
+
flush: true,
|
|
80587
|
+
flexGrow: 1,
|
|
80588
|
+
flexBasis: 0,
|
|
80589
|
+
children: /* @__PURE__ */ jsxDEV20("scrollbox", {
|
|
80590
|
+
focused: false,
|
|
80591
|
+
flexGrow: 1,
|
|
80592
|
+
scrollbarOptions: SCROLLBAR,
|
|
80593
|
+
children: items.length === 0 ? /* @__PURE__ */ jsxDEV20("text", {
|
|
80594
|
+
fg: tokens.subtle,
|
|
80595
|
+
children: " no sessions match"
|
|
80596
|
+
}, undefined, false, undefined, this) : items.map((it, i) => it.kind === "agents" ? /* @__PURE__ */ jsxDEV20(AgentNode, {
|
|
80597
|
+
count: it.count,
|
|
80598
|
+
open: agentsOpen,
|
|
80599
|
+
cursor: i === sessCursor && pane === "sessions",
|
|
80600
|
+
width: sessInner
|
|
80601
|
+
}, "agents", false, undefined, this) : /* @__PURE__ */ jsxDEV20(SessionRowView, {
|
|
80602
|
+
row: it.row,
|
|
80603
|
+
cursor: i === sessCursor && pane === "sessions",
|
|
80604
|
+
width: sessInner,
|
|
80605
|
+
even: i % 2 === 0,
|
|
80606
|
+
indent: it.kind === "agent" ? 2 : 0,
|
|
80607
|
+
maxSize,
|
|
80608
|
+
meterW
|
|
80609
|
+
}, it.row.id, false, undefined, this))
|
|
80610
|
+
}, undefined, false, undefined, this)
|
|
80611
|
+
}, undefined, false, undefined, this),
|
|
80612
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80613
|
+
height: sessionDetailH,
|
|
80614
|
+
flexShrink: 0,
|
|
80615
|
+
children: /* @__PURE__ */ jsxDEV20(Panel, {
|
|
80616
|
+
title: "session",
|
|
80617
|
+
flexGrow: 1,
|
|
80618
|
+
children: selected ? /* @__PURE__ */ jsxDEV20(SessionDetail, {
|
|
80619
|
+
row: selected,
|
|
80620
|
+
width: sessionDetailInner,
|
|
80621
|
+
turns
|
|
80622
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV20("text", {
|
|
80623
|
+
fg: tokens.subtle,
|
|
80624
|
+
children: cursorItem?.kind === "agents" ? "agent sessions \u2014 enter to expand" : "nothing selected"
|
|
80625
|
+
}, undefined, false, undefined, this)
|
|
80626
|
+
}, undefined, false, undefined, this)
|
|
80627
|
+
}, undefined, false, undefined, this)
|
|
80628
|
+
]
|
|
80629
|
+
}, undefined, true, undefined, this)
|
|
80630
|
+
]
|
|
80631
|
+
}, undefined, true, undefined, this),
|
|
80632
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80633
|
+
height: WORKTREE_DETAIL_H,
|
|
80634
|
+
flexShrink: 0,
|
|
80635
|
+
children: /* @__PURE__ */ jsxDEV20(Panel, {
|
|
80636
|
+
title: "worktree",
|
|
80637
|
+
flexGrow: 1,
|
|
80638
|
+
children: /* @__PURE__ */ jsxDEV20(WorktreeDetail, {
|
|
80639
|
+
group,
|
|
80640
|
+
width: worktreeDetailInner,
|
|
80641
|
+
count: sessions2.length
|
|
80642
|
+
}, undefined, false, undefined, this)
|
|
80643
|
+
}, undefined, false, undefined, this)
|
|
80644
|
+
}, undefined, false, undefined, this),
|
|
80645
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80646
|
+
flexDirection: "row",
|
|
80647
|
+
height: 1,
|
|
80648
|
+
paddingX: 1,
|
|
80649
|
+
gap: 2,
|
|
80650
|
+
children: [
|
|
80651
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80652
|
+
fg: tokens.subtle,
|
|
80653
|
+
children: "\u2191\u2193 move"
|
|
80654
|
+
}, undefined, false, undefined, this),
|
|
80655
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80656
|
+
fg: tokens.subtle,
|
|
80657
|
+
children: "\u21E5 pane"
|
|
80658
|
+
}, undefined, false, undefined, this),
|
|
80659
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80660
|
+
fg: tokens.subtle,
|
|
80661
|
+
children: "type to filter"
|
|
80662
|
+
}, undefined, false, undefined, this),
|
|
80663
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80664
|
+
fg: tokens.accent,
|
|
80665
|
+
children: "\u23CE resume"
|
|
80666
|
+
}, undefined, false, undefined, this),
|
|
80667
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80668
|
+
fg: selected ? tokens.info : tokens.trace,
|
|
80669
|
+
children: "v read"
|
|
80670
|
+
}, undefined, false, undefined, this),
|
|
80671
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80672
|
+
fg: agentRows.length > 0 ? tokens.subtle : tokens.trace,
|
|
80673
|
+
children: agentRows.length > 0 ? `a ${agentsOpen ? "collapse" : "expand"} agents` : ""
|
|
80674
|
+
}, undefined, false, undefined, this),
|
|
80675
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80676
|
+
fg: tokens.subtle,
|
|
80677
|
+
children: "esc cancel"
|
|
80678
|
+
}, undefined, false, undefined, this)
|
|
80679
|
+
]
|
|
80680
|
+
}, undefined, true, undefined, this)
|
|
80681
|
+
]
|
|
80682
|
+
}, undefined, true, undefined, this);
|
|
80683
|
+
}
|
|
80684
|
+
function Field({
|
|
80685
|
+
label,
|
|
80686
|
+
width,
|
|
80687
|
+
children
|
|
80688
|
+
}) {
|
|
80689
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80690
|
+
flexDirection: "row",
|
|
80691
|
+
height: 1,
|
|
80692
|
+
gap: 1,
|
|
80693
|
+
children: [
|
|
80694
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80695
|
+
fg: tokens.subtle,
|
|
80696
|
+
children: padTo(label, width)
|
|
80697
|
+
}, undefined, false, undefined, this),
|
|
80698
|
+
children
|
|
80699
|
+
]
|
|
80700
|
+
}, undefined, true, undefined, this);
|
|
80701
|
+
}
|
|
80702
|
+
function WorktreeDetail({
|
|
80703
|
+
group,
|
|
80704
|
+
width,
|
|
80705
|
+
count
|
|
80706
|
+
}) {
|
|
80707
|
+
if (!group)
|
|
80708
|
+
return /* @__PURE__ */ jsxDEV20("text", {
|
|
80709
|
+
fg: tokens.subtle,
|
|
80710
|
+
children: "no worktree selected"
|
|
80711
|
+
}, undefined, false, undefined, this);
|
|
80712
|
+
const L = 9;
|
|
80713
|
+
const badges = [];
|
|
80714
|
+
if (group.dirty !== undefined) {
|
|
80715
|
+
badges.push({
|
|
80716
|
+
label: group.dirty > 0 ? `${DIRTY_GLYPH}${group.dirty} uncommitted` : "clean",
|
|
80717
|
+
bg: group.dirty > 0 ? CHIP.dirty : CHIP.clean
|
|
80718
|
+
});
|
|
80719
|
+
}
|
|
80720
|
+
if (group.ahead)
|
|
80721
|
+
badges.push({ label: `\u2191${group.ahead}`, bg: CHIP.ahead });
|
|
80722
|
+
if (group.behind)
|
|
80723
|
+
badges.push({ label: `\u2193${group.behind}`, bg: CHIP.behind });
|
|
80724
|
+
const badgeW = badges.reduce((w, b) => w + displayWidth(b.label) + 2, 0);
|
|
80725
|
+
const marker = group.current ? " \u25B6 you are here" : !group.live ? " worktree deleted" : "";
|
|
80726
|
+
const branch = group.branch ?? (group.live ? "detached" : "\u2014");
|
|
80727
|
+
const idAvail = Math.max(12, width - L - displayWidth(marker) - (BRANCH_LEAD + 1) - badgeW - 1);
|
|
80728
|
+
const nameW = Math.max(8, Math.min(displayWidth(group.name), Math.floor(idAvail / 2)));
|
|
80729
|
+
const branchW = Math.max(6, idAvail - nameW);
|
|
80730
|
+
const spark = activitySeries(group.sessions);
|
|
80731
|
+
const summary = ` ${count} session${count === 1 ? "" : "s"} \xB7 created ${group.createdMs ? age(group.createdMs) : "?"} ago \xB7 used ${group.lastActiveMs ? age(group.lastActiveMs) : "?"} ago`;
|
|
80732
|
+
const locAvail = Math.max(20, width - L - spark.length - 2);
|
|
80733
|
+
const pathW = Math.min(displayWidth(group.path ?? "\u2014"), Math.max(16, Math.floor(locAvail * 0.45)));
|
|
80734
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80735
|
+
flexDirection: "column",
|
|
80736
|
+
children: [
|
|
80737
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80738
|
+
children: [
|
|
80739
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80740
|
+
fg: tokens.subtle,
|
|
80741
|
+
children: padTo("worktree", L)
|
|
80742
|
+
}, undefined, false, undefined, this),
|
|
80743
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80744
|
+
fg: tokens.text,
|
|
80745
|
+
attributes: A.bold,
|
|
80746
|
+
children: truncate3(group.name, nameW)
|
|
80747
|
+
}, undefined, false, undefined, this),
|
|
80748
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80749
|
+
fg: group.current ? HERE_FG : tokens.dead,
|
|
80750
|
+
children: marker
|
|
80751
|
+
}, undefined, false, undefined, this),
|
|
80752
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80753
|
+
fg: tokens.subtle,
|
|
80754
|
+
children: ` ${BRANCH_ICON} `
|
|
80755
|
+
}, undefined, false, undefined, this),
|
|
80756
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80757
|
+
fg: group.live ? tokens.info : tokens.dead,
|
|
80758
|
+
children: `${truncate3(branch, branchW)} `
|
|
80759
|
+
}, undefined, false, undefined, this),
|
|
80760
|
+
badges.map((b) => /* @__PURE__ */ jsxDEV20(BadgeSpan, {
|
|
80761
|
+
label: b.label,
|
|
80762
|
+
bg: b.bg
|
|
80763
|
+
}, b.label, false, undefined, this))
|
|
80764
|
+
]
|
|
80765
|
+
}, undefined, true, undefined, this),
|
|
80766
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80767
|
+
flexDirection: "row",
|
|
80768
|
+
height: 1,
|
|
80769
|
+
children: [
|
|
80770
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80771
|
+
fg: tokens.subtle,
|
|
80772
|
+
flexShrink: 0,
|
|
80773
|
+
children: padTo("path", L)
|
|
80774
|
+
}, undefined, false, undefined, this),
|
|
80775
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80776
|
+
fg: tokens.trace,
|
|
80777
|
+
flexShrink: 0,
|
|
80778
|
+
children: `${padTo(truncate3(group.path ?? "\u2014", pathW), pathW)} `
|
|
80779
|
+
}, undefined, false, undefined, this),
|
|
80780
|
+
/* @__PURE__ */ jsxDEV20(Sparkline, {
|
|
80781
|
+
values: hasActivity(spark) ? spark : [],
|
|
80782
|
+
fg: SPARK_FG
|
|
80783
|
+
}, undefined, false, undefined, this),
|
|
80784
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80785
|
+
fg: tokens.trace,
|
|
80786
|
+
flexShrink: 0,
|
|
80787
|
+
children: truncate3(summary, Math.max(0, locAvail - pathW))
|
|
80788
|
+
}, undefined, false, undefined, this)
|
|
80789
|
+
]
|
|
80790
|
+
}, undefined, true, undefined, this)
|
|
80791
|
+
]
|
|
80792
|
+
}, undefined, true, undefined, this);
|
|
80793
|
+
}
|
|
80794
|
+
function SessionDetail({
|
|
80795
|
+
row,
|
|
80796
|
+
width,
|
|
80797
|
+
turns
|
|
80798
|
+
}) {
|
|
80799
|
+
const mb = row.sizeBytes / 1048576;
|
|
80800
|
+
const L = 9;
|
|
80801
|
+
const size = mb >= 0.1 ? `${mb.toFixed(1)} MB` : `${Math.round(row.sizeBytes / 1024)} KB`;
|
|
80802
|
+
const full = `\xB7 ${size}${row.lastMessageChars !== undefined ? ` \xB7 last msg ${row.lastMessageChars} ch` : ""}`;
|
|
80803
|
+
const suffixRoom = width - L - 1 - row.id.length - 1;
|
|
80804
|
+
const suffix = displayWidth(full) <= suffixRoom ? full : displayWidth(`\xB7 ${size}`) <= suffixRoom ? `\xB7 ${size}` : "";
|
|
80805
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80806
|
+
flexDirection: "column",
|
|
80807
|
+
children: [
|
|
80808
|
+
/* @__PURE__ */ jsxDEV20(Field, {
|
|
80809
|
+
label: "title",
|
|
80810
|
+
width: L,
|
|
80811
|
+
children: /* @__PURE__ */ jsxDEV20("text", {
|
|
80812
|
+
fg: tokens.text,
|
|
80813
|
+
attributes: A.bold,
|
|
80814
|
+
children: truncate3(sessionLabel(row), width - L)
|
|
80815
|
+
}, undefined, false, undefined, this)
|
|
80816
|
+
}, undefined, false, undefined, this),
|
|
80817
|
+
/* @__PURE__ */ jsxDEV20(Field, {
|
|
80818
|
+
label: "id",
|
|
80819
|
+
width: L,
|
|
80820
|
+
children: [
|
|
80821
|
+
/* @__PURE__ */ jsxDEV20("text", {
|
|
80822
|
+
fg: tokens.info,
|
|
80823
|
+
flexShrink: 0,
|
|
80824
|
+
children: row.id
|
|
80825
|
+
}, undefined, false, undefined, this),
|
|
80826
|
+
suffix ? /* @__PURE__ */ jsxDEV20("text", {
|
|
80827
|
+
fg: tokens.trace,
|
|
80828
|
+
flexShrink: 0,
|
|
80829
|
+
children: suffix
|
|
80830
|
+
}, undefined, false, undefined, this) : null
|
|
80831
|
+
]
|
|
80832
|
+
}, undefined, true, undefined, this),
|
|
80833
|
+
/* @__PURE__ */ jsxDEV20("box", {
|
|
80834
|
+
height: 1
|
|
80835
|
+
}, undefined, false, undefined, this),
|
|
80836
|
+
/* @__PURE__ */ jsxDEV20(Conversation, {
|
|
80837
|
+
turns: row.recentTurns ?? [],
|
|
80838
|
+
width,
|
|
80839
|
+
max: turns
|
|
80840
|
+
}, undefined, false, undefined, this)
|
|
80841
|
+
]
|
|
80842
|
+
}, undefined, true, undefined, this);
|
|
80843
|
+
}
|
|
80844
|
+
function Conversation({
|
|
80845
|
+
turns,
|
|
80846
|
+
width,
|
|
80847
|
+
max
|
|
80848
|
+
}) {
|
|
80849
|
+
if (turns.length === 0) {
|
|
80850
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80851
|
+
flexDirection: "column",
|
|
80852
|
+
width,
|
|
80853
|
+
children: /* @__PURE__ */ jsxDEV20("text", {
|
|
80854
|
+
fg: tokens.trace,
|
|
80855
|
+
children: "no conversation recorded"
|
|
80856
|
+
}, undefined, false, undefined, this)
|
|
80857
|
+
}, undefined, false, undefined, this);
|
|
80858
|
+
}
|
|
80859
|
+
const ROLE_W2 = 6;
|
|
80860
|
+
const textW = Math.max(10, width - ROLE_W2 - 2);
|
|
80861
|
+
const shown = turns.slice(-max);
|
|
80862
|
+
return /* @__PURE__ */ jsxDEV20("box", {
|
|
80863
|
+
flexDirection: "column",
|
|
80864
|
+
width,
|
|
80865
|
+
flexShrink: 0,
|
|
80866
|
+
children: shown.map((t, i) => /* @__PURE__ */ jsxDEV20("text", {
|
|
80867
|
+
children: [
|
|
80868
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80869
|
+
fg: tokens.trace,
|
|
80870
|
+
children: "\u258D"
|
|
80871
|
+
}, undefined, false, undefined, this),
|
|
80872
|
+
/* @__PURE__ */ jsxDEV20(BadgeSpan, {
|
|
80873
|
+
label: t.role === "user" ? "you" : "ai",
|
|
80874
|
+
bg: t.role === "user" ? SPEAKER.you : SPEAKER.ai,
|
|
80875
|
+
width: ROLE_W2
|
|
80876
|
+
}, undefined, false, undefined, this),
|
|
80877
|
+
/* @__PURE__ */ jsxDEV20("span", {
|
|
80878
|
+
fg: t.role === "user" ? tokens.text : MUTED,
|
|
80879
|
+
children: truncate3(t.text, textW)
|
|
80880
|
+
}, undefined, false, undefined, this)
|
|
80881
|
+
]
|
|
80882
|
+
}, `${i}-${t.text.slice(0, 12)}`, true, undefined, this))
|
|
80883
|
+
}, undefined, false, undefined, this);
|
|
80884
|
+
}
|
|
80885
|
+
var PANEL_CHROME = 4, PANEL_BORDER = 2, SCROLL_CHROME = 1, SIDEBAR_MIN = 28, SIDEBAR_MAX = 50, DETAIL_CHROME = 5, WORKTREE_DETAIL_H = 4, STALE_MS, GH_LEVELS, CHIP, HERE_FG, LIVE_W = 2, DIRTY_GLYPH = "+", SESSION_AGE_W = 3, SESSION_AGE_COL, SIZE_FLOOR_BYTES, MUTED, SCROLLBAR, WEEK_DAYS = 7, ACTIVITY_WEEKS = 6, BRANCH_ICON = "\u2387", BRANCH_LEAD = 5, BRANCH_LEAD_DENSE = 6, WEEK_LABEL_W = 4, ACTIVITY_H, SPARK_MIN_DAYS = 7, SPARK_FG = "#3f6f9e";
|
|
80886
|
+
var init_resume_picker = __esm(() => {
|
|
80887
|
+
init_theme2();
|
|
80888
|
+
init_text();
|
|
80889
|
+
init_tokens();
|
|
80890
|
+
init_widgets();
|
|
80891
|
+
init_conversation_reader();
|
|
80892
|
+
init_conversation();
|
|
80893
|
+
init_session_discovery();
|
|
80894
|
+
STALE_MS = 3 * 86400000;
|
|
80895
|
+
GH_LEVELS = ["#21262d", "#0e4429", "#006d32", "#26a641", "#39d353"];
|
|
80896
|
+
CHIP = {
|
|
80897
|
+
fresh: GH_LEVELS[4],
|
|
80898
|
+
stale: "#a1a9b3",
|
|
80899
|
+
count: "#58a6ff",
|
|
80900
|
+
dirty: "#d29922",
|
|
80901
|
+
clean: GH_LEVELS[3],
|
|
80902
|
+
ahead: "#bc8cff",
|
|
80903
|
+
behind: "#d2a8ff"
|
|
80904
|
+
};
|
|
80905
|
+
HERE_FG = GH_LEVELS[4];
|
|
80906
|
+
SESSION_AGE_COL = SESSION_AGE_W + 3;
|
|
80907
|
+
SIZE_FLOOR_BYTES = 16 * 1024;
|
|
80908
|
+
MUTED = C.fgMuted;
|
|
80909
|
+
SCROLLBAR = {
|
|
80910
|
+
showArrows: false,
|
|
80911
|
+
trackOptions: { backgroundColor: tokens.bgPanel, foregroundColor: tokens.border }
|
|
80912
|
+
};
|
|
80913
|
+
ACTIVITY_H = 2 + ACTIVITY_WEEKS;
|
|
80914
|
+
});
|
|
80915
|
+
|
|
80916
|
+
// src/session/resume-picker-run.tsx
|
|
80917
|
+
var exports_resume_picker_run = {};
|
|
80918
|
+
__export(exports_resume_picker_run, {
|
|
80919
|
+
runResumePicker: () => runResumePicker
|
|
80920
|
+
});
|
|
80921
|
+
import { createCliRenderer as createCliRenderer3 } from "@opentui/core";
|
|
80922
|
+
import { createRoot as createRoot3 } from "@opentui/react";
|
|
80923
|
+
import { jsxDEV as jsxDEV21 } from "@opentui/react/jsx-dev-runtime";
|
|
80924
|
+
async function runResumePicker(cwd = process.cwd()) {
|
|
80925
|
+
const repo = getRepoContext(cwd);
|
|
80926
|
+
if (!repo)
|
|
80927
|
+
return { sessionId: null, hadSessions: false };
|
|
80928
|
+
const groups = discoverWorktreeGroups(repo);
|
|
80929
|
+
if (groups.length === 0)
|
|
80930
|
+
return { sessionId: null, hadSessions: false };
|
|
80931
|
+
await enrichWorktreeGit(groups, repo.root);
|
|
80932
|
+
setStderrQuiet(true);
|
|
80933
|
+
const renderer = await createCliRenderer3({
|
|
80934
|
+
useAlternateScreen: true,
|
|
80935
|
+
exitOnCtrlC: false
|
|
80936
|
+
});
|
|
80937
|
+
const root = createRoot3(renderer);
|
|
80938
|
+
let chosen = null;
|
|
80939
|
+
try {
|
|
80940
|
+
await new Promise((resolve5) => {
|
|
80941
|
+
let settled = false;
|
|
80942
|
+
const done = (id) => {
|
|
80943
|
+
if (settled)
|
|
80944
|
+
return;
|
|
80945
|
+
settled = true;
|
|
80946
|
+
chosen = id;
|
|
80947
|
+
resolve5();
|
|
80948
|
+
};
|
|
80949
|
+
root.render(/* @__PURE__ */ jsxDEV21(ResumePicker, {
|
|
80950
|
+
groups,
|
|
80951
|
+
onDone: done
|
|
80952
|
+
}, undefined, false, undefined, this));
|
|
80953
|
+
});
|
|
80954
|
+
} finally {
|
|
80955
|
+
try {
|
|
80956
|
+
root.unmount();
|
|
80957
|
+
} catch {}
|
|
80958
|
+
try {
|
|
80959
|
+
renderer.destroy();
|
|
80960
|
+
} catch {}
|
|
80961
|
+
setStderrQuiet(false);
|
|
80962
|
+
}
|
|
80963
|
+
return { sessionId: chosen, hadSessions: true };
|
|
80964
|
+
}
|
|
80965
|
+
var init_resume_picker_run = __esm(() => {
|
|
80966
|
+
init_logger();
|
|
80967
|
+
init_resume_picker();
|
|
80968
|
+
init_session_discovery();
|
|
80969
|
+
});
|
|
80970
|
+
|
|
80971
|
+
// src/session/baseline-pricing.ts
|
|
80972
|
+
function resolveBaseline(alias, label) {
|
|
80973
|
+
const entry = findEntryByAlias(alias);
|
|
80974
|
+
if (!entry)
|
|
80975
|
+
return null;
|
|
80976
|
+
const firstParty = entry.aggregators?.find((a) => a.provider === FIRST_PARTY && typeof a.pricing?.input === "number");
|
|
80977
|
+
const input = firstParty?.pricing?.input;
|
|
80978
|
+
const output = firstParty?.pricing?.output;
|
|
80979
|
+
if (typeof input !== "number" || typeof output !== "number")
|
|
80980
|
+
return null;
|
|
80981
|
+
if (input <= 0 || output <= 0)
|
|
80982
|
+
return null;
|
|
80983
|
+
return { modelId: entry.modelId, label, inputPerM: input, outputPerM: output };
|
|
80984
|
+
}
|
|
80985
|
+
function getBaselines() {
|
|
80986
|
+
return BASELINE_ALIASES.map(({ alias, label }) => resolveBaseline(alias, label)).filter((b) => b !== null);
|
|
80987
|
+
}
|
|
80988
|
+
function baselineCost(b, inputTokens, outputTokens) {
|
|
80989
|
+
return inputTokens / 1e6 * b.inputPerM + outputTokens / 1e6 * b.outputPerM;
|
|
80990
|
+
}
|
|
80991
|
+
var BASELINE_ALIASES, FIRST_PARTY = "anthropic";
|
|
80992
|
+
var init_baseline_pricing = __esm(() => {
|
|
80993
|
+
init_catalog_query();
|
|
80994
|
+
BASELINE_ALIASES = [
|
|
80995
|
+
{ alias: "~anthropic/claude-sonnet-latest", label: "Sonnet" },
|
|
80996
|
+
{ alias: "~anthropic/claude-opus-latest", label: "Opus" }
|
|
80997
|
+
];
|
|
80998
|
+
});
|
|
80999
|
+
|
|
81000
|
+
// src/session/session-stats.ts
|
|
81001
|
+
var exports_session_stats = {};
|
|
81002
|
+
__export(exports_session_stats, {
|
|
81003
|
+
tokenFilePath: () => tokenFilePath,
|
|
81004
|
+
readSessionStats: () => readSessionStats,
|
|
81005
|
+
computeSavings: () => computeSavings
|
|
81006
|
+
});
|
|
81007
|
+
import { readFileSync as readFileSync28 } from "fs";
|
|
81008
|
+
import { homedir as homedir35 } from "os";
|
|
81009
|
+
import { join as join38 } from "path";
|
|
81010
|
+
function tokenFilePath(port) {
|
|
81011
|
+
return process.env.CLAUDISH_TOKEN_FILE || join38(homedir35(), ".claudish", `tokens-${port}.json`);
|
|
81012
|
+
}
|
|
81013
|
+
function readSessionStats(port) {
|
|
81014
|
+
let raw2;
|
|
81015
|
+
try {
|
|
81016
|
+
raw2 = JSON.parse(readFileSync28(tokenFilePath(port), "utf-8"));
|
|
81017
|
+
} catch {
|
|
81018
|
+
return null;
|
|
81019
|
+
}
|
|
81020
|
+
if (!raw2 || typeof raw2 !== "object")
|
|
81021
|
+
return null;
|
|
81022
|
+
const d = raw2;
|
|
81023
|
+
const inputTokens = num(d.input_tokens);
|
|
81024
|
+
const outputTokens = num(d.output_tokens);
|
|
81025
|
+
if (inputTokens <= 0 && outputTokens <= 0)
|
|
81026
|
+
return null;
|
|
81027
|
+
const costUsd = num(d.total_cost);
|
|
81028
|
+
const isFree = d.is_free === true;
|
|
81029
|
+
const contextWindow = typeof d.context_window === "number" ? d.context_window : null;
|
|
81030
|
+
const contextUsed = contextWindow && contextWindow > 0 ? Math.min(1, Math.max(0, inputTokens / contextWindow)) : null;
|
|
81031
|
+
const toolCalls = Array.isArray(d.tool_calls) ? d.tool_calls.map((t) => t).filter((t) => typeof t?.name === "string" && num(t.count) > 0).map((t) => ({ name: String(t.name), count: num(t.count) })) : [];
|
|
81032
|
+
const startedAt = num(d.started_at);
|
|
81033
|
+
const updatedAt = num(d.updated_at);
|
|
81034
|
+
const durationMs = startedAt > 0 && updatedAt > startedAt ? updatedAt - startedAt : 0;
|
|
81035
|
+
const billedInputTokens = num(d.billed_input_tokens) || inputTokens;
|
|
81036
|
+
const rawIn = billedInputTokens / 1e6 * num(d.input_per_m);
|
|
81037
|
+
const rawOut = outputTokens / 1e6 * num(d.output_per_m);
|
|
81038
|
+
const rawTotal = rawIn + rawOut;
|
|
81039
|
+
const scale = rawTotal > 0 && costUsd > 0 ? costUsd / rawTotal : 0;
|
|
81040
|
+
return {
|
|
81041
|
+
inputTokens,
|
|
81042
|
+
outputTokens,
|
|
81043
|
+
totalTokens: num(d.total_tokens) || inputTokens + outputTokens,
|
|
81044
|
+
costUsd,
|
|
81045
|
+
isFree,
|
|
81046
|
+
isEstimated: d.is_estimated === true,
|
|
81047
|
+
providerName: typeof d.provider_name === "string" ? d.provider_name : "",
|
|
81048
|
+
modelName: typeof d.model_name === "string" ? d.model_name : "",
|
|
81049
|
+
contextWindow,
|
|
81050
|
+
contextUsed,
|
|
81051
|
+
toolCalls,
|
|
81052
|
+
toolCallTotal: toolCalls.reduce((a, t) => a + t.count, 0),
|
|
81053
|
+
durationMs,
|
|
81054
|
+
savings: computeSavings(billedInputTokens, outputTokens, costUsd),
|
|
81055
|
+
inputCostUsd: rawIn * scale,
|
|
81056
|
+
outputCostUsd: rawOut * scale,
|
|
81057
|
+
billedInputTokens
|
|
81058
|
+
};
|
|
81059
|
+
}
|
|
81060
|
+
function computeSavings(inputTokens, outputTokens, actualUsd) {
|
|
81061
|
+
return getBaselines().map((b) => {
|
|
81062
|
+
const baselineUsd = baselineCost(b, inputTokens, outputTokens);
|
|
81063
|
+
return {
|
|
81064
|
+
label: b.label,
|
|
81065
|
+
modelId: b.modelId,
|
|
81066
|
+
baselineUsd,
|
|
81067
|
+
savedUsd: baselineUsd - actualUsd
|
|
81068
|
+
};
|
|
81069
|
+
});
|
|
81070
|
+
}
|
|
81071
|
+
var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
81072
|
+
var init_session_stats = __esm(() => {
|
|
81073
|
+
init_baseline_pricing();
|
|
81074
|
+
});
|
|
81075
|
+
|
|
81076
|
+
// src/session/ansi-viz.ts
|
|
81077
|
+
function rgb(hex4) {
|
|
81078
|
+
return [
|
|
81079
|
+
Number.parseInt(hex4.slice(1, 3), 16),
|
|
81080
|
+
Number.parseInt(hex4.slice(3, 5), 16),
|
|
81081
|
+
Number.parseInt(hex4.slice(5, 7), 16)
|
|
81082
|
+
];
|
|
81083
|
+
}
|
|
81084
|
+
function fg(hex4) {
|
|
81085
|
+
const [r, g, b] = rgb(hex4);
|
|
81086
|
+
return `\x1B[38;2;${r};${g};${b}m`;
|
|
81087
|
+
}
|
|
81088
|
+
function bg(hex4) {
|
|
81089
|
+
const [r, g, b] = rgb(hex4);
|
|
81090
|
+
return `\x1B[48;2;${r};${g};${b}m`;
|
|
81091
|
+
}
|
|
81092
|
+
function paint(text, hex4, bold4 = false) {
|
|
81093
|
+
return `${bold4 ? BOLD5 : ""}${fg(hex4)}${text}${RESET4}`;
|
|
81094
|
+
}
|
|
81095
|
+
function meter(pct, width, ramp = ramps.load) {
|
|
81096
|
+
const cells = Math.floor(width);
|
|
81097
|
+
if (!Number.isFinite(cells) || cells <= 0)
|
|
81098
|
+
return "";
|
|
81099
|
+
if (Number.isNaN(pct))
|
|
81100
|
+
return paint(NODATA2.repeat(cells), tokens.dead);
|
|
81101
|
+
const cols = rampFor(cells, ramp);
|
|
81102
|
+
const filled = fillCells(pct, cells);
|
|
81103
|
+
let out = "";
|
|
81104
|
+
let last = "";
|
|
81105
|
+
for (let i = 0;i < cells; i++) {
|
|
81106
|
+
const hex4 = i < filled ? cols[i] : tokens.border;
|
|
81107
|
+
if (hex4 !== last) {
|
|
81108
|
+
out += fg(hex4);
|
|
81109
|
+
last = hex4;
|
|
81110
|
+
}
|
|
81111
|
+
out += i < filled ? FILL2 : TRACK2;
|
|
81112
|
+
}
|
|
81113
|
+
return out + RESET4;
|
|
81114
|
+
}
|
|
81115
|
+
function stackedBar(segments, width) {
|
|
81116
|
+
const cells = Math.floor(width);
|
|
81117
|
+
if (!Number.isFinite(cells) || cells <= 0 || segments.length === 0)
|
|
81118
|
+
return "";
|
|
81119
|
+
const split = splitCells(segments.map((s) => s.value), cells);
|
|
81120
|
+
if (split.reduce((a, b) => a + b, 0) === 0)
|
|
81121
|
+
return paint(TRACK2.repeat(cells), tokens.border);
|
|
81122
|
+
let out = "";
|
|
81123
|
+
for (let i = 0;i < split.length; i++) {
|
|
81124
|
+
const n = split[i];
|
|
81125
|
+
if (n > 0)
|
|
81126
|
+
out += `${bg(segments[i].color)}${" ".repeat(n)}`;
|
|
81127
|
+
}
|
|
81128
|
+
return out + RESET4;
|
|
81129
|
+
}
|
|
81130
|
+
function badge(label, hex4) {
|
|
81131
|
+
return `${BOLD5}${fg(pickInk(hex4))}${bg(hex4)} ${label} ${RESET4}`;
|
|
81132
|
+
}
|
|
81133
|
+
function stripAnsi4(s) {
|
|
81134
|
+
return s.replace(ANSI_RE3, "");
|
|
81135
|
+
}
|
|
81136
|
+
function visibleWidth(s) {
|
|
81137
|
+
return displayWidth(stripAnsi4(s));
|
|
81138
|
+
}
|
|
81139
|
+
function clipStyled(s, width) {
|
|
81140
|
+
if (width <= 0)
|
|
81141
|
+
return "";
|
|
81142
|
+
if (visibleWidth(s) <= width)
|
|
81143
|
+
return s;
|
|
81144
|
+
let out = "";
|
|
81145
|
+
let w = 0;
|
|
81146
|
+
let i = 0;
|
|
81147
|
+
while (i < s.length) {
|
|
81148
|
+
if (s[i] === "\x1B") {
|
|
81149
|
+
const end = s.indexOf("m", i);
|
|
81150
|
+
if (end === -1)
|
|
81151
|
+
break;
|
|
81152
|
+
out += s.slice(i, end + 1);
|
|
81153
|
+
i = end + 1;
|
|
81154
|
+
continue;
|
|
81155
|
+
}
|
|
81156
|
+
const ch = [...s.slice(i)][0] ?? "";
|
|
81157
|
+
const cw = displayWidth(ch);
|
|
81158
|
+
if (w + cw > width)
|
|
81159
|
+
break;
|
|
81160
|
+
out += ch;
|
|
81161
|
+
w += cw;
|
|
81162
|
+
i += ch.length;
|
|
81163
|
+
}
|
|
81164
|
+
return out + RESET4;
|
|
81165
|
+
}
|
|
81166
|
+
function padVisible2(s, width, align = "left") {
|
|
81167
|
+
const clipped = clipStyled(s, width);
|
|
81168
|
+
const pad2 = Math.max(0, width - visibleWidth(clipped));
|
|
81169
|
+
return align === "left" ? clipped + " ".repeat(pad2) : " ".repeat(pad2) + clipped;
|
|
81170
|
+
}
|
|
81171
|
+
function compact(n) {
|
|
81172
|
+
if (!Number.isFinite(n))
|
|
81173
|
+
return "\u2014";
|
|
81174
|
+
const a = Math.abs(n);
|
|
81175
|
+
if (a >= 1e9)
|
|
81176
|
+
return `${(n / 1e9).toFixed(1)}G`;
|
|
81177
|
+
if (a >= 1e6)
|
|
81178
|
+
return `${(n / 1e6).toFixed(1)}M`;
|
|
81179
|
+
if (a >= 1000)
|
|
81180
|
+
return `${(n / 1000).toFixed(1)}K`;
|
|
81181
|
+
return String(Math.round(n));
|
|
81182
|
+
}
|
|
81183
|
+
function duration3(ms) {
|
|
81184
|
+
if (!Number.isFinite(ms) || ms <= 0)
|
|
81185
|
+
return "\u2014";
|
|
81186
|
+
const s = Math.round(ms / 1000);
|
|
81187
|
+
if (s < 60)
|
|
81188
|
+
return `${s}s`;
|
|
81189
|
+
const m = Math.floor(s / 60);
|
|
81190
|
+
if (m < 60)
|
|
81191
|
+
return `${m}m ${String(s % 60).padStart(2, "0")}s`;
|
|
81192
|
+
return `${Math.floor(m / 60)}h ${String(m % 60).padStart(2, "0")}m`;
|
|
81193
|
+
}
|
|
81194
|
+
function usd(n) {
|
|
81195
|
+
if (!Number.isFinite(n))
|
|
81196
|
+
return "\u2014";
|
|
81197
|
+
const a = Math.abs(n);
|
|
81198
|
+
if (a === 0)
|
|
81199
|
+
return "$0";
|
|
81200
|
+
if (a < 0.01)
|
|
81201
|
+
return `$${n.toFixed(4)}`;
|
|
81202
|
+
if (a < 1)
|
|
81203
|
+
return `$${n.toFixed(3)}`;
|
|
81204
|
+
return `$${n.toFixed(2)}`;
|
|
81205
|
+
}
|
|
81206
|
+
var RESET4 = "\x1B[0m", BOLD5 = "\x1B[1m", FILL2 = "\u2588", TRACK2 = "\u2591", NODATA2 = "\u254C", ANSI_RE3;
|
|
81207
|
+
var init_ansi_viz = __esm(() => {
|
|
81208
|
+
init_color();
|
|
81209
|
+
init_text();
|
|
81210
|
+
init_tokens();
|
|
81211
|
+
init_widgets();
|
|
81212
|
+
ANSI_RE3 = /\x1b\[[0-9;]*m/g;
|
|
81213
|
+
});
|
|
81214
|
+
|
|
81215
|
+
// src/session/session-summary.ts
|
|
81216
|
+
var exports_session_summary = {};
|
|
81217
|
+
__export(exports_session_summary, {
|
|
81218
|
+
renderSessionSummary: () => renderSessionSummary,
|
|
81219
|
+
printSessionSummary: () => printSessionSummary
|
|
81220
|
+
});
|
|
81221
|
+
function cardWidth() {
|
|
81222
|
+
const cols = process.stdout.columns || 80;
|
|
81223
|
+
return Math.max(MIN_W, Math.min(MAX_W, cols - 2));
|
|
81224
|
+
}
|
|
81225
|
+
function renderSessionSummary(input) {
|
|
81226
|
+
const { stats, modelSpec, resumeModelSpec, resumeId, exitCode } = input;
|
|
81227
|
+
const W2 = cardWidth();
|
|
81228
|
+
const inner = W2 - CHROME;
|
|
81229
|
+
const out = [];
|
|
81230
|
+
const dim3 = (s) => paint(s, tokens.subtle);
|
|
81231
|
+
const body = (s) => paint(s, tokens.text);
|
|
81232
|
+
const titleText = exitCode === 0 ? " session " : " session \xB7 failed ";
|
|
81233
|
+
const titleHex = exitCode === 0 ? tokens.accent : tokens.error;
|
|
81234
|
+
const rule = "\u2500".repeat(Math.max(0, W2 - 2 - visibleWidth(titleText) - 1));
|
|
81235
|
+
out.push(paint("\u256D\u2500", tokens.border) + paint(titleText, titleHex) + paint(rule + "\u256E", tokens.border));
|
|
81236
|
+
const row = (s) => {
|
|
81237
|
+
out.push(`${paint("\u2502", tokens.border)} ${padVisible2(s, inner)} ${paint("\u2502", tokens.border)}`);
|
|
81238
|
+
};
|
|
81239
|
+
const blank = () => row("");
|
|
81240
|
+
const chips = [badge(truncate3(modelSpec, 34), tokens.accent)];
|
|
81241
|
+
if (stats.isFree)
|
|
81242
|
+
chips.push(badge("FREE", C.pillKeyBg));
|
|
81243
|
+
else if (stats.isEstimated)
|
|
81244
|
+
chips.push(badge("EST", "#8a7d1e"));
|
|
81245
|
+
if (exitCode !== 0)
|
|
81246
|
+
chips.push(badge(`EXIT ${exitCode}`, "#9e2b2b"));
|
|
81247
|
+
const right = body(duration3(stats.durationMs));
|
|
81248
|
+
const left = clipStyled(chips.join(" "), Math.max(0, inner - visibleWidth(right) - 1));
|
|
81249
|
+
const gap = Math.max(1, inner - visibleWidth(left) - visibleWidth(right));
|
|
81250
|
+
row(left + " ".repeat(gap) + right);
|
|
81251
|
+
if (stats.providerName)
|
|
81252
|
+
row(dim3(truncate3(stats.providerName, inner)));
|
|
81253
|
+
blank();
|
|
81254
|
+
const VALUE_W = 24;
|
|
81255
|
+
const barW = Math.max(12, inner - LABEL_W - VALUE_W);
|
|
81256
|
+
const dataRow = (label, bar, values) => {
|
|
81257
|
+
row(dim3(padTo(label, LABEL_W)) + bar + padVisible2(values, VALUE_W, "right"));
|
|
81258
|
+
};
|
|
81259
|
+
if (stats.contextUsed !== null && stats.contextWindow) {
|
|
81260
|
+
const pct = stats.contextUsed * 100;
|
|
81261
|
+
dataRow("context", meter(pct, barW, ramps.load), body(padStartTo(`${Math.round(pct)}%`, 4)) + dim3(` ${compact(stats.inputTokens)}/${compact(stats.contextWindow)}`));
|
|
81262
|
+
}
|
|
81263
|
+
dataRow("tokens", stackedBar([
|
|
81264
|
+
{ value: stats.inputTokens, color: C.blue },
|
|
81265
|
+
{ value: stats.outputTokens, color: C.cyan }
|
|
81266
|
+
], barW), dim3("in ") + body(compact(stats.inputTokens)) + dim3(" out ") + body(compact(stats.outputTokens)));
|
|
81267
|
+
if (!stats.isFree && stats.inputCostUsd + stats.outputCostUsd > 0) {
|
|
81268
|
+
dataRow("spend", stackedBar([
|
|
81269
|
+
{ value: stats.inputCostUsd, color: C.blue },
|
|
81270
|
+
{ value: stats.outputCostUsd, color: C.cyan }
|
|
81271
|
+
], barW), dim3("in ") + body(usd(stats.inputCostUsd)) + dim3(" out ") + body(usd(stats.outputCostUsd)));
|
|
81272
|
+
}
|
|
81273
|
+
if (stats.toolCallTotal > 0) {
|
|
81274
|
+
const shown = stats.toolCalls.slice(0, TOOL_COLORS.length);
|
|
81275
|
+
const rest = stats.toolCalls.slice(TOOL_COLORS.length).reduce((a, t) => a + t.count, 0);
|
|
81276
|
+
const segs = shown.map((t, i) => ({ value: t.count, color: TOOL_COLORS[i] }));
|
|
81277
|
+
if (rest > 0)
|
|
81278
|
+
segs.push({ value: rest, color: TOOL_OTHER });
|
|
81279
|
+
dataRow("tools", stackedBar(segs, barW), body(padStartTo(String(stats.toolCallTotal), 4)) + dim3(" calls"));
|
|
81280
|
+
const legend = shown.map((t, i) => paint(`${t.name} ${t.count}`, TOOL_COLORS[i])).concat(rest > 0 ? [paint(`other ${rest}`, TOOL_OTHER)] : []);
|
|
81281
|
+
for (const line of wrapStyled(legend, dim3(" \xB7 "), inner - LABEL_W)) {
|
|
81282
|
+
row(" ".repeat(LABEL_W) + line);
|
|
81283
|
+
}
|
|
81284
|
+
}
|
|
81285
|
+
blank();
|
|
81286
|
+
row(dim3(padTo("cost", LABEL_W)) + paint(stats.isFree ? "free" : usd(stats.costUsd), stats.isFree ? tokens.success : tokens.text, true) + (stats.isEstimated && !stats.isFree ? dim3(" estimated") : ""));
|
|
81287
|
+
for (const s of stats.savings) {
|
|
81288
|
+
const label = padTo(`vs ${s.label}`, LABEL_W);
|
|
81289
|
+
if (s.savedUsd >= 0) {
|
|
81290
|
+
const pct = s.baselineUsd > 0 ? s.savedUsd / s.baselineUsd * 100 : 0;
|
|
81291
|
+
dataRow(label, meter(pct, barW, ramps.savings), paint(padStartTo(`${Math.round(pct)}%`, 4), tokens.success) + dim3(" saved ") + paint(usd(s.savedUsd), tokens.success));
|
|
81292
|
+
} else {
|
|
81293
|
+
dataRow(label, meter(0, barW, ramps.savings), dim3("over by ") + paint(usd(-s.savedUsd), tokens.error));
|
|
81294
|
+
}
|
|
81295
|
+
}
|
|
81296
|
+
out.push(paint(`\u2570${"\u2500".repeat(W2 - 2)}\u256F`, tokens.border));
|
|
81297
|
+
if (resumeId) {
|
|
81298
|
+
out.push("");
|
|
81299
|
+
out.push(dim3("Resume this session with:"));
|
|
81300
|
+
const modelFlag = resumeModelSpec ? `--model ${resumeModelSpec} ` : "";
|
|
81301
|
+
out.push(`claudish ${modelFlag}--resume ${resumeId}`);
|
|
81302
|
+
}
|
|
81303
|
+
return out;
|
|
81304
|
+
}
|
|
81305
|
+
function wrapStyled(chips, sep, width) {
|
|
81306
|
+
const lines = [];
|
|
81307
|
+
let cur = "";
|
|
81308
|
+
let curW = 0;
|
|
81309
|
+
const sepW = visibleWidth(sep);
|
|
81310
|
+
for (const chip of chips) {
|
|
81311
|
+
const w = visibleWidth(chip);
|
|
81312
|
+
if (cur && curW + sepW + w > width) {
|
|
81313
|
+
lines.push(cur);
|
|
81314
|
+
cur = chip;
|
|
81315
|
+
curW = w;
|
|
81316
|
+
} else {
|
|
81317
|
+
cur = cur ? cur + sep + chip : chip;
|
|
81318
|
+
curW = cur === chip ? w : curW + sepW + w;
|
|
81319
|
+
}
|
|
81320
|
+
}
|
|
81321
|
+
if (cur)
|
|
81322
|
+
lines.push(cur);
|
|
81323
|
+
return lines;
|
|
81324
|
+
}
|
|
81325
|
+
function printSessionSummary(input, write) {
|
|
81326
|
+
for (const line of renderSessionSummary(input))
|
|
81327
|
+
write(line);
|
|
81328
|
+
write(RESET4);
|
|
81329
|
+
}
|
|
81330
|
+
var TOOL_COLORS, TOOL_OTHER, MIN_W = 62, MAX_W = 96, CHROME = 4, LABEL_W = 10;
|
|
81331
|
+
var init_session_summary = __esm(() => {
|
|
81332
|
+
init_theme2();
|
|
81333
|
+
init_text();
|
|
81334
|
+
init_tokens();
|
|
81335
|
+
init_ansi_viz();
|
|
81336
|
+
TOOL_COLORS = [
|
|
81337
|
+
C.blue,
|
|
81338
|
+
C.cyan,
|
|
81339
|
+
"#8a7d1e",
|
|
81340
|
+
"#1f6d75",
|
|
81341
|
+
C.magenta,
|
|
81342
|
+
"#2d6e3e",
|
|
81343
|
+
C.orange
|
|
81344
|
+
];
|
|
81345
|
+
TOOL_OTHER = C.dim;
|
|
81346
|
+
});
|
|
81347
|
+
|
|
78325
81348
|
// src/index.ts
|
|
78326
81349
|
init_op_source();
|
|
78327
81350
|
init_startup_trace();
|
|
78328
81351
|
var import_dotenv3 = __toESM(require_main(), 1);
|
|
78329
|
-
import { existsSync as existsSync29, readFileSync as
|
|
78330
|
-
import { join as
|
|
81352
|
+
import { existsSync as existsSync29, readFileSync as readFileSync29 } from "fs";
|
|
81353
|
+
import { join as join39, resolve as resolve5 } from "path";
|
|
78331
81354
|
import_dotenv3.config({ quiet: true });
|
|
78332
81355
|
function classifyStartupKind() {
|
|
78333
81356
|
const argv = process.argv.slice(2);
|
|
@@ -78570,14 +81593,14 @@ async function runCli() {
|
|
|
78570
81593
|
if (cliConfig.team && cliConfig.team.length > 0) {
|
|
78571
81594
|
let prompt = cliConfig.claudeArgs.join(" ");
|
|
78572
81595
|
if (cliConfig.inputFile) {
|
|
78573
|
-
prompt =
|
|
81596
|
+
prompt = readFileSync29(cliConfig.inputFile, "utf-8");
|
|
78574
81597
|
}
|
|
78575
81598
|
if (!prompt.trim()) {
|
|
78576
81599
|
console.error("Error: --team requires a prompt (positional args or -f <file>)");
|
|
78577
81600
|
process.exit(1);
|
|
78578
81601
|
}
|
|
78579
81602
|
const mode = cliConfig.teamMode ?? "default";
|
|
78580
|
-
const sessionPath =
|
|
81603
|
+
const sessionPath = join39(process.cwd(), `.claudish-team-${Date.now()}`);
|
|
78581
81604
|
if (mode === "json") {
|
|
78582
81605
|
const { setupSession: setupSession2, runModels: runModels2 } = await Promise.resolve().then(() => (init_team_orchestrator(), exports_team_orchestrator));
|
|
78583
81606
|
setupSession2(sessionPath, cliConfig.team, prompt);
|
|
@@ -78587,9 +81610,9 @@ async function runCli() {
|
|
|
78587
81610
|
});
|
|
78588
81611
|
const result = { ...status2, responses: {} };
|
|
78589
81612
|
for (const anonId of Object.keys(status2.models)) {
|
|
78590
|
-
const responsePath =
|
|
81613
|
+
const responsePath = join39(sessionPath, `response-${anonId}.md`);
|
|
78591
81614
|
try {
|
|
78592
|
-
const raw2 =
|
|
81615
|
+
const raw2 = readFileSync29(responsePath, "utf-8").trim();
|
|
78593
81616
|
try {
|
|
78594
81617
|
result.responses[anonId] = JSON.parse(raw2);
|
|
78595
81618
|
} catch {
|
|
@@ -78614,8 +81637,8 @@ async function runCli() {
|
|
|
78614
81637
|
Team Status`);
|
|
78615
81638
|
for (const id of modelIds) {
|
|
78616
81639
|
const m = status.models[id];
|
|
78617
|
-
const
|
|
78618
|
-
console.log(` ${id} ${m.state.padEnd(10)} ${
|
|
81640
|
+
const duration4 = m.startedAt && m.completedAt ? `${Math.round((new Date(m.completedAt).getTime() - new Date(m.startedAt).getTime()) / 1000)}s` : "pending";
|
|
81641
|
+
console.log(` ${id} ${m.state.padEnd(10)} ${duration4}`);
|
|
78619
81642
|
}
|
|
78620
81643
|
process.exit(0);
|
|
78621
81644
|
}
|
|
@@ -78766,6 +81789,28 @@ Team Status`);
|
|
|
78766
81789
|
haiku: cliConfig.modelHaiku,
|
|
78767
81790
|
subagent: cliConfig.modelSubagent
|
|
78768
81791
|
};
|
|
81792
|
+
let resumedSessionId = (() => {
|
|
81793
|
+
const i = cliConfig.claudeArgs.indexOf("--resume");
|
|
81794
|
+
const v = i !== -1 ? cliConfig.claudeArgs[i + 1] : undefined;
|
|
81795
|
+
return v && !v.startsWith("-") ? v : null;
|
|
81796
|
+
})();
|
|
81797
|
+
if (cliConfig._resumePicker) {
|
|
81798
|
+
const canDrawTui = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
81799
|
+
if (!canDrawTui || cliConfig._hasPrintFlag || !cliConfig.interactive) {
|
|
81800
|
+
cliConfig.claudeArgs.push("--resume");
|
|
81801
|
+
} else {
|
|
81802
|
+
const { runResumePicker: runResumePicker2 } = await Promise.resolve().then(() => (init_resume_picker_run(), exports_resume_picker_run));
|
|
81803
|
+
const outcome = await runResumePicker2();
|
|
81804
|
+
if (!outcome.hadSessions) {
|
|
81805
|
+
cliConfig.claudeArgs.push("--resume");
|
|
81806
|
+
} else if (!outcome.sessionId) {
|
|
81807
|
+
process.exit(0);
|
|
81808
|
+
} else {
|
|
81809
|
+
cliConfig.claudeArgs.push("--resume", outcome.sessionId);
|
|
81810
|
+
resumedSessionId = outcome.sessionId;
|
|
81811
|
+
}
|
|
81812
|
+
}
|
|
81813
|
+
}
|
|
78769
81814
|
const proxy = await traceSpan("startup:proxy-start", () => createProxyServer2(port, cliConfig.monitor ? undefined : cliConfig.openrouterApiKey, cliConfig.monitor ? undefined : explicitModel, cliConfig.monitor, cliConfig.anthropicApiKey, modelMap, {
|
|
78770
81815
|
summarizeTools: cliConfig.summarizeTools,
|
|
78771
81816
|
quiet: cliConfig.quiet,
|
|
@@ -78799,6 +81844,25 @@ Team Status`);
|
|
|
78799
81844
|
const write = cliConfig.interactive ? console.log : console.error;
|
|
78800
81845
|
write(`[claudish] Done
|
|
78801
81846
|
`);
|
|
81847
|
+
try {
|
|
81848
|
+
const [{ readSessionStats: readSessionStats2 }, { printSessionSummary: printSessionSummary2 }, { findLatestSessionId: findLatestSessionId2 }] = await Promise.all([
|
|
81849
|
+
Promise.resolve().then(() => (init_session_stats(), exports_session_stats)),
|
|
81850
|
+
Promise.resolve().then(() => (init_session_summary(), exports_session_summary)),
|
|
81851
|
+
Promise.resolve().then(() => (init_session_discovery(), exports_session_discovery))
|
|
81852
|
+
]);
|
|
81853
|
+
const stats = readSessionStats2(port);
|
|
81854
|
+
if (stats) {
|
|
81855
|
+
printSessionSummary2({
|
|
81856
|
+
stats,
|
|
81857
|
+
modelSpec: explicitModel || stats.modelName || "",
|
|
81858
|
+
resumeModelSpec: explicitModel ?? null,
|
|
81859
|
+
resumeId: resumedSessionId ?? findLatestSessionId2(process.cwd(), Date.now() - stats.durationMs),
|
|
81860
|
+
exitCode
|
|
81861
|
+
}, write);
|
|
81862
|
+
}
|
|
81863
|
+
} catch (e) {
|
|
81864
|
+
console.error(`[claudish] session summary unavailable: ${e}`);
|
|
81865
|
+
}
|
|
78802
81866
|
}
|
|
78803
81867
|
const sessionLogPath = getAlwaysOnLogPath2();
|
|
78804
81868
|
if (exitCode !== 0 && sessionLogPath && !cliConfig.quiet) {
|