claudish 14.13.0 → 14.14.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 +1784 -1063
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -5491,7 +5491,7 @@ var init_onepassword_config = __esm(() => {
|
|
|
5491
5491
|
});
|
|
5492
5492
|
|
|
5493
5493
|
// src/version.ts
|
|
5494
|
-
var VERSION = "14.
|
|
5494
|
+
var VERSION = "14.14.0";
|
|
5495
5495
|
|
|
5496
5496
|
// src/redact.ts
|
|
5497
5497
|
function isPlaceholder(value) {
|
|
@@ -21696,6 +21696,19 @@ class AccountRegistry {
|
|
|
21696
21696
|
this.legacyStamp = undefined;
|
|
21697
21697
|
return false;
|
|
21698
21698
|
}
|
|
21699
|
+
if (this.configStatus.state === "failed" && this.lastGoodSettings !== undefined) {
|
|
21700
|
+
const path = this.legacyPath();
|
|
21701
|
+
const detail = "not re-read while the account list config cannot be read";
|
|
21702
|
+
this.legacyStamp = undefined;
|
|
21703
|
+
this.legacyStatus = {
|
|
21704
|
+
state: "failed",
|
|
21705
|
+
error: detail,
|
|
21706
|
+
failure: { source: "legacy", path, type: "io", detail },
|
|
21707
|
+
path,
|
|
21708
|
+
servingLastKnownGood: this.lastGoodLegacy !== undefined
|
|
21709
|
+
};
|
|
21710
|
+
return true;
|
|
21711
|
+
}
|
|
21699
21712
|
this.refreshLegacy();
|
|
21700
21713
|
return true;
|
|
21701
21714
|
}
|
|
@@ -21756,10 +21769,7 @@ class AccountRegistry {
|
|
|
21756
21769
|
}
|
|
21757
21770
|
}
|
|
21758
21771
|
isSynthesizedLegacyOnly() {
|
|
21759
|
-
|
|
21760
|
-
const rows = snapshot.accounts["openai-codex"];
|
|
21761
|
-
const entry = snapshot.settings["openai-codex"];
|
|
21762
|
-
return !entry?.legacyCodexSuppressed && !entry?.accounts.length && rows.length === 1 && rows[0].legacy;
|
|
21772
|
+
return isSynthesizedLegacyOnlySnapshot(this.snapshot("openai-codex"));
|
|
21763
21773
|
}
|
|
21764
21774
|
listAccounts(provider) {
|
|
21765
21775
|
return this.snapshot(provider).accounts[provider];
|
|
@@ -21781,6 +21791,18 @@ class AccountRegistry {
|
|
|
21781
21791
|
return { config: this.configStatus, legacy: this.legacyStatus };
|
|
21782
21792
|
}
|
|
21783
21793
|
}
|
|
21794
|
+
function isSynthesizedLegacyOnlySnapshot(snapshot) {
|
|
21795
|
+
const rows = snapshot.accounts["openai-codex"];
|
|
21796
|
+
const entry = snapshot.settings["openai-codex"];
|
|
21797
|
+
return !entry?.legacyCodexSuppressed && !entry?.accounts.length && rows.length === 1 && rows[0].legacy;
|
|
21798
|
+
}
|
|
21799
|
+
function codexSourceFailed(snapshot) {
|
|
21800
|
+
if (snapshot.config.state === "failed")
|
|
21801
|
+
return snapshot.config;
|
|
21802
|
+
if (snapshot.legacy.state === "failed")
|
|
21803
|
+
return snapshot.legacy;
|
|
21804
|
+
return;
|
|
21805
|
+
}
|
|
21784
21806
|
function getAccountRegistry() {
|
|
21785
21807
|
if (!registry2)
|
|
21786
21808
|
registry2 = new AccountRegistry;
|
|
@@ -24428,6 +24450,10 @@ var init_keychain = __esm(() => {
|
|
|
24428
24450
|
// src/auth/accounts/token-store.ts
|
|
24429
24451
|
import { createHash as createHash2 } from "crypto";
|
|
24430
24452
|
import { existsSync as existsSync9, lstatSync as lstatSync2, readFileSync as readFileSync14, rmSync as rmSync4 } from "fs";
|
|
24453
|
+
import { isDeepStrictEqual } from "util";
|
|
24454
|
+
function sameDurableRecord(a, b) {
|
|
24455
|
+
return a.accessToken === b.accessToken && a.refreshToken === b.refreshToken && a.expiresAt === b.expiresAt && a.accountId === b.accountId && a.generation === b.generation && a.fingerprint === b.fingerprint && isDeepStrictEqual(a.journal ?? null, b.journal ?? null);
|
|
24456
|
+
}
|
|
24431
24457
|
function fingerprintOf(refreshToken) {
|
|
24432
24458
|
return createHash2("sha256").update(refreshToken).digest("hex").slice(0, 16);
|
|
24433
24459
|
}
|
|
@@ -24435,8 +24461,8 @@ function parseRecord(kind, text) {
|
|
|
24435
24461
|
let raw;
|
|
24436
24462
|
try {
|
|
24437
24463
|
raw = JSON.parse(text);
|
|
24438
|
-
} catch
|
|
24439
|
-
throw new TokenStoreReadError(kind,
|
|
24464
|
+
} catch {
|
|
24465
|
+
throw new TokenStoreReadError(kind, "the stored record is not JSON (invalid JSON)");
|
|
24440
24466
|
}
|
|
24441
24467
|
if (typeof raw?.accessToken !== "string" || typeof raw?.refreshToken !== "string") {
|
|
24442
24468
|
throw new TokenStoreReadError(kind, "the stored record has no token pair");
|
|
@@ -24598,8 +24624,8 @@ class LegacyCodexFileStore {
|
|
|
24598
24624
|
let raw;
|
|
24599
24625
|
try {
|
|
24600
24626
|
raw = JSON.parse(text);
|
|
24601
|
-
} catch
|
|
24602
|
-
throw new TokenStoreReadError(this.kind,
|
|
24627
|
+
} catch {
|
|
24628
|
+
throw new TokenStoreReadError(this.kind, "invalid JSON");
|
|
24603
24629
|
}
|
|
24604
24630
|
if (!raw?.access_token || !raw?.refresh_token) {
|
|
24605
24631
|
throw new TokenStoreReadError(this.kind, "the credential file has no token pair");
|
|
@@ -24765,6 +24791,7 @@ class TokenManager {
|
|
|
24765
24791
|
async resolve(ref, forced, rejectedFp) {
|
|
24766
24792
|
const current = await this.readOrThrow(ref, { uncached: forced });
|
|
24767
24793
|
if (!current) {
|
|
24794
|
+
this.validateAccount?.(ref, "store-empty");
|
|
24768
24795
|
throw new NeedsReauthError(`No stored credential for ${ref.label}. Run a login for this account.`);
|
|
24769
24796
|
}
|
|
24770
24797
|
await this.healIfMaterialMoved(ref, current);
|
|
@@ -24849,6 +24876,7 @@ class TokenManager {
|
|
|
24849
24876
|
this.validateAccount?.(ref, "before-exchange");
|
|
24850
24877
|
let record = await this.readOrThrow(ref, { uncached: true });
|
|
24851
24878
|
if (!record) {
|
|
24879
|
+
this.validateAccount?.(ref, "store-empty");
|
|
24852
24880
|
throw new NeedsReauthError(`No stored credential for ${ref.label}. Run a login for this account.`);
|
|
24853
24881
|
}
|
|
24854
24882
|
await this.healIfMaterialMoved(ref, record);
|
|
@@ -24920,6 +24948,7 @@ class TokenManager {
|
|
|
24920
24948
|
this.validateAccount?.(ref, "before-persist");
|
|
24921
24949
|
const latest = await this.readOrThrow(ref, { uncached: true });
|
|
24922
24950
|
if (!latest) {
|
|
24951
|
+
this.validateAccount?.(ref, "store-empty");
|
|
24923
24952
|
throw new NeedsReauthError(`The stored credential for ${ref.label} was removed during refresh.`);
|
|
24924
24953
|
}
|
|
24925
24954
|
if (latest.generation > record.generation || latest.fingerprint !== record.fingerprint) {
|
|
@@ -25266,6 +25295,64 @@ function tokenOptionsFor(opts, reason) {
|
|
|
25266
25295
|
...opts.rejectedAccessToken ? { rejectedAccessToken: opts.rejectedAccessToken } : {}
|
|
25267
25296
|
};
|
|
25268
25297
|
}
|
|
25298
|
+
|
|
25299
|
+
class ReauthAttemptStamps {
|
|
25300
|
+
committed = new Map;
|
|
25301
|
+
inFlight = new Map;
|
|
25302
|
+
get(id) {
|
|
25303
|
+
let latest = this.committed.get(id);
|
|
25304
|
+
for (const mark of this.inFlight.get(id) ?? []) {
|
|
25305
|
+
if (latest === undefined || mark.at > latest)
|
|
25306
|
+
latest = mark.at;
|
|
25307
|
+
}
|
|
25308
|
+
return latest;
|
|
25309
|
+
}
|
|
25310
|
+
snapshot() {
|
|
25311
|
+
const out = new Map;
|
|
25312
|
+
for (const id of new Set([...this.committed.keys(), ...this.inFlight.keys()])) {
|
|
25313
|
+
const at = this.get(id);
|
|
25314
|
+
if (at !== undefined)
|
|
25315
|
+
out.set(id, at);
|
|
25316
|
+
}
|
|
25317
|
+
return out;
|
|
25318
|
+
}
|
|
25319
|
+
begin(id, at) {
|
|
25320
|
+
const mark = { at };
|
|
25321
|
+
let marks = this.inFlight.get(id);
|
|
25322
|
+
if (!marks) {
|
|
25323
|
+
marks = new Set;
|
|
25324
|
+
this.inFlight.set(id, marks);
|
|
25325
|
+
}
|
|
25326
|
+
marks.add(mark);
|
|
25327
|
+
const withdraw = () => {
|
|
25328
|
+
const current = this.inFlight.get(id);
|
|
25329
|
+
if (!current?.delete(mark))
|
|
25330
|
+
return false;
|
|
25331
|
+
if (current.size === 0)
|
|
25332
|
+
this.inFlight.delete(id);
|
|
25333
|
+
return true;
|
|
25334
|
+
};
|
|
25335
|
+
return {
|
|
25336
|
+
commit: () => {
|
|
25337
|
+
if (!withdraw())
|
|
25338
|
+
return;
|
|
25339
|
+
const previous = this.committed.get(id);
|
|
25340
|
+
if (previous === undefined || at > previous)
|
|
25341
|
+
this.committed.set(id, at);
|
|
25342
|
+
},
|
|
25343
|
+
restore: () => {
|
|
25344
|
+
withdraw();
|
|
25345
|
+
}
|
|
25346
|
+
};
|
|
25347
|
+
}
|
|
25348
|
+
clear(id) {
|
|
25349
|
+
this.committed.delete(id);
|
|
25350
|
+
this.inFlight.delete(id);
|
|
25351
|
+
}
|
|
25352
|
+
}
|
|
25353
|
+
function isStoreOnlyMove(leased, current) {
|
|
25354
|
+
return current !== undefined && current.id === leased.id && current.enabled && current.store !== leased.store && current.identity.chatgptAccountId === leased.identity.chatgptAccountId;
|
|
25355
|
+
}
|
|
25269
25356
|
function leaseFailureBlocker(ref, err) {
|
|
25270
25357
|
if (err instanceof AccountChangedDuringLeaseError) {
|
|
25271
25358
|
return {
|
|
@@ -25317,7 +25404,7 @@ class AccountPool {
|
|
|
25317
25404
|
storeFactory;
|
|
25318
25405
|
env;
|
|
25319
25406
|
managers = new Map;
|
|
25320
|
-
lastReauthAttempt = new
|
|
25407
|
+
lastReauthAttempt = new ReauthAttemptStamps;
|
|
25321
25408
|
leaseFailures = new Map;
|
|
25322
25409
|
constructor(provider, opts = {}) {
|
|
25323
25410
|
this.provider = provider;
|
|
@@ -25341,7 +25428,7 @@ class AccountPool {
|
|
|
25341
25428
|
provider: this.provider,
|
|
25342
25429
|
failed: new Map,
|
|
25343
25430
|
leaseFailures: new Map(this.leaseFailures),
|
|
25344
|
-
reauthAttempts:
|
|
25431
|
+
reauthAttempts: this.lastReauthAttempt.snapshot()
|
|
25345
25432
|
};
|
|
25346
25433
|
if (allowance && this.allowanceStrategyOn()) {
|
|
25347
25434
|
try {
|
|
@@ -25421,9 +25508,12 @@ class AccountPool {
|
|
|
25421
25508
|
await this.healReauthMarks(context, opts.pin);
|
|
25422
25509
|
evaluation = this.evaluate(opts, context);
|
|
25423
25510
|
const attempts = evaluation.records.length + 1;
|
|
25511
|
+
let storeMovesLeft = evaluation.records.length + 1;
|
|
25512
|
+
let fresh = true;
|
|
25424
25513
|
for (let attempt = 0;attempt < attempts; attempt++) {
|
|
25425
|
-
if (
|
|
25514
|
+
if (!fresh)
|
|
25426
25515
|
evaluation = this.evaluate(opts, context);
|
|
25516
|
+
fresh = false;
|
|
25427
25517
|
const selection = evaluation.selection;
|
|
25428
25518
|
if (selection.kind === "none")
|
|
25429
25519
|
throw this.acquireNoEligibleError(evaluation, context, opts);
|
|
@@ -25439,23 +25529,15 @@ class AccountPool {
|
|
|
25439
25529
|
continue;
|
|
25440
25530
|
}
|
|
25441
25531
|
const at = this.now();
|
|
25442
|
-
this.
|
|
25443
|
-
|
|
25444
|
-
|
|
25445
|
-
|
|
25446
|
-
continue;
|
|
25447
|
-
const stillConfigured = this.accounts().some((a) => a.id === record.id && a.enabled && a.store === record.store);
|
|
25448
|
-
if (!stillConfigured) {
|
|
25449
|
-
context.failed.set(record.id, {
|
|
25450
|
-
kind: "unavailable",
|
|
25451
|
-
ref: candidate.ref,
|
|
25452
|
-
reason: "account changed while leasing",
|
|
25453
|
-
upstreamStatus: 503
|
|
25454
|
-
});
|
|
25455
|
-
if (opts.pin)
|
|
25456
|
-
throw new NoUsableAccountError(`Pinned ${vendorName(this.provider)} account ${record.label} changed while leasing.`);
|
|
25532
|
+
const step = await this.leaseStep(record, candidate.ref, reason, at, context, opts);
|
|
25533
|
+
if (step === STORE_MOVED) {
|
|
25534
|
+
if (storeMovesLeft-- > 0)
|
|
25535
|
+
attempt--;
|
|
25457
25536
|
continue;
|
|
25458
25537
|
}
|
|
25538
|
+
if (!step)
|
|
25539
|
+
continue;
|
|
25540
|
+
const token = step;
|
|
25459
25541
|
this.leaseFailures.delete(record.id);
|
|
25460
25542
|
context.leaseFailures.delete(record.id);
|
|
25461
25543
|
const threadKey = evaluation.ranked ? context.allowance?.threadKey ?? null : null;
|
|
@@ -25490,6 +25572,95 @@ class AccountPool {
|
|
|
25490
25572
|
}
|
|
25491
25573
|
return error;
|
|
25492
25574
|
}
|
|
25575
|
+
noteLeaseChanged(record, ref, rows, context, opts) {
|
|
25576
|
+
if (isStoreOnlyMove(record, rows.find((a) => a.id === record.id))) {
|
|
25577
|
+
if (opts.pin)
|
|
25578
|
+
throw new AccountStoreMovedDuringLeaseError(record.label);
|
|
25579
|
+
log(`[accounts] ${this.provider}: ${record.label} moved store while leasing; leasing it again`);
|
|
25580
|
+
return true;
|
|
25581
|
+
}
|
|
25582
|
+
context.failed.set(record.id, {
|
|
25583
|
+
kind: "unavailable",
|
|
25584
|
+
ref,
|
|
25585
|
+
reason: "account changed while leasing",
|
|
25586
|
+
upstreamStatus: 503
|
|
25587
|
+
});
|
|
25588
|
+
if (opts.pin)
|
|
25589
|
+
throw new NoUsableAccountError(`Pinned ${vendorName(this.provider)} account ${record.label} changed while leasing.`);
|
|
25590
|
+
return false;
|
|
25591
|
+
}
|
|
25592
|
+
async leaseStep(record, ref, reason, at, context, opts) {
|
|
25593
|
+
const stamps = this.stampReauthAttempt(record.id, at, context);
|
|
25594
|
+
let restored = false;
|
|
25595
|
+
const restore = () => {
|
|
25596
|
+
restored = true;
|
|
25597
|
+
stamps.restore();
|
|
25598
|
+
};
|
|
25599
|
+
try {
|
|
25600
|
+
const token = await this.tryLease(record, opts, reason, context);
|
|
25601
|
+
if (token === STORE_MOVED) {
|
|
25602
|
+
restore();
|
|
25603
|
+
return STORE_MOVED;
|
|
25604
|
+
}
|
|
25605
|
+
if (token === NOT_ATTEMPTED) {
|
|
25606
|
+
restore();
|
|
25607
|
+
return null;
|
|
25608
|
+
}
|
|
25609
|
+
if (!token)
|
|
25610
|
+
return null;
|
|
25611
|
+
const rows = this.accounts();
|
|
25612
|
+
const stillConfigured = rows.some((a) => a.id === record.id && a.enabled && a.store === record.store);
|
|
25613
|
+
if (stillConfigured)
|
|
25614
|
+
return token;
|
|
25615
|
+
if (!this.noteLeaseChanged(record, ref, rows, context, opts))
|
|
25616
|
+
return null;
|
|
25617
|
+
restore();
|
|
25618
|
+
return STORE_MOVED;
|
|
25619
|
+
} catch (err) {
|
|
25620
|
+
if (err instanceof AccountStoreMovedDuringLeaseError || err instanceof AccountListUnreadableDuringLeaseError) {
|
|
25621
|
+
restore();
|
|
25622
|
+
}
|
|
25623
|
+
throw err;
|
|
25624
|
+
} finally {
|
|
25625
|
+
if (!restored)
|
|
25626
|
+
stamps.commit();
|
|
25627
|
+
}
|
|
25628
|
+
}
|
|
25629
|
+
stampReauthAttempt(id, at, context) {
|
|
25630
|
+
const pool = this.lastReauthAttempt.begin(id, at);
|
|
25631
|
+
const request = context.reauthAttempts.get(id);
|
|
25632
|
+
context.reauthAttempts.set(id, at);
|
|
25633
|
+
return {
|
|
25634
|
+
restore: () => {
|
|
25635
|
+
pool.restore();
|
|
25636
|
+
if (request === undefined)
|
|
25637
|
+
context.reauthAttempts.delete(id);
|
|
25638
|
+
else
|
|
25639
|
+
context.reauthAttempts.set(id, request);
|
|
25640
|
+
},
|
|
25641
|
+
commit: () => pool.commit()
|
|
25642
|
+
};
|
|
25643
|
+
}
|
|
25644
|
+
unattemptedLease(record, ref, reason, context, err) {
|
|
25645
|
+
if (err instanceof AccountStoreMovedDuringLeaseError) {
|
|
25646
|
+
if (reason === "pinned")
|
|
25647
|
+
throw err;
|
|
25648
|
+
log(`[accounts] ${this.provider}: ${record.label} moved store while leasing; leasing it again`);
|
|
25649
|
+
return STORE_MOVED;
|
|
25650
|
+
}
|
|
25651
|
+
if (!(err instanceof AccountListUnreadableDuringLeaseError))
|
|
25652
|
+
return;
|
|
25653
|
+
context.failed.set(record.id, {
|
|
25654
|
+
kind: "unavailable",
|
|
25655
|
+
ref,
|
|
25656
|
+
reason: err.reason,
|
|
25657
|
+
upstreamStatus: err.upstreamStatus
|
|
25658
|
+
});
|
|
25659
|
+
if (reason === "pinned")
|
|
25660
|
+
throw err;
|
|
25661
|
+
log(`[accounts] ${this.provider}: ${record.label}'s account list could not be read; trying the next account`);
|
|
25662
|
+
return NOT_ATTEMPTED;
|
|
25663
|
+
}
|
|
25493
25664
|
async tryLease(record, opts, reason, context) {
|
|
25494
25665
|
const ref = this.refOf(record);
|
|
25495
25666
|
try {
|
|
@@ -25497,6 +25668,9 @@ class AccountPool {
|
|
|
25497
25668
|
} catch (err) {
|
|
25498
25669
|
if (classifyConnectionError(err))
|
|
25499
25670
|
throw err;
|
|
25671
|
+
const unattempted = this.unattemptedLease(record, ref, reason, context, err);
|
|
25672
|
+
if (unattempted)
|
|
25673
|
+
return unattempted;
|
|
25500
25674
|
const blocker = leaseFailureBlocker(ref, err);
|
|
25501
25675
|
if (!blocker)
|
|
25502
25676
|
throw err;
|
|
@@ -25517,8 +25691,15 @@ class AccountPool {
|
|
|
25517
25691
|
continue;
|
|
25518
25692
|
if (!this.state.stateFor(ref).state.needsReauth)
|
|
25519
25693
|
continue;
|
|
25520
|
-
|
|
25521
|
-
|
|
25694
|
+
let healed = await this.managerFor(record).healNeedsReauth(ref);
|
|
25695
|
+
if (!healed) {
|
|
25696
|
+
const current = this.accounts().find((a) => a.id === record.id);
|
|
25697
|
+
if (current && isStoreOnlyMove(record, current)) {
|
|
25698
|
+
healed = await this.managerFor(current).healNeedsReauth(this.refOf(current));
|
|
25699
|
+
}
|
|
25700
|
+
}
|
|
25701
|
+
if (healed) {
|
|
25702
|
+
this.lastReauthAttempt.clear(record.id);
|
|
25522
25703
|
context.reauthAttempts.delete(record.id);
|
|
25523
25704
|
}
|
|
25524
25705
|
}
|
|
@@ -25571,6 +25752,36 @@ class AccountPool {
|
|
|
25571
25752
|
}
|
|
25572
25753
|
return new NoUsableAccountError(buildUnusableMessage(input), blockers);
|
|
25573
25754
|
}
|
|
25755
|
+
validateLeasedRow(record, phase) {
|
|
25756
|
+
const legacyExchange = phase === "before-exchange" && record.store === "legacy-codex-file";
|
|
25757
|
+
const reread = phase === "store-empty" || legacyExchange;
|
|
25758
|
+
if (reread)
|
|
25759
|
+
this.registry.invalidate();
|
|
25760
|
+
let snapshot;
|
|
25761
|
+
try {
|
|
25762
|
+
snapshot = this.registry.snapshot(this.provider);
|
|
25763
|
+
} catch (err) {
|
|
25764
|
+
if (phase === "before-persist")
|
|
25765
|
+
return;
|
|
25766
|
+
throw err;
|
|
25767
|
+
}
|
|
25768
|
+
const failedSource = reread ? this.failedSourceOf(snapshot) : undefined;
|
|
25769
|
+
if (failedSource) {
|
|
25770
|
+
throw new AccountListUnreadableDuringLeaseError(record.label, failedSource.path, legacyExchange ? "its legacy file token is about to be exchanged" : "its store is empty");
|
|
25771
|
+
}
|
|
25772
|
+
const current = snapshot.accounts[this.provider].find((row) => row.id === record.id);
|
|
25773
|
+
if (isStoreOnlyMove(record, current))
|
|
25774
|
+
throw new AccountStoreMovedDuringLeaseError(record.label);
|
|
25775
|
+
const rebound = !current || current.store !== record.store || current.identity.chatgptAccountId !== record.identity.chatgptAccountId;
|
|
25776
|
+
if (rebound || phase !== "before-persist" && !current.enabled) {
|
|
25777
|
+
throw new AccountChangedDuringLeaseError(`Account ${record.label} was removed, disabled or rebound during refresh.`);
|
|
25778
|
+
}
|
|
25779
|
+
}
|
|
25780
|
+
failedSourceOf(snapshot) {
|
|
25781
|
+
if (this.provider === "openai-codex")
|
|
25782
|
+
return codexSourceFailed(snapshot);
|
|
25783
|
+
return snapshot.config.state === "failed" ? snapshot.config : undefined;
|
|
25784
|
+
}
|
|
25574
25785
|
managerFor(record) {
|
|
25575
25786
|
const key = `${record.id}:${record.store}:${record.identity.chatgptAccountId ?? ""}`;
|
|
25576
25787
|
const existing = this.managers.get(key);
|
|
@@ -25580,21 +25791,7 @@ class AccountPool {
|
|
|
25580
25791
|
store: this.storeFactory(record),
|
|
25581
25792
|
exchange: exchangeFor(this.provider),
|
|
25582
25793
|
lockPathFor: () => accountRefreshLockPath(record, this.env),
|
|
25583
|
-
validateAccount: (_ref, phase) =>
|
|
25584
|
-
let rows;
|
|
25585
|
-
try {
|
|
25586
|
-
rows = this.registry.snapshot(this.provider).accounts[this.provider];
|
|
25587
|
-
} catch (err) {
|
|
25588
|
-
if (phase === "before-persist")
|
|
25589
|
-
return;
|
|
25590
|
-
throw err;
|
|
25591
|
-
}
|
|
25592
|
-
const current = rows.find((row) => row.id === record.id);
|
|
25593
|
-
const rebound = !current || current.store !== record.store || current.identity.chatgptAccountId !== record.identity.chatgptAccountId;
|
|
25594
|
-
if (rebound || phase === "before-exchange" && !current.enabled) {
|
|
25595
|
-
throw new AccountChangedDuringLeaseError(`Account ${record.label} was removed, disabled or rebound during refresh.`);
|
|
25596
|
-
}
|
|
25597
|
-
},
|
|
25794
|
+
validateAccount: (_ref, phase) => this.validateLeasedRow(record, phase),
|
|
25598
25795
|
state: this.state,
|
|
25599
25796
|
now: this.now,
|
|
25600
25797
|
env: this.env
|
|
@@ -25708,7 +25905,7 @@ function getAccountPool(provider) {
|
|
|
25708
25905
|
pools.set(provider, pool);
|
|
25709
25906
|
return pool;
|
|
25710
25907
|
}
|
|
25711
|
-
var LEASE_FAILURE_DEPRIORITIZE_MS = 60000, AccountChangedDuringLeaseError, pools;
|
|
25908
|
+
var LEASE_FAILURE_DEPRIORITIZE_MS = 60000, AccountChangedDuringLeaseError, AccountStoreMovedDuringLeaseError, AccountListUnreadableDuringLeaseError, STORE_MOVED, NOT_ATTEMPTED, pools;
|
|
25712
25909
|
var init_pool = __esm(() => {
|
|
25713
25910
|
init_connection_error();
|
|
25714
25911
|
init_logger();
|
|
@@ -25724,6 +25921,18 @@ var init_pool = __esm(() => {
|
|
|
25724
25921
|
init_types();
|
|
25725
25922
|
AccountChangedDuringLeaseError = class AccountChangedDuringLeaseError extends NoUsableAccountError {
|
|
25726
25923
|
};
|
|
25924
|
+
AccountStoreMovedDuringLeaseError = class AccountStoreMovedDuringLeaseError extends RefreshUnavailableError {
|
|
25925
|
+
constructor(label) {
|
|
25926
|
+
super("account store moved while leasing", `${label} now reads from its new store; retry`);
|
|
25927
|
+
}
|
|
25928
|
+
};
|
|
25929
|
+
AccountListUnreadableDuringLeaseError = class AccountListUnreadableDuringLeaseError extends RefreshUnavailableError {
|
|
25930
|
+
constructor(label, configPath, what = "its store is empty") {
|
|
25931
|
+
super("account list could not be read", `${label}: ${what} and ${configPath} could not be read; retry`);
|
|
25932
|
+
}
|
|
25933
|
+
};
|
|
25934
|
+
STORE_MOVED = Symbol("store-moved");
|
|
25935
|
+
NOT_ATTEMPTED = Symbol("not-attempted");
|
|
25727
25936
|
pools = new Map;
|
|
25728
25937
|
});
|
|
25729
25938
|
|
|
@@ -25962,11 +26171,7 @@ var init_codex = __esm(() => {
|
|
|
25962
26171
|
return { kind: "headers" };
|
|
25963
26172
|
},
|
|
25964
26173
|
isAvailable() {
|
|
25965
|
-
|
|
25966
|
-
return existsSync10(credentialsPath());
|
|
25967
|
-
} catch {
|
|
25968
|
-
return false;
|
|
25969
|
-
}
|
|
26174
|
+
return getAccountRegistry().mayHaveConfiguredAccounts("openai-codex");
|
|
25970
26175
|
},
|
|
25971
26176
|
scrape(response) {
|
|
25972
26177
|
return scrapeCodexHeaders(response.headers);
|
|
@@ -42073,188 +42278,6 @@ var init_billing_probe = __esm(() => {
|
|
|
42073
42278
|
};
|
|
42074
42279
|
});
|
|
42075
42280
|
|
|
42076
|
-
// src/auth/codex-pkce.ts
|
|
42077
|
-
import { exec as exec3 } from "child_process";
|
|
42078
|
-
import { createHash as createHash12, randomBytes as randomBytes7 } from "crypto";
|
|
42079
|
-
import { createServer } from "http";
|
|
42080
|
-
import { promisify as promisify3 } from "util";
|
|
42081
|
-
function loginClaims(idToken) {
|
|
42082
|
-
try {
|
|
42083
|
-
if (!idToken || idToken.split(".").length !== 3)
|
|
42084
|
-
return { identity: {} };
|
|
42085
|
-
const payload = JSON.parse(Buffer.from(idToken.split(".")[1], "base64url").toString("utf-8"));
|
|
42086
|
-
const auth = payload["https://api.openai.com/auth"];
|
|
42087
|
-
const stable = auth?.chatgpt_account_id || payload.chatgpt_account_id;
|
|
42088
|
-
const text = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
42089
|
-
const wireId = stable || auth?.user_id;
|
|
42090
|
-
return {
|
|
42091
|
-
accountId: wireId ? wireId : undefined,
|
|
42092
|
-
identity: {
|
|
42093
|
-
chatgptAccountId: text(stable),
|
|
42094
|
-
email: text(payload.email),
|
|
42095
|
-
plan: text(auth?.chatgpt_plan_type)
|
|
42096
|
-
}
|
|
42097
|
-
};
|
|
42098
|
-
} catch {
|
|
42099
|
-
return { identity: {} };
|
|
42100
|
-
}
|
|
42101
|
-
}
|
|
42102
|
-
function buildAuthUrl(challenge, state, redirectUri) {
|
|
42103
|
-
const params = [
|
|
42104
|
-
"response_type=code",
|
|
42105
|
-
`client_id=${encodeURIComponent(CODEX_OAUTH_CONFIG.clientId)}`,
|
|
42106
|
-
`redirect_uri=${encodeURIComponent(redirectUri)}`,
|
|
42107
|
-
`scope=${CODEX_OAUTH_CONFIG.scopes.join("+")}`,
|
|
42108
|
-
`code_challenge=${encodeURIComponent(challenge)}`,
|
|
42109
|
-
"code_challenge_method=S256",
|
|
42110
|
-
"id_token_add_organizations=true",
|
|
42111
|
-
"codex_cli_simplified_flow=true",
|
|
42112
|
-
`state=${encodeURIComponent(state)}`,
|
|
42113
|
-
"originator=opencode"
|
|
42114
|
-
].join("&");
|
|
42115
|
-
return `${CODEX_OAUTH_CONFIG.authUrl}?${params}`;
|
|
42116
|
-
}
|
|
42117
|
-
async function openBrowser(url) {
|
|
42118
|
-
const execAsync = promisify3(exec3);
|
|
42119
|
-
try {
|
|
42120
|
-
if (process.platform === "darwin")
|
|
42121
|
-
await execAsync(`open "${url}"`);
|
|
42122
|
-
else if (process.platform === "win32")
|
|
42123
|
-
await execAsync(`start "${url}"`);
|
|
42124
|
-
else
|
|
42125
|
-
await execAsync(`xdg-open "${url}"`);
|
|
42126
|
-
console.log(`
|
|
42127
|
-
Opening browser for OpenAI authentication...`);
|
|
42128
|
-
console.log(`If the browser doesn't open, visit this URL:
|
|
42129
|
-
${url}
|
|
42130
|
-
`);
|
|
42131
|
-
} catch {
|
|
42132
|
-
console.log(`
|
|
42133
|
-
Please open this URL in your browser to authenticate:`);
|
|
42134
|
-
console.log(url);
|
|
42135
|
-
console.log("");
|
|
42136
|
-
}
|
|
42137
|
-
}
|
|
42138
|
-
function waitForCallback(options, deps) {
|
|
42139
|
-
return new Promise((resolve, reject) => {
|
|
42140
|
-
let redirectUri = "";
|
|
42141
|
-
const schedule = deps.setTimeout ?? setTimeout;
|
|
42142
|
-
const cancel = deps.clearTimeout ?? clearTimeout;
|
|
42143
|
-
const finish = (error, result) => {
|
|
42144
|
-
if (timer !== undefined)
|
|
42145
|
-
cancel(timer);
|
|
42146
|
-
server.close();
|
|
42147
|
-
if (error)
|
|
42148
|
-
reject(error);
|
|
42149
|
-
else if (result)
|
|
42150
|
-
resolve(result);
|
|
42151
|
-
};
|
|
42152
|
-
const server = createServer((req, res) => {
|
|
42153
|
-
const url = new URL(req.url, redirectUri.replace("/auth/callback", ""));
|
|
42154
|
-
if (url.pathname !== "/auth/callback") {
|
|
42155
|
-
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
42156
|
-
res.end("Not found");
|
|
42157
|
-
return;
|
|
42158
|
-
}
|
|
42159
|
-
const code = url.searchParams.get("code");
|
|
42160
|
-
const state = url.searchParams.get("state");
|
|
42161
|
-
const error = url.searchParams.get("error");
|
|
42162
|
-
if (error || !state || state !== options.state || !code) {
|
|
42163
|
-
const detail = error ? "OAuth authorization was refused." : !state || state !== options.state ? "Invalid state parameter. Possible CSRF attack." : "No authorization code received.";
|
|
42164
|
-
res.writeHead(400, { "Content-Type": "text/html" });
|
|
42165
|
-
res.end(`<html><body><h1>Authentication Failed</h1><p>${detail}</p><p>You can close this window.</p></body></html>`);
|
|
42166
|
-
finish(new Error(error ? "OAuth authorization was refused" : !state || state !== options.state ? "Invalid OAuth state parameter (CSRF protection)" : "No authorization code received"));
|
|
42167
|
-
return;
|
|
42168
|
-
}
|
|
42169
|
-
res.writeHead(200, { "Content-Type": "text/html" });
|
|
42170
|
-
res.end("<html><body><h1>Authentication Successful!</h1><p>You can now close this window and return to your terminal.</p></body></html>");
|
|
42171
|
-
finish(undefined, { authCode: code, state, redirectUri });
|
|
42172
|
-
});
|
|
42173
|
-
server.on("error", (err) => finish(new Error(`Failed to start callback server: ${err.message}`)));
|
|
42174
|
-
server.listen(options.port, () => {
|
|
42175
|
-
const address = server.address();
|
|
42176
|
-
if (!address || typeof address === "string") {
|
|
42177
|
-
finish(new Error("Failed to get server port"));
|
|
42178
|
-
return;
|
|
42179
|
-
}
|
|
42180
|
-
redirectUri = `http://localhost:${address.port}/auth/callback`;
|
|
42181
|
-
log(`[CodexOAuth] Callback server started on http://localhost:${address.port}`);
|
|
42182
|
-
options.openBrowser(options.authorize(redirectUri)).catch(() => finish(new Error("Could not open OAuth browser")));
|
|
42183
|
-
});
|
|
42184
|
-
const timer = schedule(() => finish(new Error("OAuth login timed out after 5 minutes")), options.timeoutMs);
|
|
42185
|
-
});
|
|
42186
|
-
}
|
|
42187
|
-
async function loginCodexPkce(deps = {}) {
|
|
42188
|
-
log("[CodexOAuth] Starting OAuth login flow");
|
|
42189
|
-
const verifier = randomBytes7(64).toString("base64url");
|
|
42190
|
-
const challenge = createHash12("sha256").update(verifier).digest("base64url");
|
|
42191
|
-
const state = randomBytes7(32).toString("base64url");
|
|
42192
|
-
const options = {
|
|
42193
|
-
port: 1455,
|
|
42194
|
-
timeoutMs: 5 * 60 * 1000,
|
|
42195
|
-
state,
|
|
42196
|
-
authorize: (redirectUri) => buildAuthUrl(challenge, state, redirectUri),
|
|
42197
|
-
openBrowser: deps.browser ?? openBrowser
|
|
42198
|
-
};
|
|
42199
|
-
const callback = await (deps.callback ? deps.callback(options) : waitForCallback(options, deps));
|
|
42200
|
-
if (!callback.state || callback.state !== state)
|
|
42201
|
-
throw new Error("Invalid OAuth state parameter (CSRF protection)");
|
|
42202
|
-
if (!callback.authCode)
|
|
42203
|
-
throw new Error("No authorization code received");
|
|
42204
|
-
if (callback.redirectUri !== "http://localhost:1455/auth/callback")
|
|
42205
|
-
throw new Error("Invalid OAuth redirect URI");
|
|
42206
|
-
log("[CodexOAuth] Exchanging auth code for tokens");
|
|
42207
|
-
let tokens;
|
|
42208
|
-
try {
|
|
42209
|
-
const response = await (deps.fetch ?? globalThis.fetch)(resolveUpstreamUrl(CODEX_OAUTH_CONFIG.tokenUrl), {
|
|
42210
|
-
method: "POST",
|
|
42211
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
42212
|
-
body: new URLSearchParams({
|
|
42213
|
-
grant_type: "authorization_code",
|
|
42214
|
-
code: callback.authCode,
|
|
42215
|
-
redirect_uri: callback.redirectUri,
|
|
42216
|
-
client_id: CODEX_OAUTH_CONFIG.clientId,
|
|
42217
|
-
code_verifier: verifier
|
|
42218
|
-
})
|
|
42219
|
-
});
|
|
42220
|
-
if (!response.ok) {
|
|
42221
|
-
throw new Error(`Token exchange failed: ${response.status}`);
|
|
42222
|
-
}
|
|
42223
|
-
try {
|
|
42224
|
-
tokens = await response.json();
|
|
42225
|
-
} catch {
|
|
42226
|
-
throw new Error("Invalid OAuth token response");
|
|
42227
|
-
}
|
|
42228
|
-
if (!tokens.access_token || !tokens.refresh_token)
|
|
42229
|
-
throw new Error("Token response missing access_token or refresh_token");
|
|
42230
|
-
} catch (error) {
|
|
42231
|
-
const message = error instanceof Error ? error.message : "Token exchange failed";
|
|
42232
|
-
const safe = message.split(callback.authCode).join("[redacted]").split(verifier).join("[redacted]");
|
|
42233
|
-
throw new Error(`Failed to authenticate with OpenAI OAuth: ${safe}`);
|
|
42234
|
-
}
|
|
42235
|
-
const claims = loginClaims(tokens.id_token);
|
|
42236
|
-
return {
|
|
42237
|
-
credentials: {
|
|
42238
|
-
access_token: tokens.access_token,
|
|
42239
|
-
refresh_token: tokens.refresh_token,
|
|
42240
|
-
expires_at: (deps.now ?? Date.now)() + tokens.expires_in * 1000,
|
|
42241
|
-
account_id: claims.accountId
|
|
42242
|
-
},
|
|
42243
|
-
identity: claims.identity
|
|
42244
|
-
};
|
|
42245
|
-
}
|
|
42246
|
-
var CODEX_OAUTH_CONFIG;
|
|
42247
|
-
var init_codex_pkce = __esm(() => {
|
|
42248
|
-
init_logger();
|
|
42249
|
-
init_test_upstream();
|
|
42250
|
-
CODEX_OAUTH_CONFIG = {
|
|
42251
|
-
clientId: "app_EMoamEEZ73f0CkXaXp7hrann",
|
|
42252
|
-
authUrl: "https://auth.openai.com/oauth/authorize",
|
|
42253
|
-
tokenUrl: "https://auth.openai.com/oauth/token",
|
|
42254
|
-
scopes: ["openid", "profile", "email", "offline_access"]
|
|
42255
|
-
};
|
|
42256
|
-
});
|
|
42257
|
-
|
|
42258
42281
|
// ../../node_modules/.bun/@inquirer+core@11.0.1+04f2146be16c61ef/node_modules/@inquirer/core/dist/lib/key.js
|
|
42259
42282
|
var isUpKey = (key, keybindings = []) => key.name === "up" || keybindings.includes("vim") && key.name === "k" || keybindings.includes("emacs") && key.ctrl && key.name === "p", isDownKey = (key, keybindings = []) => key.name === "down" || keybindings.includes("vim") && key.name === "j" || keybindings.includes("emacs") && key.ctrl && key.name === "n", isBackspaceKey = (key) => key.name === "backspace", isTabKey = (key) => key.name === "tab", isNumberKey = (key) => "1234567890".includes(key.name), isEnterKey = (key) => key.name === "enter" || key.name === "return";
|
|
42260
42283
|
|
|
@@ -43192,7 +43215,7 @@ function wrapAnsi(string, columns, options) {
|
|
|
43192
43215
|
return String(string).normalize().replaceAll(`\r
|
|
43193
43216
|
`, `
|
|
43194
43217
|
`).split(`
|
|
43195
|
-
`).map((line) =>
|
|
43218
|
+
`).map((line) => exec3(line, columns, options)).join(`
|
|
43196
43219
|
`);
|
|
43197
43220
|
}
|
|
43198
43221
|
var ESCAPES, END_CODE = 39, ANSI_ESCAPE_BELL = "\x07", ANSI_CSI = "[", ANSI_OSC = "]", ANSI_SGR_TERMINATOR = "m", ANSI_ESCAPE_LINK, wrapAnsiCode = (code) => `${ESCAPES.values().next().value}${ANSI_CSI}${code}${ANSI_SGR_TERMINATOR}`, wrapAnsiHyperlink = (url) => `${ESCAPES.values().next().value}${ANSI_ESCAPE_LINK}${url}${ANSI_ESCAPE_BELL}`, wordLengths = (string) => string.split(" ").map((character) => stringWidth(character)), wrapWord = (rows, word, columns) => {
|
|
@@ -43246,7 +43269,7 @@ var ESCAPES, END_CODE = 39, ANSI_ESCAPE_BELL = "\x07", ANSI_CSI = "[", ANSI_OSC
|
|
|
43246
43269
|
return string;
|
|
43247
43270
|
}
|
|
43248
43271
|
return words.slice(0, last).join(" ") + words.slice(last).join("");
|
|
43249
|
-
},
|
|
43272
|
+
}, exec3 = (string, columns, options = {}) => {
|
|
43250
43273
|
if (options.trim !== false && string.trim() === "") {
|
|
43251
43274
|
return "";
|
|
43252
43275
|
}
|
|
@@ -44525,9 +44548,727 @@ var init_dist12 = __esm(() => {
|
|
|
44525
44548
|
init_dist11();
|
|
44526
44549
|
});
|
|
44527
44550
|
|
|
44551
|
+
// src/auth/codex-pkce.ts
|
|
44552
|
+
import { exec as exec4 } from "child_process";
|
|
44553
|
+
import { createHash as createHash12, randomBytes as randomBytes7 } from "crypto";
|
|
44554
|
+
import { createServer } from "http";
|
|
44555
|
+
import { promisify as promisify3 } from "util";
|
|
44556
|
+
function loginClaims(idToken) {
|
|
44557
|
+
try {
|
|
44558
|
+
if (!idToken || idToken.split(".").length !== 3)
|
|
44559
|
+
return { identity: {} };
|
|
44560
|
+
const payload = JSON.parse(Buffer.from(idToken.split(".")[1], "base64url").toString("utf-8"));
|
|
44561
|
+
const auth = payload["https://api.openai.com/auth"];
|
|
44562
|
+
const stable = auth?.chatgpt_account_id || payload.chatgpt_account_id;
|
|
44563
|
+
const text = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
44564
|
+
const wireId = stable || auth?.user_id;
|
|
44565
|
+
return {
|
|
44566
|
+
accountId: wireId ? wireId : undefined,
|
|
44567
|
+
identity: {
|
|
44568
|
+
chatgptAccountId: text(stable),
|
|
44569
|
+
email: text(payload.email),
|
|
44570
|
+
plan: text(auth?.chatgpt_plan_type)
|
|
44571
|
+
}
|
|
44572
|
+
};
|
|
44573
|
+
} catch {
|
|
44574
|
+
return { identity: {} };
|
|
44575
|
+
}
|
|
44576
|
+
}
|
|
44577
|
+
function buildAuthUrl(challenge, state, redirectUri) {
|
|
44578
|
+
const params = [
|
|
44579
|
+
"response_type=code",
|
|
44580
|
+
`client_id=${encodeURIComponent(CODEX_OAUTH_CONFIG.clientId)}`,
|
|
44581
|
+
`redirect_uri=${encodeURIComponent(redirectUri)}`,
|
|
44582
|
+
`scope=${CODEX_OAUTH_CONFIG.scopes.join("+")}`,
|
|
44583
|
+
`code_challenge=${encodeURIComponent(challenge)}`,
|
|
44584
|
+
"code_challenge_method=S256",
|
|
44585
|
+
"id_token_add_organizations=true",
|
|
44586
|
+
"codex_cli_simplified_flow=true",
|
|
44587
|
+
`state=${encodeURIComponent(state)}`,
|
|
44588
|
+
"originator=opencode"
|
|
44589
|
+
].join("&");
|
|
44590
|
+
return `${CODEX_OAUTH_CONFIG.authUrl}?${params}`;
|
|
44591
|
+
}
|
|
44592
|
+
async function openBrowser(url) {
|
|
44593
|
+
const execAsync = promisify3(exec4);
|
|
44594
|
+
try {
|
|
44595
|
+
if (process.platform === "darwin")
|
|
44596
|
+
await execAsync(`open "${url}"`);
|
|
44597
|
+
else if (process.platform === "win32")
|
|
44598
|
+
await execAsync(`start "${url}"`);
|
|
44599
|
+
else
|
|
44600
|
+
await execAsync(`xdg-open "${url}"`);
|
|
44601
|
+
console.log(`
|
|
44602
|
+
Opening browser for OpenAI authentication...`);
|
|
44603
|
+
console.log(`If the browser doesn't open, visit this URL:
|
|
44604
|
+
${url}
|
|
44605
|
+
`);
|
|
44606
|
+
} catch {
|
|
44607
|
+
console.log(`
|
|
44608
|
+
Please open this URL in your browser to authenticate:`);
|
|
44609
|
+
console.log(url);
|
|
44610
|
+
console.log("");
|
|
44611
|
+
}
|
|
44612
|
+
}
|
|
44613
|
+
function waitForCallback(options, deps) {
|
|
44614
|
+
return new Promise((resolve, reject) => {
|
|
44615
|
+
let redirectUri = "";
|
|
44616
|
+
const schedule = deps.setTimeout ?? setTimeout;
|
|
44617
|
+
const cancel = deps.clearTimeout ?? clearTimeout;
|
|
44618
|
+
const finish = (error, result) => {
|
|
44619
|
+
if (timer !== undefined)
|
|
44620
|
+
cancel(timer);
|
|
44621
|
+
server.close();
|
|
44622
|
+
if (error)
|
|
44623
|
+
reject(error);
|
|
44624
|
+
else if (result)
|
|
44625
|
+
resolve(result);
|
|
44626
|
+
};
|
|
44627
|
+
const server = createServer((req, res) => {
|
|
44628
|
+
const url = new URL(req.url, redirectUri.replace("/auth/callback", ""));
|
|
44629
|
+
if (url.pathname !== "/auth/callback") {
|
|
44630
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
44631
|
+
res.end("Not found");
|
|
44632
|
+
return;
|
|
44633
|
+
}
|
|
44634
|
+
const code = url.searchParams.get("code");
|
|
44635
|
+
const state = url.searchParams.get("state");
|
|
44636
|
+
const error = url.searchParams.get("error");
|
|
44637
|
+
if (error || !state || state !== options.state || !code) {
|
|
44638
|
+
const detail = error ? "OAuth authorization was refused." : !state || state !== options.state ? "Invalid state parameter. Possible CSRF attack." : "No authorization code received.";
|
|
44639
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
44640
|
+
res.end(`<html><body><h1>Authentication Failed</h1><p>${detail}</p><p>You can close this window.</p></body></html>`);
|
|
44641
|
+
finish(new Error(error ? "OAuth authorization was refused" : !state || state !== options.state ? "Invalid OAuth state parameter (CSRF protection)" : "No authorization code received"));
|
|
44642
|
+
return;
|
|
44643
|
+
}
|
|
44644
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
44645
|
+
res.end("<html><body><h1>Authentication Successful!</h1><p>You can now close this window and return to your terminal.</p></body></html>");
|
|
44646
|
+
finish(undefined, { authCode: code, state, redirectUri });
|
|
44647
|
+
});
|
|
44648
|
+
server.on("error", (err) => finish(new Error(`Failed to start callback server: ${err.message}`)));
|
|
44649
|
+
server.listen(options.port, () => {
|
|
44650
|
+
const address = server.address();
|
|
44651
|
+
if (!address || typeof address === "string") {
|
|
44652
|
+
finish(new Error("Failed to get server port"));
|
|
44653
|
+
return;
|
|
44654
|
+
}
|
|
44655
|
+
redirectUri = `http://localhost:${address.port}/auth/callback`;
|
|
44656
|
+
log(`[CodexOAuth] Callback server started on http://localhost:${address.port}`);
|
|
44657
|
+
options.openBrowser(options.authorize(redirectUri)).catch(() => finish(new Error("Could not open OAuth browser")));
|
|
44658
|
+
});
|
|
44659
|
+
const timer = schedule(() => finish(new Error("OAuth login timed out after 5 minutes")), options.timeoutMs);
|
|
44660
|
+
});
|
|
44661
|
+
}
|
|
44662
|
+
async function loginCodexPkce(deps = {}) {
|
|
44663
|
+
log("[CodexOAuth] Starting OAuth login flow");
|
|
44664
|
+
const verifier = randomBytes7(64).toString("base64url");
|
|
44665
|
+
const challenge = createHash12("sha256").update(verifier).digest("base64url");
|
|
44666
|
+
const state = randomBytes7(32).toString("base64url");
|
|
44667
|
+
const options = {
|
|
44668
|
+
port: 1455,
|
|
44669
|
+
timeoutMs: 5 * 60 * 1000,
|
|
44670
|
+
state,
|
|
44671
|
+
authorize: (redirectUri) => buildAuthUrl(challenge, state, redirectUri),
|
|
44672
|
+
openBrowser: deps.browser ?? openBrowser
|
|
44673
|
+
};
|
|
44674
|
+
const callback = await (deps.callback ? deps.callback(options) : waitForCallback(options, deps));
|
|
44675
|
+
if (!callback.state || callback.state !== state)
|
|
44676
|
+
throw new Error("Invalid OAuth state parameter (CSRF protection)");
|
|
44677
|
+
if (!callback.authCode)
|
|
44678
|
+
throw new Error("No authorization code received");
|
|
44679
|
+
if (callback.redirectUri !== "http://localhost:1455/auth/callback")
|
|
44680
|
+
throw new Error("Invalid OAuth redirect URI");
|
|
44681
|
+
log("[CodexOAuth] Exchanging auth code for tokens");
|
|
44682
|
+
let tokens;
|
|
44683
|
+
try {
|
|
44684
|
+
const response = await (deps.fetch ?? globalThis.fetch)(resolveUpstreamUrl(CODEX_OAUTH_CONFIG.tokenUrl), {
|
|
44685
|
+
method: "POST",
|
|
44686
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
44687
|
+
body: new URLSearchParams({
|
|
44688
|
+
grant_type: "authorization_code",
|
|
44689
|
+
code: callback.authCode,
|
|
44690
|
+
redirect_uri: callback.redirectUri,
|
|
44691
|
+
client_id: CODEX_OAUTH_CONFIG.clientId,
|
|
44692
|
+
code_verifier: verifier
|
|
44693
|
+
})
|
|
44694
|
+
});
|
|
44695
|
+
if (!response.ok) {
|
|
44696
|
+
throw new Error(`Token exchange failed: ${response.status}`);
|
|
44697
|
+
}
|
|
44698
|
+
try {
|
|
44699
|
+
tokens = await response.json();
|
|
44700
|
+
} catch {
|
|
44701
|
+
throw new Error("Invalid OAuth token response");
|
|
44702
|
+
}
|
|
44703
|
+
if (!tokens.access_token || !tokens.refresh_token)
|
|
44704
|
+
throw new Error("Token response missing access_token or refresh_token");
|
|
44705
|
+
} catch (error) {
|
|
44706
|
+
const message = error instanceof Error ? error.message : "Token exchange failed";
|
|
44707
|
+
const safe = message.split(callback.authCode).join("[redacted]").split(verifier).join("[redacted]");
|
|
44708
|
+
throw new Error(`Failed to authenticate with OpenAI OAuth: ${safe}`);
|
|
44709
|
+
}
|
|
44710
|
+
const claims = loginClaims(tokens.id_token);
|
|
44711
|
+
return {
|
|
44712
|
+
credentials: {
|
|
44713
|
+
access_token: tokens.access_token,
|
|
44714
|
+
refresh_token: tokens.refresh_token,
|
|
44715
|
+
expires_at: (deps.now ?? Date.now)() + tokens.expires_in * 1000,
|
|
44716
|
+
account_id: claims.accountId
|
|
44717
|
+
},
|
|
44718
|
+
identity: claims.identity
|
|
44719
|
+
};
|
|
44720
|
+
}
|
|
44721
|
+
var CODEX_OAUTH_CONFIG;
|
|
44722
|
+
var init_codex_pkce = __esm(() => {
|
|
44723
|
+
init_logger();
|
|
44724
|
+
init_test_upstream();
|
|
44725
|
+
CODEX_OAUTH_CONFIG = {
|
|
44726
|
+
clientId: "app_EMoamEEZ73f0CkXaXp7hrann",
|
|
44727
|
+
authUrl: "https://auth.openai.com/oauth/authorize",
|
|
44728
|
+
tokenUrl: "https://auth.openai.com/oauth/token",
|
|
44729
|
+
scopes: ["openid", "profile", "email", "offline_access"]
|
|
44730
|
+
};
|
|
44731
|
+
});
|
|
44732
|
+
|
|
44733
|
+
// src/auth/codex-oauth.ts
|
|
44734
|
+
var exports_codex_oauth = {};
|
|
44735
|
+
__export(exports_codex_oauth, {
|
|
44736
|
+
CodexOAuth: () => CodexOAuth,
|
|
44737
|
+
CodexOAuthLocalError: () => CodexOAuthLocalError,
|
|
44738
|
+
getCodexOAuth: () => getCodexOAuth
|
|
44739
|
+
});
|
|
44740
|
+
import { existsSync as existsSync18, readFileSync as readFileSync21, unlinkSync as unlinkSync6 } from "fs";
|
|
44741
|
+
function isRecord2(value) {
|
|
44742
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
44743
|
+
}
|
|
44744
|
+
function isAbsenceError(e) {
|
|
44745
|
+
const code = e?.code;
|
|
44746
|
+
return code === "ENOENT" || code === "ENOTDIR";
|
|
44747
|
+
}
|
|
44748
|
+
function errorMessage(e) {
|
|
44749
|
+
return e instanceof Error ? e.message : String(e);
|
|
44750
|
+
}
|
|
44751
|
+
function credentialFileUnreadable(credPath, detail) {
|
|
44752
|
+
return new CodexOAuthLocalError(`Could not read the OpenAI Codex credential file (${credPath}): ${detail}. ` + `Fix its permissions or contents, or run \`${LOGIN_COMMAND}\` to replace it; ` + "claudish does not fall back to OPENAI_CODEX_API_KEY while this file exists.");
|
|
44753
|
+
}
|
|
44754
|
+
function asCredentialFileError(e, credPath) {
|
|
44755
|
+
return e instanceof CodexOAuthLocalError ? e : credentialFileUnreadable(credPath, errorMessage(e));
|
|
44756
|
+
}
|
|
44757
|
+
function describeTokenEndpointError(body, presentedRefreshToken) {
|
|
44758
|
+
let text = "";
|
|
44759
|
+
try {
|
|
44760
|
+
const parsed = JSON.parse(body);
|
|
44761
|
+
const err = parsed?.error;
|
|
44762
|
+
const nested = typeof err === "object" && err !== null ? err : undefined;
|
|
44763
|
+
const code = typeof err === "string" ? err : typeof nested?.code === "string" ? nested.code : undefined;
|
|
44764
|
+
const message = typeof parsed?.error_description === "string" ? parsed.error_description : typeof nested?.message === "string" ? nested.message : undefined;
|
|
44765
|
+
text = [code, message].filter(Boolean).join(": ");
|
|
44766
|
+
} catch {
|
|
44767
|
+
text = body;
|
|
44768
|
+
}
|
|
44769
|
+
if (presentedRefreshToken)
|
|
44770
|
+
text = text.split(presentedRefreshToken).join("[redacted]");
|
|
44771
|
+
text = text.replace(/\s+/g, " ").trim();
|
|
44772
|
+
if (text.length > 200)
|
|
44773
|
+
text = `${text.slice(0, 200)}\u2026`;
|
|
44774
|
+
return text ? ` ${text}` : "";
|
|
44775
|
+
}
|
|
44776
|
+
function tokenHostUnreachable(e, tokenUrl) {
|
|
44777
|
+
log(`[CodexOAuth] Could not reach ${tokenUrl}: ${errorMessage(e)}`);
|
|
44778
|
+
return Object.assign(new Error(`Could not reach ${tokenUrl} to refresh the Codex token: ${errorMessage(e)}`, {
|
|
44779
|
+
cause: markOwnTimeout(e)
|
|
44780
|
+
}), { claudishEndpoint: tokenUrl });
|
|
44781
|
+
}
|
|
44782
|
+
|
|
44783
|
+
class CodexOAuth {
|
|
44784
|
+
static instance = null;
|
|
44785
|
+
credentials = null;
|
|
44786
|
+
credentialFileFailure = null;
|
|
44787
|
+
readCredentialText = (path) => readFileSync21(path, "utf-8");
|
|
44788
|
+
refreshPromise = null;
|
|
44789
|
+
tokenRefreshMargin = 5 * 60 * 1000;
|
|
44790
|
+
gateOverrides = {};
|
|
44791
|
+
static _resetInstanceForTests() {
|
|
44792
|
+
const instance = CodexOAuth.instance;
|
|
44793
|
+
if (!instance)
|
|
44794
|
+
return;
|
|
44795
|
+
instance.credentials = null;
|
|
44796
|
+
instance.credentialFileFailure = null;
|
|
44797
|
+
instance.readCredentialText = (path) => readFileSync21(path, "utf-8");
|
|
44798
|
+
instance.refreshPromise = null;
|
|
44799
|
+
instance.tokenRefreshMargin = 5 * 60 * 1000;
|
|
44800
|
+
instance.gateOverrides = {};
|
|
44801
|
+
instance.loadCredentials();
|
|
44802
|
+
}
|
|
44803
|
+
_setRefreshGateOptionsForTests(opts) {
|
|
44804
|
+
this.gateOverrides = { ...opts };
|
|
44805
|
+
}
|
|
44806
|
+
_setCredentialFileReaderForTests(read) {
|
|
44807
|
+
this.readCredentialText = read ?? ((path) => readFileSync21(path, "utf-8"));
|
|
44808
|
+
}
|
|
44809
|
+
static getInstance() {
|
|
44810
|
+
if (!CodexOAuth.instance) {
|
|
44811
|
+
CodexOAuth.instance = new CodexOAuth;
|
|
44812
|
+
}
|
|
44813
|
+
return CodexOAuth.instance;
|
|
44814
|
+
}
|
|
44815
|
+
constructor() {
|
|
44816
|
+
this.loadCredentials();
|
|
44817
|
+
}
|
|
44818
|
+
reloadCredentials() {
|
|
44819
|
+
this.loadCredentials();
|
|
44820
|
+
this.refreshPromise = null;
|
|
44821
|
+
}
|
|
44822
|
+
hasCredentials() {
|
|
44823
|
+
return this.credentials !== null && !!this.credentials.refresh_token || this.credentialFileFailure !== null;
|
|
44824
|
+
}
|
|
44825
|
+
getCredentialsPath() {
|
|
44826
|
+
return legacyCodexCredentialsPath();
|
|
44827
|
+
}
|
|
44828
|
+
getRefreshGatePath() {
|
|
44829
|
+
return this.gateOverrides.path ?? accountLockPath("openai-codex", LEGACY_CODEX_ACCOUNT_ID);
|
|
44830
|
+
}
|
|
44831
|
+
async login(deps = {}) {
|
|
44832
|
+
await Promise.resolve().then(() => init_management());
|
|
44833
|
+
if (codexLoginUsesKeychain()) {
|
|
44834
|
+
const lines = await loginCodexAccount({ pkce: () => loginCodexPkce(deps) });
|
|
44835
|
+
for (const line of lines)
|
|
44836
|
+
console.log(line);
|
|
44837
|
+
this.reloadCredentials();
|
|
44838
|
+
log("[CodexOAuth] Login saved to the Keychain account store");
|
|
44839
|
+
return;
|
|
44840
|
+
}
|
|
44841
|
+
assertLegacyCodexLoginPermitted();
|
|
44842
|
+
const { credentials } = await loginCodexPkce(deps);
|
|
44843
|
+
const accountId = credentials.account_id;
|
|
44844
|
+
await saveLegacyCodexLogin(() => {
|
|
44845
|
+
this.saveCredentials(credentials);
|
|
44846
|
+
this.setCredentials(credentials);
|
|
44847
|
+
});
|
|
44848
|
+
log("[CodexOAuth] Login successful");
|
|
44849
|
+
if (accountId) {
|
|
44850
|
+
log(`[CodexOAuth] Account ID: ${accountId}`);
|
|
44851
|
+
}
|
|
44852
|
+
}
|
|
44853
|
+
async logout() {
|
|
44854
|
+
await Promise.resolve().then(() => init_management());
|
|
44855
|
+
assertLegacyCodexLogoutPermitted();
|
|
44856
|
+
const credPath = this.getCredentialsPath();
|
|
44857
|
+
if (existsSync18(credPath)) {
|
|
44858
|
+
unlinkSync6(credPath);
|
|
44859
|
+
log("[CodexOAuth] Credentials deleted");
|
|
44860
|
+
}
|
|
44861
|
+
this.setCredentials(null);
|
|
44862
|
+
}
|
|
44863
|
+
async getAccessToken() {
|
|
44864
|
+
if (this.refreshPromise) {
|
|
44865
|
+
log("[CodexOAuth] Waiting for in-progress refresh");
|
|
44866
|
+
return this.refreshPromise;
|
|
44867
|
+
}
|
|
44868
|
+
if (!this.credentials && this.credentialFileFailure) {
|
|
44869
|
+
this.loadCredentials();
|
|
44870
|
+
if (this.credentialFileFailure)
|
|
44871
|
+
throw this.credentialFileFailure;
|
|
44872
|
+
}
|
|
44873
|
+
if (!this.credentials)
|
|
44874
|
+
throw await this.credentialFileGoneError();
|
|
44875
|
+
if (this.isTokenValid()) {
|
|
44876
|
+
return this.credentials.access_token;
|
|
44877
|
+
}
|
|
44878
|
+
return this.startRefresh({ kind: "expiry" });
|
|
44879
|
+
}
|
|
44880
|
+
getAccountId() {
|
|
44881
|
+
return this.credentials?.account_id;
|
|
44882
|
+
}
|
|
44883
|
+
async forceRefresh() {
|
|
44884
|
+
if (this.refreshPromise) {
|
|
44885
|
+
log("[CodexOAuth] Forced refresh joined the in-progress refresh");
|
|
44886
|
+
return this.refreshPromise;
|
|
44887
|
+
}
|
|
44888
|
+
return this.startRefresh({ kind: "forced", rejected: this.credentials?.access_token });
|
|
44889
|
+
}
|
|
44890
|
+
async refreshToken() {
|
|
44891
|
+
await this.forceRefresh();
|
|
44892
|
+
}
|
|
44893
|
+
isTokenValid() {
|
|
44894
|
+
if (!this.credentials)
|
|
44895
|
+
return false;
|
|
44896
|
+
return this.isFresh(this.credentials);
|
|
44897
|
+
}
|
|
44898
|
+
isFresh(credentials) {
|
|
44899
|
+
return Date.now() < credentials.expires_at - this.tokenRefreshMargin;
|
|
44900
|
+
}
|
|
44901
|
+
isAdoptable(onDisk, trigger) {
|
|
44902
|
+
if (!this.isFresh(onDisk))
|
|
44903
|
+
return false;
|
|
44904
|
+
return trigger.kind === "expiry" || onDisk.access_token !== trigger.rejected;
|
|
44905
|
+
}
|
|
44906
|
+
startRefresh(trigger) {
|
|
44907
|
+
const flight = this.refreshUnderGate(trigger).finally(() => {
|
|
44908
|
+
if (this.refreshPromise === flight)
|
|
44909
|
+
this.refreshPromise = null;
|
|
44910
|
+
});
|
|
44911
|
+
this.refreshPromise = flight;
|
|
44912
|
+
return flight;
|
|
44913
|
+
}
|
|
44914
|
+
async refreshUnderGate(trigger) {
|
|
44915
|
+
const before = this.readCredentialFile();
|
|
44916
|
+
try {
|
|
44917
|
+
return await withRefreshGate("openai-codex", LEGACY_CODEX_ACCOUNT_ID, (handle) => this.refreshHoldingGate(trigger, handle), {
|
|
44918
|
+
path: this.getRefreshGatePath(),
|
|
44919
|
+
gen: before?.generation ?? 0,
|
|
44920
|
+
...this.gateOverrides.deps ? { deps: this.gateOverrides.deps } : {}
|
|
44921
|
+
});
|
|
44922
|
+
} catch (err) {
|
|
44923
|
+
if (!isGateUnavailable(err))
|
|
44924
|
+
throw err;
|
|
44925
|
+
return this.refreshWithoutGate(trigger, err);
|
|
44926
|
+
}
|
|
44927
|
+
}
|
|
44928
|
+
async refreshWithoutGate(trigger, err) {
|
|
44929
|
+
const latest = this.readCredentialFile();
|
|
44930
|
+
const ownership = latest ? await this.legacyFileOwnership() : undefined;
|
|
44931
|
+
if (latest && ownership === "facade" && this.isAdoptable(latest.credentials, trigger)) {
|
|
44932
|
+
log(`[CodexOAuth] Refresh gate unavailable (${err.reason}); the holder already rotated \u2014 using its token`);
|
|
44933
|
+
this.setCredentials(latest.credentials);
|
|
44934
|
+
return latest.credentials.access_token;
|
|
44935
|
+
}
|
|
44936
|
+
if (ownership === "elsewhere")
|
|
44937
|
+
this.setCredentials(null);
|
|
44938
|
+
else if (!latest)
|
|
44939
|
+
await this.credentialFileGoneError({ dropCredentials: true });
|
|
44940
|
+
log(`[CodexOAuth] Refresh gate unavailable: ${err.message}`);
|
|
44941
|
+
if (err.reason !== "unwritable") {
|
|
44942
|
+
throw credentialHttpError(`Another claudish process is refreshing the OpenAI Codex login (${err.reason}); retrying will usually work.`, 503);
|
|
44943
|
+
}
|
|
44944
|
+
throw new CodexOAuthLocalError(`Could not refresh the OpenAI Codex login: the refresh lock could not be created (${err.reason}). Retry the request; if this persists, run \`${LOGIN_COMMAND}\`.`);
|
|
44945
|
+
}
|
|
44946
|
+
async refreshHoldingGate(trigger, handle) {
|
|
44947
|
+
const onDisk = this.readCredentialFile();
|
|
44948
|
+
if (!onDisk)
|
|
44949
|
+
throw await this.credentialFileGoneError({ dropCredentials: true });
|
|
44950
|
+
const ownership = await this.legacyFileOwnership();
|
|
44951
|
+
if (ownership !== "facade") {
|
|
44952
|
+
if (ownership === "elsewhere")
|
|
44953
|
+
this.setCredentials(null);
|
|
44954
|
+
throw this.facadeYieldError(ownership);
|
|
44955
|
+
}
|
|
44956
|
+
if (this.isAdoptable(onDisk.credentials, trigger)) {
|
|
44957
|
+
log("[CodexOAuth] Another process already rotated the token \u2014 using it, no exchange");
|
|
44958
|
+
this.setCredentials(onDisk.credentials);
|
|
44959
|
+
return onDisk.credentials.access_token;
|
|
44960
|
+
}
|
|
44961
|
+
const updated = await this.exchangeRefreshToken(onDisk.credentials);
|
|
44962
|
+
if (!handle.holdsLock()) {
|
|
44963
|
+
throw new CodexOAuthLocalError(`Could not refresh the OpenAI Codex login: the refresh lock was taken over before the new token could be saved. Retry the request; if this persists, run \`${LOGIN_COMMAND}\`.`);
|
|
44964
|
+
}
|
|
44965
|
+
const current = this.readCredentialFile();
|
|
44966
|
+
if (!current) {
|
|
44967
|
+
log("[CodexOAuth] The credential file was removed during the exchange \u2014 not recreating it");
|
|
44968
|
+
this.setCredentials(null);
|
|
44969
|
+
throw await this.credentialFileGoneError();
|
|
44970
|
+
}
|
|
44971
|
+
if (current.credentials.refresh_token !== onDisk.credentials.refresh_token) {
|
|
44972
|
+
log("[CodexOAuth] The credential file changed during the exchange \u2014 keeping it");
|
|
44973
|
+
this.setCredentials(current.credentials);
|
|
44974
|
+
return current.credentials.access_token;
|
|
44975
|
+
}
|
|
44976
|
+
if (await this.legacyFileOwnership() === "elsewhere") {
|
|
44977
|
+
log("[CodexOAuth] The account list no longer names the credential file \u2014 not saving to it");
|
|
44978
|
+
this.setCredentials(null);
|
|
44979
|
+
throw this.facadeYieldError("elsewhere");
|
|
44980
|
+
}
|
|
44981
|
+
try {
|
|
44982
|
+
this.saveCredentials(updated);
|
|
44983
|
+
} catch (e) {
|
|
44984
|
+
log(`[CodexOAuth] Could not save the refreshed token: ${errorMessage(e)}`);
|
|
44985
|
+
throw new CodexOAuthLocalError(`Could not save the refreshed OpenAI Codex login (${errorMessage(e)}). Run \`${LOGIN_COMMAND}\` to sign in again.`);
|
|
44986
|
+
}
|
|
44987
|
+
this.setCredentials(updated);
|
|
44988
|
+
log(`[CodexOAuth] Token refreshed, valid until ${new Date(updated.expires_at).toISOString()}`);
|
|
44989
|
+
return updated.access_token;
|
|
44990
|
+
}
|
|
44991
|
+
async legacyFileOwnership() {
|
|
44992
|
+
await Promise.resolve().then(() => init_registry());
|
|
44993
|
+
const registry = getAccountRegistry();
|
|
44994
|
+
registry.invalidate();
|
|
44995
|
+
let snapshot;
|
|
44996
|
+
try {
|
|
44997
|
+
snapshot = registry.snapshot("openai-codex");
|
|
44998
|
+
} catch (err) {
|
|
44999
|
+
log(`[CodexOAuth] Could not read the Codex account list: ${errorMessage(err)}`);
|
|
45000
|
+
return "unknown";
|
|
45001
|
+
}
|
|
45002
|
+
const failed = codexSourceFailed(snapshot);
|
|
45003
|
+
if (failed) {
|
|
45004
|
+
log(`[CodexOAuth] Could not read the Codex account list (${failed.path}); not trusting the last-known-good copy`);
|
|
45005
|
+
return "unknown";
|
|
45006
|
+
}
|
|
45007
|
+
if (isSynthesizedLegacyOnlySnapshot(snapshot))
|
|
45008
|
+
return "facade";
|
|
45009
|
+
const suppressed = snapshot.settings["openai-codex"]?.legacyCodexSuppressed === true;
|
|
45010
|
+
const named = snapshot.accounts["openai-codex"].some((row) => row.store === "legacy-codex-file");
|
|
45011
|
+
return !suppressed && named ? "file-row" : "elsewhere";
|
|
45012
|
+
}
|
|
45013
|
+
async missingLoginError() {
|
|
45014
|
+
return this.credentialFileGoneError();
|
|
45015
|
+
}
|
|
45016
|
+
facadeYieldError(ownership) {
|
|
45017
|
+
if (ownership === "unknown") {
|
|
45018
|
+
return new CodexOAuthLocalError("The Codex account list could not be read, so the OpenAI Codex login was not refreshed. Retry the request.");
|
|
45019
|
+
}
|
|
45020
|
+
if (ownership === "file-row") {
|
|
45021
|
+
return new CodexOAuthLocalError("The Codex login is now a configured account; retry the request.");
|
|
45022
|
+
}
|
|
45023
|
+
return new CodexOAuthLocalError("The Codex login moved to the macOS Keychain; retry the request.");
|
|
45024
|
+
}
|
|
45025
|
+
async credentialFileGoneError(opts = {}) {
|
|
45026
|
+
await Promise.resolve().then(() => init_registry());
|
|
45027
|
+
const registry = getAccountRegistry();
|
|
45028
|
+
registry.invalidate();
|
|
45029
|
+
const unreadable = (detail) => {
|
|
45030
|
+
log(`[CodexOAuth] Credential file absent and the account list is unreadable: ${detail}`);
|
|
45031
|
+
return new CodexOAuthLocalError("The OpenAI Codex credential file is gone and the Codex account list could not be read. Retry the request.");
|
|
45032
|
+
};
|
|
45033
|
+
let configured;
|
|
45034
|
+
try {
|
|
45035
|
+
const snapshot = registry.snapshot("openai-codex");
|
|
45036
|
+
const failed = codexSourceFailed(snapshot);
|
|
45037
|
+
if (failed)
|
|
45038
|
+
return unreadable(`${failed.path}: ${failed.error ?? "read failed"}`);
|
|
45039
|
+
configured = snapshot.accounts["openai-codex"].length > 0;
|
|
45040
|
+
} catch (err) {
|
|
45041
|
+
return unreadable(errorMessage(err));
|
|
45042
|
+
}
|
|
45043
|
+
if (opts.dropCredentials)
|
|
45044
|
+
this.setCredentials(null);
|
|
45045
|
+
if (configured) {
|
|
45046
|
+
log("[CodexOAuth] Credential file absent; Codex accounts are configured \u2014 answering transient");
|
|
45047
|
+
return new CodexOAuthLocalError("The Codex login moved to the macOS Keychain; retry the request.");
|
|
45048
|
+
}
|
|
45049
|
+
return new CodexOAuthRefreshError(`No OpenAI Codex OAuth credentials found. Please run \`${LOGIN_COMMAND}\` first.`);
|
|
45050
|
+
}
|
|
45051
|
+
async exchangeRefreshToken(from) {
|
|
45052
|
+
log("[CodexOAuth] Refreshing access token");
|
|
45053
|
+
const tokenUrl = resolveUpstreamUrl(CODEX_OAUTH_CONFIG.tokenUrl);
|
|
45054
|
+
let response;
|
|
45055
|
+
try {
|
|
45056
|
+
response = await fetch(tokenUrl, {
|
|
45057
|
+
method: "POST",
|
|
45058
|
+
headers: {
|
|
45059
|
+
"Content-Type": "application/json"
|
|
45060
|
+
},
|
|
45061
|
+
body: JSON.stringify({
|
|
45062
|
+
grant_type: "refresh_token",
|
|
45063
|
+
refresh_token: from.refresh_token,
|
|
45064
|
+
client_id: CODEX_OAUTH_CONFIG.clientId
|
|
45065
|
+
}),
|
|
45066
|
+
signal: AbortSignal.timeout(CODEX_EXCHANGE_TIMEOUT_MS)
|
|
45067
|
+
});
|
|
45068
|
+
} catch (e) {
|
|
45069
|
+
throw tokenHostUnreachable(e, tokenUrl);
|
|
45070
|
+
}
|
|
45071
|
+
if (!response.ok) {
|
|
45072
|
+
const errorText = await response.text().catch(() => "");
|
|
45073
|
+
const detail = describeTokenEndpointError(errorText, from.refresh_token);
|
|
45074
|
+
log(`[CodexOAuth] Refresh failed: HTTP ${response.status}${detail}`);
|
|
45075
|
+
if (response.status === 429 || response.status >= 500 && response.status <= 599) {
|
|
45076
|
+
throw credentialHttpError(`The Codex token host is busy or unavailable (HTTP ${response.status}); retrying will usually work.${detail}`, response.status);
|
|
45077
|
+
}
|
|
45078
|
+
throw new CodexOAuthRefreshError(`OpenAI Codex login expired or was revoked \u2014 run \`${LOGIN_COMMAND}\` to sign in again. (token refresh: HTTP ${response.status}${detail})`);
|
|
45079
|
+
}
|
|
45080
|
+
let tokens;
|
|
45081
|
+
try {
|
|
45082
|
+
tokens = await response.json();
|
|
45083
|
+
} catch (e) {
|
|
45084
|
+
throw new CodexOAuthRefreshError(`Run \`${LOGIN_COMMAND}\` to sign in again: the Codex token endpoint returned an unreadable body (${errorMessage(e)}). A retry can succeed only if another claudish process already saved a newer token.`);
|
|
45085
|
+
}
|
|
45086
|
+
if (typeof tokens?.access_token !== "string" || tokens.access_token.length === 0) {
|
|
45087
|
+
throw new CodexOAuthRefreshError(`Could not refresh the OpenAI Codex login: the token endpoint returned no access token. Retry; if this persists, run \`${LOGIN_COMMAND}\`.`);
|
|
45088
|
+
}
|
|
45089
|
+
const accountId = (tokens.id_token ? this.extractAccountId(tokens.id_token) : undefined) ?? from.account_id;
|
|
45090
|
+
const expiresIn = typeof tokens.expires_in === "number" ? tokens.expires_in : 3600;
|
|
45091
|
+
return {
|
|
45092
|
+
access_token: tokens.access_token,
|
|
45093
|
+
refresh_token: tokens.refresh_token || from.refresh_token,
|
|
45094
|
+
expires_at: Date.now() + expiresIn * 1000,
|
|
45095
|
+
account_id: accountId
|
|
45096
|
+
};
|
|
45097
|
+
}
|
|
45098
|
+
setCredentials(credentials) {
|
|
45099
|
+
this.credentials = credentials;
|
|
45100
|
+
this.credentialFileFailure = null;
|
|
45101
|
+
}
|
|
45102
|
+
loadCredentials() {
|
|
45103
|
+
const outcome = this.classifyCredentialFile();
|
|
45104
|
+
switch (outcome.kind) {
|
|
45105
|
+
case "present":
|
|
45106
|
+
this.setCredentials(outcome.read.credentials);
|
|
45107
|
+
log("[CodexOAuth] Loaded credentials from file");
|
|
45108
|
+
return;
|
|
45109
|
+
case "absent":
|
|
45110
|
+
this.setCredentials(null);
|
|
45111
|
+
return;
|
|
45112
|
+
case "failed":
|
|
45113
|
+
this.credentials = null;
|
|
45114
|
+
this.credentialFileFailure = outcome.error;
|
|
45115
|
+
log(`[CodexOAuth] Credential file present but unusable \u2014 Codex requests fail until it is fixed: ${outcome.error.message}`);
|
|
45116
|
+
return;
|
|
45117
|
+
}
|
|
45118
|
+
}
|
|
45119
|
+
classifyCredentialFile() {
|
|
45120
|
+
let raw;
|
|
45121
|
+
try {
|
|
45122
|
+
raw = this.readRawCredentialFile();
|
|
45123
|
+
} catch (e) {
|
|
45124
|
+
return { kind: "failed", error: asCredentialFileError(e, this.getCredentialsPath()) };
|
|
45125
|
+
}
|
|
45126
|
+
if (raw === null)
|
|
45127
|
+
return { kind: "absent" };
|
|
45128
|
+
if (typeof raw.refresh_token !== "string" || !raw.refresh_token) {
|
|
45129
|
+
log("[CodexOAuth] Credential file names no refresh token \u2014 treating it as no login");
|
|
45130
|
+
return { kind: "absent" };
|
|
45131
|
+
}
|
|
45132
|
+
try {
|
|
45133
|
+
return { kind: "present", read: this.projectCredentialFile(raw) };
|
|
45134
|
+
} catch (e) {
|
|
45135
|
+
return { kind: "failed", error: asCredentialFileError(e, this.getCredentialsPath()) };
|
|
45136
|
+
}
|
|
45137
|
+
}
|
|
45138
|
+
readCredentialFile() {
|
|
45139
|
+
const raw = this.readRawCredentialFile();
|
|
45140
|
+
return raw === null ? null : this.projectCredentialFile(raw);
|
|
45141
|
+
}
|
|
45142
|
+
readRawCredentialFile() {
|
|
45143
|
+
const credPath = this.getCredentialsPath();
|
|
45144
|
+
let text;
|
|
45145
|
+
try {
|
|
45146
|
+
text = this.readCredentialText(credPath);
|
|
45147
|
+
} catch (e) {
|
|
45148
|
+
if (isAbsenceError(e))
|
|
45149
|
+
return null;
|
|
45150
|
+
throw credentialFileUnreadable(credPath, errorMessage(e));
|
|
45151
|
+
}
|
|
45152
|
+
let parsed;
|
|
45153
|
+
try {
|
|
45154
|
+
parsed = JSON.parse(text);
|
|
45155
|
+
} catch {
|
|
45156
|
+
throw credentialFileUnreadable(credPath, `it is not valid JSON (${text.length === 0 ? "empty" : "truncated or corrupted"})`);
|
|
45157
|
+
}
|
|
45158
|
+
if (!isRecord2(parsed)) {
|
|
45159
|
+
throw credentialFileUnreadable(credPath, "it is not a JSON object");
|
|
45160
|
+
}
|
|
45161
|
+
return parsed;
|
|
45162
|
+
}
|
|
45163
|
+
projectCredentialFile(raw) {
|
|
45164
|
+
const credPath = this.getCredentialsPath();
|
|
45165
|
+
const accessToken = raw.access_token;
|
|
45166
|
+
const refreshToken = raw.refresh_token;
|
|
45167
|
+
const expiresAt = raw.expires_at;
|
|
45168
|
+
if (typeof accessToken !== "string" || !accessToken || typeof refreshToken !== "string" || !refreshToken || typeof expiresAt !== "number" || !expiresAt) {
|
|
45169
|
+
log("[CodexOAuth] Invalid credentials file structure");
|
|
45170
|
+
throw new CodexOAuthLocalError(`The OpenAI Codex credential file (${credPath}) has no usable token pair. Run \`${LOGIN_COMMAND}\` to replace it.`);
|
|
45171
|
+
}
|
|
45172
|
+
const accountId = raw.account_id;
|
|
45173
|
+
const generation = raw.claudish_generation;
|
|
45174
|
+
return {
|
|
45175
|
+
credentials: {
|
|
45176
|
+
access_token: accessToken,
|
|
45177
|
+
refresh_token: refreshToken,
|
|
45178
|
+
expires_at: expiresAt,
|
|
45179
|
+
...typeof accountId === "string" && accountId ? { account_id: accountId } : {}
|
|
45180
|
+
},
|
|
45181
|
+
generation: typeof generation === "number" ? generation : 0
|
|
45182
|
+
};
|
|
45183
|
+
}
|
|
45184
|
+
saveCredentials(credentials) {
|
|
45185
|
+
const credPath = this.getCredentialsPath();
|
|
45186
|
+
let existing = {};
|
|
45187
|
+
try {
|
|
45188
|
+
if (existsSync18(credPath)) {
|
|
45189
|
+
const parsed = JSON.parse(readFileSync21(credPath, "utf-8"));
|
|
45190
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
|
|
45191
|
+
existing = parsed;
|
|
45192
|
+
}
|
|
45193
|
+
} catch {
|
|
45194
|
+
existing = {};
|
|
45195
|
+
}
|
|
45196
|
+
const previousGeneration = typeof existing.claudish_generation === "number" ? existing.claudish_generation : 0;
|
|
45197
|
+
const document = {
|
|
45198
|
+
...existing,
|
|
45199
|
+
...credentials,
|
|
45200
|
+
claudish_generation: previousGeneration + 1
|
|
45201
|
+
};
|
|
45202
|
+
atomicWriteFileSync(credPath, JSON.stringify(document, null, 2), { mode: 384 });
|
|
45203
|
+
log(`[CodexOAuth] Credentials saved to ${credPath}`);
|
|
45204
|
+
}
|
|
45205
|
+
extractAccountId(idToken) {
|
|
45206
|
+
try {
|
|
45207
|
+
const parts = idToken.split(".");
|
|
45208
|
+
if (parts.length !== 3)
|
|
45209
|
+
return;
|
|
45210
|
+
const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf-8"));
|
|
45211
|
+
const authClaim = payload["https://api.openai.com/auth"];
|
|
45212
|
+
const accountId = authClaim?.chatgpt_account_id || payload.chatgpt_account_id || authClaim?.user_id;
|
|
45213
|
+
if (accountId) {
|
|
45214
|
+
log(`[CodexOAuth] Extracted account ID from id_token: ${accountId}`);
|
|
45215
|
+
return accountId;
|
|
45216
|
+
}
|
|
45217
|
+
return;
|
|
45218
|
+
} catch (e) {
|
|
45219
|
+
log(`[CodexOAuth] Failed to extract account ID from id_token: ${e.message}`);
|
|
45220
|
+
return;
|
|
45221
|
+
}
|
|
45222
|
+
}
|
|
45223
|
+
}
|
|
45224
|
+
function getCodexOAuth() {
|
|
45225
|
+
return CodexOAuth.getInstance();
|
|
45226
|
+
}
|
|
45227
|
+
var LOGIN_COMMAND = "claudish login codex", CodexOAuthRefreshError, CodexOAuthLocalError, CODEX_EXCHANGE_TIMEOUT_MS;
|
|
45228
|
+
var init_codex_oauth = __esm(() => {
|
|
45229
|
+
init_connection_error();
|
|
45230
|
+
init_transient_retry();
|
|
45231
|
+
init_logger();
|
|
45232
|
+
init_test_upstream();
|
|
45233
|
+
init_atomic_write();
|
|
45234
|
+
init_exchange();
|
|
45235
|
+
init_paths();
|
|
45236
|
+
init_refresh_lock();
|
|
45237
|
+
init_types();
|
|
45238
|
+
init_codex_pkce();
|
|
45239
|
+
CodexOAuthRefreshError = class CodexOAuthRefreshError extends Error {
|
|
45240
|
+
terminal = true;
|
|
45241
|
+
upstreamStatus = 401;
|
|
45242
|
+
constructor(message) {
|
|
45243
|
+
super(message);
|
|
45244
|
+
this.name = "CodexOAuthRefreshError";
|
|
45245
|
+
}
|
|
45246
|
+
};
|
|
45247
|
+
CodexOAuthLocalError = class CodexOAuthLocalError extends Error {
|
|
45248
|
+
transient = true;
|
|
45249
|
+
constructor(message) {
|
|
45250
|
+
super(message);
|
|
45251
|
+
this.name = "CodexOAuthLocalError";
|
|
45252
|
+
}
|
|
45253
|
+
};
|
|
45254
|
+
CODEX_EXCHANGE_TIMEOUT_MS = Math.min(TOKEN_REFRESH_TIMEOUT_MS, EXCHANGE_TIMEOUT_MS);
|
|
45255
|
+
});
|
|
45256
|
+
|
|
44528
45257
|
// src/auth/accounts/management.ts
|
|
44529
45258
|
import { randomBytes as randomBytes8 } from "crypto";
|
|
44530
|
-
import { readFileSync as
|
|
45259
|
+
import { accessSync, constants as fsConstants, lstatSync as lstatSync3, readFileSync as readFileSync22 } from "fs";
|
|
45260
|
+
function legacyMoveDeferredMessage(outcome, env = process.env) {
|
|
45261
|
+
return `Could not move the existing Codex login into the macOS Keychain (${outcome.reason}: ` + `${redactSecrets(outcome.detail)}). The login was not moved; ${legacyCodexCredentialsPath(env)} was kept. ` + MOVE_HINTS[outcome.reason];
|
|
45262
|
+
}
|
|
45263
|
+
function moveLegacyCodexLogin(deps = {}, opts = {}) {
|
|
45264
|
+
return new CodexAccountManagement(deps).moveLegacyLogin(opts);
|
|
45265
|
+
}
|
|
45266
|
+
function codexLoginUsesKeychain(env = process.env) {
|
|
45267
|
+
return !legacyMoveKeychainBlocked(env) && defaultAccountStore(env).kind === "keychain";
|
|
45268
|
+
}
|
|
45269
|
+
function loginCodexAccount(deps = {}) {
|
|
45270
|
+
return new CodexAccountManagement(deps).loginCodex();
|
|
45271
|
+
}
|
|
44531
45272
|
function codexEntry(raw) {
|
|
44532
45273
|
const block = raw.subscriptionAccounts;
|
|
44533
45274
|
return block?.[PROVIDER];
|
|
@@ -44649,7 +45390,7 @@ class CodexAccountManagement {
|
|
|
44649
45390
|
configPath: () => config,
|
|
44650
45391
|
legacyCodexPath: () => legacyCodexCredentialsPath(this.env),
|
|
44651
45392
|
env: this.env,
|
|
44652
|
-
readFile: (path) => path === config ? "{}" :
|
|
45393
|
+
readFile: (path) => path === config ? "{}" : readFileSync22(path, "utf-8")
|
|
44653
45394
|
});
|
|
44654
45395
|
return registry.snapshot(PROVIDER).accounts[PROVIDER].find((row) => row.legacy);
|
|
44655
45396
|
}
|
|
@@ -44793,7 +45534,8 @@ class CodexAccountManagement {
|
|
|
44793
45534
|
if (command.verb === "add") {
|
|
44794
45535
|
if (command.label !== undefined)
|
|
44795
45536
|
validLabel(command.label);
|
|
44796
|
-
|
|
45537
|
+
await this.moveBeforeMutation();
|
|
45538
|
+
return this.add({ label: command.label });
|
|
44797
45539
|
}
|
|
44798
45540
|
if (command.verb === "rename")
|
|
44799
45541
|
validLabel(command.newLabel);
|
|
@@ -44805,6 +45547,8 @@ class CodexAccountManagement {
|
|
|
44805
45547
|
}
|
|
44806
45548
|
if (command.selector !== undefined)
|
|
44807
45549
|
validSelector(command.selector);
|
|
45550
|
+
if (command.verb !== "remove")
|
|
45551
|
+
await this.moveBeforeMutation();
|
|
44808
45552
|
const target = this.inspect((entry) => this.select(entry, command));
|
|
44809
45553
|
if (command.verb === "login")
|
|
44810
45554
|
return this.login(target);
|
|
@@ -44814,10 +45558,14 @@ class CodexAccountManagement {
|
|
|
44814
45558
|
return this.remove(target, command);
|
|
44815
45559
|
return this.setEnabled(target, command.verb === "enable");
|
|
44816
45560
|
}
|
|
44817
|
-
async add(
|
|
45561
|
+
async add(opts = {}) {
|
|
45562
|
+
const requestedLabel = opts.label;
|
|
45563
|
+
const askNotice = opts.notice !== false;
|
|
45564
|
+
const enrollLegacy = opts.enrollLegacy !== false;
|
|
45565
|
+
const rowsFor = (entry) => enrollLegacy ? this.rowsOf(entry) : entry.accounts.map(asRecord);
|
|
44818
45566
|
const initial = this.inspect((entry) => {
|
|
44819
|
-
const legacy = this.synthesizedLegacy(entry);
|
|
44820
|
-
if (requestedLabel !== undefined && labelTaken(
|
|
45567
|
+
const legacy = enrollLegacy ? this.synthesizedLegacy(entry) : undefined;
|
|
45568
|
+
if (requestedLabel !== undefined && labelTaken(rowsFor(entry).map((r) => r.label), requestedLabel)) {
|
|
44821
45569
|
throw new Error(`A Codex account labelled ${requestedLabel} already exists.`);
|
|
44822
45570
|
}
|
|
44823
45571
|
if (legacy && !legacy.identity.chatgptAccountId) {
|
|
@@ -44825,7 +45573,7 @@ class CodexAccountManagement {
|
|
|
44825
45573
|
}
|
|
44826
45574
|
return { notice: entry.noticeAcceptedAt, legacy, id: this.newId(entry) };
|
|
44827
45575
|
});
|
|
44828
|
-
if (!initial.notice && !await this.ask(OWNED_ACCOUNTS_NOTICE)) {
|
|
45576
|
+
if (askNotice && !initial.notice && !await this.ask(OWNED_ACCOUNTS_NOTICE)) {
|
|
44829
45577
|
return ["Cancelled; no account was added."];
|
|
44830
45578
|
}
|
|
44831
45579
|
const acceptedAt = new Date(this.now()).toISOString();
|
|
@@ -44850,7 +45598,7 @@ class CodexAccountManagement {
|
|
|
44850
45598
|
this.inspect((entry) => {
|
|
44851
45599
|
if (entry.accounts.some((a) => a.id === row.id))
|
|
44852
45600
|
throw new Error("The generated account id is taken; retry the add.");
|
|
44853
|
-
const rows =
|
|
45601
|
+
const rows = rowsFor(entry);
|
|
44854
45602
|
this.refuseIdentity(rows, result.identity);
|
|
44855
45603
|
this.chooseLabel(rows, result.identity, requestedLabel);
|
|
44856
45604
|
});
|
|
@@ -44865,10 +45613,10 @@ class CodexAccountManagement {
|
|
|
44865
45613
|
this.fence(holds);
|
|
44866
45614
|
if (entry.accounts.some((a) => a.id === row.id))
|
|
44867
45615
|
throw new Error("The generated account id is taken; retry the add.");
|
|
44868
|
-
const rows =
|
|
45616
|
+
const rows = rowsFor(entry);
|
|
44869
45617
|
this.refuseIdentity(rows, result.identity);
|
|
44870
45618
|
const label = this.chooseLabel(rows, result.identity, requestedLabel);
|
|
44871
|
-
const legacy = rows.find((r) => r.legacy);
|
|
45619
|
+
const legacy = enrollLegacy ? rows.find((r) => r.legacy) : undefined;
|
|
44872
45620
|
if (legacy) {
|
|
44873
45621
|
if (legacy.identity.chatgptAccountId !== initial.legacy?.identity.chatgptAccountId) {
|
|
44874
45622
|
throw new Error("The legacy Codex login changed during the add; retry to confirm its enrollment.");
|
|
@@ -44883,7 +45631,8 @@ class CodexAccountManagement {
|
|
|
44883
45631
|
});
|
|
44884
45632
|
}
|
|
44885
45633
|
entry.accounts.push({ ...row, label });
|
|
44886
|
-
|
|
45634
|
+
if (askNotice)
|
|
45635
|
+
entry.noticeAcceptedAt ??= acceptedAt;
|
|
44887
45636
|
finalLabel = label;
|
|
44888
45637
|
});
|
|
44889
45638
|
} catch (error) {
|
|
@@ -44912,8 +45661,10 @@ class CodexAccountManagement {
|
|
|
44912
45661
|
}
|
|
44913
45662
|
throw new Error(`The add's outcome is unresolved: its token item (${store.kind}, id ${record.id}) was kept because the account list could not confirm the row is absent. Run \`claudish account list\`. Cause: ${errorText7(error)}`);
|
|
44914
45663
|
}
|
|
44915
|
-
async login(target) {
|
|
45664
|
+
async login(target, opts = {}) {
|
|
44916
45665
|
if (target.legacy) {
|
|
45666
|
+
if (codexLoginUsesKeychain(this.env))
|
|
45667
|
+
return this.loginCodex();
|
|
44917
45668
|
if (this.deps.legacyLogin)
|
|
44918
45669
|
await this.deps.legacyLogin();
|
|
44919
45670
|
else
|
|
@@ -44925,7 +45676,7 @@ class CodexAccountManagement {
|
|
|
44925
45676
|
const result = await (this.deps.pkce ?? loginCodexPkce)();
|
|
44926
45677
|
const stable = stableIdentity(result.identity);
|
|
44927
45678
|
if (target.identity.chatgptAccountId && stable !== target.identity.chatgptAccountId) {
|
|
44928
|
-
throw new Error(`This login is a different Codex identity than ${target.label}; use \`claudish account add codex\` for a different account.`);
|
|
45679
|
+
throw new Error(opts.differentIdentity ?? `This login is a different Codex identity than ${target.label}; use \`claudish account add codex\` for a different account.`);
|
|
44929
45680
|
}
|
|
44930
45681
|
await this.gate(target, async (holds) => {
|
|
44931
45682
|
const validate = (entry) => {
|
|
@@ -44982,575 +45733,530 @@ class CodexAccountManagement {
|
|
|
44982
45733
|
if (changed)
|
|
44983
45734
|
await this.invalidate();
|
|
44984
45735
|
const lines = [
|
|
44985
|
-
`Codex account ${named(target)} ${changed ? "" : "is already "}${enabled ? "enabled" : "disabled"}.`
|
|
44986
|
-
];
|
|
44987
|
-
if (!enabled) {
|
|
44988
|
-
lines.push("Configured Codex accounts still reserve the subscription: with no enabled account, Codex requests fail visibly instead of using OPENAI_CODEX_API_KEY.");
|
|
44989
|
-
}
|
|
44990
|
-
if (enabled && poolDisabled) {
|
|
44991
|
-
lines.push("The Codex pool itself is disabled in config.json (`enabled: false`); no account is selected.");
|
|
44992
|
-
}
|
|
44993
|
-
return lines;
|
|
44994
|
-
}
|
|
44995
|
-
async rename(target, newLabel) {
|
|
44996
|
-
if (target.legacy) {
|
|
44997
|
-
if (target.label === newLabel) {
|
|
44998
|
-
return [`Codex account ${named(target)} is already labelled ${newLabel}.`];
|
|
44999
|
-
}
|
|
45000
|
-
if (!target.identity.chatgptAccountId) {
|
|
45001
|
-
throw new Error("The legacy Codex login records no account id, so enrolling it cannot be checked for duplicates. Run `claudish login codex` once to refresh it, then retry.");
|
|
45002
|
-
}
|
|
45003
|
-
}
|
|
45004
|
-
let changed = false;
|
|
45005
|
-
await this.gate(target, async (holds) => {
|
|
45006
|
-
changed = mutateAccountsConfig(this.configPath(), PROVIDER, (entry) => {
|
|
45007
|
-
this.fence(holds);
|
|
45008
|
-
const current = this.resolve(entry, target.legacy ? { label: target.label } : { id: target.id });
|
|
45009
|
-
sameTarget(current, target);
|
|
45010
|
-
if (current.label === newLabel)
|
|
45011
|
-
return;
|
|
45012
|
-
const others = this.rowsOf(entry).filter((row) => row.id !== current.id).map((row) => row.label);
|
|
45013
|
-
if (labelTaken(others, newLabel)) {
|
|
45014
|
-
throw new Error(`A Codex account labelled ${newLabel} already exists.`);
|
|
45015
|
-
}
|
|
45016
|
-
if (current.legacy) {
|
|
45017
|
-
entry.accounts.push({
|
|
45018
|
-
id: this.newId(entry),
|
|
45019
|
-
label: newLabel,
|
|
45020
|
-
identity: current.identity,
|
|
45021
|
-
store: "legacy-codex-file",
|
|
45022
|
-
enabled: true,
|
|
45023
|
-
addedAt: current.addedAt
|
|
45024
|
-
});
|
|
45025
|
-
return;
|
|
45026
|
-
}
|
|
45027
|
-
const row = entry.accounts.find((a) => a.id === target.id);
|
|
45028
|
-
if (row)
|
|
45029
|
-
row.label = newLabel;
|
|
45030
|
-
});
|
|
45031
|
-
});
|
|
45032
|
-
if (changed)
|
|
45033
|
-
await this.invalidate();
|
|
45034
|
-
if (changed && target.legacy) {
|
|
45035
|
-
this.print(`This enrolled your legacy login ${named(target)}${note(target.identity, target.label)} as a configured account, store legacy-codex-file; its credential file stays where it is.`);
|
|
45036
|
-
}
|
|
45037
|
-
return [
|
|
45038
|
-
changed ? `Renamed Codex account ${named(target)} to ${newLabel}.` : `Codex account ${named(target)} is already labelled ${newLabel}.`
|
|
45039
|
-
];
|
|
45040
|
-
}
|
|
45041
|
-
async remove(target, command) {
|
|
45042
|
-
const legacyFile = target.store === "legacy-codex-file";
|
|
45043
|
-
if (legacyFile && !command.deleteFile) {
|
|
45044
|
-
throw new Error(`${target.label} is the legacy Codex login; removing it deletes its credential file, which needs an explicit --delete-file. A label alone never authorizes that.`);
|
|
45045
|
-
}
|
|
45046
|
-
if (!legacyFile && command.deleteFile) {
|
|
45047
|
-
throw new Error(`--delete-file applies only to the legacy Codex login, not ${target.label}.`);
|
|
45048
|
-
}
|
|
45049
|
-
if (!command.yes && !await this.ask(`Remove Codex account ${named(target)}${note(target.identity, target.label)}${legacyFile ? " and delete its legacy credential file" : ""}? Requests already in flight cannot be recalled.`)) {
|
|
45050
|
-
return ["Cancelled; no account was removed."];
|
|
45051
|
-
}
|
|
45052
|
-
const warnings = [];
|
|
45053
|
-
await this.gate(target, async (holds) => {
|
|
45054
|
-
mutateAccountsConfig(this.configPath(), PROVIDER, (entry) => {
|
|
45055
|
-
this.fence(holds);
|
|
45056
|
-
const current = this.resolve(entry, target.legacy ? { label: target.label } : { id: target.id });
|
|
45057
|
-
sameTarget(current, target);
|
|
45058
|
-
entry.accounts = entry.accounts.filter((row) => row.id !== target.id);
|
|
45059
|
-
if (legacyFile)
|
|
45060
|
-
entry.legacyCodexSuppressed = true;
|
|
45061
|
-
});
|
|
45062
|
-
await this.invalidate();
|
|
45063
|
-
const state = this.deps.state ?? getStateStore();
|
|
45064
|
-
try {
|
|
45065
|
-
if (!await state.setActive(PROVIDER, target.id, target.id)) {
|
|
45066
|
-
warnings.push("Clearing the active-account pointer is pending; state.json was not written.");
|
|
45067
|
-
}
|
|
45068
|
-
} catch {
|
|
45069
|
-
warnings.push("Clearing the active-account pointer is pending; state.json was not written.");
|
|
45070
|
-
}
|
|
45071
|
-
try {
|
|
45072
|
-
this.fence(holds);
|
|
45073
|
-
await this.store(target.store).delete(target);
|
|
45074
|
-
} catch {
|
|
45075
|
-
warnings.push(`The removal is committed, but deleting ${target.label}'s token item (${target.store}) failed; the account was not restored${legacyFile ? " and the legacy login stays suppressed" : ""}.`);
|
|
45076
|
-
}
|
|
45077
|
-
});
|
|
45078
|
-
const next = this.inspect((entry) => {
|
|
45079
|
-
if (entry.accounts.length > 0) {
|
|
45080
|
-
return entry.enabled === false || entry.accounts.every((row) => row.enabled === false) ? "pool-disabled" : "pool";
|
|
45081
|
-
}
|
|
45082
|
-
return this.synthesizedLegacy(entry) ? "legacy" : "key";
|
|
45083
|
-
});
|
|
45084
|
-
const nextText = {
|
|
45085
|
-
pool: "the remaining Codex accounts",
|
|
45086
|
-
"pool-disabled": "none: Codex accounts remain configured but all are disabled, so requests fail visibly",
|
|
45087
|
-
legacy: "the legacy Codex login",
|
|
45088
|
-
key: "OPENAI_CODEX_API_KEY if one is configured, otherwise none"
|
|
45089
|
-
}[next];
|
|
45090
|
-
return [
|
|
45091
|
-
`Removed Codex account ${named(target)}${note(target.identity, target.label)}. Next Codex credential for new requests: ${nextText}.`,
|
|
45092
|
-
...next === "key" ? ["Warning: new Codex requests may now use OPENAI_CODEX_API_KEY, billed per token."] : [],
|
|
45093
|
-
...warnings
|
|
45094
|
-
];
|
|
45095
|
-
}
|
|
45096
|
-
}
|
|
45097
|
-
function errorText7(err) {
|
|
45098
|
-
return err instanceof Error ? err.message : String(err);
|
|
45099
|
-
}
|
|
45100
|
-
var PROVIDER = "openai-codex", OWNED_ACCOUNTS_NOTICE, LEGACY_LOGIN_REFUSED, LEGACY_SAVE_WAIT_MS = 15000;
|
|
45101
|
-
var init_management = __esm(() => {
|
|
45102
|
-
init_dist12();
|
|
45103
|
-
init_profile_config();
|
|
45104
|
-
init_codex_pkce();
|
|
45105
|
-
init_account_label();
|
|
45106
|
-
init_config_lock();
|
|
45107
|
-
init_file_lock();
|
|
45108
|
-
init_paths();
|
|
45109
|
-
init_refresh_lock();
|
|
45110
|
-
init_registry();
|
|
45111
|
-
init_state_store();
|
|
45112
|
-
init_token_store();
|
|
45113
|
-
init_types();
|
|
45114
|
-
OWNED_ACCOUNTS_NOTICE = "Add only Codex accounts you own and pay for. Switching between subscriptions may be " + "restricted by OpenAI's terms; this tool is not permission to bypass them. Accept and continue?";
|
|
45115
|
-
LEGACY_LOGIN_REFUSED = "Codex accounts are managed by `claudish account`, or the legacy login was removed. " + "Run `claudish account add codex` for a new account, or " + "`claudish account login codex <label>` to log an existing one in again.";
|
|
45116
|
-
});
|
|
45117
|
-
|
|
45118
|
-
// src/auth/codex-oauth.ts
|
|
45119
|
-
var exports_codex_oauth = {};
|
|
45120
|
-
__export(exports_codex_oauth, {
|
|
45121
|
-
CodexOAuth: () => CodexOAuth,
|
|
45122
|
-
getCodexOAuth: () => getCodexOAuth
|
|
45123
|
-
});
|
|
45124
|
-
import { existsSync as existsSync18, readFileSync as readFileSync22, unlinkSync as unlinkSync6 } from "fs";
|
|
45125
|
-
function isRecord2(value) {
|
|
45126
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
45127
|
-
}
|
|
45128
|
-
function isAbsenceError(e) {
|
|
45129
|
-
const code = e?.code;
|
|
45130
|
-
return code === "ENOENT" || code === "ENOTDIR";
|
|
45131
|
-
}
|
|
45132
|
-
function errorMessage(e) {
|
|
45133
|
-
return e instanceof Error ? e.message : String(e);
|
|
45134
|
-
}
|
|
45135
|
-
function credentialFileUnreadable(credPath, detail) {
|
|
45136
|
-
return new CodexOAuthLocalError(`Could not read the OpenAI Codex credential file (${credPath}): ${detail}. ` + `Fix its permissions or contents, or run \`${LOGIN_COMMAND}\` to replace it; ` + "claudish does not fall back to OPENAI_CODEX_API_KEY while this file exists.");
|
|
45137
|
-
}
|
|
45138
|
-
function asCredentialFileError(e, credPath) {
|
|
45139
|
-
return e instanceof CodexOAuthLocalError ? e : credentialFileUnreadable(credPath, errorMessage(e));
|
|
45140
|
-
}
|
|
45141
|
-
function describeTokenEndpointError(body, presentedRefreshToken) {
|
|
45142
|
-
let text = "";
|
|
45143
|
-
try {
|
|
45144
|
-
const parsed = JSON.parse(body);
|
|
45145
|
-
const err = parsed?.error;
|
|
45146
|
-
const nested = typeof err === "object" && err !== null ? err : undefined;
|
|
45147
|
-
const code = typeof err === "string" ? err : typeof nested?.code === "string" ? nested.code : undefined;
|
|
45148
|
-
const message = typeof parsed?.error_description === "string" ? parsed.error_description : typeof nested?.message === "string" ? nested.message : undefined;
|
|
45149
|
-
text = [code, message].filter(Boolean).join(": ");
|
|
45150
|
-
} catch {
|
|
45151
|
-
text = body;
|
|
45152
|
-
}
|
|
45153
|
-
if (presentedRefreshToken)
|
|
45154
|
-
text = text.split(presentedRefreshToken).join("[redacted]");
|
|
45155
|
-
text = text.replace(/\s+/g, " ").trim();
|
|
45156
|
-
if (text.length > 200)
|
|
45157
|
-
text = `${text.slice(0, 200)}\u2026`;
|
|
45158
|
-
return text ? ` ${text}` : "";
|
|
45159
|
-
}
|
|
45160
|
-
function tokenHostUnreachable(e, tokenUrl) {
|
|
45161
|
-
log(`[CodexOAuth] Could not reach ${tokenUrl}: ${errorMessage(e)}`);
|
|
45162
|
-
return Object.assign(new Error(`Could not reach ${tokenUrl} to refresh the Codex token: ${errorMessage(e)}`, {
|
|
45163
|
-
cause: markOwnTimeout(e)
|
|
45164
|
-
}), { claudishEndpoint: tokenUrl });
|
|
45165
|
-
}
|
|
45166
|
-
|
|
45167
|
-
class CodexOAuth {
|
|
45168
|
-
static instance = null;
|
|
45169
|
-
credentials = null;
|
|
45170
|
-
credentialFileFailure = null;
|
|
45171
|
-
readCredentialText = (path) => readFileSync22(path, "utf-8");
|
|
45172
|
-
refreshPromise = null;
|
|
45173
|
-
tokenRefreshMargin = 5 * 60 * 1000;
|
|
45174
|
-
gateOverrides = {};
|
|
45175
|
-
static _resetInstanceForTests() {
|
|
45176
|
-
const instance = CodexOAuth.instance;
|
|
45177
|
-
if (!instance)
|
|
45178
|
-
return;
|
|
45179
|
-
instance.credentials = null;
|
|
45180
|
-
instance.credentialFileFailure = null;
|
|
45181
|
-
instance.readCredentialText = (path) => readFileSync22(path, "utf-8");
|
|
45182
|
-
instance.refreshPromise = null;
|
|
45183
|
-
instance.tokenRefreshMargin = 5 * 60 * 1000;
|
|
45184
|
-
instance.gateOverrides = {};
|
|
45185
|
-
instance.loadCredentials();
|
|
45186
|
-
}
|
|
45187
|
-
_setRefreshGateOptionsForTests(opts) {
|
|
45188
|
-
this.gateOverrides = { ...opts };
|
|
45189
|
-
}
|
|
45190
|
-
_setCredentialFileReaderForTests(read) {
|
|
45191
|
-
this.readCredentialText = read ?? ((path) => readFileSync22(path, "utf-8"));
|
|
45192
|
-
}
|
|
45193
|
-
static getInstance() {
|
|
45194
|
-
if (!CodexOAuth.instance) {
|
|
45195
|
-
CodexOAuth.instance = new CodexOAuth;
|
|
45196
|
-
}
|
|
45197
|
-
return CodexOAuth.instance;
|
|
45198
|
-
}
|
|
45199
|
-
constructor() {
|
|
45200
|
-
this.loadCredentials();
|
|
45201
|
-
}
|
|
45202
|
-
reloadCredentials() {
|
|
45203
|
-
this.loadCredentials();
|
|
45204
|
-
this.refreshPromise = null;
|
|
45205
|
-
}
|
|
45206
|
-
hasCredentials() {
|
|
45207
|
-
return this.credentials !== null && !!this.credentials.refresh_token || this.credentialFileFailure !== null;
|
|
45208
|
-
}
|
|
45209
|
-
getCredentialsPath() {
|
|
45210
|
-
return legacyCodexCredentialsPath();
|
|
45211
|
-
}
|
|
45212
|
-
getRefreshGatePath() {
|
|
45213
|
-
return this.gateOverrides.path ?? accountLockPath("openai-codex", LEGACY_CODEX_ACCOUNT_ID);
|
|
45214
|
-
}
|
|
45215
|
-
async login(deps = {}) {
|
|
45216
|
-
await Promise.resolve().then(() => init_management());
|
|
45217
|
-
assertLegacyCodexLoginPermitted();
|
|
45218
|
-
const { credentials } = await loginCodexPkce(deps);
|
|
45219
|
-
const accountId = credentials.account_id;
|
|
45220
|
-
await saveLegacyCodexLogin(() => {
|
|
45221
|
-
this.saveCredentials(credentials);
|
|
45222
|
-
this.setCredentials(credentials);
|
|
45223
|
-
});
|
|
45224
|
-
log("[CodexOAuth] Login successful");
|
|
45225
|
-
if (accountId) {
|
|
45226
|
-
log(`[CodexOAuth] Account ID: ${accountId}`);
|
|
45736
|
+
`Codex account ${named(target)} ${changed ? "" : "is already "}${enabled ? "enabled" : "disabled"}.`
|
|
45737
|
+
];
|
|
45738
|
+
if (!enabled) {
|
|
45739
|
+
lines.push("Configured Codex accounts still reserve the subscription: with no enabled account, Codex requests fail visibly instead of using OPENAI_CODEX_API_KEY.");
|
|
45227
45740
|
}
|
|
45228
|
-
|
|
45229
|
-
|
|
45230
|
-
await Promise.resolve().then(() => init_management());
|
|
45231
|
-
assertLegacyCodexLogoutPermitted();
|
|
45232
|
-
const credPath = this.getCredentialsPath();
|
|
45233
|
-
if (existsSync18(credPath)) {
|
|
45234
|
-
unlinkSync6(credPath);
|
|
45235
|
-
log("[CodexOAuth] Credentials deleted");
|
|
45741
|
+
if (enabled && poolDisabled) {
|
|
45742
|
+
lines.push("The Codex pool itself is disabled in config.json (`enabled: false`); no account is selected.");
|
|
45236
45743
|
}
|
|
45237
|
-
|
|
45744
|
+
return lines;
|
|
45238
45745
|
}
|
|
45239
|
-
async
|
|
45240
|
-
if (
|
|
45241
|
-
|
|
45242
|
-
|
|
45746
|
+
async rename(target, newLabel) {
|
|
45747
|
+
if (target.legacy) {
|
|
45748
|
+
if (target.label === newLabel) {
|
|
45749
|
+
return [`Codex account ${named(target)} is already labelled ${newLabel}.`];
|
|
45750
|
+
}
|
|
45751
|
+
if (!target.identity.chatgptAccountId) {
|
|
45752
|
+
throw new Error("The legacy Codex login records no account id, so enrolling it cannot be checked for duplicates. Run `claudish login codex` once to refresh it, then retry.");
|
|
45753
|
+
}
|
|
45243
45754
|
}
|
|
45244
|
-
|
|
45245
|
-
|
|
45246
|
-
|
|
45247
|
-
|
|
45755
|
+
let changed = false;
|
|
45756
|
+
await this.gate(target, async (holds) => {
|
|
45757
|
+
changed = mutateAccountsConfig(this.configPath(), PROVIDER, (entry) => {
|
|
45758
|
+
this.fence(holds);
|
|
45759
|
+
const current = this.resolve(entry, target.legacy ? { label: target.label } : { id: target.id });
|
|
45760
|
+
sameTarget(current, target);
|
|
45761
|
+
if (current.label === newLabel)
|
|
45762
|
+
return;
|
|
45763
|
+
const others = this.rowsOf(entry).filter((row) => row.id !== current.id).map((row) => row.label);
|
|
45764
|
+
if (labelTaken(others, newLabel)) {
|
|
45765
|
+
throw new Error(`A Codex account labelled ${newLabel} already exists.`);
|
|
45766
|
+
}
|
|
45767
|
+
if (current.legacy) {
|
|
45768
|
+
entry.accounts.push({
|
|
45769
|
+
id: this.newId(entry),
|
|
45770
|
+
label: newLabel,
|
|
45771
|
+
identity: current.identity,
|
|
45772
|
+
store: "legacy-codex-file",
|
|
45773
|
+
enabled: true,
|
|
45774
|
+
addedAt: current.addedAt
|
|
45775
|
+
});
|
|
45776
|
+
return;
|
|
45777
|
+
}
|
|
45778
|
+
const row = entry.accounts.find((a) => a.id === target.id);
|
|
45779
|
+
if (row)
|
|
45780
|
+
row.label = newLabel;
|
|
45781
|
+
});
|
|
45782
|
+
});
|
|
45783
|
+
if (changed)
|
|
45784
|
+
await this.invalidate();
|
|
45785
|
+
if (changed && target.legacy) {
|
|
45786
|
+
this.print(`This enrolled your legacy login ${named(target)}${note(target.identity, target.label)} as a configured account, store legacy-codex-file; its credential file stays where it is.`);
|
|
45248
45787
|
}
|
|
45249
|
-
|
|
45250
|
-
|
|
45788
|
+
return [
|
|
45789
|
+
changed ? `Renamed Codex account ${named(target)} to ${newLabel}.` : `Codex account ${named(target)} is already labelled ${newLabel}.`
|
|
45790
|
+
];
|
|
45791
|
+
}
|
|
45792
|
+
async remove(target, command) {
|
|
45793
|
+
const legacyFile = target.store === "legacy-codex-file";
|
|
45794
|
+
if (legacyFile && !command.deleteFile) {
|
|
45795
|
+
throw new Error(`${target.label} is the legacy Codex login; removing it deletes its credential file, which needs an explicit --delete-file. A label alone never authorizes that.`);
|
|
45251
45796
|
}
|
|
45252
|
-
if (
|
|
45253
|
-
|
|
45797
|
+
if (!legacyFile && command.deleteFile) {
|
|
45798
|
+
throw new Error(`--delete-file applies only to the legacy Codex login, not ${target.label}.`);
|
|
45254
45799
|
}
|
|
45255
|
-
|
|
45256
|
-
|
|
45257
|
-
getAccountId() {
|
|
45258
|
-
return this.credentials?.account_id;
|
|
45259
|
-
}
|
|
45260
|
-
async forceRefresh() {
|
|
45261
|
-
if (this.refreshPromise) {
|
|
45262
|
-
log("[CodexOAuth] Forced refresh joined the in-progress refresh");
|
|
45263
|
-
return this.refreshPromise;
|
|
45800
|
+
if (!command.yes && !await this.ask(`Remove Codex account ${named(target)}${note(target.identity, target.label)}${legacyFile ? " and delete its legacy credential file" : ""}? Requests already in flight cannot be recalled.`)) {
|
|
45801
|
+
return ["Cancelled; no account was removed."];
|
|
45264
45802
|
}
|
|
45265
|
-
|
|
45266
|
-
|
|
45267
|
-
|
|
45268
|
-
|
|
45269
|
-
|
|
45270
|
-
|
|
45271
|
-
|
|
45272
|
-
|
|
45273
|
-
|
|
45274
|
-
}
|
|
45275
|
-
isFresh(credentials) {
|
|
45276
|
-
return Date.now() < credentials.expires_at - this.tokenRefreshMargin;
|
|
45277
|
-
}
|
|
45278
|
-
isAdoptable(onDisk, trigger) {
|
|
45279
|
-
if (!this.isFresh(onDisk))
|
|
45280
|
-
return false;
|
|
45281
|
-
return trigger.kind === "expiry" || onDisk.access_token !== trigger.rejected;
|
|
45282
|
-
}
|
|
45283
|
-
startRefresh(trigger) {
|
|
45284
|
-
const flight = this.refreshUnderGate(trigger).finally(() => {
|
|
45285
|
-
if (this.refreshPromise === flight)
|
|
45286
|
-
this.refreshPromise = null;
|
|
45287
|
-
});
|
|
45288
|
-
this.refreshPromise = flight;
|
|
45289
|
-
return flight;
|
|
45290
|
-
}
|
|
45291
|
-
async refreshUnderGate(trigger) {
|
|
45292
|
-
const before = this.readCredentialFile();
|
|
45293
|
-
try {
|
|
45294
|
-
return await withRefreshGate("openai-codex", LEGACY_CODEX_ACCOUNT_ID, (handle) => this.refreshHoldingGate(trigger, handle), {
|
|
45295
|
-
path: this.getRefreshGatePath(),
|
|
45296
|
-
gen: before?.generation ?? 0,
|
|
45297
|
-
...this.gateOverrides.deps ? { deps: this.gateOverrides.deps } : {}
|
|
45803
|
+
const warnings = [];
|
|
45804
|
+
await this.gate(target, async (holds) => {
|
|
45805
|
+
mutateAccountsConfig(this.configPath(), PROVIDER, (entry) => {
|
|
45806
|
+
this.fence(holds);
|
|
45807
|
+
const current = this.resolve(entry, target.legacy ? { label: target.label } : { id: target.id });
|
|
45808
|
+
sameTarget(current, target);
|
|
45809
|
+
entry.accounts = entry.accounts.filter((row) => row.id !== target.id);
|
|
45810
|
+
if (legacyFile)
|
|
45811
|
+
entry.legacyCodexSuppressed = true;
|
|
45298
45812
|
});
|
|
45299
|
-
|
|
45300
|
-
|
|
45301
|
-
|
|
45302
|
-
|
|
45303
|
-
|
|
45304
|
-
|
|
45305
|
-
|
|
45306
|
-
|
|
45813
|
+
await this.invalidate();
|
|
45814
|
+
const state = this.deps.state ?? getStateStore();
|
|
45815
|
+
try {
|
|
45816
|
+
if (!await state.setActive(PROVIDER, target.id, target.id)) {
|
|
45817
|
+
warnings.push("Clearing the active-account pointer is pending; state.json was not written.");
|
|
45818
|
+
}
|
|
45819
|
+
} catch {
|
|
45820
|
+
warnings.push("Clearing the active-account pointer is pending; state.json was not written.");
|
|
45821
|
+
}
|
|
45822
|
+
try {
|
|
45823
|
+
this.fence(holds);
|
|
45824
|
+
await this.store(target.store).delete(target);
|
|
45825
|
+
} catch {
|
|
45826
|
+
warnings.push(`The removal is committed, but deleting ${target.label}'s token item (${target.store}) failed; the account was not restored${legacyFile ? " and the legacy login stays suppressed" : ""}.`);
|
|
45307
45827
|
}
|
|
45308
|
-
|
|
45309
|
-
|
|
45310
|
-
|
|
45828
|
+
});
|
|
45829
|
+
const next = this.inspect((entry) => {
|
|
45830
|
+
if (entry.accounts.length > 0) {
|
|
45831
|
+
return entry.enabled === false || entry.accounts.every((row) => row.enabled === false) ? "pool-disabled" : "pool";
|
|
45311
45832
|
}
|
|
45312
|
-
|
|
45313
|
-
}
|
|
45833
|
+
return this.synthesizedLegacy(entry) ? "legacy" : "key";
|
|
45834
|
+
});
|
|
45835
|
+
const nextText = {
|
|
45836
|
+
pool: "the remaining Codex accounts",
|
|
45837
|
+
"pool-disabled": "none: Codex accounts remain configured but all are disabled, so requests fail visibly",
|
|
45838
|
+
legacy: "the legacy Codex login",
|
|
45839
|
+
key: "OPENAI_CODEX_API_KEY if one is configured, otherwise none"
|
|
45840
|
+
}[next];
|
|
45841
|
+
return [
|
|
45842
|
+
`Removed Codex account ${named(target)}${note(target.identity, target.label)}. Next Codex credential for new requests: ${nextText}.`,
|
|
45843
|
+
...next === "key" ? ["Warning: new Codex requests may now use OPENAI_CODEX_API_KEY, billed per token."] : [],
|
|
45844
|
+
...warnings
|
|
45845
|
+
];
|
|
45314
45846
|
}
|
|
45315
|
-
async
|
|
45316
|
-
const
|
|
45317
|
-
if (
|
|
45318
|
-
throw new
|
|
45319
|
-
|
|
45320
|
-
|
|
45321
|
-
|
|
45322
|
-
|
|
45323
|
-
|
|
45847
|
+
async moveBeforeMutation() {
|
|
45848
|
+
const moved = await this.moveLegacyLogin();
|
|
45849
|
+
if (moved.kind === "deferred")
|
|
45850
|
+
throw new Error(legacyMoveDeferredMessage(moved, this.env));
|
|
45851
|
+
}
|
|
45852
|
+
async loginCodex() {
|
|
45853
|
+
await this.moveBeforeMutation();
|
|
45854
|
+
const rows = this.inspect((entry) => entry.accounts.map(asRecord));
|
|
45855
|
+
if (rows.length === 0) {
|
|
45856
|
+
return this.add({ label: LEGACY_CODEX_LABEL, notice: false, enrollLegacy: false });
|
|
45857
|
+
}
|
|
45858
|
+
if (rows.length === 1) {
|
|
45859
|
+
const [row] = rows;
|
|
45860
|
+
const remove = `claudish account remove codex ${shellWord(row.label)}` + (row.store === "legacy-codex-file" ? " --delete-file" : "");
|
|
45861
|
+
return this.login(row, {
|
|
45862
|
+
differentIdentity: `This login is a different Codex identity than ${row.label}. Run \`claudish account add codex\` to add it, or \`${remove}\` and then \`claudish login codex\` to replace it.`
|
|
45863
|
+
});
|
|
45324
45864
|
}
|
|
45325
|
-
|
|
45326
|
-
|
|
45327
|
-
|
|
45865
|
+
throw new Error(`Codex has ${rows.length} accounts; log one in with \`claudish account login codex <label>\`.`);
|
|
45866
|
+
}
|
|
45867
|
+
async moveLegacyLogin(opts = {}) {
|
|
45868
|
+
if (legacyMoveKeychainBlocked(this.env))
|
|
45869
|
+
return notApplicable("no-keychain");
|
|
45870
|
+
const dest = this.deps.newStore?.() ?? defaultAccountStore(this.env);
|
|
45871
|
+
if (dest.kind !== "keychain")
|
|
45872
|
+
return notApplicable("no-keychain");
|
|
45873
|
+
if (!isDefaultConfigPath(this.configPath(), this.env))
|
|
45874
|
+
return notApplicable("alternate-config");
|
|
45875
|
+
let target;
|
|
45876
|
+
try {
|
|
45877
|
+
target = this.inspect((entry) => this.moveTargetOf(entry));
|
|
45878
|
+
} catch (err) {
|
|
45879
|
+
if (this.legacyFileDenied())
|
|
45880
|
+
return this.moveDeferred(err, "file-unreadable");
|
|
45881
|
+
return this.moveDeferred(err, "config-unavailable");
|
|
45882
|
+
}
|
|
45883
|
+
if (target.kind === "none")
|
|
45884
|
+
return notApplicable("no-legacy-account");
|
|
45885
|
+
if (target.kind === "leftover")
|
|
45886
|
+
return this.deleteLeftover();
|
|
45887
|
+
const found = target;
|
|
45888
|
+
let outcome;
|
|
45889
|
+
try {
|
|
45890
|
+
outcome = await this.gate(found.row, (holds) => this.moveHoldingGate(found, dest, holds, opts));
|
|
45891
|
+
} catch (err) {
|
|
45892
|
+
return this.moveDeferred(err, "gate-busy");
|
|
45328
45893
|
}
|
|
45329
|
-
|
|
45330
|
-
|
|
45331
|
-
log("[CodexOAuth] The credential file was removed during the exchange \u2014 not recreating it");
|
|
45332
|
-
this.setCredentials(null);
|
|
45333
|
-
throw new CodexOAuthRefreshError(`No OpenAI Codex OAuth credentials found. Please run \`${LOGIN_COMMAND}\` first.`);
|
|
45894
|
+
if (outcome.kind === "moved") {
|
|
45895
|
+
log(`[accounts] moved the legacy Codex login into the Keychain as ${outcome.label} (id ${outcome.id})${outcome.fileDeleted ? "" : "; the credential file was kept"}`);
|
|
45334
45896
|
}
|
|
45335
|
-
|
|
45336
|
-
|
|
45337
|
-
|
|
45338
|
-
|
|
45897
|
+
return outcome;
|
|
45898
|
+
}
|
|
45899
|
+
moveTargetOf(entry) {
|
|
45900
|
+
const legacyRow = entry.accounts.find((row) => row.store === "legacy-codex-file");
|
|
45901
|
+
if (legacyRow)
|
|
45902
|
+
return { kind: "row", row: asRecord(legacyRow) };
|
|
45903
|
+
if (entry.accounts.length === 0 && !entry.legacyCodexSuppressed) {
|
|
45904
|
+
const synthesized = this.synthesizedLegacy(entry);
|
|
45905
|
+
return synthesized ? { kind: "synthesized", row: synthesized } : { kind: "none" };
|
|
45339
45906
|
}
|
|
45907
|
+
if (entry.legacyCodexSuppressed && this.legacyFilePresent())
|
|
45908
|
+
return { kind: "leftover" };
|
|
45909
|
+
return { kind: "none" };
|
|
45910
|
+
}
|
|
45911
|
+
legacyFilePresent() {
|
|
45340
45912
|
try {
|
|
45341
|
-
this.
|
|
45342
|
-
|
|
45343
|
-
|
|
45344
|
-
|
|
45913
|
+
lstatSync3(legacyCodexCredentialsPath(this.env));
|
|
45914
|
+
return true;
|
|
45915
|
+
} catch (err) {
|
|
45916
|
+
const code = err?.code;
|
|
45917
|
+
if (code !== "ENOENT" && code !== "ENOTDIR") {
|
|
45918
|
+
log(`[accounts] legacy Codex file check failed: ${redactSecrets(errorText7(err))}`);
|
|
45919
|
+
}
|
|
45920
|
+
return false;
|
|
45345
45921
|
}
|
|
45346
|
-
this.setCredentials(updated);
|
|
45347
|
-
log(`[CodexOAuth] Token refreshed, valid until ${new Date(updated.expires_at).toISOString()}`);
|
|
45348
|
-
return updated.access_token;
|
|
45349
45922
|
}
|
|
45350
|
-
|
|
45351
|
-
log("[CodexOAuth] Refreshing access token");
|
|
45352
|
-
const tokenUrl = resolveUpstreamUrl(CODEX_OAUTH_CONFIG.tokenUrl);
|
|
45353
|
-
let response;
|
|
45923
|
+
legacyFileDenied() {
|
|
45354
45924
|
try {
|
|
45355
|
-
|
|
45356
|
-
|
|
45357
|
-
|
|
45358
|
-
|
|
45359
|
-
|
|
45360
|
-
body: JSON.stringify({
|
|
45361
|
-
grant_type: "refresh_token",
|
|
45362
|
-
refresh_token: from.refresh_token,
|
|
45363
|
-
client_id: CODEX_OAUTH_CONFIG.clientId
|
|
45364
|
-
}),
|
|
45365
|
-
signal: AbortSignal.timeout(CODEX_EXCHANGE_TIMEOUT_MS)
|
|
45366
|
-
});
|
|
45367
|
-
} catch (e) {
|
|
45368
|
-
throw tokenHostUnreachable(e, tokenUrl);
|
|
45925
|
+
accessSync(legacyCodexCredentialsPath(this.env), fsConstants.R_OK);
|
|
45926
|
+
return false;
|
|
45927
|
+
} catch (err) {
|
|
45928
|
+
const code = err?.code;
|
|
45929
|
+
return code === "EACCES" || code === "EPERM";
|
|
45369
45930
|
}
|
|
45370
|
-
|
|
45371
|
-
|
|
45372
|
-
|
|
45373
|
-
|
|
45374
|
-
|
|
45375
|
-
|
|
45931
|
+
}
|
|
45932
|
+
async moveHoldingGate(target, dest, holds, opts) {
|
|
45933
|
+
const hooks = this.deps.moveHooks;
|
|
45934
|
+
const source = this.store("legacy-codex-file");
|
|
45935
|
+
let phase = "config-unavailable";
|
|
45936
|
+
let row = target.row;
|
|
45937
|
+
let material;
|
|
45938
|
+
try {
|
|
45939
|
+
if (target.kind === "synthesized") {
|
|
45940
|
+
const enrolled = this.enrolSynthesizedLegacy(target.row, holds);
|
|
45941
|
+
if (enrolled === "already-moved")
|
|
45942
|
+
return { kind: "already-moved" };
|
|
45943
|
+
if (enrolled === "gone")
|
|
45944
|
+
return notApplicable("no-legacy-account");
|
|
45945
|
+
row = enrolled;
|
|
45946
|
+
await hooks?.afterEnroll?.();
|
|
45947
|
+
}
|
|
45948
|
+
phase = "file-unreadable";
|
|
45949
|
+
const read = await source.read(row, { uncached: true });
|
|
45950
|
+
phase = "config-unavailable";
|
|
45951
|
+
if (!read) {
|
|
45952
|
+
const state = this.inspect((entry) => moveTargetState(entry, row));
|
|
45953
|
+
return state === "moved" ? { kind: "already-moved" } : notApplicable("no-legacy-account");
|
|
45954
|
+
}
|
|
45955
|
+
material = read;
|
|
45956
|
+
const state = this.inspect((entry) => moveTargetState(entry, row));
|
|
45957
|
+
if (state === "moved")
|
|
45958
|
+
return { kind: "already-moved" };
|
|
45959
|
+
if (state === "removed")
|
|
45960
|
+
return notApplicable("no-legacy-account");
|
|
45961
|
+
if (state === "changed") {
|
|
45962
|
+
throw new LegacyMoveDeferredError("target-changed", "the Codex account row changed while the move was pending");
|
|
45963
|
+
}
|
|
45964
|
+
phase = "keychain-unreadable";
|
|
45965
|
+
const existing = await dest.read(row, { uncached: true });
|
|
45966
|
+
const record = {
|
|
45967
|
+
...material,
|
|
45968
|
+
writeId: randomBytes8(16).toString("hex"),
|
|
45969
|
+
updatedAt: this.now()
|
|
45970
|
+
};
|
|
45971
|
+
const resumed = existing !== null && sameDurableRecord(existing, material);
|
|
45972
|
+
if (!resumed) {
|
|
45973
|
+
phase = "keychain-write-failed";
|
|
45974
|
+
this.fence(holds);
|
|
45975
|
+
await this.persistMovedRecord(dest, row, record);
|
|
45376
45976
|
}
|
|
45377
|
-
|
|
45977
|
+
phase = "config-unavailable";
|
|
45978
|
+
await hooks?.beforePublish?.();
|
|
45979
|
+
this.fence(holds);
|
|
45980
|
+
const published = row;
|
|
45981
|
+
mutateAccountsConfig(this.configPath(), PROVIDER, (entry) => {
|
|
45982
|
+
this.fence(holds);
|
|
45983
|
+
if (moveTargetState(entry, published) !== "same") {
|
|
45984
|
+
throw new LegacyMoveDeferredError("target-changed", "the Codex account row changed while the move was pending");
|
|
45985
|
+
}
|
|
45986
|
+
const current = entry.accounts.find((a) => a.id === published.id);
|
|
45987
|
+
if (current)
|
|
45988
|
+
current.store = dest.kind;
|
|
45989
|
+
entry.legacyCodexSuppressed = true;
|
|
45990
|
+
});
|
|
45991
|
+
} catch (err) {
|
|
45992
|
+
return this.moveDeferred(err, phase);
|
|
45378
45993
|
}
|
|
45379
|
-
let tokens;
|
|
45380
45994
|
try {
|
|
45381
|
-
|
|
45382
|
-
|
|
45383
|
-
|
|
45995
|
+
if (opts.trigger === "request")
|
|
45996
|
+
getAccountRegistry().invalidate();
|
|
45997
|
+
else
|
|
45998
|
+
await this.invalidate();
|
|
45999
|
+
} catch (err) {
|
|
46000
|
+
log(`[accounts] legacy Codex move: invalidation failed: ${redactSecrets(errorText7(err))}`);
|
|
45384
46001
|
}
|
|
45385
|
-
|
|
45386
|
-
|
|
46002
|
+
try {
|
|
46003
|
+
await hooks?.beforeDelete?.();
|
|
46004
|
+
} catch (err) {
|
|
46005
|
+
log(`[accounts] legacy Codex move: stopped before the file delete: ${errorText7(err)}`);
|
|
46006
|
+
return { kind: "moved", id: row.id, label: row.label, fileDeleted: false };
|
|
45387
46007
|
}
|
|
45388
|
-
const
|
|
45389
|
-
|
|
45390
|
-
return {
|
|
45391
|
-
access_token: tokens.access_token,
|
|
45392
|
-
refresh_token: tokens.refresh_token || from.refresh_token,
|
|
45393
|
-
expires_at: Date.now() + expiresIn * 1000,
|
|
45394
|
-
account_id: accountId
|
|
45395
|
-
};
|
|
45396
|
-
}
|
|
45397
|
-
setCredentials(credentials) {
|
|
45398
|
-
this.credentials = credentials;
|
|
45399
|
-
this.credentialFileFailure = null;
|
|
46008
|
+
const fileDeleted = await this.deleteMovedLegacyFile(source, row, material);
|
|
46009
|
+
return { kind: "moved", id: row.id, label: row.label, fileDeleted };
|
|
45400
46010
|
}
|
|
45401
|
-
|
|
45402
|
-
const
|
|
45403
|
-
|
|
45404
|
-
|
|
45405
|
-
|
|
45406
|
-
|
|
46011
|
+
enrolSynthesizedLegacy(expected, holds) {
|
|
46012
|
+
const out = { value: "gone" };
|
|
46013
|
+
mutateAccountsConfig(this.configPath(), PROVIDER, (entry) => {
|
|
46014
|
+
this.fence(holds);
|
|
46015
|
+
const legacyRow = entry.accounts.find((a) => a.store === "legacy-codex-file");
|
|
46016
|
+
if (legacyRow) {
|
|
46017
|
+
out.value = asRecord(legacyRow);
|
|
45407
46018
|
return;
|
|
45408
|
-
|
|
45409
|
-
|
|
46019
|
+
}
|
|
46020
|
+
if (entry.accounts.length > 0 || entry.legacyCodexSuppressed) {
|
|
46021
|
+
out.value = "already-moved";
|
|
45410
46022
|
return;
|
|
45411
|
-
|
|
45412
|
-
|
|
45413
|
-
|
|
45414
|
-
log(`[CodexOAuth] Credential file present but unusable \u2014 Codex requests fail until it is fixed: ${outcome.error.message}`);
|
|
46023
|
+
}
|
|
46024
|
+
const current = this.synthesizedLegacy(entry);
|
|
46025
|
+
if (!current)
|
|
45415
46026
|
return;
|
|
45416
|
-
|
|
45417
|
-
|
|
45418
|
-
|
|
45419
|
-
|
|
45420
|
-
|
|
45421
|
-
|
|
45422
|
-
|
|
45423
|
-
|
|
45424
|
-
|
|
45425
|
-
|
|
45426
|
-
|
|
45427
|
-
|
|
45428
|
-
|
|
45429
|
-
|
|
45430
|
-
|
|
45431
|
-
try {
|
|
45432
|
-
return { kind: "present", read: this.projectCredentialFile(raw) };
|
|
45433
|
-
} catch (e) {
|
|
45434
|
-
return { kind: "failed", error: asCredentialFileError(e, this.getCredentialsPath()) };
|
|
45435
|
-
}
|
|
45436
|
-
}
|
|
45437
|
-
readCredentialFile() {
|
|
45438
|
-
const raw = this.readRawCredentialFile();
|
|
45439
|
-
return raw === null ? null : this.projectCredentialFile(raw);
|
|
46027
|
+
if (current.identity.chatgptAccountId !== expected.identity.chatgptAccountId) {
|
|
46028
|
+
throw new LegacyMoveDeferredError("target-changed", "the legacy Codex login changed while the move was pending");
|
|
46029
|
+
}
|
|
46030
|
+
const row = {
|
|
46031
|
+
id: this.newId(entry),
|
|
46032
|
+
label: current.label,
|
|
46033
|
+
identity: current.identity,
|
|
46034
|
+
store: "legacy-codex-file",
|
|
46035
|
+
enabled: true,
|
|
46036
|
+
addedAt: current.addedAt
|
|
46037
|
+
};
|
|
46038
|
+
entry.accounts.push(row);
|
|
46039
|
+
out.value = asRecord(row);
|
|
46040
|
+
});
|
|
46041
|
+
return out.value;
|
|
45440
46042
|
}
|
|
45441
|
-
|
|
45442
|
-
|
|
45443
|
-
let text;
|
|
46043
|
+
async persistMovedRecord(dest, ref, record) {
|
|
46044
|
+
let ambiguous = false;
|
|
45444
46045
|
try {
|
|
45445
|
-
|
|
45446
|
-
} catch (
|
|
45447
|
-
if (
|
|
45448
|
-
|
|
45449
|
-
|
|
46046
|
+
await dest.write(ref, record);
|
|
46047
|
+
} catch (err) {
|
|
46048
|
+
if (!(err instanceof TokenStoreAmbiguousWriteError)) {
|
|
46049
|
+
throw new LegacyMoveDeferredError("keychain-write-failed", errorText7(err));
|
|
46050
|
+
}
|
|
46051
|
+
ambiguous = true;
|
|
46052
|
+
log("[accounts] legacy Codex move: ambiguous Keychain write, reading it back");
|
|
45450
46053
|
}
|
|
45451
|
-
let
|
|
46054
|
+
let proven;
|
|
45452
46055
|
try {
|
|
45453
|
-
|
|
45454
|
-
} catch {
|
|
45455
|
-
throw
|
|
45456
|
-
}
|
|
45457
|
-
if (!isRecord2(parsed)) {
|
|
45458
|
-
throw credentialFileUnreadable(credPath, "it is not a JSON object");
|
|
46056
|
+
proven = await dest.read(ref, { uncached: true });
|
|
46057
|
+
} catch (err) {
|
|
46058
|
+
throw new LegacyMoveDeferredError("keychain-unverified", `the Keychain item could not be read back: ${errorText7(err)}`);
|
|
45459
46059
|
}
|
|
45460
|
-
|
|
45461
|
-
|
|
45462
|
-
projectCredentialFile(raw) {
|
|
45463
|
-
const credPath = this.getCredentialsPath();
|
|
45464
|
-
const accessToken = raw.access_token;
|
|
45465
|
-
const refreshToken = raw.refresh_token;
|
|
45466
|
-
const expiresAt = raw.expires_at;
|
|
45467
|
-
if (typeof accessToken !== "string" || !accessToken || typeof refreshToken !== "string" || !refreshToken || typeof expiresAt !== "number" || !expiresAt) {
|
|
45468
|
-
log("[CodexOAuth] Invalid credentials file structure");
|
|
45469
|
-
throw new CodexOAuthLocalError(`The OpenAI Codex credential file (${credPath}) has no usable token pair. Run \`${LOGIN_COMMAND}\` to replace it.`);
|
|
46060
|
+
if (!proven || proven.writeId !== record.writeId || proven.generation !== record.generation || proven.fingerprint !== record.fingerprint) {
|
|
46061
|
+
throw new LegacyMoveDeferredError("keychain-unverified", ambiguous ? "the Keychain write timed out and its read-back does not show it" : "the Keychain read-back does not show this write");
|
|
45470
46062
|
}
|
|
45471
|
-
const accountId = raw.account_id;
|
|
45472
|
-
const generation = raw.claudish_generation;
|
|
45473
|
-
return {
|
|
45474
|
-
credentials: {
|
|
45475
|
-
access_token: accessToken,
|
|
45476
|
-
refresh_token: refreshToken,
|
|
45477
|
-
expires_at: expiresAt,
|
|
45478
|
-
...typeof accountId === "string" && accountId ? { account_id: accountId } : {}
|
|
45479
|
-
},
|
|
45480
|
-
generation: typeof generation === "number" ? generation : 0
|
|
45481
|
-
};
|
|
45482
46063
|
}
|
|
45483
|
-
|
|
45484
|
-
const credPath = this.getCredentialsPath();
|
|
45485
|
-
let existing = {};
|
|
46064
|
+
async deleteMovedLegacyFile(source, ref, copied) {
|
|
45486
46065
|
try {
|
|
45487
|
-
|
|
45488
|
-
|
|
45489
|
-
|
|
45490
|
-
|
|
46066
|
+
const now = await source.read(ref, { uncached: true });
|
|
46067
|
+
if (!now)
|
|
46068
|
+
return true;
|
|
46069
|
+
if (!sameDurableRecord(now, copied)) {
|
|
46070
|
+
log("[accounts] legacy Codex move: the credential file changed after the copy; kept it (the account list no longer names it)");
|
|
46071
|
+
return false;
|
|
45491
46072
|
}
|
|
45492
|
-
|
|
45493
|
-
|
|
46073
|
+
await source.delete(ref);
|
|
46074
|
+
return true;
|
|
46075
|
+
} catch (err) {
|
|
46076
|
+
log("[accounts] legacy Codex move: could not delete the credential file: " + redactSecrets(errorText7(err)));
|
|
46077
|
+
return false;
|
|
45494
46078
|
}
|
|
45495
|
-
const previousGeneration = typeof existing.claudish_generation === "number" ? existing.claudish_generation : 0;
|
|
45496
|
-
const document = {
|
|
45497
|
-
...existing,
|
|
45498
|
-
...credentials,
|
|
45499
|
-
claudish_generation: previousGeneration + 1
|
|
45500
|
-
};
|
|
45501
|
-
atomicWriteFileSync(credPath, JSON.stringify(document, null, 2), { mode: 384 });
|
|
45502
|
-
log(`[CodexOAuth] Credentials saved to ${credPath}`);
|
|
45503
46079
|
}
|
|
45504
|
-
|
|
46080
|
+
async deleteLeftover() {
|
|
46081
|
+
const legacy = {
|
|
46082
|
+
id: LEGACY_CODEX_ACCOUNT_ID,
|
|
46083
|
+
label: LEGACY_CODEX_LABEL,
|
|
46084
|
+
provider: PROVIDER,
|
|
46085
|
+
identity: {},
|
|
46086
|
+
store: "legacy-codex-file",
|
|
46087
|
+
enabled: true,
|
|
46088
|
+
addedAt: "",
|
|
46089
|
+
legacy: true
|
|
46090
|
+
};
|
|
45505
46091
|
try {
|
|
45506
|
-
|
|
45507
|
-
|
|
45508
|
-
|
|
45509
|
-
|
|
45510
|
-
|
|
45511
|
-
|
|
45512
|
-
|
|
45513
|
-
|
|
45514
|
-
|
|
45515
|
-
}
|
|
45516
|
-
return;
|
|
45517
|
-
} catch (e) {
|
|
45518
|
-
log(`[CodexOAuth] Failed to extract account ID from id_token: ${e.message}`);
|
|
45519
|
-
return;
|
|
46092
|
+
return await this.gate(legacy, async () => {
|
|
46093
|
+
const still = this.inspect((entry) => entry.legacyCodexSuppressed === true && !entry.accounts.some((row) => row.store === "legacy-codex-file") && this.legacyFilePresent());
|
|
46094
|
+
if (!still)
|
|
46095
|
+
return notApplicable("no-legacy-account");
|
|
46096
|
+
await this.store("legacy-codex-file").delete(legacy);
|
|
46097
|
+
log("[accounts] removed the leftover legacy Codex credential file; nothing reads it");
|
|
46098
|
+
return { kind: "leftover-deleted" };
|
|
46099
|
+
});
|
|
46100
|
+
} catch (err) {
|
|
46101
|
+
log(`[accounts] leftover legacy Codex file not removed: ${redactSecrets(errorText7(err))}`);
|
|
46102
|
+
return notApplicable("no-legacy-account");
|
|
45520
46103
|
}
|
|
45521
46104
|
}
|
|
46105
|
+
moveDeferred(err, fallback) {
|
|
46106
|
+
const reason = moveDeferralOf(err, fallback);
|
|
46107
|
+
const detail = redactSecrets(errorText7(err));
|
|
46108
|
+
log(`[accounts] legacy Codex move deferred (${reason}): ${detail}`);
|
|
46109
|
+
return { kind: "deferred", reason, detail };
|
|
46110
|
+
}
|
|
45522
46111
|
}
|
|
45523
|
-
function
|
|
45524
|
-
return
|
|
46112
|
+
function notApplicable(reason) {
|
|
46113
|
+
return { kind: "not-applicable", reason };
|
|
45525
46114
|
}
|
|
45526
|
-
|
|
45527
|
-
|
|
45528
|
-
|
|
45529
|
-
|
|
46115
|
+
function moveTargetState(entry, row) {
|
|
46116
|
+
const current = entry.accounts.find((a) => a.id === row.id);
|
|
46117
|
+
if (!current)
|
|
46118
|
+
return "removed";
|
|
46119
|
+
if (current.identity?.chatgptAccountId !== row.identity.chatgptAccountId)
|
|
46120
|
+
return "changed";
|
|
46121
|
+
if (current.store !== "legacy-codex-file")
|
|
46122
|
+
return "moved";
|
|
46123
|
+
return "same";
|
|
46124
|
+
}
|
|
46125
|
+
function moveDeferralOf(err, fallback) {
|
|
46126
|
+
if (err instanceof LegacyMoveDeferredError)
|
|
46127
|
+
return err.reason;
|
|
46128
|
+
if (err instanceof AccountListUnavailableError && err.failure?.source === "legacy") {
|
|
46129
|
+
return "file-unreadable";
|
|
46130
|
+
}
|
|
46131
|
+
if (err instanceof RefreshUnavailableError)
|
|
46132
|
+
return "gate-busy";
|
|
46133
|
+
return fallback;
|
|
46134
|
+
}
|
|
46135
|
+
function errorText7(err) {
|
|
46136
|
+
return err instanceof Error ? err.message : String(err);
|
|
46137
|
+
}
|
|
46138
|
+
var PROVIDER = "openai-codex", OWNED_ACCOUNTS_NOTICE, LEGACY_LOGIN_REFUSED, LEGACY_SAVE_WAIT_MS = 15000, LegacyMoveDeferredError, MOVE_HINTS;
|
|
46139
|
+
var init_management = __esm(() => {
|
|
46140
|
+
init_dist12();
|
|
45530
46141
|
init_logger();
|
|
45531
|
-
|
|
45532
|
-
|
|
45533
|
-
|
|
46142
|
+
init_profile_config();
|
|
46143
|
+
init_redact();
|
|
46144
|
+
init_codex_pkce();
|
|
46145
|
+
init_account_label();
|
|
46146
|
+
init_config_lock();
|
|
46147
|
+
init_file_lock();
|
|
46148
|
+
init_legacy_move();
|
|
45534
46149
|
init_paths();
|
|
45535
46150
|
init_refresh_lock();
|
|
46151
|
+
init_registry();
|
|
46152
|
+
init_state_store();
|
|
46153
|
+
init_token_store();
|
|
45536
46154
|
init_types();
|
|
45537
|
-
|
|
45538
|
-
|
|
45539
|
-
|
|
45540
|
-
|
|
45541
|
-
constructor(
|
|
45542
|
-
super(
|
|
45543
|
-
this.
|
|
46155
|
+
OWNED_ACCOUNTS_NOTICE = "Add only Codex accounts you own and pay for. Switching between subscriptions may be " + "restricted by OpenAI's terms; this tool is not permission to bypass them. Accept and continue?";
|
|
46156
|
+
LEGACY_LOGIN_REFUSED = "Codex accounts are managed by `claudish account`, or the legacy login was removed. " + "Run `claudish account add codex` for a new account, or " + "`claudish account login codex <label>` to log an existing one in again.";
|
|
46157
|
+
LegacyMoveDeferredError = class LegacyMoveDeferredError extends Error {
|
|
46158
|
+
reason;
|
|
46159
|
+
constructor(reason, detail) {
|
|
46160
|
+
super(detail);
|
|
46161
|
+
this.reason = reason;
|
|
46162
|
+
this.name = "LegacyMoveDeferredError";
|
|
45544
46163
|
}
|
|
45545
46164
|
};
|
|
45546
|
-
|
|
45547
|
-
|
|
45548
|
-
|
|
45549
|
-
|
|
45550
|
-
|
|
45551
|
-
|
|
46165
|
+
MOVE_HINTS = {
|
|
46166
|
+
"gate-busy": "Retry in a moment.",
|
|
46167
|
+
"config-unavailable": "Retry in a moment; if this persists, repair config.json.",
|
|
46168
|
+
"target-changed": "Retry in a moment.",
|
|
46169
|
+
"file-unreadable": "Repair or remove the file, then retry.",
|
|
46170
|
+
"keychain-unreadable": "Unlock the login keychain and retry.",
|
|
46171
|
+
"keychain-write-failed": "Unlock the login keychain and retry.",
|
|
46172
|
+
"keychain-unverified": "Unlock the login keychain and retry."
|
|
45552
46173
|
};
|
|
45553
|
-
|
|
46174
|
+
});
|
|
46175
|
+
|
|
46176
|
+
// src/auth/accounts/legacy-move.ts
|
|
46177
|
+
import { lstatSync as lstatSync4, realpathSync as realpathSync3 } from "fs";
|
|
46178
|
+
import { join as join22, resolve as resolve2 } from "path";
|
|
46179
|
+
function legacyMoveKeychainBlocked(env = process.env) {
|
|
46180
|
+
return env.CLAUDISH_DISABLE_KEYCHAIN === "1" || !isKeychainSupported();
|
|
46181
|
+
}
|
|
46182
|
+
function canonical(path) {
|
|
46183
|
+
try {
|
|
46184
|
+
return realpathSync3(path);
|
|
46185
|
+
} catch {
|
|
46186
|
+
return resolve2(path);
|
|
46187
|
+
}
|
|
46188
|
+
}
|
|
46189
|
+
function isDefaultConfigPath(configPath, env = process.env) {
|
|
46190
|
+
return canonical(configPath) === canonical(join22(claudishHome(env), "config.json"));
|
|
46191
|
+
}
|
|
46192
|
+
function legacyMoveApplies(env, configPath, registry) {
|
|
46193
|
+
if (legacyMoveKeychainBlocked(env))
|
|
46194
|
+
return false;
|
|
46195
|
+
let due;
|
|
46196
|
+
try {
|
|
46197
|
+
const snapshot = registry.snapshot("openai-codex");
|
|
46198
|
+
const rows = snapshot.accounts["openai-codex"];
|
|
46199
|
+
const legacyRow = rows.some((row) => row.legacy || row.store === "legacy-codex-file");
|
|
46200
|
+
due = legacyRow || snapshot.settings["openai-codex"]?.legacyCodexSuppressed === true && legacyFileExists(env);
|
|
46201
|
+
} catch {
|
|
46202
|
+
return false;
|
|
46203
|
+
}
|
|
46204
|
+
if (!due || !isDefaultConfigPath(configPath, env))
|
|
46205
|
+
return false;
|
|
46206
|
+
return defaultAccountStore(env).kind === "keychain";
|
|
46207
|
+
}
|
|
46208
|
+
function legacyFileExists(env) {
|
|
46209
|
+
try {
|
|
46210
|
+
lstatSync4(legacyCodexCredentialsPath(env));
|
|
46211
|
+
return true;
|
|
46212
|
+
} catch {
|
|
46213
|
+
return false;
|
|
46214
|
+
}
|
|
46215
|
+
}
|
|
46216
|
+
function startLegacyCodexMove(opts = {}) {
|
|
46217
|
+
try {
|
|
46218
|
+
if (inFlight2)
|
|
46219
|
+
return;
|
|
46220
|
+
const now = opts.now ?? Date.now;
|
|
46221
|
+
if (now() < cooldownUntil)
|
|
46222
|
+
return;
|
|
46223
|
+
const env = opts.env ?? process.env;
|
|
46224
|
+
const configPath = (opts.configPath ?? activeConfigFile)();
|
|
46225
|
+
if (!legacyMoveApplies(env, configPath, opts.registry ?? getAccountRegistry()))
|
|
46226
|
+
return;
|
|
46227
|
+
const move = (async () => {
|
|
46228
|
+
await Promise.resolve().then(() => init_management());
|
|
46229
|
+
return moveLegacyCodexLogin({ env, configPath: () => configPath, ...opts.management }, { trigger: "request" });
|
|
46230
|
+
})().catch((err) => ({
|
|
46231
|
+
kind: "deferred",
|
|
46232
|
+
reason: "config-unavailable",
|
|
46233
|
+
detail: redactSecrets(err instanceof Error ? err.message : String(err))
|
|
46234
|
+
})).then((outcome) => {
|
|
46235
|
+
log(`[accounts] legacy Codex move (request path): ${outcome.kind}`);
|
|
46236
|
+
if (outcome.kind === "deferred" || outcome.kind === "not-applicable") {
|
|
46237
|
+
cooldownUntil = now() + LEGACY_MOVE_RETRY_MS;
|
|
46238
|
+
}
|
|
46239
|
+
return outcome;
|
|
46240
|
+
}).finally(() => {
|
|
46241
|
+
if (inFlight2 === move)
|
|
46242
|
+
inFlight2 = null;
|
|
46243
|
+
});
|
|
46244
|
+
inFlight2 = move;
|
|
46245
|
+
lastMove = move;
|
|
46246
|
+
} catch (err) {
|
|
46247
|
+
log(`[accounts] legacy Codex move not started: ${err instanceof Error ? err.message : String(err)}`);
|
|
46248
|
+
}
|
|
46249
|
+
}
|
|
46250
|
+
var LEGACY_MOVE_RETRY_MS, inFlight2 = null, lastMove = null, cooldownUntil = 0;
|
|
46251
|
+
var init_legacy_move = __esm(() => {
|
|
46252
|
+
init_logger();
|
|
46253
|
+
init_profile_config();
|
|
46254
|
+
init_keychain();
|
|
46255
|
+
init_redact();
|
|
46256
|
+
init_paths();
|
|
46257
|
+
init_registry();
|
|
46258
|
+
init_token_store();
|
|
46259
|
+
LEGACY_MOVE_RETRY_MS = 10 * 60 * 1000;
|
|
45554
46260
|
});
|
|
45555
46261
|
|
|
45556
46262
|
// src/auth/credentials/composite-credential.ts
|
|
@@ -45654,6 +46360,9 @@ class CodexOAuthHalf {
|
|
|
45654
46360
|
}
|
|
45655
46361
|
async getRequestAuth(ctx) {
|
|
45656
46362
|
if (ctx.requireOAuth && !this.oauth.hasCredentials()) {
|
|
46363
|
+
const why = await this.oauth.missingLoginError();
|
|
46364
|
+
if (why instanceof CodexOAuthLocalError)
|
|
46365
|
+
throw why;
|
|
45657
46366
|
throw new MeteredEscapeError("Codex OAuth login disappeared during this request");
|
|
45658
46367
|
}
|
|
45659
46368
|
const token = ctx.forceRefresh ? await this.oauth.forceRefresh() : await this.oauth.getAccessToken();
|
|
@@ -45693,6 +46402,7 @@ class CodexPoolHalf {
|
|
|
45693
46402
|
return this.registry.hasConfiguredAccounts("openai-codex");
|
|
45694
46403
|
}
|
|
45695
46404
|
async getRequestAuth(ctx) {
|
|
46405
|
+
startLegacyCodexMove();
|
|
45696
46406
|
if (!ctx.pin && this.registry.isSynthesizedLegacyOnly()) {
|
|
45697
46407
|
return this.legacy.getRequestAuth(ctx);
|
|
45698
46408
|
}
|
|
@@ -45733,6 +46443,7 @@ var CODEX_RESPONSES_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses"
|
|
|
45733
46443
|
var init_codex_credential = __esm(() => {
|
|
45734
46444
|
init_test_upstream();
|
|
45735
46445
|
init_errors3();
|
|
46446
|
+
init_legacy_move();
|
|
45736
46447
|
init_pool();
|
|
45737
46448
|
init_registry();
|
|
45738
46449
|
init_codex_oauth();
|
|
@@ -45743,9 +46454,9 @@ var init_codex_credential = __esm(() => {
|
|
|
45743
46454
|
// src/providers/devin/devin-credentials.ts
|
|
45744
46455
|
import { readFileSync as readFileSync23 } from "fs";
|
|
45745
46456
|
import { homedir as homedir22 } from "os";
|
|
45746
|
-
import { join as
|
|
46457
|
+
import { join as join23 } from "path";
|
|
45747
46458
|
function devinCredentialsPath() {
|
|
45748
|
-
return credentialsPathOverride ??
|
|
46459
|
+
return credentialsPathOverride ?? join23(homedir22(), ".local", "share", "devin", "credentials.toml");
|
|
45749
46460
|
}
|
|
45750
46461
|
function readTomlString(source, key) {
|
|
45751
46462
|
const match = source.match(new RegExp(`^\\s*${key}\\s*=\\s*"([^"]*)"`, "m"));
|
|
@@ -45885,13 +46596,13 @@ import {
|
|
|
45885
46596
|
writeSync as writeSync6
|
|
45886
46597
|
} from "fs";
|
|
45887
46598
|
import { homedir as homedir23, hostname as hostname3, platform, release as release2 } from "os";
|
|
45888
|
-
import { dirname as dirname12, join as
|
|
46599
|
+
import { dirname as dirname12, join as join24 } from "path";
|
|
45889
46600
|
import { promisify as promisify4 } from "util";
|
|
45890
46601
|
function kimiOAuthPath() {
|
|
45891
|
-
return kimiOAuthPathOverride ??
|
|
46602
|
+
return kimiOAuthPathOverride ?? join24(homedir23(), ".claudish", "kimi-oauth.json");
|
|
45892
46603
|
}
|
|
45893
46604
|
function kimiDeviceIdPath() {
|
|
45894
|
-
return kimiDeviceIdPathOverride ??
|
|
46605
|
+
return kimiDeviceIdPathOverride ?? join24(homedir23(), ".claudish", "kimi-device-id");
|
|
45895
46606
|
}
|
|
45896
46607
|
function isKimiCredentials(data) {
|
|
45897
46608
|
if (!data || typeof data !== "object")
|
|
@@ -46236,7 +46947,7 @@ var init_kimi_oauth = __esm(() => {
|
|
|
46236
46947
|
// src/auth/oauth-registry.ts
|
|
46237
46948
|
import { existsSync as existsSync20, readFileSync as readFileSync25 } from "fs";
|
|
46238
46949
|
import { homedir as homedir24 } from "os";
|
|
46239
|
-
import { join as
|
|
46950
|
+
import { join as join25 } from "path";
|
|
46240
46951
|
function credentialSatisfies(descriptor, data) {
|
|
46241
46952
|
if (!data?.access_token)
|
|
46242
46953
|
return false;
|
|
@@ -46249,7 +46960,7 @@ function credentialSatisfies(descriptor, data) {
|
|
|
46249
46960
|
return true;
|
|
46250
46961
|
}
|
|
46251
46962
|
function hasValidOAuthCredentials(descriptor) {
|
|
46252
|
-
const credPath =
|
|
46963
|
+
const credPath = join25(homedir24(), ".claudish", descriptor.credentialFile);
|
|
46253
46964
|
if (!existsSync20(credPath))
|
|
46254
46965
|
return false;
|
|
46255
46966
|
if (descriptor.validationMode === "file-exists") {
|
|
@@ -46265,10 +46976,18 @@ function hasOAuthCredentials(providerName) {
|
|
|
46265
46976
|
const descriptor = OAUTH_PROVIDERS[providerName];
|
|
46266
46977
|
if (!descriptor)
|
|
46267
46978
|
return false;
|
|
46979
|
+
if (providerName === "openai-codex") {
|
|
46980
|
+
try {
|
|
46981
|
+
return getAccountRegistry().hasConfiguredAccounts("openai-codex");
|
|
46982
|
+
} catch {
|
|
46983
|
+
return false;
|
|
46984
|
+
}
|
|
46985
|
+
}
|
|
46268
46986
|
return hasValidOAuthCredentials(descriptor);
|
|
46269
46987
|
}
|
|
46270
46988
|
var OAUTH_PROVIDERS;
|
|
46271
46989
|
var init_oauth_registry = __esm(() => {
|
|
46990
|
+
init_registry();
|
|
46272
46991
|
OAUTH_PROVIDERS = {
|
|
46273
46992
|
"kimi-coding": {
|
|
46274
46993
|
credentialFile: "kimi-oauth.json",
|
|
@@ -46442,12 +47161,12 @@ import {
|
|
|
46442
47161
|
writeFileSync as writeFileSync9
|
|
46443
47162
|
} from "fs";
|
|
46444
47163
|
import { homedir as homedir25 } from "os";
|
|
46445
|
-
import { join as
|
|
47164
|
+
import { join as join26 } from "path";
|
|
46446
47165
|
function defaultMuseStoreDir() {
|
|
46447
|
-
return
|
|
47166
|
+
return join26(homedir25(), ".claudish");
|
|
46448
47167
|
}
|
|
46449
47168
|
function museStorePath(kind, dir = defaultMuseStoreDir()) {
|
|
46450
|
-
return
|
|
47169
|
+
return join26(dir, FILE_NAMES[kind]);
|
|
46451
47170
|
}
|
|
46452
47171
|
function isValidMuseStore(value) {
|
|
46453
47172
|
if (!value || typeof value !== "object")
|
|
@@ -46502,7 +47221,7 @@ var init_muse_store = __esm(() => {
|
|
|
46502
47221
|
|
|
46503
47222
|
// src/providers/meta-muse/muse-keychain.ts
|
|
46504
47223
|
import { mkdirSync as mkdirSync14, readFileSync as readFileSync27, rmSync as rmSync8, statSync as statSync8, writeFileSync as writeFileSync10 } from "fs";
|
|
46505
|
-
import { join as
|
|
47224
|
+
import { join as join27 } from "path";
|
|
46506
47225
|
function processExists(pid) {
|
|
46507
47226
|
try {
|
|
46508
47227
|
process.kill(pid, 0);
|
|
@@ -46515,7 +47234,7 @@ function resetMuseKeychainState() {
|
|
|
46515
47234
|
keychainEpoch++;
|
|
46516
47235
|
retainedToken = null;
|
|
46517
47236
|
lateResult = null;
|
|
46518
|
-
|
|
47237
|
+
inFlight3 = null;
|
|
46519
47238
|
}
|
|
46520
47239
|
function disabled() {
|
|
46521
47240
|
return deps2.env().CLAUDISH_DISABLE_KEYCHAIN === "1";
|
|
@@ -46552,7 +47271,7 @@ function parseMuseKeychainValue(value) {
|
|
|
46552
47271
|
}
|
|
46553
47272
|
}
|
|
46554
47273
|
function lockPath2() {
|
|
46555
|
-
return
|
|
47274
|
+
return join27(deps2.dir(), LOCK_FILE);
|
|
46556
47275
|
}
|
|
46557
47276
|
function readLock() {
|
|
46558
47277
|
let mtime;
|
|
@@ -46639,8 +47358,8 @@ async function withMuseKeychainLogin(use, deadlineMs = MUSE_KEYCHAIN_READ_DEADLI
|
|
|
46639
47358
|
lateResult = null;
|
|
46640
47359
|
return settle(result, use);
|
|
46641
47360
|
}
|
|
46642
|
-
if (
|
|
46643
|
-
return raceJoin(
|
|
47361
|
+
if (inFlight3)
|
|
47362
|
+
return raceJoin(inFlight3, deadline, use);
|
|
46644
47363
|
if (deps2.now() >= deadline)
|
|
46645
47364
|
return { kind: "timeout" };
|
|
46646
47365
|
const lock = readLock();
|
|
@@ -46679,7 +47398,7 @@ async function withMuseKeychainLogin(use, deadlineMs = MUSE_KEYCHAIN_READ_DEADLI
|
|
|
46679
47398
|
const done = spawn.done.finally(() => {
|
|
46680
47399
|
rewriteLock(deps2.pid(), "claudish");
|
|
46681
47400
|
});
|
|
46682
|
-
|
|
47401
|
+
inFlight3 = done;
|
|
46683
47402
|
inFlightEpoch = keychainEpoch;
|
|
46684
47403
|
return raceJoin(done, deadline, use);
|
|
46685
47404
|
}
|
|
@@ -46696,16 +47415,16 @@ async function raceJoin(done, deadline, use) {
|
|
|
46696
47415
|
if (winner === "timeout") {
|
|
46697
47416
|
const startedIn = inFlightEpoch;
|
|
46698
47417
|
done.then((result) => {
|
|
46699
|
-
if (
|
|
46700
|
-
|
|
47418
|
+
if (inFlight3 === done)
|
|
47419
|
+
inFlight3 = null;
|
|
46701
47420
|
if (startedIn === keychainEpoch)
|
|
46702
47421
|
lateResult = result;
|
|
46703
47422
|
releaseLock();
|
|
46704
47423
|
});
|
|
46705
47424
|
return { kind: "timeout" };
|
|
46706
47425
|
}
|
|
46707
|
-
if (
|
|
46708
|
-
|
|
47426
|
+
if (inFlight3 === done)
|
|
47427
|
+
inFlight3 = null;
|
|
46709
47428
|
try {
|
|
46710
47429
|
return await settle(winner, use);
|
|
46711
47430
|
} finally {
|
|
@@ -46750,7 +47469,7 @@ async function waitForLockThenCopy(deadline, sinceMtime) {
|
|
|
46750
47469
|
await deps2.sleep(Math.min(LOCK_POLL_MS, Math.max(0, deadline - deps2.now())));
|
|
46751
47470
|
}
|
|
46752
47471
|
}
|
|
46753
|
-
var MUSE_KEYCHAIN_SERVICE = "ai.meta.dev.credentials", MUSE_KEYCHAIN_ACCOUNT = "meta", LOCK_FILE = "muse-keychain-read.lock", PRESENCE_TTL_MS = 3000, LOCK_POLL_MS = 500, LOCK_MAX_AGE_MS, UNREADABLE_LOCK_GRACE_MS = 5000, MUSE_KEYCHAIN_READ_DEADLINE_MS = 120000, defaultDeps4, deps2, presenceMemo = null,
|
|
47472
|
+
var MUSE_KEYCHAIN_SERVICE = "ai.meta.dev.credentials", MUSE_KEYCHAIN_ACCOUNT = "meta", LOCK_FILE = "muse-keychain-read.lock", PRESENCE_TTL_MS = 3000, LOCK_POLL_MS = 500, LOCK_MAX_AGE_MS, UNREADABLE_LOCK_GRACE_MS = 5000, MUSE_KEYCHAIN_READ_DEADLINE_MS = 120000, defaultDeps4, deps2, presenceMemo = null, inFlight3 = null, lateResult = null, keychainEpoch = 0, inFlightEpoch = 0, holdsLock = false, retainedToken = null;
|
|
46754
47473
|
var init_muse_keychain = __esm(() => {
|
|
46755
47474
|
init_prompt_role();
|
|
46756
47475
|
init_keychain();
|
|
@@ -50314,7 +51033,7 @@ var init_vision_proxy = __esm(() => {
|
|
|
50314
51033
|
|
|
50315
51034
|
// src/mcp-session/home-dir.ts
|
|
50316
51035
|
import { homedir as homedir27 } from "os";
|
|
50317
|
-
import { join as
|
|
51036
|
+
import { join as join28 } from "path";
|
|
50318
51037
|
function userHomeFrom(env, homedirFn = homedir27) {
|
|
50319
51038
|
const home = env.HOME;
|
|
50320
51039
|
return home && home.length > 0 ? home : homedirFn();
|
|
@@ -50323,7 +51042,7 @@ function sessionsDirFrom(env, homedirFn = homedir27) {
|
|
|
50323
51042
|
const explicit = env.CLAUDISH_SESSIONS_DIR;
|
|
50324
51043
|
if (explicit && explicit.length > 0)
|
|
50325
51044
|
return explicit;
|
|
50326
|
-
return
|
|
51045
|
+
return join28(userHomeFrom(env, homedirFn), ".claudish", "sessions");
|
|
50327
51046
|
}
|
|
50328
51047
|
function claudeCodeStringHash(s) {
|
|
50329
51048
|
let h = 0;
|
|
@@ -50498,7 +51217,7 @@ var init_transcript_tailer = () => {};
|
|
|
50498
51217
|
// src/session-events/index.ts
|
|
50499
51218
|
import { existsSync as existsSync22, readFileSync as readFileSync28, readdirSync as readdirSync3 } from "fs";
|
|
50500
51219
|
import { homedir as homedir28 } from "os";
|
|
50501
|
-
import { join as
|
|
51220
|
+
import { join as join29 } from "path";
|
|
50502
51221
|
function extractSessionId2(metadata) {
|
|
50503
51222
|
const userId = metadata?.user_id;
|
|
50504
51223
|
if (typeof userId !== "string")
|
|
@@ -50523,7 +51242,7 @@ class SessionEventRegistry {
|
|
|
50523
51242
|
claudeHome;
|
|
50524
51243
|
pollIntervalMs;
|
|
50525
51244
|
constructor(opts = {}) {
|
|
50526
|
-
this.claudeHome = opts.claudeHome ??
|
|
51245
|
+
this.claudeHome = opts.claudeHome ?? join29(homedir28(), ".claude");
|
|
50527
51246
|
this.pollIntervalMs = opts.pollIntervalMs;
|
|
50528
51247
|
}
|
|
50529
51248
|
ensureSession(sessionId) {
|
|
@@ -50609,13 +51328,13 @@ class SessionEventRegistry {
|
|
|
50609
51328
|
}
|
|
50610
51329
|
}
|
|
50611
51330
|
locateTranscript(sessionId) {
|
|
50612
|
-
const projectsDir =
|
|
50613
|
-
const primary =
|
|
51331
|
+
const projectsDir = join29(this.claudeHome, "projects");
|
|
51332
|
+
const primary = join29(projectsDir, slugFromCwd(process.cwd()), `${sessionId}.jsonl`);
|
|
50614
51333
|
if (existsSync22(primary))
|
|
50615
51334
|
return primary;
|
|
50616
51335
|
try {
|
|
50617
51336
|
for (const dir of readdirSync3(projectsDir)) {
|
|
50618
|
-
const candidate =
|
|
51337
|
+
const candidate = join29(projectsDir, dir, `${sessionId}.jsonl`);
|
|
50619
51338
|
if (existsSync22(candidate))
|
|
50620
51339
|
return candidate;
|
|
50621
51340
|
}
|
|
@@ -50624,7 +51343,7 @@ class SessionEventRegistry {
|
|
|
50624
51343
|
}
|
|
50625
51344
|
readSettingsEffortLevel() {
|
|
50626
51345
|
try {
|
|
50627
|
-
const settings = JSON.parse(readFileSync28(
|
|
51346
|
+
const settings = JSON.parse(readFileSync28(join29(this.claudeHome, "settings.json"), "utf-8"));
|
|
50628
51347
|
return typeof settings.effortLevel === "string" ? settings.effortLevel : undefined;
|
|
50629
51348
|
} catch {
|
|
50630
51349
|
return;
|
|
@@ -50735,7 +51454,7 @@ import {
|
|
|
50735
51454
|
writeFileSync as writeFileSync11
|
|
50736
51455
|
} from "fs";
|
|
50737
51456
|
import { homedir as homedir29 } from "os";
|
|
50738
|
-
import { join as
|
|
51457
|
+
import { join as join30 } from "path";
|
|
50739
51458
|
function ensureDir() {
|
|
50740
51459
|
if (!existsSync23(CLAUDISH_DIR)) {
|
|
50741
51460
|
mkdirSync15(CLAUDISH_DIR, { recursive: true });
|
|
@@ -50770,7 +51489,7 @@ function writeToDisk(events) {
|
|
|
50770
51489
|
ensureDir();
|
|
50771
51490
|
const trimmed = enforceSizeCap([...events]);
|
|
50772
51491
|
const payload = { version: 1, events: trimmed };
|
|
50773
|
-
const tmpFile =
|
|
51492
|
+
const tmpFile = join30(CLAUDISH_DIR, `stats-buffer.tmp.${process.pid}.json`);
|
|
50774
51493
|
writeFileSync11(tmpFile, JSON.stringify(payload, null, 2), "utf-8");
|
|
50775
51494
|
renameSync8(tmpFile, BUFFER_FILE);
|
|
50776
51495
|
memoryCache = trimmed;
|
|
@@ -50843,8 +51562,8 @@ function syncFlushOnExit() {
|
|
|
50843
51562
|
var BUFFER_MAX_BYTES, CLAUDISH_DIR, BUFFER_FILE, memoryCache = null, eventsSinceLastFlush = 0, flushScheduled = false, SIGNAL_EXIT_CODE;
|
|
50844
51563
|
var init_stats_buffer = __esm(() => {
|
|
50845
51564
|
BUFFER_MAX_BYTES = 64 * 1024;
|
|
50846
|
-
CLAUDISH_DIR =
|
|
50847
|
-
BUFFER_FILE =
|
|
51565
|
+
CLAUDISH_DIR = join30(homedir29(), ".claudish");
|
|
51566
|
+
BUFFER_FILE = join30(CLAUDISH_DIR, "stats-buffer.json");
|
|
50848
51567
|
process.on("exit", syncFlushOnExit);
|
|
50849
51568
|
SIGNAL_EXIT_CODE = { SIGTERM: 143, SIGINT: 130 };
|
|
50850
51569
|
for (const signal of ["SIGTERM", "SIGINT"]) {
|
|
@@ -54891,7 +55610,7 @@ var init_openai_responses_sse = __esm(() => {
|
|
|
54891
55610
|
});
|
|
54892
55611
|
|
|
54893
55612
|
// src/session/token-file.ts
|
|
54894
|
-
import { join as
|
|
55613
|
+
import { join as join31 } from "path";
|
|
54895
55614
|
function assignedTokenFile(env) {
|
|
54896
55615
|
const f = env.CLAUDISH_TOKEN_FILE;
|
|
54897
55616
|
if (!f || f === env.CLAUDISH_PUBLISHED_TOKEN_FILE)
|
|
@@ -54899,7 +55618,7 @@ function assignedTokenFile(env) {
|
|
|
54899
55618
|
return f;
|
|
54900
55619
|
}
|
|
54901
55620
|
function resolveTokenFilePath(port, env = process.env) {
|
|
54902
|
-
return assignedTokenFile(env) ??
|
|
55621
|
+
return assignedTokenFile(env) ?? join31(userHomeFrom(env), ".claudish", `tokens-${port}.json`);
|
|
54903
55622
|
}
|
|
54904
55623
|
var init_token_file = __esm(() => {
|
|
54905
55624
|
init_home_dir();
|
|
@@ -57340,9 +58059,9 @@ var init_composed_handler = __esm(() => {
|
|
|
57340
58059
|
// src/providers/api-key-provenance.ts
|
|
57341
58060
|
import { existsSync as existsSync24, readFileSync as readFileSync30 } from "fs";
|
|
57342
58061
|
import { homedir as homedir30 } from "os";
|
|
57343
|
-
import { join as
|
|
58062
|
+
import { join as join32, resolve as resolve3 } from "path";
|
|
57344
58063
|
function activeConfigPath() {
|
|
57345
|
-
return activeGlobalConfigFile(
|
|
58064
|
+
return activeGlobalConfigFile(join32(homedir30(), ".claudish", "config.json"));
|
|
57346
58065
|
}
|
|
57347
58066
|
function configLayerLabel() {
|
|
57348
58067
|
return getConfigFileOverride() ? activeConfigPath() : "~/.claudish/config.json";
|
|
@@ -57360,7 +58079,7 @@ function resolveApiKeyProvenance(envVar, aliases) {
|
|
|
57360
58079
|
const allVars = [envVar, ...aliases || []];
|
|
57361
58080
|
const dotenvValue = readDotenvKey(allVars);
|
|
57362
58081
|
layers.push({
|
|
57363
|
-
source: `.env (${
|
|
58082
|
+
source: `.env (${resolve3(".env")})`,
|
|
57364
58083
|
maskedValue: maskKey(dotenvValue),
|
|
57365
58084
|
isActive: false
|
|
57366
58085
|
});
|
|
@@ -57466,7 +58185,7 @@ function formatProvenanceLog(p) {
|
|
|
57466
58185
|
}
|
|
57467
58186
|
function readDotenvKey(envVars) {
|
|
57468
58187
|
try {
|
|
57469
|
-
const dotenvPath =
|
|
58188
|
+
const dotenvPath = resolve3(".env");
|
|
57470
58189
|
if (!existsSync24(dotenvPath))
|
|
57471
58190
|
return null;
|
|
57472
58191
|
const parsed = readAllowedDotenv(readFileSync30(dotenvPath, "utf-8"));
|
|
@@ -71181,7 +71900,7 @@ function compareByConnectionPrice(a, b) {
|
|
|
71181
71900
|
// src/providers/probe-catalog.ts
|
|
71182
71901
|
import { existsSync as existsSync25, mkdirSync as mkdirSync17, readFileSync as readFileSync31, writeFileSync as writeFileSync13 } from "fs";
|
|
71183
71902
|
import { homedir as homedir31 } from "os";
|
|
71184
|
-
import { dirname as dirname14, join as
|
|
71903
|
+
import { dirname as dirname14, join as join33 } from "path";
|
|
71185
71904
|
function describeProbeCatalogFailure(outcome) {
|
|
71186
71905
|
switch (outcome.kind) {
|
|
71187
71906
|
case "incompatible":
|
|
@@ -71352,7 +72071,7 @@ var PROBE_MODELS_URL = "https://us-central1-claudish-6da10.cloudfunctions.net/pr
|
|
|
71352
72071
|
var init_probe_catalog = __esm(() => {
|
|
71353
72072
|
init_catalog_route_bindings();
|
|
71354
72073
|
CACHE_TTL_MS4 = 60 * 60 * 1000;
|
|
71355
|
-
PROBE_MODELS_CACHE_PATH =
|
|
72074
|
+
PROBE_MODELS_CACHE_PATH = join33(homedir31(), ".claudish", "probe-models.json");
|
|
71356
72075
|
});
|
|
71357
72076
|
|
|
71358
72077
|
// src/providers/route-candidates.ts
|
|
@@ -73369,7 +74088,7 @@ var init_slot_row = __esm(() => {
|
|
|
73369
74088
|
|
|
73370
74089
|
// src/pane/prompt-delivery.ts
|
|
73371
74090
|
import { writeFileSync as writeFileSync14 } from "fs";
|
|
73372
|
-
import { join as
|
|
74091
|
+
import { join as join34 } from "path";
|
|
73373
74092
|
function stripControl(s) {
|
|
73374
74093
|
let out = "";
|
|
73375
74094
|
for (const ch of s) {
|
|
@@ -73436,7 +74155,7 @@ function renderTurnFile(text, limit = READ_LINE_LIMIT) {
|
|
|
73436
74155
|
`), lines: out.length, marker };
|
|
73437
74156
|
}
|
|
73438
74157
|
function turnFilePath(turnDir, turnIndex) {
|
|
73439
|
-
return
|
|
74158
|
+
return join34(turnDir, `turn-${turnIndex}.md`);
|
|
73440
74159
|
}
|
|
73441
74160
|
function fileTemplate(file, marker) {
|
|
73442
74161
|
const base = `Your task is in the file \`${file}\`. Read all of it with the Read tool (in parts if it is long), then do exactly what it says, treating its content as the user's message.`;
|
|
@@ -73517,12 +74236,12 @@ var init_prompt_delivery = __esm(() => {
|
|
|
73517
74236
|
|
|
73518
74237
|
// src/team-stats.ts
|
|
73519
74238
|
import { existsSync as existsSync26, readFileSync as readFileSync32, writeFileSync as writeFileSync15 } from "fs";
|
|
73520
|
-
import { join as
|
|
74239
|
+
import { join as join35 } from "path";
|
|
73521
74240
|
function statsDir(sessionPath) {
|
|
73522
|
-
return
|
|
74241
|
+
return join35(sessionPath, "stats");
|
|
73523
74242
|
}
|
|
73524
74243
|
function tokenFileFor(sessionPath, anonId) {
|
|
73525
|
-
return
|
|
74244
|
+
return join35(statsDir(sessionPath), `${anonId}.json`);
|
|
73526
74245
|
}
|
|
73527
74246
|
function readTokenStatsAt(path) {
|
|
73528
74247
|
if (!existsSync26(path))
|
|
@@ -73685,7 +74404,7 @@ ${segs.join(" \xB7 ")}`;
|
|
|
73685
74404
|
}
|
|
73686
74405
|
function writeStatusFile(sessionPath, manifest, status, opts) {
|
|
73687
74406
|
try {
|
|
73688
|
-
writeFileSync15(
|
|
74407
|
+
writeFileSync15(join35(sessionPath, "status.txt"), `${renderTeamStats(sessionPath, manifest, status, opts)}
|
|
73689
74408
|
`, "utf-8");
|
|
73690
74409
|
} catch {}
|
|
73691
74410
|
}
|
|
@@ -73928,7 +74647,7 @@ var init_effort_recommendations = __esm(() => {
|
|
|
73928
74647
|
// src/model-loader.ts
|
|
73929
74648
|
import { existsSync as existsSync27, readFileSync as readFileSync34 } from "fs";
|
|
73930
74649
|
import { homedir as homedir32 } from "os";
|
|
73931
|
-
import { join as
|
|
74650
|
+
import { join as join36 } from "path";
|
|
73932
74651
|
function isRecommendationTier(value) {
|
|
73933
74652
|
return typeof value === "string" && RECOMMENDATION_TIER_INDEX.has(value);
|
|
73934
74653
|
}
|
|
@@ -74509,7 +75228,7 @@ var init_model_loader = __esm(() => {
|
|
|
74509
75228
|
];
|
|
74510
75229
|
RECOMMENDATION_TIER_INDEX = new Map(RECOMMENDATION_SECTIONS.map((section, index) => [section.recommendationTier, index]));
|
|
74511
75230
|
FIREBASE_RECOMMENDED_URL = `${FIREBASE_BASE_URL}?catalog=recommended`;
|
|
74512
|
-
RECOMMENDED_MODELS_CACHE_PATH =
|
|
75231
|
+
RECOMMENDED_MODELS_CACHE_PATH = join36(homedir32(), ".claudish", "recommended-models-cache-v2.json");
|
|
74513
75232
|
RECOMMENDED_ROUTE_TIER_ORDER = {
|
|
74514
75233
|
native: 0,
|
|
74515
75234
|
general: 1,
|
|
@@ -75807,7 +76526,7 @@ var init_DiscoveryNotice = __esm(() => {
|
|
|
75807
76526
|
// src/providers/model-descriptions.ts
|
|
75808
76527
|
import { existsSync as existsSync28, mkdirSync as mkdirSync18, readFileSync as readFileSync35, writeFileSync as writeFileSync16 } from "fs";
|
|
75809
76528
|
import { homedir as homedir33 } from "os";
|
|
75810
|
-
import { dirname as dirname15, join as
|
|
76529
|
+
import { dirname as dirname15, join as join37 } from "path";
|
|
75811
76530
|
function emptyDescriptionIndex() {
|
|
75812
76531
|
return EMPTY_INDEX;
|
|
75813
76532
|
}
|
|
@@ -75900,7 +76619,7 @@ var init_model_descriptions = __esm(() => {
|
|
|
75900
76619
|
init_logger();
|
|
75901
76620
|
init_model_loader();
|
|
75902
76621
|
init_cache_ttl();
|
|
75903
|
-
MODEL_DESCRIPTIONS_CACHE_PATH =
|
|
76622
|
+
MODEL_DESCRIPTIONS_CACHE_PATH = join37(homedir33(), ".claudish", "model-descriptions.json");
|
|
75904
76623
|
EMPTY_INDEX = { get: () => {
|
|
75905
76624
|
return;
|
|
75906
76625
|
}, size: 0 };
|
|
@@ -82233,22 +82952,22 @@ var init_branding = __esm(() => {
|
|
|
82233
82952
|
// src/update-checker.ts
|
|
82234
82953
|
import { existsSync as existsSync29, mkdirSync as mkdirSync19, readFileSync as readFileSync36, unlinkSync as unlinkSync9, writeFileSync as writeFileSync17 } from "fs";
|
|
82235
82954
|
import { homedir as homedir34, platform as platform2, tmpdir } from "os";
|
|
82236
|
-
import { join as
|
|
82955
|
+
import { join as join38 } from "path";
|
|
82237
82956
|
function getCacheFilePath() {
|
|
82238
82957
|
let cacheDir;
|
|
82239
82958
|
if (isWindows) {
|
|
82240
|
-
const localAppData = process.env.LOCALAPPDATA ||
|
|
82241
|
-
cacheDir =
|
|
82959
|
+
const localAppData = process.env.LOCALAPPDATA || join38(homedir34(), "AppData", "Local");
|
|
82960
|
+
cacheDir = join38(localAppData, "claudish");
|
|
82242
82961
|
} else {
|
|
82243
|
-
cacheDir =
|
|
82962
|
+
cacheDir = join38(homedir34(), ".cache", "claudish");
|
|
82244
82963
|
}
|
|
82245
82964
|
try {
|
|
82246
82965
|
if (!existsSync29(cacheDir)) {
|
|
82247
82966
|
mkdirSync19(cacheDir, { recursive: true });
|
|
82248
82967
|
}
|
|
82249
|
-
return
|
|
82968
|
+
return join38(cacheDir, "update-check.json");
|
|
82250
82969
|
} catch {
|
|
82251
|
-
return
|
|
82970
|
+
return join38(tmpdir(), "claudish-update-check.json");
|
|
82252
82971
|
}
|
|
82253
82972
|
}
|
|
82254
82973
|
function readCache2() {
|
|
@@ -88575,7 +89294,7 @@ var init_response_seam = __esm(() => {
|
|
|
88575
89294
|
// src/services/pricing-cache.ts
|
|
88576
89295
|
import { existsSync as existsSync30, readFileSync as readFileSync37, statSync as statSync13 } from "fs";
|
|
88577
89296
|
import { homedir as homedir35 } from "os";
|
|
88578
|
-
import { join as
|
|
89297
|
+
import { join as join39 } from "path";
|
|
88579
89298
|
function prefixMatch(modelName) {
|
|
88580
89299
|
for (const [key, pricing] of pricingMap) {
|
|
88581
89300
|
if (modelName.startsWith(key))
|
|
@@ -88635,14 +89354,14 @@ var init_pricing_cache = __esm(() => {
|
|
|
88635
89354
|
init_logger();
|
|
88636
89355
|
init_catalog_query();
|
|
88637
89356
|
pricingMap = new Map;
|
|
88638
|
-
CACHE_DIR =
|
|
88639
|
-
CACHE_FILE =
|
|
89357
|
+
CACHE_DIR = join39(homedir35(), ".claudish");
|
|
89358
|
+
CACHE_FILE = join39(CACHE_DIR, "pricing-cache.json");
|
|
88640
89359
|
CACHE_TTL_MS5 = 24 * 60 * 60 * 1000;
|
|
88641
89360
|
});
|
|
88642
89361
|
|
|
88643
89362
|
// src/proxy-server.ts
|
|
88644
89363
|
import { appendFileSync as appendFileSync6, mkdirSync as mkdirSync20 } from "fs";
|
|
88645
|
-
import { join as
|
|
89364
|
+
import { join as join40 } from "path";
|
|
88646
89365
|
function isTerminalRoutingFailure(e) {
|
|
88647
89366
|
return e instanceof RoutingError;
|
|
88648
89367
|
}
|
|
@@ -88650,7 +89369,7 @@ function maybeCaptureClassifierRequest(c, body) {
|
|
|
88650
89369
|
if (!process.env.CLAUDISH_CLASSIFIER_DEBUG)
|
|
88651
89370
|
return;
|
|
88652
89371
|
try {
|
|
88653
|
-
const dir =
|
|
89372
|
+
const dir = join40(process.cwd(), "logs");
|
|
88654
89373
|
if (!classifierCaptureDirReady) {
|
|
88655
89374
|
mkdirSync20(dir, { recursive: true });
|
|
88656
89375
|
classifierCaptureDirReady = true;
|
|
@@ -88675,7 +89394,7 @@ function maybeCaptureClassifierRequest(c, body) {
|
|
|
88675
89394
|
"x-api-key": c.req.header("x-api-key") ? "<present>" : null
|
|
88676
89395
|
}
|
|
88677
89396
|
};
|
|
88678
|
-
appendFileSync6(
|
|
89397
|
+
appendFileSync6(join40(dir, "classifier-capture.jsonl"), `${JSON.stringify(record)}
|
|
88679
89398
|
`);
|
|
88680
89399
|
} catch {}
|
|
88681
89400
|
}
|
|
@@ -89210,13 +89929,13 @@ import {
|
|
|
89210
89929
|
unlinkSync as unlinkSync10
|
|
89211
89930
|
} from "fs";
|
|
89212
89931
|
import { homedir as homedir36 } from "os";
|
|
89213
|
-
import { dirname as dirname16, join as
|
|
89932
|
+
import { dirname as dirname16, join as join41 } from "path";
|
|
89214
89933
|
import { fileURLToPath } from "url";
|
|
89215
89934
|
function getVersion3() {
|
|
89216
89935
|
return VERSION;
|
|
89217
89936
|
}
|
|
89218
89937
|
function clearAllModelCaches() {
|
|
89219
|
-
const cacheDir =
|
|
89938
|
+
const cacheDir = join41(homedir36(), ".claudish");
|
|
89220
89939
|
if (!existsSync31(cacheDir))
|
|
89221
89940
|
return;
|
|
89222
89941
|
const cachePatterns = ["pricing-cache.json", "recommended-models-cache-v2.json"];
|
|
@@ -89225,7 +89944,7 @@ function clearAllModelCaches() {
|
|
|
89225
89944
|
const files = readdirSync4(cacheDir);
|
|
89226
89945
|
for (const file of files) {
|
|
89227
89946
|
if (cachePatterns.includes(file)) {
|
|
89228
|
-
unlinkSync10(
|
|
89947
|
+
unlinkSync10(join41(cacheDir, file));
|
|
89229
89948
|
cleared++;
|
|
89230
89949
|
}
|
|
89231
89950
|
}
|
|
@@ -90745,7 +91464,7 @@ ${h("MORE INFO")}
|
|
|
90745
91464
|
}
|
|
90746
91465
|
function printAIAgentGuide() {
|
|
90747
91466
|
try {
|
|
90748
|
-
const guidePath =
|
|
91467
|
+
const guidePath = join41(__dirname2, "../AI_AGENT_GUIDE.md");
|
|
90749
91468
|
const guideContent = readFileSync38(guidePath, "utf-8");
|
|
90750
91469
|
console.log(guideContent);
|
|
90751
91470
|
} catch (error) {
|
|
@@ -90762,10 +91481,10 @@ async function initializeClaudishSkill() {
|
|
|
90762
91481
|
console.log(`\uD83D\uDD27 Initializing Claudish skill in current project...
|
|
90763
91482
|
`);
|
|
90764
91483
|
const cwd = process.cwd();
|
|
90765
|
-
const claudeDir =
|
|
90766
|
-
const skillsDir =
|
|
90767
|
-
const claudishSkillDir =
|
|
90768
|
-
const skillFile =
|
|
91484
|
+
const claudeDir = join41(cwd, ".claude");
|
|
91485
|
+
const skillsDir = join41(claudeDir, "skills");
|
|
91486
|
+
const claudishSkillDir = join41(skillsDir, "claudish-usage");
|
|
91487
|
+
const skillFile = join41(claudishSkillDir, "SKILL.md");
|
|
90769
91488
|
if (existsSync31(skillFile)) {
|
|
90770
91489
|
console.log("\u2705 Claudish skill already installed at:");
|
|
90771
91490
|
console.log(` ${skillFile}
|
|
@@ -90773,7 +91492,7 @@ async function initializeClaudishSkill() {
|
|
|
90773
91492
|
console.log("\uD83D\uDCA1 To reinstall, delete the file and run 'claudish --init' again.");
|
|
90774
91493
|
return;
|
|
90775
91494
|
}
|
|
90776
|
-
const sourceSkillPath =
|
|
91495
|
+
const sourceSkillPath = join41(__dirname2, "../skills/claudish-usage/SKILL.md");
|
|
90777
91496
|
if (!existsSync31(sourceSkillPath)) {
|
|
90778
91497
|
console.error("\u274C Error: Claudish skill file not found in installation.");
|
|
90779
91498
|
console.error(` Expected at: ${sourceSkillPath}`);
|
|
@@ -90969,16 +91688,16 @@ var init_cli = __esm(() => {
|
|
|
90969
91688
|
// src/launcher/magmux-binary.ts
|
|
90970
91689
|
import { execSync } from "child_process";
|
|
90971
91690
|
import { existsSync as existsSync32 } from "fs";
|
|
90972
|
-
import { dirname as dirname17, join as
|
|
91691
|
+
import { dirname as dirname17, join as join42 } from "path";
|
|
90973
91692
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
90974
91693
|
function findMagmuxBinaryOrNull() {
|
|
90975
91694
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
90976
91695
|
const thisDir = dirname17(thisFile);
|
|
90977
|
-
const pkgRoots = [
|
|
91696
|
+
const pkgRoots = [join42(thisDir, ".."), join42(thisDir, "..", "..")];
|
|
90978
91697
|
const platform = process.platform;
|
|
90979
91698
|
const arch = process.arch;
|
|
90980
91699
|
for (const pkgRoot of pkgRoots) {
|
|
90981
|
-
const bundled =
|
|
91700
|
+
const bundled = join42(pkgRoot, "native", `magmux-${platform}-${arch}`);
|
|
90982
91701
|
if (existsSync32(bundled))
|
|
90983
91702
|
return bundled;
|
|
90984
91703
|
}
|
|
@@ -90986,7 +91705,7 @@ function findMagmuxBinaryOrNull() {
|
|
|
90986
91705
|
const pkgName = `@claudish/magmux-${platform}-${arch}`;
|
|
90987
91706
|
let searchDir = pkgRoots[0];
|
|
90988
91707
|
for (let i = 0;i < 5; i++) {
|
|
90989
|
-
const candidate =
|
|
91708
|
+
const candidate = join42(searchDir, "node_modules", pkgName, "bin", "magmux");
|
|
90990
91709
|
if (existsSync32(candidate))
|
|
90991
91710
|
return candidate;
|
|
90992
91711
|
const parent = dirname17(searchDir);
|
|
@@ -91023,7 +91742,7 @@ import {
|
|
|
91023
91742
|
writeFileSync as writeFileSync18
|
|
91024
91743
|
} from "fs";
|
|
91025
91744
|
import { connect as netConnect } from "net";
|
|
91026
|
-
import { join as
|
|
91745
|
+
import { join as join43 } from "path";
|
|
91027
91746
|
function envDeltas(parentEnv, childEnv) {
|
|
91028
91747
|
const set = [];
|
|
91029
91748
|
const unset = [];
|
|
@@ -91089,9 +91808,9 @@ function planMagmuxWrap(input) {
|
|
|
91089
91808
|
const controlSocket = `/tmp/magmux-${id}.sock`;
|
|
91090
91809
|
const tmpRoot = input.tmpRoot ?? "/tmp";
|
|
91091
91810
|
mkdirSync22(tmpRoot, { recursive: true });
|
|
91092
|
-
const scriptDir = mkdtempSync(
|
|
91811
|
+
const scriptDir = mkdtempSync(join43(tmpRoot, "claudish-launch-"));
|
|
91093
91812
|
chmodSync3(scriptDir, 448);
|
|
91094
|
-
const scriptPath =
|
|
91813
|
+
const scriptPath = join43(scriptDir, "claude-launch.sh");
|
|
91095
91814
|
const parentEnv = input.parentEnv ?? process.env;
|
|
91096
91815
|
const deltas = envDeltas(parentEnv, input.childEnv);
|
|
91097
91816
|
const fd = openSync10(scriptPath, "wx", 384);
|
|
@@ -91236,8 +91955,8 @@ var DEFAULT_PANE_COLS = 160, DEFAULT_PANE_ROWS = 50, MIN_PANE_COLS = 40, MAX_PAN
|
|
|
91236
91955
|
|
|
91237
91956
|
// src/pane/process-identity.ts
|
|
91238
91957
|
import { execFile as execFile2, execFileSync as execFileSync2 } from "child_process";
|
|
91239
|
-
import { lstatSync as
|
|
91240
|
-
import { basename as basename2, dirname as dirname18, join as
|
|
91958
|
+
import { lstatSync as lstatSync5 } from "fs";
|
|
91959
|
+
import { basename as basename2, dirname as dirname18, join as join44 } from "path";
|
|
91241
91960
|
function collapse(s) {
|
|
91242
91961
|
return s.trim().replace(/\s+/g, " ");
|
|
91243
91962
|
}
|
|
@@ -91447,7 +92166,7 @@ function isLaunchDirPath(root, p) {
|
|
|
91447
92166
|
if (dirname18(p) !== root || !LAUNCH_DIR_RE.test(basename2(p)))
|
|
91448
92167
|
return false;
|
|
91449
92168
|
try {
|
|
91450
|
-
const st =
|
|
92169
|
+
const st = lstatSync5(p);
|
|
91451
92170
|
return st.isDirectory() && !st.isSymbolicLink();
|
|
91452
92171
|
} catch {
|
|
91453
92172
|
return false;
|
|
@@ -91457,16 +92176,16 @@ function isValidPaneId(id) {
|
|
|
91457
92176
|
return PANE_ID_RE.test(id) && !/^\d+$/.test(id);
|
|
91458
92177
|
}
|
|
91459
92178
|
function sockPathOf(root, paneId) {
|
|
91460
|
-
return
|
|
92179
|
+
return join44(root, `magmux-${paneId}.sock`);
|
|
91461
92180
|
}
|
|
91462
92181
|
function recordPathOf(root, paneId) {
|
|
91463
|
-
return
|
|
92182
|
+
return join44(root, "panes", `${paneId}.json`);
|
|
91464
92183
|
}
|
|
91465
92184
|
function isPaneSockPath(root, p, paneId) {
|
|
91466
92185
|
return isValidPaneId(paneId) && dirname18(p) === root && basename2(p) === `magmux-${paneId}.sock`;
|
|
91467
92186
|
}
|
|
91468
92187
|
function isPaneRecordPath(root, p, paneId) {
|
|
91469
|
-
return isValidPaneId(paneId) && dirname18(p) ===
|
|
92188
|
+
return isValidPaneId(paneId) && dirname18(p) === join44(root, "panes") && basename2(p) === `${paneId}.json`;
|
|
91470
92189
|
}
|
|
91471
92190
|
function watcherArgv(a) {
|
|
91472
92191
|
return [
|
|
@@ -91572,17 +92291,17 @@ var init_process_identity = __esm(() => {
|
|
|
91572
92291
|
// src/pane/pane-launch.ts
|
|
91573
92292
|
import { execFile as execFile3, spawn } from "child_process";
|
|
91574
92293
|
import { randomBytes as randomBytes12 } from "crypto";
|
|
91575
|
-
import { chmodSync as chmodSync4, lstatSync as
|
|
91576
|
-
import { join as
|
|
92294
|
+
import { chmodSync as chmodSync4, lstatSync as lstatSync6, mkdirSync as mkdirSync23, mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync19 } from "fs";
|
|
92295
|
+
import { join as join45, resolve as resolve4 } from "path";
|
|
91577
92296
|
function uid() {
|
|
91578
92297
|
return typeof process.getuid === "function" ? process.getuid() : 0;
|
|
91579
92298
|
}
|
|
91580
92299
|
function sockRootFor(env = process.env) {
|
|
91581
92300
|
const override = env[ENV.CLAUDISH_PANE_ROOT]?.trim();
|
|
91582
|
-
return override ?
|
|
92301
|
+
return override ? resolve4(override) : `/tmp/claudish-mux-${uid()}`;
|
|
91583
92302
|
}
|
|
91584
92303
|
function assertPrivateDir(p) {
|
|
91585
|
-
const st =
|
|
92304
|
+
const st = lstatSync6(p);
|
|
91586
92305
|
if (st.isSymbolicLink() || !st.isDirectory())
|
|
91587
92306
|
throw new PaneRootError(`pane socket root ${p} is not a real directory`);
|
|
91588
92307
|
if (st.uid !== uid())
|
|
@@ -91591,7 +92310,7 @@ function assertPrivateDir(p) {
|
|
|
91591
92310
|
throw new PaneRootError(`pane socket root ${p} has mode ${(st.mode & 511).toString(8)}; it must be 0700`);
|
|
91592
92311
|
}
|
|
91593
92312
|
function ensureSockRoot(given = sockRootFor()) {
|
|
91594
|
-
const root =
|
|
92313
|
+
const root = resolve4(given);
|
|
91595
92314
|
try {
|
|
91596
92315
|
mkdirSync23(root, { mode: 448 });
|
|
91597
92316
|
} catch (e) {
|
|
@@ -91601,7 +92320,7 @@ function ensureSockRoot(given = sockRootFor()) {
|
|
|
91601
92320
|
throw e;
|
|
91602
92321
|
}
|
|
91603
92322
|
assertPrivateDir(root);
|
|
91604
|
-
const panes =
|
|
92323
|
+
const panes = join45(root, "panes");
|
|
91605
92324
|
try {
|
|
91606
92325
|
mkdirSync23(panes, { mode: 448 });
|
|
91607
92326
|
} catch (e) {
|
|
@@ -91655,7 +92374,7 @@ function buildPaneEnv(input) {
|
|
|
91655
92374
|
if ((input.platform ?? process.platform) === "linux" && entryBytes > LINUX_ENV_STRING_LIMIT)
|
|
91656
92375
|
throw new PaneEnvTooLargeError(`pane environment snapshot is ${entryBytes} bytes, over Linux's 131072-byte limit per variable`);
|
|
91657
92376
|
env[ENV.CLAUDISH_PANE_ENV] = json;
|
|
91658
|
-
env.SHELL =
|
|
92377
|
+
env.SHELL = join45(input.ctlDir, "sh-shim");
|
|
91659
92378
|
return { magmuxEnv: env };
|
|
91660
92379
|
}
|
|
91661
92380
|
function buildClaudishPaneArgv(spawnModel, sessionUuid, turnDir, callerFlags) {
|
|
@@ -91865,21 +92584,21 @@ exec ${words}
|
|
|
91865
92584
|
`;
|
|
91866
92585
|
}
|
|
91867
92586
|
function paneCommand(ctlDir) {
|
|
91868
|
-
const cmd = `. ${shq2(
|
|
92587
|
+
const cmd = `. ${shq2(join45(ctlDir, "pane-launch.sh"))}`;
|
|
91869
92588
|
if (/claude /.test(cmd) || /claude$/.test(cmd) || /(^|\/)claude['"]?$/.test(cmd))
|
|
91870
92589
|
throw new Error(`pane command would attach magmux's Claude Code controller: ${cmd}`);
|
|
91871
92590
|
return cmd;
|
|
91872
92591
|
}
|
|
91873
92592
|
function createLaunchDirs(root) {
|
|
91874
|
-
const ctlDir = mkdtempSync2(
|
|
92593
|
+
const ctlDir = mkdtempSync2(join45(root, "launch-"));
|
|
91875
92594
|
chmodSync4(ctlDir, 448);
|
|
91876
|
-
const turnDir = mkdtempSync2(
|
|
92595
|
+
const turnDir = mkdtempSync2(join45(root, "launch-"));
|
|
91877
92596
|
chmodSync4(turnDir, 448);
|
|
91878
92597
|
return { ctlDir, turnDir };
|
|
91879
92598
|
}
|
|
91880
92599
|
function writeLauncherFiles(ctlDir, script) {
|
|
91881
|
-
writeFileSync19(
|
|
91882
|
-
writeFileSync19(
|
|
92600
|
+
writeFileSync19(join45(ctlDir, "pane-launch.sh"), script, { mode: 384, flag: "wx" });
|
|
92601
|
+
writeFileSync19(join45(ctlDir, "sh-shim"), SHELL_SHIM, { mode: 448, flag: "wx" });
|
|
91883
92602
|
}
|
|
91884
92603
|
function spawnPaneWatcher(args) {
|
|
91885
92604
|
const proc = spawn("/bin/sh", watcherArgv(args), {
|
|
@@ -91902,7 +92621,7 @@ function paneMagmuxArgv(input) {
|
|
|
91902
92621
|
"--session-id",
|
|
91903
92622
|
input.sessionUuid,
|
|
91904
92623
|
"--transcript",
|
|
91905
|
-
|
|
92624
|
+
resolve4(input.transcriptPath),
|
|
91906
92625
|
"--id",
|
|
91907
92626
|
input.paneId,
|
|
91908
92627
|
"--sock-dir",
|
|
@@ -92281,9 +93000,9 @@ var init_magmux_client = __esm(() => {
|
|
|
92281
93000
|
// src/pane/pane-registry.ts
|
|
92282
93001
|
import {
|
|
92283
93002
|
constants as constants3,
|
|
92284
|
-
accessSync,
|
|
93003
|
+
accessSync as accessSync2,
|
|
92285
93004
|
existsSync as existsSync34,
|
|
92286
|
-
lstatSync as
|
|
93005
|
+
lstatSync as lstatSync7,
|
|
92287
93006
|
readFileSync as readFileSync39,
|
|
92288
93007
|
readdirSync as readdirSync5,
|
|
92289
93008
|
renameSync as renameSync9,
|
|
@@ -92291,7 +93010,7 @@ import {
|
|
|
92291
93010
|
unlinkSync as unlinkSync11,
|
|
92292
93011
|
writeFileSync as writeFileSync20
|
|
92293
93012
|
} from "fs";
|
|
92294
|
-
import { join as
|
|
93013
|
+
import { join as join46 } from "path";
|
|
92295
93014
|
function ownerStartOfSelf() {
|
|
92296
93015
|
if (ownStart)
|
|
92297
93016
|
return ownStart;
|
|
@@ -92312,7 +93031,7 @@ function ownerAlive(owner, table) {
|
|
|
92312
93031
|
return !minted || startedAt(row, minted.startMs);
|
|
92313
93032
|
}
|
|
92314
93033
|
function recordsDir(root) {
|
|
92315
|
-
return
|
|
93034
|
+
return join46(root, "panes");
|
|
92316
93035
|
}
|
|
92317
93036
|
function writeRecord(root, rec) {
|
|
92318
93037
|
const p = recordPathOf(root, rec.paneId);
|
|
@@ -92382,13 +93101,13 @@ function readRecords(root) {
|
|
|
92382
93101
|
}
|
|
92383
93102
|
function readGroupFile(ctlDir) {
|
|
92384
93103
|
try {
|
|
92385
|
-
return parseGroupFile(readFileSync39(
|
|
93104
|
+
return parseGroupFile(readFileSync39(join46(ctlDir, "group"), "utf8"));
|
|
92386
93105
|
} catch {
|
|
92387
93106
|
return null;
|
|
92388
93107
|
}
|
|
92389
93108
|
}
|
|
92390
93109
|
function writeGroupFile(ctlDir, snap) {
|
|
92391
|
-
const p =
|
|
93110
|
+
const p = join46(ctlDir, "group");
|
|
92392
93111
|
const tmp = `${p}.tmp`;
|
|
92393
93112
|
try {
|
|
92394
93113
|
writeFileSync20(tmp, formatGroupFile(snap), { mode: 384 });
|
|
@@ -92620,14 +93339,14 @@ function dialErrno(reported, sockPath) {
|
|
|
92620
93339
|
return reported;
|
|
92621
93340
|
let isSocket;
|
|
92622
93341
|
try {
|
|
92623
|
-
isSocket =
|
|
93342
|
+
isSocket = lstatSync7(sockPath).isSocket();
|
|
92624
93343
|
} catch (e) {
|
|
92625
93344
|
return e.code ?? "unknown";
|
|
92626
93345
|
}
|
|
92627
93346
|
if (!isSocket)
|
|
92628
93347
|
return "ENOTSOCK";
|
|
92629
93348
|
try {
|
|
92630
|
-
|
|
93349
|
+
accessSync2(sockPath, constants3.W_OK);
|
|
92631
93350
|
} catch {
|
|
92632
93351
|
return "EACCES";
|
|
92633
93352
|
}
|
|
@@ -92665,7 +93384,7 @@ async function sweepRecordlessSocket(root, name, out) {
|
|
|
92665
93384
|
const owner = ownerOfSocketId(id);
|
|
92666
93385
|
if (!owner || ownerAlive(owner, table))
|
|
92667
93386
|
return;
|
|
92668
|
-
const sockPath =
|
|
93387
|
+
const sockPath = join46(root, name);
|
|
92669
93388
|
const magmuxMayLive = !table || table.some((r) => isPaneMagmuxCommand(r.command, id));
|
|
92670
93389
|
const probe = await probeSocket(sockPath, magmuxMayLive);
|
|
92671
93390
|
if (probe.state === "stale") {
|
|
@@ -92835,7 +93554,7 @@ var init_queue = __esm(() => {
|
|
|
92835
93554
|
|
|
92836
93555
|
// src/mcp-session/parent-session.ts
|
|
92837
93556
|
import { readFile as readFile7 } from "fs/promises";
|
|
92838
|
-
import { join as
|
|
93557
|
+
import { join as join47 } from "path";
|
|
92839
93558
|
function positiveInt(value) {
|
|
92840
93559
|
if (value === undefined || !/^[1-9][0-9]*$/.test(value))
|
|
92841
93560
|
return;
|
|
@@ -92859,11 +93578,11 @@ function parentSessionIdFrom(env) {
|
|
|
92859
93578
|
}
|
|
92860
93579
|
function claudeConfigDir(env) {
|
|
92861
93580
|
const fromEnv = env.CLAUDE_CONFIG_DIR;
|
|
92862
|
-
return fromEnv && fromEnv.length > 0 ? fromEnv :
|
|
93581
|
+
return fromEnv && fromEnv.length > 0 ? fromEnv : join47(userHomeFrom(env), ".claude");
|
|
92863
93582
|
}
|
|
92864
93583
|
async function readHostSessionRecord(opts) {
|
|
92865
93584
|
try {
|
|
92866
|
-
const raw = await readFile7(
|
|
93585
|
+
const raw = await readFile7(join47(opts.configDir, "sessions", `${opts.hostPid}.json`), "utf-8");
|
|
92867
93586
|
const parsed = JSON.parse(raw);
|
|
92868
93587
|
if (!parsed || typeof parsed !== "object")
|
|
92869
93588
|
return;
|
|
@@ -93213,15 +93932,15 @@ var init_input_gate = __esm(() => {
|
|
|
93213
93932
|
});
|
|
93214
93933
|
|
|
93215
93934
|
// src/pane/gate-binding.ts
|
|
93216
|
-
import { realpathSync as
|
|
93217
|
-
import { basename as basename3, dirname as dirname19, join as
|
|
93935
|
+
import { realpathSync as realpathSync4 } from "fs";
|
|
93936
|
+
import { basename as basename3, dirname as dirname19, join as join48, resolve as resolve5 } from "path";
|
|
93218
93937
|
function normaliseTranscriptPath(p) {
|
|
93219
|
-
const abs =
|
|
93938
|
+
const abs = resolve5(p);
|
|
93220
93939
|
const tail = [];
|
|
93221
93940
|
let cur = abs;
|
|
93222
93941
|
for (;; ) {
|
|
93223
93942
|
try {
|
|
93224
|
-
return tail.length ?
|
|
93943
|
+
return tail.length ? join48(realpathSync4(cur), ...tail.reverse()) : realpathSync4(cur);
|
|
93225
93944
|
} catch {
|
|
93226
93945
|
const parent = dirname19(cur);
|
|
93227
93946
|
if (parent === cur)
|
|
@@ -93635,7 +94354,7 @@ var init_scroll_input = __esm(() => {
|
|
|
93635
94354
|
|
|
93636
94355
|
// src/pane/session-registry.ts
|
|
93637
94356
|
import { readFileSync as readFileSync40, readdirSync as readdirSync6 } from "fs";
|
|
93638
|
-
import { join as
|
|
94357
|
+
import { join as join49 } from "path";
|
|
93639
94358
|
function parseRegistryRecord(raw) {
|
|
93640
94359
|
let parsed;
|
|
93641
94360
|
try {
|
|
@@ -93680,7 +94399,7 @@ class SessionRegistry {
|
|
|
93680
94399
|
isAlive;
|
|
93681
94400
|
constructor(o) {
|
|
93682
94401
|
this.o = o;
|
|
93683
|
-
this.dir =
|
|
94402
|
+
this.dir = join49(o.configDir, "sessions");
|
|
93684
94403
|
this.now = o.now ?? Date.now;
|
|
93685
94404
|
this.isAlive = o.isAlive ?? pidAlive;
|
|
93686
94405
|
}
|
|
@@ -93716,7 +94435,7 @@ class SessionRegistry {
|
|
|
93716
94435
|
readPid(pid) {
|
|
93717
94436
|
let raw;
|
|
93718
94437
|
try {
|
|
93719
|
-
raw = readFileSync40(
|
|
94438
|
+
raw = readFileSync40(join49(this.dir, `${pid}.json`), "utf8");
|
|
93720
94439
|
} catch {
|
|
93721
94440
|
return null;
|
|
93722
94441
|
}
|
|
@@ -94018,10 +94737,10 @@ import {
|
|
|
94018
94737
|
openSync as openSync11,
|
|
94019
94738
|
readSync as readSync4,
|
|
94020
94739
|
readdirSync as readdirSync7,
|
|
94021
|
-
realpathSync as
|
|
94740
|
+
realpathSync as realpathSync5,
|
|
94022
94741
|
statSync as statSync14
|
|
94023
94742
|
} from "fs";
|
|
94024
|
-
import { join as
|
|
94743
|
+
import { join as join50 } from "path";
|
|
94025
94744
|
function initialFollowerState(resolvePath = safeRealpath) {
|
|
94026
94745
|
return {
|
|
94027
94746
|
turns: [],
|
|
@@ -94037,7 +94756,7 @@ function initialFollowerState(resolvePath = safeRealpath) {
|
|
|
94037
94756
|
}
|
|
94038
94757
|
function safeRealpath(p) {
|
|
94039
94758
|
try {
|
|
94040
|
-
return
|
|
94759
|
+
return realpathSync5(p);
|
|
94041
94760
|
} catch {
|
|
94042
94761
|
return p;
|
|
94043
94762
|
}
|
|
@@ -94595,7 +95314,7 @@ class TranscriptFollower {
|
|
|
94595
95314
|
for (const f of this.subagentFiles()) {
|
|
94596
95315
|
let tail = this.subs.get(f);
|
|
94597
95316
|
if (!tail) {
|
|
94598
|
-
tail = new Tail(
|
|
95317
|
+
tail = new Tail(join50(this.subagentsDir, f));
|
|
94599
95318
|
this.subs.set(f, tail);
|
|
94600
95319
|
}
|
|
94601
95320
|
for (const { line } of tail.read()) {
|
|
@@ -94694,8 +95413,8 @@ var init_transcript_follower = __esm(() => {
|
|
|
94694
95413
|
});
|
|
94695
95414
|
|
|
94696
95415
|
// src/pane/pane-session.ts
|
|
94697
|
-
import { readFileSync as readFileSync41, realpathSync as
|
|
94698
|
-
import { join as
|
|
95416
|
+
import { readFileSync as readFileSync41, realpathSync as realpathSync6, rmSync as rmSync11, statSync as statSync15, writeFileSync as writeFileSync21 } from "fs";
|
|
95417
|
+
import { join as join51 } from "path";
|
|
94699
95418
|
function iso3(ms) {
|
|
94700
95419
|
return new Date(ms).toISOString();
|
|
94701
95420
|
}
|
|
@@ -94847,7 +95566,7 @@ class PaneSessionImpl {
|
|
|
94847
95566
|
});
|
|
94848
95567
|
this.secondaryQuietMs = o.timings?.secondaryQuietMs ?? SECONDARY_QUIET_MS;
|
|
94849
95568
|
this.corroborationMs = o.timings?.corroborationMs ?? CORROBORATION_MS;
|
|
94850
|
-
this.follower = new TranscriptFollower(o.transcriptPath,
|
|
95569
|
+
this.follower = new TranscriptFollower(o.transcriptPath, join51(o.transcriptPath.replace(/\.jsonl$/, ""), "subagents"));
|
|
94851
95570
|
this.registry = new SessionRegistry({
|
|
94852
95571
|
configDir: claudeConfigDir(o.parentEnv ?? process.env),
|
|
94853
95572
|
sessionUuid: o.sessionUuid
|
|
@@ -94894,7 +95613,7 @@ class PaneSessionImpl {
|
|
|
94894
95613
|
return this.spawnFailed(new Error("magmux did not start"));
|
|
94895
95614
|
this.magmux = proc;
|
|
94896
95615
|
try {
|
|
94897
|
-
writeFileSync21(
|
|
95616
|
+
writeFileSync21(join51(this.ctlDir, "magmux.pid"), `${proc.pid}
|
|
94898
95617
|
`, { mode: 384 });
|
|
94899
95618
|
} catch {}
|
|
94900
95619
|
updateRecord(this.root, this.paneId, { magmuxPid: proc.pid });
|
|
@@ -97345,7 +98064,7 @@ function preparePane(o, root, parentEnv) {
|
|
|
97345
98064
|
try {
|
|
97346
98065
|
const paneId = mintPaneId(o.kind, o.label);
|
|
97347
98066
|
const sockPath = sockPathOf(root, paneId);
|
|
97348
|
-
const cwd =
|
|
98067
|
+
const cwd = realpathSync6(o.cwd);
|
|
97349
98068
|
const launch = o.claudishSpawn ?? resolveClaudishLaunch(parentEnv);
|
|
97350
98069
|
dirs = createLaunchDirs(root);
|
|
97351
98070
|
const argv = buildClaudishPaneArgv(o.spawnModel, o.sessionUuid, dirs.turnDir, o.callerFlags);
|
|
@@ -97893,15 +98612,15 @@ import {
|
|
|
97893
98612
|
constants as constants4,
|
|
97894
98613
|
closeSync as closeSync12,
|
|
97895
98614
|
fstatSync as fstatSync5,
|
|
97896
|
-
lstatSync as
|
|
98615
|
+
lstatSync as lstatSync8,
|
|
97897
98616
|
openSync as openSync12,
|
|
97898
98617
|
readSync as readSync5,
|
|
97899
98618
|
readdirSync as readdirSync8,
|
|
97900
98619
|
readlinkSync,
|
|
97901
|
-
realpathSync as
|
|
98620
|
+
realpathSync as realpathSync7,
|
|
97902
98621
|
statSync as statSync16
|
|
97903
98622
|
} from "fs";
|
|
97904
|
-
import { basename as basename4, dirname as dirname20, join as
|
|
98623
|
+
import { basename as basename4, dirname as dirname20, join as join52, sep } from "path";
|
|
97905
98624
|
function isUuid(v) {
|
|
97906
98625
|
return typeof v === "string" && UUID_RE.test(v);
|
|
97907
98626
|
}
|
|
@@ -98051,7 +98770,7 @@ function locateTranscriptByUuid(projectsRoot, uuid) {
|
|
|
98051
98770
|
}
|
|
98052
98771
|
let hit = null;
|
|
98053
98772
|
for (const d of dirs) {
|
|
98054
|
-
const p =
|
|
98773
|
+
const p = join52(projectsRoot, d, `${uuid}.jsonl`);
|
|
98055
98774
|
try {
|
|
98056
98775
|
if (!statSync16(p).isFile())
|
|
98057
98776
|
continue;
|
|
@@ -98113,8 +98832,8 @@ function openContainedTranscript(path, uuid, projectsRoot) {
|
|
|
98113
98832
|
let real;
|
|
98114
98833
|
let root;
|
|
98115
98834
|
try {
|
|
98116
|
-
real =
|
|
98117
|
-
root =
|
|
98835
|
+
real = realpathSync7(path);
|
|
98836
|
+
root = realpathSync7(projectsRoot);
|
|
98118
98837
|
} catch (e) {
|
|
98119
98838
|
if (!isEnoent(e))
|
|
98120
98839
|
logRejection(path, errText(e));
|
|
@@ -98142,8 +98861,8 @@ function openContainedTranscript(path, uuid, projectsRoot) {
|
|
|
98142
98861
|
return null;
|
|
98143
98862
|
}
|
|
98144
98863
|
const parent = dirname20(real);
|
|
98145
|
-
const named =
|
|
98146
|
-
if (
|
|
98864
|
+
const named = lstatSync8(real);
|
|
98865
|
+
if (realpathSync7(parent) !== parent || !named.isFile() || named.dev !== st.dev || named.ino !== st.ino || !kernelPathMatches(fd, real)) {
|
|
98147
98866
|
logRejection(path, "the opened file is not the one under the projects directory");
|
|
98148
98867
|
closeQuietly(fd);
|
|
98149
98868
|
return null;
|
|
@@ -98275,10 +98994,10 @@ __export(exports_session_discovery, {
|
|
|
98275
98994
|
transcriptPathFor: () => transcriptPathFor
|
|
98276
98995
|
});
|
|
98277
98996
|
import { execFile as execFile4, execFileSync as execFileSync3 } from "child_process";
|
|
98278
|
-
import { closeSync as closeSync13, openSync as openSync13, readSync as readSync6, readdirSync as readdirSync9, realpathSync as
|
|
98279
|
-
import { basename as basename5, join as
|
|
98997
|
+
import { closeSync as closeSync13, openSync as openSync13, readSync as readSync6, readdirSync as readdirSync9, realpathSync as realpathSync8, statSync as statSync17 } from "fs";
|
|
98998
|
+
import { basename as basename5, join as join53 } from "path";
|
|
98280
98999
|
function projectsDir(env = process.env) {
|
|
98281
|
-
return
|
|
99000
|
+
return join53(claudeConfigDir(env), "projects");
|
|
98282
99001
|
}
|
|
98283
99002
|
function slugForPath(absPath) {
|
|
98284
99003
|
return projectDirNameFor(absPath);
|
|
@@ -98286,9 +99005,9 @@ function slugForPath(absPath) {
|
|
|
98286
99005
|
function transcriptPathFor(cwd, sessionUuid, projectsRoot = projectsDir()) {
|
|
98287
99006
|
let real = cwd;
|
|
98288
99007
|
try {
|
|
98289
|
-
real =
|
|
99008
|
+
real = realpathSync8(cwd);
|
|
98290
99009
|
} catch {}
|
|
98291
|
-
return
|
|
99010
|
+
return join53(projectsRoot, slugForPath(real), `${sessionUuid}.jsonl`);
|
|
98292
99011
|
}
|
|
98293
99012
|
function isAgentSession(row) {
|
|
98294
99013
|
return row.entrypoint !== undefined && row.entrypoint !== "cli";
|
|
@@ -98335,7 +99054,7 @@ function projectDirs() {
|
|
|
98335
99054
|
}
|
|
98336
99055
|
}
|
|
98337
99056
|
function sessionsIn2(dirName) {
|
|
98338
|
-
const dir =
|
|
99057
|
+
const dir = join53(projectsDir(), dirName);
|
|
98339
99058
|
let names;
|
|
98340
99059
|
try {
|
|
98341
99060
|
names = readdirSync9(dir).filter((n) => n.endsWith(".jsonl"));
|
|
@@ -98344,7 +99063,7 @@ function sessionsIn2(dirName) {
|
|
|
98344
99063
|
}
|
|
98345
99064
|
const rows = [];
|
|
98346
99065
|
for (const n of names) {
|
|
98347
|
-
const file =
|
|
99066
|
+
const file = join53(dir, n);
|
|
98348
99067
|
try {
|
|
98349
99068
|
const st = statSync17(file);
|
|
98350
99069
|
if (st.size === 0)
|
|
@@ -98723,16 +99442,16 @@ var init_session_discovery = __esm(() => {
|
|
|
98723
99442
|
import { randomBytes as randomBytes13, randomUUID as randomUUID8 } from "crypto";
|
|
98724
99443
|
import {
|
|
98725
99444
|
existsSync as existsSync36,
|
|
98726
|
-
lstatSync as
|
|
99445
|
+
lstatSync as lstatSync9,
|
|
98727
99446
|
mkdirSync as mkdirSync24,
|
|
98728
99447
|
readFileSync as readFileSync42,
|
|
98729
99448
|
readdirSync as readdirSync10,
|
|
98730
|
-
realpathSync as
|
|
99449
|
+
realpathSync as realpathSync9,
|
|
98731
99450
|
rmSync as rmSync12,
|
|
98732
99451
|
statSync as statSync18,
|
|
98733
99452
|
writeFileSync as writeFileSync22
|
|
98734
99453
|
} from "fs";
|
|
98735
|
-
import { basename as basename6, dirname as dirname21, join as
|
|
99454
|
+
import { basename as basename6, dirname as dirname21, join as join54, resolve as resolve6, sep as sep2 } from "path";
|
|
98736
99455
|
function slotTerminal(e) {
|
|
98737
99456
|
if (!e.session)
|
|
98738
99457
|
return true;
|
|
@@ -98769,13 +99488,13 @@ function addressRun(path, runId) {
|
|
|
98769
99488
|
pruneRuns();
|
|
98770
99489
|
if (runId) {
|
|
98771
99490
|
const run = teamRuns.get(runId) ?? null;
|
|
98772
|
-
if (run && path !== undefined &&
|
|
99491
|
+
if (run && path !== undefined && resolve6(path) !== run.path)
|
|
98773
99492
|
return null;
|
|
98774
99493
|
return run;
|
|
98775
99494
|
}
|
|
98776
99495
|
if (path === undefined)
|
|
98777
99496
|
return null;
|
|
98778
|
-
const id = newestRunByPath.get(
|
|
99497
|
+
const id = newestRunByPath.get(resolve6(path));
|
|
98779
99498
|
return id ? teamRuns.get(id) ?? null : null;
|
|
98780
99499
|
}
|
|
98781
99500
|
function activeRunAt(path) {
|
|
@@ -98801,7 +99520,7 @@ function notableAnomalies(snap) {
|
|
|
98801
99520
|
return snap.anomalies.filter((a) => a.startsWith("background_shell_open") || a.startsWith("turn_end_record_missing") || a.startsWith("agent_count_mismatch") || a.startsWith("prompt_not_read"));
|
|
98802
99521
|
}
|
|
98803
99522
|
function upstreamLogPath(path, id) {
|
|
98804
|
-
return
|
|
99523
|
+
return join54(path, "errors", `${id}-upstream.jsonl`);
|
|
98805
99524
|
}
|
|
98806
99525
|
function neverSpawnedRow(e, m) {
|
|
98807
99526
|
return {
|
|
@@ -98910,14 +99629,14 @@ function teamRunRowFromDisk(path, status) {
|
|
|
98910
99629
|
}
|
|
98911
99630
|
function statusMtimeMs(path) {
|
|
98912
99631
|
try {
|
|
98913
|
-
return statSync18(
|
|
99632
|
+
return statSync18(join54(path, "status.json")).mtimeMs;
|
|
98914
99633
|
} catch {
|
|
98915
99634
|
return null;
|
|
98916
99635
|
}
|
|
98917
99636
|
}
|
|
98918
99637
|
function readManifestOrNull(path) {
|
|
98919
99638
|
try {
|
|
98920
|
-
return JSON.parse(readFileSync42(
|
|
99639
|
+
return JSON.parse(readFileSync42(join54(path, "manifest.json"), "utf-8"));
|
|
98921
99640
|
} catch {
|
|
98922
99641
|
return null;
|
|
98923
99642
|
}
|
|
@@ -99016,7 +99735,7 @@ function transcriptTeamSlot(path, slot, since, runId, opts = {}) {
|
|
|
99016
99735
|
}
|
|
99017
99736
|
if (path === undefined)
|
|
99018
99737
|
throw unknownRun(path, runId);
|
|
99019
|
-
const resolved =
|
|
99738
|
+
const resolved = resolve6(path);
|
|
99020
99739
|
let status;
|
|
99021
99740
|
try {
|
|
99022
99741
|
status = getStatus(resolved);
|
|
@@ -99177,13 +99896,13 @@ function answerFailureDetail(v, answer, requirePattern, outcomes) {
|
|
|
99177
99896
|
function readWrittenFile(path, root) {
|
|
99178
99897
|
let realRoot;
|
|
99179
99898
|
try {
|
|
99180
|
-
realRoot =
|
|
99899
|
+
realRoot = realpathSync9(root);
|
|
99181
99900
|
} catch {
|
|
99182
|
-
realRoot =
|
|
99901
|
+
realRoot = resolve6(root);
|
|
99183
99902
|
}
|
|
99184
99903
|
let real;
|
|
99185
99904
|
try {
|
|
99186
|
-
real =
|
|
99905
|
+
real = realpathSync9(resolve6(root, path));
|
|
99187
99906
|
} catch {
|
|
99188
99907
|
return { skip: "missing" };
|
|
99189
99908
|
}
|
|
@@ -99237,13 +99956,13 @@ function persistErrorLog(errorLogPath, header, sections) {
|
|
|
99237
99956
|
} catch {}
|
|
99238
99957
|
}
|
|
99239
99958
|
function validateSessionPath(sessionPath) {
|
|
99240
|
-
const resolved =
|
|
99959
|
+
const resolved = resolve6(sessionPath);
|
|
99241
99960
|
const outside = () => new Error(`Session path must be within current directory: ${sessionPath}`);
|
|
99242
99961
|
let existing = resolved;
|
|
99243
99962
|
const tail = [];
|
|
99244
99963
|
for (;; ) {
|
|
99245
99964
|
try {
|
|
99246
|
-
|
|
99965
|
+
lstatSync9(existing);
|
|
99247
99966
|
break;
|
|
99248
99967
|
} catch {
|
|
99249
99968
|
const parent = dirname21(existing);
|
|
@@ -99255,18 +99974,18 @@ function validateSessionPath(sessionPath) {
|
|
|
99255
99974
|
}
|
|
99256
99975
|
let real;
|
|
99257
99976
|
try {
|
|
99258
|
-
real =
|
|
99977
|
+
real = join54(realpathSync9(existing), ...tail);
|
|
99259
99978
|
} catch {
|
|
99260
99979
|
throw outside();
|
|
99261
99980
|
}
|
|
99262
|
-
const cwd =
|
|
99981
|
+
const cwd = realpathSync9(process.cwd());
|
|
99263
99982
|
if (real !== cwd && !real.startsWith(cwd + sep2)) {
|
|
99264
99983
|
throw outside();
|
|
99265
99984
|
}
|
|
99266
99985
|
return resolved;
|
|
99267
99986
|
}
|
|
99268
99987
|
function readTeamInputFile(inputPath) {
|
|
99269
|
-
const resolved =
|
|
99988
|
+
const resolved = resolve6(inputPath);
|
|
99270
99989
|
const cwd = process.cwd();
|
|
99271
99990
|
if (!resolved.startsWith(`${cwd}/`) && resolved !== cwd) {
|
|
99272
99991
|
throw new Error(`Input file must be within current directory: ${inputPath}`);
|
|
@@ -99284,14 +100003,14 @@ function setupSession(sessionPath, models, input) {
|
|
|
99284
100003
|
if (models.length === 0) {
|
|
99285
100004
|
throw new Error("At least one model is required");
|
|
99286
100005
|
}
|
|
99287
|
-
if (existsSync36(
|
|
100006
|
+
if (existsSync36(join54(sessionPath, "manifest.json"))) {
|
|
99288
100007
|
throw new Error(`Session already exists at ${sessionPath}. Use a new directory path or delete the existing session first.`);
|
|
99289
100008
|
}
|
|
99290
|
-
mkdirSync24(
|
|
99291
|
-
mkdirSync24(
|
|
100009
|
+
mkdirSync24(join54(sessionPath, "work"), { recursive: true });
|
|
100010
|
+
mkdirSync24(join54(sessionPath, "errors"), { recursive: true });
|
|
99292
100011
|
if (input !== undefined) {
|
|
99293
|
-
writeFileSync22(
|
|
99294
|
-
} else if (!existsSync36(
|
|
100012
|
+
writeFileSync22(join54(sessionPath, "input.md"), input, "utf-8");
|
|
100013
|
+
} else if (!existsSync36(join54(sessionPath, "input.md"))) {
|
|
99295
100014
|
throw new Error(`No input.md found at ${sessionPath} and no input provided`);
|
|
99296
100015
|
}
|
|
99297
100016
|
const ids = models.map((_, i) => String(i + 1).padStart(2, "0"));
|
|
@@ -99308,9 +100027,9 @@ function setupSession(sessionPath, models, input) {
|
|
|
99308
100027
|
model: models[i],
|
|
99309
100028
|
assignedAt: now
|
|
99310
100029
|
};
|
|
99311
|
-
mkdirSync24(
|
|
100030
|
+
mkdirSync24(join54(sessionPath, "work", anonId), { recursive: true });
|
|
99312
100031
|
}
|
|
99313
|
-
writeFileSync22(
|
|
100032
|
+
writeFileSync22(join54(sessionPath, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
|
|
99314
100033
|
const status = {
|
|
99315
100034
|
startedAt: now,
|
|
99316
100035
|
models: Object.fromEntries(Object.keys(manifest.models).map((id) => [
|
|
@@ -99325,7 +100044,7 @@ function setupSession(sessionPath, models, input) {
|
|
|
99325
100044
|
}
|
|
99326
100045
|
]))
|
|
99327
100046
|
};
|
|
99328
|
-
writeFileAtomic(
|
|
100047
|
+
writeFileAtomic(join54(sessionPath, "status.json"), JSON.stringify(status, null, 2));
|
|
99329
100048
|
return manifest;
|
|
99330
100049
|
}
|
|
99331
100050
|
function assertValidRequirePattern(pattern) {
|
|
@@ -99353,7 +100072,7 @@ async function preflightTeamRun(opts) {
|
|
|
99353
100072
|
throw new Error(`invalid_args: ${refusal}`);
|
|
99354
100073
|
}
|
|
99355
100074
|
await assertMagmuxAvailable();
|
|
99356
|
-
assertNoActiveRun(
|
|
100075
|
+
assertNoActiveRun(resolve6(opts.path));
|
|
99357
100076
|
const root = sockRootFor(opts.parentEnv ?? process.env);
|
|
99358
100077
|
const live = livePaneCount(root);
|
|
99359
100078
|
if (live + opts.slots > MAX_LIVE_PANES)
|
|
@@ -99385,10 +100104,10 @@ async function startModelsIn(flight, sessionPath, opts) {
|
|
|
99385
100104
|
if (!flagCheck.ok)
|
|
99386
100105
|
throw new Error(`invalid_args: ${flagCheck.message}`);
|
|
99387
100106
|
const agentRequested = requestedAgentOf(claudeFlags);
|
|
99388
|
-
const path =
|
|
99389
|
-
const manifest = JSON.parse(readFileSync42(
|
|
99390
|
-
const statusPath =
|
|
99391
|
-
const inputContent = readFileSync42(
|
|
100107
|
+
const path = resolve6(sessionPath);
|
|
100108
|
+
const manifest = JSON.parse(readFileSync42(join54(path, "manifest.json"), "utf-8"));
|
|
100109
|
+
const statusPath = join54(path, "status.json");
|
|
100110
|
+
const inputContent = readFileSync42(join54(path, "input.md"), "utf-8");
|
|
99392
100111
|
const readAvailable = !flagsRemoveRead(claudeFlags);
|
|
99393
100112
|
const refusal = deliveryRefusal(inputContent, readAvailable);
|
|
99394
100113
|
if (refusal)
|
|
@@ -99428,7 +100147,7 @@ async function startModelsIn(flight, sessionPath, opts) {
|
|
|
99428
100147
|
mkdirSync24(statsDir(path), { recursive: true });
|
|
99429
100148
|
const entries = new Map;
|
|
99430
100149
|
let run = null;
|
|
99431
|
-
const errorLogPathOf = (id) =>
|
|
100150
|
+
const errorLogPathOf = (id) => join54(path, "errors", `${id}.log`);
|
|
99432
100151
|
const upstreamLogOf = (id) => upstreamLogPath(path, id);
|
|
99433
100152
|
const flagsOf = (e) => {
|
|
99434
100153
|
const effort = opts.efforts?.[e.model];
|
|
@@ -99438,7 +100157,7 @@ async function startModelsIn(flight, sessionPath, opts) {
|
|
|
99438
100157
|
function writeResponse(e, text) {
|
|
99439
100158
|
e.answer = text;
|
|
99440
100159
|
try {
|
|
99441
|
-
writeFileSync22(
|
|
100160
|
+
writeFileSync22(join54(path, `response-${e.id}.md`), text, "utf-8");
|
|
99442
100161
|
} catch (err) {
|
|
99443
100162
|
persistErrorLog(errorLogPathOf(e.id), `response file not written: ${String(err)}`, []);
|
|
99444
100163
|
}
|
|
@@ -99448,7 +100167,7 @@ async function startModelsIn(flight, sessionPath, opts) {
|
|
|
99448
100167
|
if (e.answer !== null || !e.session)
|
|
99449
100168
|
return;
|
|
99450
100169
|
try {
|
|
99451
|
-
writeFileSync22(
|
|
100170
|
+
writeFileSync22(join54(path, `response-${e.id}.md`), e.session.chainText(), "utf-8");
|
|
99452
100171
|
} catch (err) {
|
|
99453
100172
|
persistErrorLog(errorLogPathOf(e.id), `response file not written: ${String(err)}`, []);
|
|
99454
100173
|
}
|
|
@@ -99465,7 +100184,7 @@ async function startModelsIn(flight, sessionPath, opts) {
|
|
|
99465
100184
|
return { file: null, outcomes: turn.writtenFiles ?? [] };
|
|
99466
100185
|
const outcomes = [];
|
|
99467
100186
|
for (const written of turn.writtenFiles ?? []) {
|
|
99468
|
-
const path =
|
|
100187
|
+
const path = resolve6(cwd, written);
|
|
99469
100188
|
const r = readWrittenFile(path, cwd);
|
|
99470
100189
|
if ("skip" in r) {
|
|
99471
100190
|
outcomes.push(`${path} (skipped: ${r.skip})`);
|
|
@@ -99645,7 +100364,7 @@ async function startModelsIn(flight, sessionPath, opts) {
|
|
|
99645
100364
|
}
|
|
99646
100365
|
async function startSlot(e, uuid, transcriptPath) {
|
|
99647
100366
|
let session;
|
|
99648
|
-
e.inboxPath = createHookInbox(
|
|
100367
|
+
e.inboxPath = createHookInbox(join54(path, "work"), `${e.id}.hook-inbox.jsonl`);
|
|
99649
100368
|
const binding = bindingFor(e, uuid);
|
|
99650
100369
|
e.binding = binding;
|
|
99651
100370
|
try {
|
|
@@ -99875,9 +100594,9 @@ async function runModels(sessionPath, opts = {}) {
|
|
|
99875
100594
|
return handle.done;
|
|
99876
100595
|
}
|
|
99877
100596
|
async function judgeResponses(sessionPath, opts = {}) {
|
|
99878
|
-
const active = activeRunAt(
|
|
100597
|
+
const active = activeRunAt(resolve6(sessionPath));
|
|
99879
100598
|
if (active)
|
|
99880
|
-
throw new Error(`invalid_args: the team run at ${
|
|
100599
|
+
throw new Error(`invalid_args: the team run at ${resolve6(sessionPath)} is still ACTIVE (run_id ${active.runId}); judge it once it has settled`);
|
|
99881
100600
|
const responseFiles = readdirSync10(sessionPath).filter((f) => f.startsWith("response-") && f.endsWith(".md")).sort();
|
|
99882
100601
|
if (responseFiles.length < 2) {
|
|
99883
100602
|
throw new Error(`Need at least 2 responses to judge, found ${responseFiles.length}`);
|
|
@@ -99885,13 +100604,13 @@ async function judgeResponses(sessionPath, opts = {}) {
|
|
|
99885
100604
|
const responses = {};
|
|
99886
100605
|
for (const file of responseFiles) {
|
|
99887
100606
|
const id = file.replace(/^response-/, "").replace(/\.md$/, "");
|
|
99888
|
-
responses[id] = readFileSync42(
|
|
100607
|
+
responses[id] = readFileSync42(join54(sessionPath, file), "utf-8");
|
|
99889
100608
|
}
|
|
99890
|
-
const input = readFileSync42(
|
|
100609
|
+
const input = readFileSync42(join54(sessionPath, "input.md"), "utf-8");
|
|
99891
100610
|
const judgePrompt = buildJudgePrompt(input, responses);
|
|
99892
|
-
writeFileSync22(
|
|
100611
|
+
writeFileSync22(join54(sessionPath, "judge-prompt.md"), judgePrompt, "utf-8");
|
|
99893
100612
|
const judgeModels = opts.judges ?? getDefaultJudgeModels(sessionPath);
|
|
99894
|
-
const judgePath =
|
|
100613
|
+
const judgePath = join54(sessionPath, "judging");
|
|
99895
100614
|
mkdirSync24(judgePath, { recursive: true });
|
|
99896
100615
|
setupSession(judgePath, judgeModels, judgePrompt);
|
|
99897
100616
|
await runModels(judgePath, {
|
|
@@ -99903,11 +100622,11 @@ async function judgeResponses(sessionPath, opts = {}) {
|
|
|
99903
100622
|
});
|
|
99904
100623
|
const votes = parseJudgeVotes(judgePath, Object.keys(responses));
|
|
99905
100624
|
const verdict = aggregateVerdict(votes, Object.keys(responses));
|
|
99906
|
-
writeFileSync22(
|
|
100625
|
+
writeFileSync22(join54(sessionPath, "verdict.md"), formatVerdict(verdict, sessionPath), "utf-8");
|
|
99907
100626
|
return verdict;
|
|
99908
100627
|
}
|
|
99909
100628
|
function getStatus(sessionPath) {
|
|
99910
|
-
return JSON.parse(readFileSync42(
|
|
100629
|
+
return JSON.parse(readFileSync42(join54(sessionPath, "status.json"), "utf-8"));
|
|
99911
100630
|
}
|
|
99912
100631
|
function readTeamStatus(sessionPath) {
|
|
99913
100632
|
try {
|
|
@@ -99947,7 +100666,7 @@ function fisherYatesShuffle(arr) {
|
|
|
99947
100666
|
return arr;
|
|
99948
100667
|
}
|
|
99949
100668
|
function getDefaultJudgeModels(sessionPath) {
|
|
99950
|
-
const manifest = JSON.parse(readFileSync42(
|
|
100669
|
+
const manifest = JSON.parse(readFileSync42(join54(sessionPath, "manifest.json"), "utf-8"));
|
|
99951
100670
|
return Object.values(manifest.models).map((e) => e.model);
|
|
99952
100671
|
}
|
|
99953
100672
|
function buildJudgePrompt(input, responses) {
|
|
@@ -100010,7 +100729,7 @@ function parseJudgeVotes(judgePath, responseIds) {
|
|
|
100010
100729
|
const judgeId = file.replace(/^response-/, "").replace(/\.md$/, "");
|
|
100011
100730
|
let content;
|
|
100012
100731
|
try {
|
|
100013
|
-
content = readFileSync42(
|
|
100732
|
+
content = readFileSync42(join54(judgePath, file), "utf-8");
|
|
100014
100733
|
} catch (err) {
|
|
100015
100734
|
log(`[team] judge answer ${file} unreadable: ${err instanceof Error ? err.message : String(err)}`);
|
|
100016
100735
|
continue;
|
|
@@ -100063,7 +100782,7 @@ function aggregateVerdict(votes, responseIds) {
|
|
|
100063
100782
|
function formatVerdict(verdict, sessionPath) {
|
|
100064
100783
|
let manifest = null;
|
|
100065
100784
|
try {
|
|
100066
|
-
manifest = JSON.parse(readFileSync42(
|
|
100785
|
+
manifest = JSON.parse(readFileSync42(join54(sessionPath, "manifest.json"), "utf-8"));
|
|
100067
100786
|
} catch {}
|
|
100068
100787
|
let output = `# Team Verdict
|
|
100069
100788
|
|
|
@@ -100120,7 +100839,7 @@ var init_team_orchestrator = __esm(() => {
|
|
|
100120
100839
|
|
|
100121
100840
|
// src/mcp-session/inherited-settings.ts
|
|
100122
100841
|
import { existsSync as existsSync37, readFileSync as readFileSync43, statSync as statSync19 } from "fs";
|
|
100123
|
-
import { join as
|
|
100842
|
+
import { join as join55, resolve as resolve7 } from "path";
|
|
100124
100843
|
function isPlainObject7(value) {
|
|
100125
100844
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
100126
100845
|
}
|
|
@@ -100174,7 +100893,7 @@ function readSettingsValue(value) {
|
|
|
100174
100893
|
function buildInheritedSettingsLayer(setupFrom, callerFlags) {
|
|
100175
100894
|
const layers = [];
|
|
100176
100895
|
for (const name of ["settings.json", "settings.local.json"]) {
|
|
100177
|
-
const file =
|
|
100896
|
+
const file = join55(setupFrom, ".claude", name);
|
|
100178
100897
|
if (!existsSync37(file))
|
|
100179
100898
|
continue;
|
|
100180
100899
|
layers.push(parseLayer(readFileSync43(file, "utf-8"), file));
|
|
@@ -100206,14 +100925,14 @@ function resolveSetupFrom(arg, workDir, serverCwd) {
|
|
|
100206
100925
|
return;
|
|
100207
100926
|
let setupFrom = serverCwd;
|
|
100208
100927
|
if (typeof arg === "string") {
|
|
100209
|
-
setupFrom =
|
|
100928
|
+
setupFrom = resolve7(arg);
|
|
100210
100929
|
if (!statSync19(setupFrom, { throwIfNoEntry: false })?.isDirectory()) {
|
|
100211
100930
|
throw new Error(`invalid_args: setup_from ${setupFrom} is not an existing directory`);
|
|
100212
100931
|
}
|
|
100213
100932
|
} else if (arg !== undefined) {
|
|
100214
100933
|
throw new Error("invalid_args: setup_from must be a directory path or null");
|
|
100215
100934
|
}
|
|
100216
|
-
return setupFrom ===
|
|
100935
|
+
return setupFrom === resolve7(workDir ?? serverCwd) ? undefined : setupFrom;
|
|
100217
100936
|
}
|
|
100218
100937
|
var init_inherited_settings = () => {};
|
|
100219
100938
|
|
|
@@ -100233,7 +100952,7 @@ import {
|
|
|
100233
100952
|
statSync as statSync20,
|
|
100234
100953
|
writeFileSync as writeFileSync23
|
|
100235
100954
|
} from "fs";
|
|
100236
|
-
import { join as
|
|
100955
|
+
import { join as join56, resolve as resolve8, sep as sep3 } from "path";
|
|
100237
100956
|
function normaliseTimeoutSeconds(raw) {
|
|
100238
100957
|
const n = typeof raw === "string" && raw.trim() !== "" ? Number(raw) : raw;
|
|
100239
100958
|
if (typeof n !== "number" || !Number.isFinite(n))
|
|
@@ -100403,7 +101122,7 @@ function dropLeadingFragment(tail) {
|
|
|
100403
101122
|
return firstBreak === -1 ? tail.text : tail.text.slice(firstBreak + 1);
|
|
100404
101123
|
}
|
|
100405
101124
|
function diskAccounting(sessionDir) {
|
|
100406
|
-
const stats = readTokenStatsAt(
|
|
101125
|
+
const stats = readTokenStatsAt(join56(sessionDir, "tokens.json"));
|
|
100407
101126
|
return {
|
|
100408
101127
|
tokensIn: stats?.billed_input_tokens ?? null,
|
|
100409
101128
|
tokensOut: stats?.output_tokens ?? null,
|
|
@@ -100488,7 +101207,7 @@ class SessionManager {
|
|
|
100488
101207
|
let dir = "";
|
|
100489
101208
|
for (let attempt = 0;; attempt++) {
|
|
100490
101209
|
id = `team-${randomUUID9().replace(/-/g, "").slice(0, 8)}`;
|
|
100491
|
-
dir =
|
|
101210
|
+
dir = join56(this.sessionsDir, id);
|
|
100492
101211
|
try {
|
|
100493
101212
|
mkdirSync25(dir);
|
|
100494
101213
|
break;
|
|
@@ -100503,7 +101222,7 @@ class SessionManager {
|
|
|
100503
101222
|
sessionId: id,
|
|
100504
101223
|
parentClaudeSessionId: opts.parentClaudeSessionId,
|
|
100505
101224
|
startedAt,
|
|
100506
|
-
teamPath:
|
|
101225
|
+
teamPath: resolve8(opts.teamPath),
|
|
100507
101226
|
slots: opts.slots
|
|
100508
101227
|
});
|
|
100509
101228
|
this.teamRunStarts.set(id, startedAt);
|
|
@@ -100520,9 +101239,9 @@ class SessionManager {
|
|
|
100520
101239
|
if (dir === null)
|
|
100521
101240
|
throw new Error("not a record id");
|
|
100522
101241
|
const completedAt = new Date().toISOString();
|
|
100523
|
-
const startedAt = this.teamRunStarts.get(record) ?? metaString(readJsonObject(
|
|
101242
|
+
const startedAt = this.teamRunStarts.get(record) ?? metaString(readJsonObject(join56(dir, "spawn.json"), META_READ_LIMIT)?.startedAt) ?? completedAt;
|
|
100524
101243
|
const elapsedMs = Date.parse(completedAt) - Date.parse(startedAt);
|
|
100525
|
-
writeJsonAtomic(
|
|
101244
|
+
writeJsonAtomic(join56(dir, "meta.json"), {
|
|
100526
101245
|
kind: "team",
|
|
100527
101246
|
status: outcome.status,
|
|
100528
101247
|
startedAt,
|
|
@@ -100554,7 +101273,7 @@ class SessionManager {
|
|
|
100554
101273
|
startedAt,
|
|
100555
101274
|
...rest
|
|
100556
101275
|
};
|
|
100557
|
-
writeJsonAtomic(
|
|
101276
|
+
writeJsonAtomic(join56(dir, "spawn.json"), body);
|
|
100558
101277
|
}
|
|
100559
101278
|
async createSession(opts) {
|
|
100560
101279
|
if (this.activeSessions + this.starting >= this.maxSessions) {
|
|
@@ -100594,7 +101313,7 @@ class SessionManager {
|
|
|
100594
101313
|
throw err;
|
|
100595
101314
|
}
|
|
100596
101315
|
if (inherited.layer) {
|
|
100597
|
-
flags = [...inherited.restFlags, "--settings",
|
|
101316
|
+
flags = [...inherited.restFlags, "--settings", join56(entry.sessionDir, INHERITED_SETTINGS)];
|
|
100598
101317
|
}
|
|
100599
101318
|
this.sessions.set(entry.info.sessionId, entry);
|
|
100600
101319
|
uncount();
|
|
@@ -100619,10 +101338,10 @@ class SessionManager {
|
|
|
100619
101338
|
const timeout = normaliseTimeoutSeconds(opts.timeoutSeconds);
|
|
100620
101339
|
const agentRequested = requestedAgentOf(opts.claudishFlags ?? []);
|
|
100621
101340
|
const startedAt = new Date().toISOString();
|
|
100622
|
-
const sessionDir = opts.sessionDir ??
|
|
101341
|
+
const sessionDir = opts.sessionDir ?? join56(this.sessionsDir, sessionId);
|
|
100623
101342
|
mkdirSync25(sessionDir, { recursive: true });
|
|
100624
101343
|
if (opts.prompt)
|
|
100625
|
-
writeFileSync23(
|
|
101344
|
+
writeFileSync23(join56(sessionDir, "prompt.md"), opts.prompt, "utf-8");
|
|
100626
101345
|
this.writeSpawnRecord(sessionDir, {
|
|
100627
101346
|
kind: "session",
|
|
100628
101347
|
sessionId,
|
|
@@ -100635,13 +101354,13 @@ class SessionManager {
|
|
|
100635
101354
|
...agentRequested ? { agentRequested } : {}
|
|
100636
101355
|
});
|
|
100637
101356
|
if (inheritedLayer) {
|
|
100638
|
-
writeFileSync23(
|
|
101357
|
+
writeFileSync23(join56(sessionDir, INHERITED_SETTINGS), `${JSON.stringify(inheritedLayer, null, 2)}
|
|
100639
101358
|
`, { encoding: "utf-8", mode: 384 });
|
|
100640
101359
|
}
|
|
100641
101360
|
const cwd = opts.cwd ?? process.cwd();
|
|
100642
101361
|
const parentEnv = opts.parentEnv ?? this.parentEnv;
|
|
100643
101362
|
const spawnModel = opts.spawnModel ?? null;
|
|
100644
|
-
const tokenFile = opts.tokenFile ??
|
|
101363
|
+
const tokenFile = opts.tokenFile ?? join56(sessionDir, "tokens.json");
|
|
100645
101364
|
return {
|
|
100646
101365
|
info: {
|
|
100647
101366
|
sessionId,
|
|
@@ -100679,18 +101398,18 @@ class SessionManager {
|
|
|
100679
101398
|
},
|
|
100680
101399
|
session: null,
|
|
100681
101400
|
scrollback: new ScrollbackBuffer(this.scrollbackCapacity),
|
|
100682
|
-
outputLogStream: createWriteStream(
|
|
101401
|
+
outputLogStream: createWriteStream(join56(sessionDir, "output.log")),
|
|
100683
101402
|
sessionDir,
|
|
100684
101403
|
cwd,
|
|
100685
101404
|
tokenFile,
|
|
100686
|
-
eventLogPath:
|
|
101405
|
+
eventLogPath: join56(sessionDir, "events.jsonl"),
|
|
100687
101406
|
eventLogBytes: 0,
|
|
100688
|
-
upstreamErrorLogPath:
|
|
101407
|
+
upstreamErrorLogPath: join56(sessionDir, "upstream-errors.jsonl"),
|
|
100689
101408
|
eventRing: [],
|
|
100690
101409
|
proseBytes: 0,
|
|
100691
101410
|
answeredTurns: new Map,
|
|
100692
101411
|
inboxPath: null,
|
|
100693
|
-
waitLogPath:
|
|
101412
|
+
waitLogPath: join56(sessionDir, "waits.jsonl"),
|
|
100694
101413
|
waitLogBytes: 0,
|
|
100695
101414
|
waitSince: null,
|
|
100696
101415
|
ended: false,
|
|
@@ -100885,7 +101604,7 @@ class SessionManager {
|
|
|
100885
101604
|
try {
|
|
100886
101605
|
const text = screenLines.join(`
|
|
100887
101606
|
`).replace(/\s+$/, "");
|
|
100888
|
-
writeFileSync23(
|
|
101607
|
+
writeFileSync23(join56(entry.sessionDir, "screen.txt"), `${redactSecrets(text)}
|
|
100889
101608
|
`, "utf-8");
|
|
100890
101609
|
} catch {}
|
|
100891
101610
|
}
|
|
@@ -100899,7 +101618,7 @@ class SessionManager {
|
|
|
100899
101618
|
entry.waitSince = null;
|
|
100900
101619
|
}
|
|
100901
101620
|
try {
|
|
100902
|
-
writeJsonAtomic(
|
|
101621
|
+
writeJsonAtomic(join56(entry.sessionDir, "meta.json"), toMetaRecord(entry.info, entry.cwd));
|
|
100903
101622
|
} catch (err) {
|
|
100904
101623
|
process.stderr.write(`[claudish] session ${entry.info.sessionId}: could not write meta.json: ` + `${err instanceof Error ? err.message : String(err)}
|
|
100905
101624
|
`);
|
|
@@ -101140,8 +101859,8 @@ class SessionManager {
|
|
|
101140
101859
|
} catch {
|
|
101141
101860
|
return null;
|
|
101142
101861
|
}
|
|
101143
|
-
const spawnPath =
|
|
101144
|
-
const metaPath =
|
|
101862
|
+
const spawnPath = join56(sessionDir, "spawn.json");
|
|
101863
|
+
const metaPath = join56(sessionDir, "meta.json");
|
|
101145
101864
|
const spawnRecord = readJsonObject(spawnPath, META_READ_LIMIT);
|
|
101146
101865
|
if (spawnRecord?.kind === "team")
|
|
101147
101866
|
return null;
|
|
@@ -101233,9 +101952,9 @@ class SessionManager {
|
|
|
101233
101952
|
diskSessionDir(sessionId) {
|
|
101234
101953
|
if (!SESSION_ID_RE.test(sessionId))
|
|
101235
101954
|
return null;
|
|
101236
|
-
const root =
|
|
101237
|
-
const dir =
|
|
101238
|
-
if (dir !==
|
|
101955
|
+
const root = resolve8(this.sessionsDir);
|
|
101956
|
+
const dir = resolve8(root, sessionId);
|
|
101957
|
+
if (dir !== join56(root, sessionId))
|
|
101239
101958
|
return null;
|
|
101240
101959
|
if (!dir.startsWith(root + sep3))
|
|
101241
101960
|
return null;
|
|
@@ -101254,10 +101973,10 @@ class SessionManager {
|
|
|
101254
101973
|
} catch {
|
|
101255
101974
|
return null;
|
|
101256
101975
|
}
|
|
101257
|
-
const spawnRecord = readJsonObject(
|
|
101976
|
+
const spawnRecord = readJsonObject(join56(sessionDir, "spawn.json"), META_READ_LIMIT);
|
|
101258
101977
|
if (spawnRecord?.kind === "team")
|
|
101259
101978
|
return null;
|
|
101260
|
-
const meta = readJsonObject(
|
|
101979
|
+
const meta = readJsonObject(join56(sessionDir, "meta.json"), META_READ_LIMIT);
|
|
101261
101980
|
if (meta?.kind === "team")
|
|
101262
101981
|
return null;
|
|
101263
101982
|
const partial = meta === null;
|
|
@@ -101279,7 +101998,7 @@ class SessionManager {
|
|
|
101279
101998
|
return stored;
|
|
101280
101999
|
}
|
|
101281
102000
|
diskOutput(record, tailLines) {
|
|
101282
|
-
const tail = readTailText(
|
|
102001
|
+
const tail = readTailText(join56(record.sessionDir, "output.log"), OUTPUT_TAIL_BYTES);
|
|
101283
102002
|
const buffer = new ScrollbackBuffer(this.scrollbackCapacity);
|
|
101284
102003
|
if (tail?.text)
|
|
101285
102004
|
buffer.append(dropLeadingFragment(tail));
|
|
@@ -101299,12 +102018,12 @@ class SessionManager {
|
|
|
101299
102018
|
}
|
|
101300
102019
|
diskDiagnostics(record, limit) {
|
|
101301
102020
|
const { sessionDir, info } = record;
|
|
101302
|
-
const eventLogPath =
|
|
101303
|
-
const upstreamErrorLogPath =
|
|
101304
|
-
const outputLogPath =
|
|
102021
|
+
const eventLogPath = join56(sessionDir, "events.jsonl");
|
|
102022
|
+
const upstreamErrorLogPath = join56(sessionDir, "upstream-errors.jsonl");
|
|
102023
|
+
const outputLogPath = join56(sessionDir, "output.log");
|
|
101305
102024
|
const events = readTailLines2(eventLogPath, EVENT_TAIL_BYTES);
|
|
101306
102025
|
const outputTail = readTailText(outputLogPath, OUTPUT_TAIL_BYTES);
|
|
101307
|
-
const screen = readTailText(
|
|
102026
|
+
const screen = readTailText(join56(sessionDir, "screen.txt"), SCREEN_READ_BYTES);
|
|
101308
102027
|
return {
|
|
101309
102028
|
sessionId: info.sessionId,
|
|
101310
102029
|
state: info.state,
|
|
@@ -101710,7 +102429,7 @@ var init_progress_heartbeat = __esm(() => {
|
|
|
101710
102429
|
import { spawn as spawn2 } from "child_process";
|
|
101711
102430
|
import { mkdirSync as mkdirSync26, mkdtempSync as mkdtempSync3, rmSync as rmSync14, writeFileSync as writeFileSync24 } from "fs";
|
|
101712
102431
|
import { tmpdir as tmpdir2 } from "os";
|
|
101713
|
-
import { join as
|
|
102432
|
+
import { join as join57 } from "path";
|
|
101714
102433
|
function claudeCodePrintArgv(model, systemPromptFile) {
|
|
101715
102434
|
const head = [PRINT_FLAGS[0], "--model", model, ...PRINT_FLAGS.slice(1)];
|
|
101716
102435
|
return systemPromptFile !== undefined ? [...head, "--system-prompt-file", systemPromptFile] : [...head, "--system-prompt", ""];
|
|
@@ -101811,7 +102530,7 @@ function killGroup(proc) {
|
|
|
101811
102530
|
timer.unref?.();
|
|
101812
102531
|
}
|
|
101813
102532
|
function stopAllClaudeCodePrintChildren() {
|
|
101814
|
-
for (const proc of
|
|
102533
|
+
for (const proc of inFlight4)
|
|
101815
102534
|
killGroup(proc);
|
|
101816
102535
|
}
|
|
101817
102536
|
function stderrTail(stderr) {
|
|
@@ -101828,13 +102547,13 @@ async function runPromptViaClaudeCode(model, prompt, systemPrompt, opts = {}) {
|
|
|
101828
102547
|
} catch (e) {
|
|
101829
102548
|
throw new Error(`cannot start claudish (${parentEnv.CLAUDISH_BIN?.trim() || "claudish"}): ${e instanceof Error ? e.message : String(e)}`);
|
|
101830
102549
|
}
|
|
101831
|
-
const root = mkdtempSync3(
|
|
102550
|
+
const root = mkdtempSync3(join57(tmpdir2(), "claudish-run-prompt-"));
|
|
101832
102551
|
try {
|
|
101833
|
-
const cwd =
|
|
102552
|
+
const cwd = join57(root, "cwd");
|
|
101834
102553
|
mkdirSync26(cwd, { mode: 448 });
|
|
101835
102554
|
let systemPromptFile;
|
|
101836
102555
|
if (typeof systemPrompt === "string" && systemPrompt.length > 0) {
|
|
101837
|
-
systemPromptFile =
|
|
102556
|
+
systemPromptFile = join57(root, "system-prompt.txt");
|
|
101838
102557
|
writeFileSync24(systemPromptFile, systemPrompt, { mode: 384 });
|
|
101839
102558
|
}
|
|
101840
102559
|
const argv = claudeCodePrintArgv(model, systemPromptFile);
|
|
@@ -101887,7 +102606,7 @@ function runChild(launch, argv, cwd, env, prompt, signal) {
|
|
|
101887
102606
|
if (settled)
|
|
101888
102607
|
return;
|
|
101889
102608
|
settled = true;
|
|
101890
|
-
|
|
102609
|
+
inFlight4.delete(proc);
|
|
101891
102610
|
signal?.removeEventListener("abort", onAbort);
|
|
101892
102611
|
fn();
|
|
101893
102612
|
};
|
|
@@ -101902,7 +102621,7 @@ function runChild(launch, argv, cwd, env, prompt, signal) {
|
|
|
101902
102621
|
proc.stdin?.on("error", () => {});
|
|
101903
102622
|
proc.on("error", (e) => finish(() => reject(startError(launch.command, e))));
|
|
101904
102623
|
proc.on("close", (code, sig) => finish(() => resolvePromise({ code, signal: sig, stdout, stderr })));
|
|
101905
|
-
|
|
102624
|
+
inFlight4.add(proc);
|
|
101906
102625
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
101907
102626
|
if (signal?.aborted)
|
|
101908
102627
|
onAbort();
|
|
@@ -101914,7 +102633,7 @@ function startError(command, e) {
|
|
|
101914
102633
|
const detail = typeof code === "string" ? code : e instanceof Error ? e.message : String(e ?? "unknown");
|
|
101915
102634
|
return new Error(`cannot start claudish (${command}): ${detail}`);
|
|
101916
102635
|
}
|
|
101917
|
-
var PRINT_FLAGS, PRINT_CHILD_DROPPED_MARKERS,
|
|
102636
|
+
var PRINT_FLAGS, PRINT_CHILD_DROPPED_MARKERS, inFlight4, KILL_GRACE_MS = 5000, STDERR_TAIL_CHARS = 2000;
|
|
101918
102637
|
var init_run_prompt_claude_code = __esm(() => {
|
|
101919
102638
|
init_config();
|
|
101920
102639
|
init_child_env();
|
|
@@ -101936,7 +102655,7 @@ var init_run_prompt_claude_code = __esm(() => {
|
|
|
101936
102655
|
...PANE_LAUNCH_VARS,
|
|
101937
102656
|
ENV.CLAUDISH_PANE_CHILD
|
|
101938
102657
|
];
|
|
101939
|
-
|
|
102658
|
+
inFlight4 = new Set;
|
|
101940
102659
|
});
|
|
101941
102660
|
|
|
101942
102661
|
// src/providers/preflight-chain.ts
|
|
@@ -102136,7 +102855,7 @@ var init_preflight_chain = __esm(() => {
|
|
|
102136
102855
|
// src/mcp-server.ts
|
|
102137
102856
|
import { existsSync as existsSync39, readFileSync as readFileSync45, readdirSync as readdirSync11 } from "fs";
|
|
102138
102857
|
import { homedir as homedir37 } from "os";
|
|
102139
|
-
import { dirname as dirname22, join as
|
|
102858
|
+
import { dirname as dirname22, join as join58, resolve as resolve9 } from "path";
|
|
102140
102859
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
102141
102860
|
async function loadAllModels(forceRefresh = false) {
|
|
102142
102861
|
if (!forceRefresh && existsSync39(OPENROUTER_MODELS_CACHE_PATH)) {
|
|
@@ -103328,7 +104047,7 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
|
|
|
103328
104047
|
const promptText = () => {
|
|
103329
104048
|
if (input !== undefined)
|
|
103330
104049
|
return input;
|
|
103331
|
-
const existing =
|
|
104050
|
+
const existing = join58(resolved, "input.md");
|
|
103332
104051
|
return existsSync39(existing) ? readFileSync45(existing, "utf-8") : undefined;
|
|
103333
104052
|
};
|
|
103334
104053
|
const choice = modelChoiceStop([
|
|
@@ -103511,16 +104230,16 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
|
|
|
103511
104230
|
const sp = session_path;
|
|
103512
104231
|
for (const file of ["status.json", "manifest.json", "input.md"]) {
|
|
103513
104232
|
try {
|
|
103514
|
-
sessionData[file] = readFileSync45(
|
|
104233
|
+
sessionData[file] = readFileSync45(join58(sp, file), "utf-8");
|
|
103515
104234
|
} catch {}
|
|
103516
104235
|
}
|
|
103517
104236
|
try {
|
|
103518
|
-
const errorDir =
|
|
104237
|
+
const errorDir = join58(sp, "errors");
|
|
103519
104238
|
if (existsSync39(errorDir)) {
|
|
103520
104239
|
for (const f of readdirSync11(errorDir)) {
|
|
103521
104240
|
if (f.endsWith(".log")) {
|
|
103522
104241
|
try {
|
|
103523
|
-
sessionData[`errors/${f}`] = readFileSync45(
|
|
104242
|
+
sessionData[`errors/${f}`] = readFileSync45(join58(errorDir, f), "utf-8");
|
|
103524
104243
|
} catch {}
|
|
103525
104244
|
}
|
|
103526
104245
|
}
|
|
@@ -103530,7 +104249,7 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
|
|
|
103530
104249
|
for (const f of readdirSync11(sp)) {
|
|
103531
104250
|
if (f.startsWith("response-") && f.endsWith(".md")) {
|
|
103532
104251
|
try {
|
|
103533
|
-
const content = readFileSync45(
|
|
104252
|
+
const content = readFileSync45(join58(sp, f), "utf-8");
|
|
103534
104253
|
sessionData[f] = content.slice(0, 200) + (content.length > 200 ? "... (truncated)" : "");
|
|
103535
104254
|
} catch {}
|
|
103536
104255
|
}
|
|
@@ -103539,7 +104258,7 @@ This searched OpenRouter's listing only. Subscription wire ids and catalog alias
|
|
|
103539
104258
|
}
|
|
103540
104259
|
let version = "unknown";
|
|
103541
104260
|
try {
|
|
103542
|
-
const pkgPath =
|
|
104261
|
+
const pkgPath = join58(__dirname3, "../package.json");
|
|
103543
104262
|
if (existsSync39(pkgPath)) {
|
|
103544
104263
|
version = JSON.parse(readFileSync45(pkgPath, "utf-8")).version;
|
|
103545
104264
|
}
|
|
@@ -103722,7 +104441,7 @@ Report manually at https://github.com/anthropics/claudish/issues${autoSendHint}`
|
|
|
103722
104441
|
const { specs, stripped } = strippedProviders([args.model], new Set(args.user_required_provider === true ? [args.model] : []));
|
|
103723
104442
|
const requestedModel = specs[0];
|
|
103724
104443
|
const choice = modelChoiceStop([{ given: args.model, spec: requestedModel }], new Set(args.user_required_model === true ? [args.model] : []), "with user_required_model: true", {
|
|
103725
|
-
projectRulesReadable: typeof args.work_dir !== "string" ||
|
|
104444
|
+
projectRulesReadable: typeof args.work_dir !== "string" || resolve9(args.work_dir) === process.cwd()
|
|
103726
104445
|
});
|
|
103727
104446
|
if (choice)
|
|
103728
104447
|
return choice;
|
|
@@ -103732,7 +104451,7 @@ Report manually at https://github.com/anthropics/claudish/issues${autoSendHint}`
|
|
|
103732
104451
|
}
|
|
103733
104452
|
const workDir = args.work_dir;
|
|
103734
104453
|
const plan = await prehydrateCredentialsForSpawn([requestedModel], {
|
|
103735
|
-
pin: workDir === undefined ||
|
|
104454
|
+
pin: workDir === undefined || resolve9(workDir) === process.cwd()
|
|
103736
104455
|
});
|
|
103737
104456
|
const parentClaudeSessionId = await parentSessionForCall({
|
|
103738
104457
|
hostPid: sessionManager.hostPid
|
|
@@ -104221,8 +104940,8 @@ var init_mcp_server = __esm(() => {
|
|
|
104221
104940
|
init_preflight_chain();
|
|
104222
104941
|
__filename3 = fileURLToPath3(import.meta.url);
|
|
104223
104942
|
__dirname3 = dirname22(__filename3);
|
|
104224
|
-
CLAUDISH_CACHE_DIR =
|
|
104225
|
-
OPENROUTER_MODELS_CACHE_PATH =
|
|
104943
|
+
CLAUDISH_CACHE_DIR = join58(homedir37(), ".claudish");
|
|
104944
|
+
OPENROUTER_MODELS_CACHE_PATH = join58(CLAUDISH_CACHE_DIR, "openrouter-models.json");
|
|
104226
104945
|
NEXT_STEP = {
|
|
104227
104946
|
cancelled: "you stopped this slot; nothing is wrong with it. Re-run it if you still want its vote",
|
|
104228
104947
|
timeout: "grid mode only \u2014 magmux ended the pane. The orchestrator has no deadline",
|
|
@@ -104353,7 +105072,7 @@ var HOOKS_AMBIENT_DTS = '// claudish hook types \u2014 written by `claudish hook
|
|
|
104353
105072
|
// src/hooks/observer/corpus.ts
|
|
104354
105073
|
import { appendFileSync as appendFileSync8, readFileSync as readFileSync47, readdirSync as readdirSync12, statSync as statSync21 } from "fs";
|
|
104355
105074
|
import { homedir as homedir38 } from "os";
|
|
104356
|
-
import { join as
|
|
105075
|
+
import { join as join59 } from "path";
|
|
104357
105076
|
function directoryOf2(filePath) {
|
|
104358
105077
|
const slash = filePath.lastIndexOf("/");
|
|
104359
105078
|
return slash > 0 ? filePath.slice(0, slash) : undefined;
|
|
@@ -104432,26 +105151,26 @@ function listTranscripts(root) {
|
|
|
104432
105151
|
return files;
|
|
104433
105152
|
}
|
|
104434
105153
|
for (const project of projects) {
|
|
104435
|
-
const dir =
|
|
105154
|
+
const dir = join59(root, project);
|
|
104436
105155
|
try {
|
|
104437
105156
|
if (!statSync21(dir).isDirectory())
|
|
104438
105157
|
continue;
|
|
104439
105158
|
for (const f of readdirSync12(dir)) {
|
|
104440
105159
|
if (f.endsWith(".jsonl"))
|
|
104441
|
-
files.push(
|
|
105160
|
+
files.push(join59(dir, f));
|
|
104442
105161
|
}
|
|
104443
105162
|
} catch {}
|
|
104444
105163
|
}
|
|
104445
105164
|
return files;
|
|
104446
105165
|
}
|
|
104447
105166
|
function buildCorpus(options = {}) {
|
|
104448
|
-
const root = options.projectsRoot ??
|
|
105167
|
+
const root = options.projectsRoot ?? join59(homedir38(), ".claude", "projects");
|
|
104449
105168
|
const files = listTranscripts(root);
|
|
104450
105169
|
const records = [];
|
|
104451
105170
|
for (const f of files)
|
|
104452
105171
|
records.push(...replayTranscript(f));
|
|
104453
105172
|
if (options.write && records.length > 0) {
|
|
104454
|
-
const outputPath = options.outputPath ??
|
|
105173
|
+
const outputPath = options.outputPath ?? join59(homedir38(), ".claudish", "behavior-divergences.jsonl");
|
|
104455
105174
|
try {
|
|
104456
105175
|
appendFileSync8(outputPath, `${records.map((r) => JSON.stringify(r)).join(`
|
|
104457
105176
|
`)}
|
|
@@ -104469,7 +105188,7 @@ var init_corpus = __esm(() => {
|
|
|
104469
105188
|
// src/hooks-command.ts
|
|
104470
105189
|
import { existsSync as existsSync41, mkdirSync as mkdirSync27, readFileSync as readFileSync48, writeFileSync as writeFileSync25 } from "fs";
|
|
104471
105190
|
import { homedir as homedir39 } from "os";
|
|
104472
|
-
import { join as
|
|
105191
|
+
import { join as join60, resolve as resolve10 } from "path";
|
|
104473
105192
|
function severityColor(sev) {
|
|
104474
105193
|
if (sev === "fix")
|
|
104475
105194
|
return green(sev);
|
|
@@ -104559,7 +105278,7 @@ async function check(file, json) {
|
|
|
104559
105278
|
console.error("Usage: claudish hooks check <file> [--json]");
|
|
104560
105279
|
return 1;
|
|
104561
105280
|
}
|
|
104562
|
-
const path =
|
|
105281
|
+
const path = resolve10(process.cwd(), file);
|
|
104563
105282
|
const notes = [];
|
|
104564
105283
|
const loaded = await loadUserHooks({
|
|
104565
105284
|
modules: [path],
|
|
@@ -104592,10 +105311,10 @@ async function check(file, json) {
|
|
|
104592
105311
|
return valid ? 0 : 1;
|
|
104593
105312
|
}
|
|
104594
105313
|
function init(dirArg) {
|
|
104595
|
-
const dir = dirArg ?
|
|
105314
|
+
const dir = dirArg ? resolve10(process.cwd(), dirArg) : join60(homedir39(), ".claudish", "hooks");
|
|
104596
105315
|
const files = [
|
|
104597
|
-
[
|
|
104598
|
-
[
|
|
105316
|
+
[join60(dir, "example-hook.ts"), EXAMPLE_HOOK],
|
|
105317
|
+
[join60(dir, "claudish-hooks.d.ts"), HOOKS_AMBIENT_DTS]
|
|
104599
105318
|
];
|
|
104600
105319
|
const existing = files.filter(([path]) => existsSync41(path)).map(([path]) => path);
|
|
104601
105320
|
if (existing.length > 0) {
|
|
@@ -104798,7 +105517,7 @@ var init_hooks_command = __esm(() => {
|
|
|
104798
105517
|
import { spawn as spawn3 } from "child_process";
|
|
104799
105518
|
import { existsSync as existsSync42, readFileSync as readFileSync49, writeFileSync as writeFileSync26 } from "fs";
|
|
104800
105519
|
import { connect as netConnect2 } from "net";
|
|
104801
|
-
import { join as
|
|
105520
|
+
import { join as join61 } from "path";
|
|
104802
105521
|
import { setTimeout as wait } from "timers/promises";
|
|
104803
105522
|
function paneRouteLine(explanation) {
|
|
104804
105523
|
const origin = describeRouteExplanation(explanation);
|
|
@@ -104948,9 +105667,9 @@ async function runWithGrid(sessionPath, models, input, opts) {
|
|
|
104948
105667
|
const keep = opts?.keep ?? false;
|
|
104949
105668
|
const manifest = setupSession(sessionPath, models, input);
|
|
104950
105669
|
const startedAt = new Date().toISOString();
|
|
104951
|
-
const gridfilePath =
|
|
104952
|
-
const prompt = readFileSync49(
|
|
104953
|
-
const rawPrompt = readFileSync49(
|
|
105670
|
+
const gridfilePath = join61(sessionPath, "gridfile.txt");
|
|
105671
|
+
const prompt = readFileSync49(join61(sessionPath, "input.md"), "utf-8").replace(/'/g, "'\\''").replace(/\n/g, " ");
|
|
105672
|
+
const rawPrompt = readFileSync49(join61(sessionPath, "input.md"), "utf-8");
|
|
104954
105673
|
const usedBannerColors = new Set;
|
|
104955
105674
|
const routeLines = mode === "interactive" ? new Map : await resolvePaneRouteLines(Object.values(manifest.models).map((entry) => entry.model));
|
|
104956
105675
|
const gridLines = Object.entries(manifest.models).map(([anonId]) => {
|
|
@@ -104982,7 +105701,7 @@ async function runWithGrid(sessionPath, models, input, opts) {
|
|
|
104982
105701
|
});
|
|
104983
105702
|
const [{ results }] = await Promise.all([subscription, procExit]);
|
|
104984
105703
|
const status = buildTeamStatus(manifest, startedAt, results?.panes ?? null);
|
|
104985
|
-
const statusPath =
|
|
105704
|
+
const statusPath = join61(sessionPath, "status.json");
|
|
104986
105705
|
writeFileSync26(statusPath, JSON.stringify(status, null, 2), "utf-8");
|
|
104987
105706
|
return status;
|
|
104988
105707
|
}
|
|
@@ -105003,7 +105722,7 @@ var init_team_grid = __esm(() => {
|
|
|
105003
105722
|
|
|
105004
105723
|
// src/team-cli.ts
|
|
105005
105724
|
import { readFileSync as readFileSync50 } from "fs";
|
|
105006
|
-
import { join as
|
|
105725
|
+
import { join as join62 } from "path";
|
|
105007
105726
|
async function runPaneTeam(sessionPath, models, input, opts) {
|
|
105008
105727
|
installPaneShutdownHooks({ exitAfter: true });
|
|
105009
105728
|
try {
|
|
@@ -105148,7 +105867,7 @@ async function teamCommand(args) {
|
|
|
105148
105867
|
}
|
|
105149
105868
|
case "judge": {
|
|
105150
105869
|
await judgeResponses(sessionPath, { judges });
|
|
105151
|
-
console.log(readFileSync50(
|
|
105870
|
+
console.log(readFileSync50(join62(sessionPath, "verdict.md"), "utf-8"));
|
|
105152
105871
|
break;
|
|
105153
105872
|
}
|
|
105154
105873
|
case "run-and-judge": {
|
|
@@ -105164,7 +105883,7 @@ async function teamCommand(args) {
|
|
|
105164
105883
|
});
|
|
105165
105884
|
printStatus(status);
|
|
105166
105885
|
await judgeResponses(sessionPath, { judges });
|
|
105167
|
-
console.log(readFileSync50(
|
|
105886
|
+
console.log(readFileSync50(join62(sessionPath, "verdict.md"), "utf-8"));
|
|
105168
105887
|
break;
|
|
105169
105888
|
}
|
|
105170
105889
|
case "status": {
|
|
@@ -107068,8 +107787,10 @@ A command that takes <label> also takes the account's email.
|
|
|
107068
107787
|
--id <generated-id> may replace or confirm the label; labels are unique per claudish provider.
|
|
107069
107788
|
Removing the legacy login (default, legacy-codex-file) requires --delete-file.
|
|
107070
107789
|
New accounts use the macOS Keychain, or protected plaintext files where there is none.
|
|
107790
|
+
On macOS the legacy login (~/.claudish/codex-oauth.json) moves into the Keychain on first use.
|
|
107071
107791
|
Add only accounts you own; vendor terms may restrict switching between subscriptions.
|
|
107072
|
-
|
|
107792
|
+
On macOS, \`claudish login codex\` adds or logs in your one Codex account; with several, use account login.
|
|
107793
|
+
Elsewhere, with Codex accounts configured, use account add/login instead of \`claudish login codex\`.
|
|
107073
107794
|
Claude account mutations, import and use are not available. No token input or export.`, MUTATION_VERBS, HOUR_MS19 = 3600000, FORECAST_HELP = `Usage:
|
|
107074
107795
|
claudish account forecast [--json] [--horizon <h|d>] [--provider codex]
|
|
107075
107796
|
|
|
@@ -107092,7 +107813,7 @@ var init_account_command = __esm(() => {
|
|
|
107092
107813
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
107093
107814
|
import { existsSync as existsSync43, unlinkSync as unlinkSync12 } from "fs";
|
|
107094
107815
|
import { homedir as homedir40 } from "os";
|
|
107095
|
-
import { join as
|
|
107816
|
+
import { join as join63 } from "path";
|
|
107096
107817
|
async function defaultSuggestModel() {
|
|
107097
107818
|
try {
|
|
107098
107819
|
const token = await getValidAntigravityAccessToken();
|
|
@@ -107211,7 +107932,7 @@ No session detected yet. Starting the Antigravity CLI interactively \u2014
|
|
|
107211
107932
|
async logout(deps) {
|
|
107212
107933
|
deleteSharedAntigravityToken(deps);
|
|
107213
107934
|
try {
|
|
107214
|
-
const tokenFile =
|
|
107935
|
+
const tokenFile = join63(homedir40(), ".gemini", "antigravity-cli", "antigravity-oauth-token");
|
|
107215
107936
|
if (existsSync43(tokenFile))
|
|
107216
107937
|
unlinkSync12(tokenFile);
|
|
107217
107938
|
} catch {}
|
|
@@ -115403,9 +116124,9 @@ var init_tui = __esm(() => {
|
|
|
115403
116124
|
// src/auth/claude-code-facts.ts
|
|
115404
116125
|
import { createHash as createHash14 } from "crypto";
|
|
115405
116126
|
import { homedir as homedir41 } from "os";
|
|
115406
|
-
import { isAbsolute as isAbsolute2, resolve as
|
|
116127
|
+
import { isAbsolute as isAbsolute2, resolve as resolve11 } from "path";
|
|
115407
116128
|
function defaultConfigDir(env) {
|
|
115408
|
-
return
|
|
116129
|
+
return resolve11(env.HOME || homedir41(), ".claude");
|
|
115409
116130
|
}
|
|
115410
116131
|
function configDirHash(configDir) {
|
|
115411
116132
|
return createHash14("sha256").update(configDir).digest("hex").slice(0, 8);
|
|
@@ -115414,7 +116135,7 @@ function claudeCodeKeychainService(env = process.env) {
|
|
|
115414
116135
|
const raw = env.CLAUDE_CONFIG_DIR?.trim();
|
|
115415
116136
|
if (!raw)
|
|
115416
116137
|
return `${BASE_KEYCHAIN_SERVICE}${KEYCHAIN_SERVICE_SUFFIX}`;
|
|
115417
|
-
const resolved = isAbsolute2(raw) ?
|
|
116138
|
+
const resolved = isAbsolute2(raw) ? resolve11(raw) : resolve11(process.cwd(), raw);
|
|
115418
116139
|
if (resolved === defaultConfigDir(env)) {
|
|
115419
116140
|
return `${BASE_KEYCHAIN_SERVICE}${KEYCHAIN_SERVICE_SUFFIX}`;
|
|
115420
116141
|
}
|
|
@@ -115490,7 +116211,7 @@ var init_own_pane = __esm(() => {
|
|
|
115490
116211
|
|
|
115491
116212
|
// src/hooks/session/interactive-driver.ts
|
|
115492
116213
|
import { existsSync as existsSync44, statSync as statSync22 } from "fs";
|
|
115493
|
-
import { join as
|
|
116214
|
+
import { join as join64 } from "path";
|
|
115494
116215
|
function nextDriverPhase(phase, ev) {
|
|
115495
116216
|
const row = EDGES[phase];
|
|
115496
116217
|
return row[ev] ?? null;
|
|
@@ -115708,7 +116429,7 @@ class InteractiveDriver {
|
|
|
115708
116429
|
}
|
|
115709
116430
|
follow(sid, offset) {
|
|
115710
116431
|
const path = transcriptPathFor(this.o.cwd, sid, this.o.projectsRoot);
|
|
115711
|
-
this.follower = new TranscriptFollower(path,
|
|
116432
|
+
this.follower = new TranscriptFollower(path, join64(path.replace(/\.jsonl$/, ""), "subagents"));
|
|
115712
116433
|
this.sessionId = sid;
|
|
115713
116434
|
this.settledTurn = null;
|
|
115714
116435
|
this.lastPollAt = Date.now();
|
|
@@ -116823,7 +117544,7 @@ import {
|
|
|
116823
117544
|
writeFileSync as writeFileSync27
|
|
116824
117545
|
} from "fs";
|
|
116825
117546
|
import { homedir as homedir42, tmpdir as tmpdir3 } from "os";
|
|
116826
|
-
import { dirname as dirname23, join as
|
|
117547
|
+
import { dirname as dirname23, join as join65 } from "path";
|
|
116827
117548
|
import { isatty } from "tty";
|
|
116828
117549
|
function releaseTerminalIsolation() {
|
|
116829
117550
|
if (!restoreTerminal)
|
|
@@ -116895,7 +117616,7 @@ function hasResolvableAnthropicAuth(deps = {}) {
|
|
|
116895
117616
|
if (isRealAnthropicEnvCredential(env, "ANTHROPIC_API_KEY") || isRealAnthropicEnvCredential(env, "ANTHROPIC_AUTH_TOKEN")) {
|
|
116896
117617
|
return true;
|
|
116897
117618
|
}
|
|
116898
|
-
if (fileExists(
|
|
117619
|
+
if (fileExists(join65(homedir42(), ".claude", ".credentials.json")))
|
|
116899
117620
|
return true;
|
|
116900
117621
|
return keychainProbe();
|
|
116901
117622
|
}
|
|
@@ -116995,10 +117716,10 @@ function readableClaudeSettingsLayers(config, env, deps) {
|
|
|
116995
117716
|
const cwd = deps.cwd ?? process.cwd();
|
|
116996
117717
|
const files = [
|
|
116997
117718
|
managedSettingsPath(),
|
|
116998
|
-
|
|
116999
|
-
...env.CLAUDE_CONFIG_DIR ? [
|
|
117000
|
-
|
|
117001
|
-
|
|
117719
|
+
join65(home, ".claude", "settings.json"),
|
|
117720
|
+
...env.CLAUDE_CONFIG_DIR ? [join65(env.CLAUDE_CONFIG_DIR, "settings.json")] : [],
|
|
117721
|
+
join65(cwd, ".claude", "settings.json"),
|
|
117722
|
+
join65(cwd, ".claude", "settings.local.json")
|
|
117002
117723
|
];
|
|
117003
117724
|
const layers = [];
|
|
117004
117725
|
for (const file of files) {
|
|
@@ -117030,7 +117751,7 @@ function readSettingsSource(source, readFile) {
|
|
|
117030
117751
|
}
|
|
117031
117752
|
function managedSettingsPath() {
|
|
117032
117753
|
if (isWindows2()) {
|
|
117033
|
-
return
|
|
117754
|
+
return join65(process.env.PROGRAMDATA || "C:\\ProgramData", "ClaudeCode", "managed-settings.json");
|
|
117034
117755
|
}
|
|
117035
117756
|
if (process.platform === "darwin") {
|
|
117036
117757
|
return "/Library/Application Support/ClaudeCode/managed-settings.json";
|
|
@@ -117051,8 +117772,8 @@ function isWindows2() {
|
|
|
117051
117772
|
}
|
|
117052
117773
|
function createStatusLineScript(tokenFilePath) {
|
|
117053
117774
|
const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir3();
|
|
117054
|
-
const claudishDir =
|
|
117055
|
-
const scriptPath =
|
|
117775
|
+
const claudishDir = join65(homeDir, ".claudish");
|
|
117776
|
+
const scriptPath = join65(claudishDir, `status-${process.pid}-${Date.now()}.js`);
|
|
117056
117777
|
const escapedTokenPath = tokenFilePath.replace(/\\/g, "\\\\");
|
|
117057
117778
|
const light = getThemeMode() === "light";
|
|
117058
117779
|
const cyanCode = light ? "38;2;14;116;144" : "96";
|
|
@@ -117244,7 +117965,7 @@ function cleanupStaleTokenFiles(dir, now = Date.now(), maxAgeMs = STALE_TOKEN_FI
|
|
|
117244
117965
|
if (!name.startsWith("tokens-") || !name.endsWith(".json"))
|
|
117245
117966
|
continue;
|
|
117246
117967
|
scanned++;
|
|
117247
|
-
const full =
|
|
117968
|
+
const full = join65(dir, name);
|
|
117248
117969
|
try {
|
|
117249
117970
|
if (statSync23(full).mtimeMs >= cutoff)
|
|
117250
117971
|
continue;
|
|
@@ -117273,9 +117994,9 @@ function parseSettingsArgSafe(value) {
|
|
|
117273
117994
|
}
|
|
117274
117995
|
function userSettingsFileCandidates(cwd) {
|
|
117275
117996
|
return [
|
|
117276
|
-
|
|
117277
|
-
|
|
117278
|
-
|
|
117997
|
+
join65(homedir42(), ".claude", "settings.json"),
|
|
117998
|
+
join65(cwd, ".claude", "settings.json"),
|
|
117999
|
+
join65(cwd, ".claude", "settings.local.json")
|
|
117279
118000
|
];
|
|
117280
118001
|
}
|
|
117281
118002
|
function discoverUserStatusLineCommand(claudeArgs = [], cwd = process.cwd()) {
|
|
@@ -117316,11 +118037,11 @@ function buildChainedStatusCommand(userCommand, claudishBody, claudishSegment) {
|
|
|
117316
118037
|
}
|
|
117317
118038
|
function createTempSettingsFile(_modelDisplay, port, proxyAuthMode, userStatusLineCommand) {
|
|
117318
118039
|
const homeDir = process.env.HOME || process.env.USERPROFILE || tmpdir3();
|
|
117319
|
-
const claudishDir =
|
|
118040
|
+
const claudishDir = join65(homeDir, ".claudish");
|
|
117320
118041
|
try {
|
|
117321
118042
|
mkdirSync28(claudishDir, { recursive: true });
|
|
117322
118043
|
} catch {}
|
|
117323
|
-
const tempPath =
|
|
118044
|
+
const tempPath = join65(claudishDir, `settings-${process.pid}-${Date.now()}.json`);
|
|
117324
118045
|
const assignedFile = assignedTokenFile(process.env) !== null;
|
|
117325
118046
|
const tokenFilePath = resolveTokenFilePath(port);
|
|
117326
118047
|
cleanupStaleTokenFiles(claudishDir);
|
|
@@ -117711,7 +118432,7 @@ async function runClaudeWithProxy(config, proxyUrl, onCleanup) {
|
|
|
117711
118432
|
console.error(`
|
|
117712
118433
|
Or set CLAUDE_PATH to your custom installation:`);
|
|
117713
118434
|
const home = homedir42();
|
|
117714
|
-
const localPath = isWindows2() ?
|
|
118435
|
+
const localPath = isWindows2() ? join65(home, ".claude", "local", "claude.exe") : join65(home, ".claude", "local", "claude");
|
|
117715
118436
|
console.error(` export CLAUDE_PATH=${localPath}`);
|
|
117716
118437
|
process.exit(1);
|
|
117717
118438
|
}
|
|
@@ -117746,7 +118467,7 @@ Or set CLAUDE_PATH to your custom installation:`);
|
|
|
117746
118467
|
let driverLaunch = null;
|
|
117747
118468
|
if (surfaces.driver) {
|
|
117748
118469
|
const minted = sessionFlags.sessionId || sessionFlags.resumes ? null : randomUUID11();
|
|
117749
|
-
const turnDir = mkdtempSync4(
|
|
118470
|
+
const turnDir = mkdtempSync4(join65(tmpdir3(), "claudish-turns-"));
|
|
117750
118471
|
chmodSync5(turnDir, 448);
|
|
117751
118472
|
claudeArgs.unshift(...driverLeadingArgs({ mintedSessionId: minted, turnDir }));
|
|
117752
118473
|
const sink = new InProcessSink;
|
|
@@ -117895,15 +118616,15 @@ async function findClaudeBinary() {
|
|
|
117895
118616
|
}
|
|
117896
118617
|
}
|
|
117897
118618
|
const home = homedir42();
|
|
117898
|
-
const localPath = isWindows ?
|
|
118619
|
+
const localPath = isWindows ? join65(home, ".claude", "local", "claude.exe") : join65(home, ".claude", "local", "claude");
|
|
117899
118620
|
if (existsSync46(localPath)) {
|
|
117900
118621
|
return localPath;
|
|
117901
118622
|
}
|
|
117902
118623
|
if (isWindows) {
|
|
117903
118624
|
const windowsPaths = [
|
|
117904
|
-
|
|
117905
|
-
|
|
117906
|
-
|
|
118625
|
+
join65(home, "AppData", "Roaming", "npm", "claude.cmd"),
|
|
118626
|
+
join65(home, ".npm-global", "claude.cmd"),
|
|
118627
|
+
join65(home, "node_modules", ".bin", "claude.cmd")
|
|
117907
118628
|
];
|
|
117908
118629
|
for (const path of windowsPaths) {
|
|
117909
118630
|
if (existsSync46(path)) {
|
|
@@ -117914,11 +118635,11 @@ async function findClaudeBinary() {
|
|
|
117914
118635
|
const commonPaths = [
|
|
117915
118636
|
"/usr/local/bin/claude",
|
|
117916
118637
|
"/opt/homebrew/bin/claude",
|
|
117917
|
-
|
|
117918
|
-
|
|
117919
|
-
|
|
118638
|
+
join65(home, ".npm-global/bin/claude"),
|
|
118639
|
+
join65(home, ".local/bin/claude"),
|
|
118640
|
+
join65(home, "node_modules/.bin/claude"),
|
|
117920
118641
|
"/data/data/com.termux/files/usr/bin/claude",
|
|
117921
|
-
|
|
118642
|
+
join65(home, "../usr/bin/claude")
|
|
117922
118643
|
];
|
|
117923
118644
|
for (const path of commonPaths) {
|
|
117924
118645
|
if (existsSync46(path)) {
|
|
@@ -118027,16 +118748,16 @@ var init_claude_runner = __esm(() => {
|
|
|
118027
118748
|
// src/diag-output.ts
|
|
118028
118749
|
import { createWriteStream as createWriteStream2, mkdirSync as mkdirSync29, unlinkSync as unlinkSync14, writeFileSync as writeFileSync28 } from "fs";
|
|
118029
118750
|
import { homedir as homedir43 } from "os";
|
|
118030
|
-
import { join as
|
|
118751
|
+
import { join as join66 } from "path";
|
|
118031
118752
|
function getClaudishDir() {
|
|
118032
|
-
const dir =
|
|
118753
|
+
const dir = join66(homedir43(), ".claudish");
|
|
118033
118754
|
try {
|
|
118034
118755
|
mkdirSync29(dir, { recursive: true });
|
|
118035
118756
|
} catch {}
|
|
118036
118757
|
return dir;
|
|
118037
118758
|
}
|
|
118038
118759
|
function getDiagLogPath() {
|
|
118039
|
-
return
|
|
118760
|
+
return join66(getClaudishDir(), `diag-${process.pid}.log`);
|
|
118040
118761
|
}
|
|
118041
118762
|
|
|
118042
118763
|
class LogFileDiagOutput {
|
|
@@ -120698,7 +121419,7 @@ init_op_source();
|
|
|
120698
121419
|
init_startup_trace();
|
|
120699
121420
|
init_picker_cancelled();
|
|
120700
121421
|
import { existsSync as existsSync47, readFileSync as readFileSync53 } from "fs";
|
|
120701
|
-
import { join as
|
|
121422
|
+
import { join as join67, resolve as resolve12 } from "path";
|
|
120702
121423
|
|
|
120703
121424
|
// src/tui/runtime/should-open-picker.ts
|
|
120704
121425
|
function noModelConfigured(gate) {
|
|
@@ -120715,7 +121436,7 @@ function requiresExplicitModel(gate, tty) {
|
|
|
120715
121436
|
var autoloadDropped = [...droppedAtLaunch, ...takeLauncherDropped(process.env)];
|
|
120716
121437
|
var dotenvText;
|
|
120717
121438
|
try {
|
|
120718
|
-
dotenvText = readFileSync53(
|
|
121439
|
+
dotenvText = readFileSync53(join67(process.cwd(), ".env"));
|
|
120719
121440
|
} catch {}
|
|
120720
121441
|
if (dotenvText !== undefined) {
|
|
120721
121442
|
await Promise.resolve().then(() => init_dotenv_allowlist());
|
|
@@ -120830,7 +121551,7 @@ async function applyOpImport() {
|
|
|
120830
121551
|
async function applyConfigOverride() {
|
|
120831
121552
|
await Promise.resolve();
|
|
120832
121553
|
const plan = planConfigOverride(process.argv.slice(2), process.env, {
|
|
120833
|
-
resolve:
|
|
121554
|
+
resolve: resolve12,
|
|
120834
121555
|
exists: existsSync47
|
|
120835
121556
|
});
|
|
120836
121557
|
if (plan.kind === "none")
|
|
@@ -121063,7 +121784,7 @@ async function runCli() {
|
|
|
121063
121784
|
process.exit(1);
|
|
121064
121785
|
}
|
|
121065
121786
|
const mode = cliConfig.teamMode ?? "default";
|
|
121066
|
-
const sessionPath =
|
|
121787
|
+
const sessionPath = join67(process.cwd(), `.claudish-team-${Date.now()}`);
|
|
121067
121788
|
if (mode === "json") {
|
|
121068
121789
|
await Promise.resolve().then(() => init_pane());
|
|
121069
121790
|
await Promise.resolve().then(() => init_team_orchestrator());
|
|
@@ -121083,7 +121804,7 @@ async function runCli() {
|
|
|
121083
121804
|
const status = await runModels(sessionPath);
|
|
121084
121805
|
const result = { ...status, responses: {} };
|
|
121085
121806
|
for (const anonId of Object.keys(status.models)) {
|
|
121086
|
-
const responsePath =
|
|
121807
|
+
const responsePath = join67(sessionPath, `response-${anonId}.md`);
|
|
121087
121808
|
try {
|
|
121088
121809
|
const raw = readFileSync53(responsePath, "utf-8").trim();
|
|
121089
121810
|
try {
|