codex-team 0.0.6 → 0.0.7
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/cli.cjs +64 -1
- package/dist/main.cjs +64 -1
- package/dist/main.js +64 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -13,7 +13,7 @@ var __webpack_modules__ = {
|
|
|
13
13
|
const utc_js_namespaceObject = require("dayjs/plugin/utc.js");
|
|
14
14
|
var utc_js_default = /*#__PURE__*/ __webpack_require__.n(utc_js_namespaceObject);
|
|
15
15
|
var package_namespaceObject = {
|
|
16
|
-
rE: "0.0.
|
|
16
|
+
rE: "0.0.7"
|
|
17
17
|
};
|
|
18
18
|
const external_node_crypto_namespaceObject = require("node:crypto");
|
|
19
19
|
const promises_namespaceObject = require("node:fs/promises");
|
|
@@ -456,6 +456,7 @@ var __webpack_modules__ = {
|
|
|
456
456
|
codexDir,
|
|
457
457
|
codexTeamDir,
|
|
458
458
|
currentAuthPath: (0, external_node_path_namespaceObject.join)(codexDir, "auth.json"),
|
|
459
|
+
currentConfigPath: (0, external_node_path_namespaceObject.join)(codexDir, "config.toml"),
|
|
459
460
|
accountsDir: (0, external_node_path_namespaceObject.join)(codexTeamDir, "accounts"),
|
|
460
461
|
backupsDir: (0, external_node_path_namespaceObject.join)(codexTeamDir, "backups"),
|
|
461
462
|
statePath: (0, external_node_path_namespaceObject.join)(codexTeamDir, "state.json")
|
|
@@ -548,12 +549,44 @@ var __webpack_modules__ = {
|
|
|
548
549
|
accountMetaPath(name) {
|
|
549
550
|
return (0, external_node_path_namespaceObject.join)(this.accountDirectory(name), "meta.json");
|
|
550
551
|
}
|
|
552
|
+
accountConfigPath(name) {
|
|
553
|
+
return (0, external_node_path_namespaceObject.join)(this.accountDirectory(name), "config.toml");
|
|
554
|
+
}
|
|
551
555
|
async writeAccountAuthSnapshot(name, snapshot) {
|
|
552
556
|
await atomicWriteFile(this.accountAuthPath(name), stringifyJson(snapshot));
|
|
553
557
|
}
|
|
554
558
|
async writeAccountMeta(name, meta) {
|
|
555
559
|
await atomicWriteFile(this.accountMetaPath(name), stringifyJson(meta));
|
|
556
560
|
}
|
|
561
|
+
validateConfigSnapshot(name, snapshot, rawConfig) {
|
|
562
|
+
if ("apikey" !== snapshot.auth_mode) return;
|
|
563
|
+
if (!rawConfig) throw new Error(`Current ~/.codex/config.toml is required to save apikey account "${name}".`);
|
|
564
|
+
if (!/^\s*model_provider\s*=\s*["'][^"']+["']/mu.test(rawConfig)) throw new Error(`Current ~/.codex/config.toml is missing model_provider for apikey account "${name}".`);
|
|
565
|
+
if (!/^\s*base_url\s*=\s*["'][^"']+["']/mu.test(rawConfig)) throw new Error(`Current ~/.codex/config.toml is missing base_url for apikey account "${name}".`);
|
|
566
|
+
}
|
|
567
|
+
sanitizeConfigForAccountAuth(rawConfig) {
|
|
568
|
+
const lines = rawConfig.split(/\r?\n/u);
|
|
569
|
+
const result = [];
|
|
570
|
+
let skippingProviderSection = false;
|
|
571
|
+
for (const line of lines){
|
|
572
|
+
const trimmed = line.trim();
|
|
573
|
+
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
574
|
+
skippingProviderSection = /^\[model_providers\.[^\]]+\]$/u.test(trimmed);
|
|
575
|
+
if (skippingProviderSection) continue;
|
|
576
|
+
}
|
|
577
|
+
if (!skippingProviderSection) {
|
|
578
|
+
if (!/^\s*model_provider\s*=/u.test(line)) {
|
|
579
|
+
if (!/^\s*preferred_auth_method\s*=\s*["']apikey["']\s*$/u.test(line)) result.push(line);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
return `${result.join("\n").replace(/\n{3,}/gu, "\n\n").trimEnd()}\n`;
|
|
584
|
+
}
|
|
585
|
+
async ensureEmptyAccountConfigSnapshot(name) {
|
|
586
|
+
const configPath = this.accountConfigPath(name);
|
|
587
|
+
await atomicWriteFile(configPath, "");
|
|
588
|
+
return configPath;
|
|
589
|
+
}
|
|
557
590
|
async syncCurrentAuthIfMatching(snapshot) {
|
|
558
591
|
if (!await pathExists(this.paths.currentAuthPath)) return;
|
|
559
592
|
try {
|
|
@@ -619,6 +652,7 @@ var __webpack_modules__ = {
|
|
|
619
652
|
...meta,
|
|
620
653
|
authPath,
|
|
621
654
|
metaPath,
|
|
655
|
+
configPath: await pathExists(this.accountConfigPath(name)) ? this.accountConfigPath(name) : null,
|
|
622
656
|
duplicateAccountId: false
|
|
623
657
|
};
|
|
624
658
|
}
|
|
@@ -675,14 +709,21 @@ var __webpack_modules__ = {
|
|
|
675
709
|
if (!await pathExists(this.paths.currentAuthPath)) throw new Error("Current ~/.codex/auth.json does not exist.");
|
|
676
710
|
const rawSnapshot = await readJsonFile(this.paths.currentAuthPath);
|
|
677
711
|
const snapshot = parseAuthSnapshot(rawSnapshot);
|
|
712
|
+
const rawConfig = await pathExists(this.paths.currentConfigPath) ? await readJsonFile(this.paths.currentConfigPath) : null;
|
|
678
713
|
const accountDir = this.accountDirectory(name);
|
|
679
714
|
const authPath = this.accountAuthPath(name);
|
|
680
715
|
const metaPath = this.accountMetaPath(name);
|
|
716
|
+
const configPath = this.accountConfigPath(name);
|
|
681
717
|
const accountExists = await pathExists(accountDir);
|
|
682
718
|
const existingMeta = accountExists && await pathExists(metaPath) ? parseSnapshotMeta(await readJsonFile(metaPath)) : void 0;
|
|
683
719
|
if (accountExists && !force) throw new Error(`Account "${name}" already exists. Use --force to overwrite it.`);
|
|
720
|
+
this.validateConfigSnapshot(name, snapshot, rawConfig);
|
|
684
721
|
await ensureDirectory(accountDir, DIRECTORY_MODE);
|
|
685
722
|
await atomicWriteFile(authPath, `${rawSnapshot.trimEnd()}\n`);
|
|
723
|
+
if ("apikey" === snapshot.auth_mode && rawConfig) await atomicWriteFile(configPath, rawConfig.endsWith("\n") ? rawConfig : `${rawConfig}\n`);
|
|
724
|
+
else if (await pathExists(configPath)) await (0, promises_namespaceObject.rm)(configPath, {
|
|
725
|
+
force: true
|
|
726
|
+
});
|
|
686
727
|
const meta = createSnapshotMeta(name, snapshot, new Date(), existingMeta?.created_at);
|
|
687
728
|
meta.last_switched_at = existingMeta?.last_switched_at ?? null;
|
|
688
729
|
meta.quota = existingMeta?.quota ?? meta.quota;
|
|
@@ -698,9 +739,15 @@ var __webpack_modules__ = {
|
|
|
698
739
|
const name = current.matched_accounts[0];
|
|
699
740
|
const currentRawSnapshot = await readJsonFile(this.paths.currentAuthPath);
|
|
700
741
|
const currentSnapshot = parseAuthSnapshot(currentRawSnapshot);
|
|
742
|
+
const currentRawConfig = await pathExists(this.paths.currentConfigPath) ? await readJsonFile(this.paths.currentConfigPath) : null;
|
|
701
743
|
const metaPath = this.accountMetaPath(name);
|
|
702
744
|
const existingMeta = parseSnapshotMeta(await readJsonFile(metaPath));
|
|
745
|
+
this.validateConfigSnapshot(name, currentSnapshot, currentRawConfig);
|
|
703
746
|
await atomicWriteFile(this.accountAuthPath(name), `${currentRawSnapshot.trimEnd()}\n`);
|
|
747
|
+
if ("apikey" === currentSnapshot.auth_mode && currentRawConfig) await atomicWriteFile(this.accountConfigPath(name), currentRawConfig.endsWith("\n") ? currentRawConfig : `${currentRawConfig}\n`);
|
|
748
|
+
else if (await pathExists(this.accountConfigPath(name))) await (0, promises_namespaceObject.rm)(this.accountConfigPath(name), {
|
|
749
|
+
force: true
|
|
750
|
+
});
|
|
704
751
|
await atomicWriteFile(metaPath, stringifyJson({
|
|
705
752
|
...createSnapshotMeta(name, currentSnapshot, new Date(), existingMeta.created_at),
|
|
706
753
|
last_switched_at: existingMeta.last_switched_at,
|
|
@@ -722,8 +769,23 @@ var __webpack_modules__ = {
|
|
|
722
769
|
await (0, promises_namespaceObject.copyFile)(this.paths.currentAuthPath, backupPath);
|
|
723
770
|
await chmodIfPossible(backupPath, FILE_MODE);
|
|
724
771
|
}
|
|
772
|
+
if (await pathExists(this.paths.currentConfigPath)) {
|
|
773
|
+
const configBackupPath = (0, external_node_path_namespaceObject.join)(this.paths.backupsDir, "last-active-config.toml");
|
|
774
|
+
await (0, promises_namespaceObject.copyFile)(this.paths.currentConfigPath, configBackupPath);
|
|
775
|
+
await chmodIfPossible(configBackupPath, FILE_MODE);
|
|
776
|
+
}
|
|
725
777
|
const rawAuth = await readJsonFile(account.authPath);
|
|
726
778
|
await atomicWriteFile(this.paths.currentAuthPath, `${rawAuth.trimEnd()}\n`);
|
|
779
|
+
if ("apikey" === account.auth_mode && account.configPath) {
|
|
780
|
+
const rawConfig = await readJsonFile(account.configPath);
|
|
781
|
+
await atomicWriteFile(this.paths.currentConfigPath, rawConfig.endsWith("\n") ? rawConfig : `${rawConfig}\n`);
|
|
782
|
+
} else if ("apikey" === account.auth_mode) {
|
|
783
|
+
await this.ensureEmptyAccountConfigSnapshot(name);
|
|
784
|
+
warnings.push(`Saved apikey account "${name}" was missing config.toml snapshot. Created an empty snapshot; configure baseUrl manually if needed.`);
|
|
785
|
+
} else if (await pathExists(this.paths.currentConfigPath)) {
|
|
786
|
+
const currentRawConfig = await readJsonFile(this.paths.currentConfigPath);
|
|
787
|
+
await atomicWriteFile(this.paths.currentConfigPath, this.sanitizeConfigForAccountAuth(currentRawConfig));
|
|
788
|
+
}
|
|
727
789
|
const writtenSnapshot = await readAuthSnapshotFile(this.paths.currentAuthPath);
|
|
728
790
|
if (getSnapshotIdentity(writtenSnapshot) !== account.account_id) throw new Error(`Switch verification failed for account "${name}".`);
|
|
729
791
|
const meta = parseSnapshotMeta(await readJsonFile(account.metaPath));
|
|
@@ -881,6 +943,7 @@ var __webpack_modules__ = {
|
|
|
881
943
|
const metaStat = await (0, promises_namespaceObject.stat)(account.metaPath);
|
|
882
944
|
if ((511 & authStat.mode) !== FILE_MODE) issues.push(`Account "${account.name}" auth permissions must be 600.`);
|
|
883
945
|
if ((511 & metaStat.mode) !== FILE_MODE) issues.push(`Account "${account.name}" metadata permissions must be 600.`);
|
|
946
|
+
if ("apikey" === account.auth_mode && !account.configPath) issues.push(`Account "${account.name}" is missing config.toml snapshot required for apikey auth.`);
|
|
884
947
|
if (account.duplicateAccountId) warnings.push(`Account "${account.name}" shares identity ${account.account_id} with another saved account.`);
|
|
885
948
|
}
|
|
886
949
|
let currentAuthPresent = false;
|
package/dist/main.cjs
CHANGED
|
@@ -43,7 +43,7 @@ var timezone_js_default = /*#__PURE__*/ __webpack_require__.n(timezone_js_namesp
|
|
|
43
43
|
const utc_js_namespaceObject = require("dayjs/plugin/utc.js");
|
|
44
44
|
var utc_js_default = /*#__PURE__*/ __webpack_require__.n(utc_js_namespaceObject);
|
|
45
45
|
var package_namespaceObject = {
|
|
46
|
-
rE: "0.0.
|
|
46
|
+
rE: "0.0.7"
|
|
47
47
|
};
|
|
48
48
|
const external_node_crypto_namespaceObject = require("node:crypto");
|
|
49
49
|
const promises_namespaceObject = require("node:fs/promises");
|
|
@@ -486,6 +486,7 @@ function defaultPaths(homeDir = (0, external_node_os_namespaceObject.homedir)())
|
|
|
486
486
|
codexDir,
|
|
487
487
|
codexTeamDir,
|
|
488
488
|
currentAuthPath: (0, external_node_path_namespaceObject.join)(codexDir, "auth.json"),
|
|
489
|
+
currentConfigPath: (0, external_node_path_namespaceObject.join)(codexDir, "config.toml"),
|
|
489
490
|
accountsDir: (0, external_node_path_namespaceObject.join)(codexTeamDir, "accounts"),
|
|
490
491
|
backupsDir: (0, external_node_path_namespaceObject.join)(codexTeamDir, "backups"),
|
|
491
492
|
statePath: (0, external_node_path_namespaceObject.join)(codexTeamDir, "state.json")
|
|
@@ -578,12 +579,44 @@ class AccountStore {
|
|
|
578
579
|
accountMetaPath(name) {
|
|
579
580
|
return (0, external_node_path_namespaceObject.join)(this.accountDirectory(name), "meta.json");
|
|
580
581
|
}
|
|
582
|
+
accountConfigPath(name) {
|
|
583
|
+
return (0, external_node_path_namespaceObject.join)(this.accountDirectory(name), "config.toml");
|
|
584
|
+
}
|
|
581
585
|
async writeAccountAuthSnapshot(name, snapshot) {
|
|
582
586
|
await atomicWriteFile(this.accountAuthPath(name), stringifyJson(snapshot));
|
|
583
587
|
}
|
|
584
588
|
async writeAccountMeta(name, meta) {
|
|
585
589
|
await atomicWriteFile(this.accountMetaPath(name), stringifyJson(meta));
|
|
586
590
|
}
|
|
591
|
+
validateConfigSnapshot(name, snapshot, rawConfig) {
|
|
592
|
+
if ("apikey" !== snapshot.auth_mode) return;
|
|
593
|
+
if (!rawConfig) throw new Error(`Current ~/.codex/config.toml is required to save apikey account "${name}".`);
|
|
594
|
+
if (!/^\s*model_provider\s*=\s*["'][^"']+["']/mu.test(rawConfig)) throw new Error(`Current ~/.codex/config.toml is missing model_provider for apikey account "${name}".`);
|
|
595
|
+
if (!/^\s*base_url\s*=\s*["'][^"']+["']/mu.test(rawConfig)) throw new Error(`Current ~/.codex/config.toml is missing base_url for apikey account "${name}".`);
|
|
596
|
+
}
|
|
597
|
+
sanitizeConfigForAccountAuth(rawConfig) {
|
|
598
|
+
const lines = rawConfig.split(/\r?\n/u);
|
|
599
|
+
const result = [];
|
|
600
|
+
let skippingProviderSection = false;
|
|
601
|
+
for (const line of lines){
|
|
602
|
+
const trimmed = line.trim();
|
|
603
|
+
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
604
|
+
skippingProviderSection = /^\[model_providers\.[^\]]+\]$/u.test(trimmed);
|
|
605
|
+
if (skippingProviderSection) continue;
|
|
606
|
+
}
|
|
607
|
+
if (!skippingProviderSection) {
|
|
608
|
+
if (!/^\s*model_provider\s*=/u.test(line)) {
|
|
609
|
+
if (!/^\s*preferred_auth_method\s*=\s*["']apikey["']\s*$/u.test(line)) result.push(line);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
return `${result.join("\n").replace(/\n{3,}/gu, "\n\n").trimEnd()}\n`;
|
|
614
|
+
}
|
|
615
|
+
async ensureEmptyAccountConfigSnapshot(name) {
|
|
616
|
+
const configPath = this.accountConfigPath(name);
|
|
617
|
+
await atomicWriteFile(configPath, "");
|
|
618
|
+
return configPath;
|
|
619
|
+
}
|
|
587
620
|
async syncCurrentAuthIfMatching(snapshot) {
|
|
588
621
|
if (!await pathExists(this.paths.currentAuthPath)) return;
|
|
589
622
|
try {
|
|
@@ -649,6 +682,7 @@ class AccountStore {
|
|
|
649
682
|
...meta,
|
|
650
683
|
authPath,
|
|
651
684
|
metaPath,
|
|
685
|
+
configPath: await pathExists(this.accountConfigPath(name)) ? this.accountConfigPath(name) : null,
|
|
652
686
|
duplicateAccountId: false
|
|
653
687
|
};
|
|
654
688
|
}
|
|
@@ -705,14 +739,21 @@ class AccountStore {
|
|
|
705
739
|
if (!await pathExists(this.paths.currentAuthPath)) throw new Error("Current ~/.codex/auth.json does not exist.");
|
|
706
740
|
const rawSnapshot = await readJsonFile(this.paths.currentAuthPath);
|
|
707
741
|
const snapshot = parseAuthSnapshot(rawSnapshot);
|
|
742
|
+
const rawConfig = await pathExists(this.paths.currentConfigPath) ? await readJsonFile(this.paths.currentConfigPath) : null;
|
|
708
743
|
const accountDir = this.accountDirectory(name);
|
|
709
744
|
const authPath = this.accountAuthPath(name);
|
|
710
745
|
const metaPath = this.accountMetaPath(name);
|
|
746
|
+
const configPath = this.accountConfigPath(name);
|
|
711
747
|
const accountExists = await pathExists(accountDir);
|
|
712
748
|
const existingMeta = accountExists && await pathExists(metaPath) ? parseSnapshotMeta(await readJsonFile(metaPath)) : void 0;
|
|
713
749
|
if (accountExists && !force) throw new Error(`Account "${name}" already exists. Use --force to overwrite it.`);
|
|
750
|
+
this.validateConfigSnapshot(name, snapshot, rawConfig);
|
|
714
751
|
await ensureDirectory(accountDir, DIRECTORY_MODE);
|
|
715
752
|
await atomicWriteFile(authPath, `${rawSnapshot.trimEnd()}\n`);
|
|
753
|
+
if ("apikey" === snapshot.auth_mode && rawConfig) await atomicWriteFile(configPath, rawConfig.endsWith("\n") ? rawConfig : `${rawConfig}\n`);
|
|
754
|
+
else if (await pathExists(configPath)) await (0, promises_namespaceObject.rm)(configPath, {
|
|
755
|
+
force: true
|
|
756
|
+
});
|
|
716
757
|
const meta = createSnapshotMeta(name, snapshot, new Date(), existingMeta?.created_at);
|
|
717
758
|
meta.last_switched_at = existingMeta?.last_switched_at ?? null;
|
|
718
759
|
meta.quota = existingMeta?.quota ?? meta.quota;
|
|
@@ -728,9 +769,15 @@ class AccountStore {
|
|
|
728
769
|
const name = current.matched_accounts[0];
|
|
729
770
|
const currentRawSnapshot = await readJsonFile(this.paths.currentAuthPath);
|
|
730
771
|
const currentSnapshot = parseAuthSnapshot(currentRawSnapshot);
|
|
772
|
+
const currentRawConfig = await pathExists(this.paths.currentConfigPath) ? await readJsonFile(this.paths.currentConfigPath) : null;
|
|
731
773
|
const metaPath = this.accountMetaPath(name);
|
|
732
774
|
const existingMeta = parseSnapshotMeta(await readJsonFile(metaPath));
|
|
775
|
+
this.validateConfigSnapshot(name, currentSnapshot, currentRawConfig);
|
|
733
776
|
await atomicWriteFile(this.accountAuthPath(name), `${currentRawSnapshot.trimEnd()}\n`);
|
|
777
|
+
if ("apikey" === currentSnapshot.auth_mode && currentRawConfig) await atomicWriteFile(this.accountConfigPath(name), currentRawConfig.endsWith("\n") ? currentRawConfig : `${currentRawConfig}\n`);
|
|
778
|
+
else if (await pathExists(this.accountConfigPath(name))) await (0, promises_namespaceObject.rm)(this.accountConfigPath(name), {
|
|
779
|
+
force: true
|
|
780
|
+
});
|
|
734
781
|
await atomicWriteFile(metaPath, stringifyJson({
|
|
735
782
|
...createSnapshotMeta(name, currentSnapshot, new Date(), existingMeta.created_at),
|
|
736
783
|
last_switched_at: existingMeta.last_switched_at,
|
|
@@ -752,8 +799,23 @@ class AccountStore {
|
|
|
752
799
|
await (0, promises_namespaceObject.copyFile)(this.paths.currentAuthPath, backupPath);
|
|
753
800
|
await chmodIfPossible(backupPath, FILE_MODE);
|
|
754
801
|
}
|
|
802
|
+
if (await pathExists(this.paths.currentConfigPath)) {
|
|
803
|
+
const configBackupPath = (0, external_node_path_namespaceObject.join)(this.paths.backupsDir, "last-active-config.toml");
|
|
804
|
+
await (0, promises_namespaceObject.copyFile)(this.paths.currentConfigPath, configBackupPath);
|
|
805
|
+
await chmodIfPossible(configBackupPath, FILE_MODE);
|
|
806
|
+
}
|
|
755
807
|
const rawAuth = await readJsonFile(account.authPath);
|
|
756
808
|
await atomicWriteFile(this.paths.currentAuthPath, `${rawAuth.trimEnd()}\n`);
|
|
809
|
+
if ("apikey" === account.auth_mode && account.configPath) {
|
|
810
|
+
const rawConfig = await readJsonFile(account.configPath);
|
|
811
|
+
await atomicWriteFile(this.paths.currentConfigPath, rawConfig.endsWith("\n") ? rawConfig : `${rawConfig}\n`);
|
|
812
|
+
} else if ("apikey" === account.auth_mode) {
|
|
813
|
+
await this.ensureEmptyAccountConfigSnapshot(name);
|
|
814
|
+
warnings.push(`Saved apikey account "${name}" was missing config.toml snapshot. Created an empty snapshot; configure baseUrl manually if needed.`);
|
|
815
|
+
} else if (await pathExists(this.paths.currentConfigPath)) {
|
|
816
|
+
const currentRawConfig = await readJsonFile(this.paths.currentConfigPath);
|
|
817
|
+
await atomicWriteFile(this.paths.currentConfigPath, this.sanitizeConfigForAccountAuth(currentRawConfig));
|
|
818
|
+
}
|
|
757
819
|
const writtenSnapshot = await readAuthSnapshotFile(this.paths.currentAuthPath);
|
|
758
820
|
if (getSnapshotIdentity(writtenSnapshot) !== account.account_id) throw new Error(`Switch verification failed for account "${name}".`);
|
|
759
821
|
const meta = parseSnapshotMeta(await readJsonFile(account.metaPath));
|
|
@@ -911,6 +973,7 @@ class AccountStore {
|
|
|
911
973
|
const metaStat = await (0, promises_namespaceObject.stat)(account.metaPath);
|
|
912
974
|
if ((511 & authStat.mode) !== FILE_MODE) issues.push(`Account "${account.name}" auth permissions must be 600.`);
|
|
913
975
|
if ((511 & metaStat.mode) !== FILE_MODE) issues.push(`Account "${account.name}" metadata permissions must be 600.`);
|
|
976
|
+
if ("apikey" === account.auth_mode && !account.configPath) issues.push(`Account "${account.name}" is missing config.toml snapshot required for apikey auth.`);
|
|
914
977
|
if (account.duplicateAccountId) warnings.push(`Account "${account.name}" shares identity ${account.account_id} with another saved account.`);
|
|
915
978
|
}
|
|
916
979
|
let currentAuthPresent = false;
|
package/dist/main.js
CHANGED
|
@@ -9,7 +9,7 @@ import { basename, dirname, join } from "node:path";
|
|
|
9
9
|
import { execFile } from "node:child_process";
|
|
10
10
|
import { promisify } from "node:util";
|
|
11
11
|
var package_namespaceObject = {
|
|
12
|
-
rE: "0.0.
|
|
12
|
+
rE: "0.0.7"
|
|
13
13
|
};
|
|
14
14
|
function isRecord(value) {
|
|
15
15
|
return "object" == typeof value && null !== value && !Array.isArray(value);
|
|
@@ -446,6 +446,7 @@ function defaultPaths(homeDir = homedir()) {
|
|
|
446
446
|
codexDir,
|
|
447
447
|
codexTeamDir,
|
|
448
448
|
currentAuthPath: join(codexDir, "auth.json"),
|
|
449
|
+
currentConfigPath: join(codexDir, "config.toml"),
|
|
449
450
|
accountsDir: join(codexTeamDir, "accounts"),
|
|
450
451
|
backupsDir: join(codexTeamDir, "backups"),
|
|
451
452
|
statePath: join(codexTeamDir, "state.json")
|
|
@@ -538,12 +539,44 @@ class AccountStore {
|
|
|
538
539
|
accountMetaPath(name) {
|
|
539
540
|
return join(this.accountDirectory(name), "meta.json");
|
|
540
541
|
}
|
|
542
|
+
accountConfigPath(name) {
|
|
543
|
+
return join(this.accountDirectory(name), "config.toml");
|
|
544
|
+
}
|
|
541
545
|
async writeAccountAuthSnapshot(name, snapshot) {
|
|
542
546
|
await atomicWriteFile(this.accountAuthPath(name), stringifyJson(snapshot));
|
|
543
547
|
}
|
|
544
548
|
async writeAccountMeta(name, meta) {
|
|
545
549
|
await atomicWriteFile(this.accountMetaPath(name), stringifyJson(meta));
|
|
546
550
|
}
|
|
551
|
+
validateConfigSnapshot(name, snapshot, rawConfig) {
|
|
552
|
+
if ("apikey" !== snapshot.auth_mode) return;
|
|
553
|
+
if (!rawConfig) throw new Error(`Current ~/.codex/config.toml is required to save apikey account "${name}".`);
|
|
554
|
+
if (!/^\s*model_provider\s*=\s*["'][^"']+["']/mu.test(rawConfig)) throw new Error(`Current ~/.codex/config.toml is missing model_provider for apikey account "${name}".`);
|
|
555
|
+
if (!/^\s*base_url\s*=\s*["'][^"']+["']/mu.test(rawConfig)) throw new Error(`Current ~/.codex/config.toml is missing base_url for apikey account "${name}".`);
|
|
556
|
+
}
|
|
557
|
+
sanitizeConfigForAccountAuth(rawConfig) {
|
|
558
|
+
const lines = rawConfig.split(/\r?\n/u);
|
|
559
|
+
const result = [];
|
|
560
|
+
let skippingProviderSection = false;
|
|
561
|
+
for (const line of lines){
|
|
562
|
+
const trimmed = line.trim();
|
|
563
|
+
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
564
|
+
skippingProviderSection = /^\[model_providers\.[^\]]+\]$/u.test(trimmed);
|
|
565
|
+
if (skippingProviderSection) continue;
|
|
566
|
+
}
|
|
567
|
+
if (!skippingProviderSection) {
|
|
568
|
+
if (!/^\s*model_provider\s*=/u.test(line)) {
|
|
569
|
+
if (!/^\s*preferred_auth_method\s*=\s*["']apikey["']\s*$/u.test(line)) result.push(line);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
return `${result.join("\n").replace(/\n{3,}/gu, "\n\n").trimEnd()}\n`;
|
|
574
|
+
}
|
|
575
|
+
async ensureEmptyAccountConfigSnapshot(name) {
|
|
576
|
+
const configPath = this.accountConfigPath(name);
|
|
577
|
+
await atomicWriteFile(configPath, "");
|
|
578
|
+
return configPath;
|
|
579
|
+
}
|
|
547
580
|
async syncCurrentAuthIfMatching(snapshot) {
|
|
548
581
|
if (!await pathExists(this.paths.currentAuthPath)) return;
|
|
549
582
|
try {
|
|
@@ -609,6 +642,7 @@ class AccountStore {
|
|
|
609
642
|
...meta,
|
|
610
643
|
authPath,
|
|
611
644
|
metaPath,
|
|
645
|
+
configPath: await pathExists(this.accountConfigPath(name)) ? this.accountConfigPath(name) : null,
|
|
612
646
|
duplicateAccountId: false
|
|
613
647
|
};
|
|
614
648
|
}
|
|
@@ -665,14 +699,21 @@ class AccountStore {
|
|
|
665
699
|
if (!await pathExists(this.paths.currentAuthPath)) throw new Error("Current ~/.codex/auth.json does not exist.");
|
|
666
700
|
const rawSnapshot = await readJsonFile(this.paths.currentAuthPath);
|
|
667
701
|
const snapshot = parseAuthSnapshot(rawSnapshot);
|
|
702
|
+
const rawConfig = await pathExists(this.paths.currentConfigPath) ? await readJsonFile(this.paths.currentConfigPath) : null;
|
|
668
703
|
const accountDir = this.accountDirectory(name);
|
|
669
704
|
const authPath = this.accountAuthPath(name);
|
|
670
705
|
const metaPath = this.accountMetaPath(name);
|
|
706
|
+
const configPath = this.accountConfigPath(name);
|
|
671
707
|
const accountExists = await pathExists(accountDir);
|
|
672
708
|
const existingMeta = accountExists && await pathExists(metaPath) ? parseSnapshotMeta(await readJsonFile(metaPath)) : void 0;
|
|
673
709
|
if (accountExists && !force) throw new Error(`Account "${name}" already exists. Use --force to overwrite it.`);
|
|
710
|
+
this.validateConfigSnapshot(name, snapshot, rawConfig);
|
|
674
711
|
await ensureDirectory(accountDir, DIRECTORY_MODE);
|
|
675
712
|
await atomicWriteFile(authPath, `${rawSnapshot.trimEnd()}\n`);
|
|
713
|
+
if ("apikey" === snapshot.auth_mode && rawConfig) await atomicWriteFile(configPath, rawConfig.endsWith("\n") ? rawConfig : `${rawConfig}\n`);
|
|
714
|
+
else if (await pathExists(configPath)) await rm(configPath, {
|
|
715
|
+
force: true
|
|
716
|
+
});
|
|
676
717
|
const meta = createSnapshotMeta(name, snapshot, new Date(), existingMeta?.created_at);
|
|
677
718
|
meta.last_switched_at = existingMeta?.last_switched_at ?? null;
|
|
678
719
|
meta.quota = existingMeta?.quota ?? meta.quota;
|
|
@@ -688,9 +729,15 @@ class AccountStore {
|
|
|
688
729
|
const name = current.matched_accounts[0];
|
|
689
730
|
const currentRawSnapshot = await readJsonFile(this.paths.currentAuthPath);
|
|
690
731
|
const currentSnapshot = parseAuthSnapshot(currentRawSnapshot);
|
|
732
|
+
const currentRawConfig = await pathExists(this.paths.currentConfigPath) ? await readJsonFile(this.paths.currentConfigPath) : null;
|
|
691
733
|
const metaPath = this.accountMetaPath(name);
|
|
692
734
|
const existingMeta = parseSnapshotMeta(await readJsonFile(metaPath));
|
|
735
|
+
this.validateConfigSnapshot(name, currentSnapshot, currentRawConfig);
|
|
693
736
|
await atomicWriteFile(this.accountAuthPath(name), `${currentRawSnapshot.trimEnd()}\n`);
|
|
737
|
+
if ("apikey" === currentSnapshot.auth_mode && currentRawConfig) await atomicWriteFile(this.accountConfigPath(name), currentRawConfig.endsWith("\n") ? currentRawConfig : `${currentRawConfig}\n`);
|
|
738
|
+
else if (await pathExists(this.accountConfigPath(name))) await rm(this.accountConfigPath(name), {
|
|
739
|
+
force: true
|
|
740
|
+
});
|
|
694
741
|
await atomicWriteFile(metaPath, stringifyJson({
|
|
695
742
|
...createSnapshotMeta(name, currentSnapshot, new Date(), existingMeta.created_at),
|
|
696
743
|
last_switched_at: existingMeta.last_switched_at,
|
|
@@ -712,8 +759,23 @@ class AccountStore {
|
|
|
712
759
|
await copyFile(this.paths.currentAuthPath, backupPath);
|
|
713
760
|
await chmodIfPossible(backupPath, FILE_MODE);
|
|
714
761
|
}
|
|
762
|
+
if (await pathExists(this.paths.currentConfigPath)) {
|
|
763
|
+
const configBackupPath = join(this.paths.backupsDir, "last-active-config.toml");
|
|
764
|
+
await copyFile(this.paths.currentConfigPath, configBackupPath);
|
|
765
|
+
await chmodIfPossible(configBackupPath, FILE_MODE);
|
|
766
|
+
}
|
|
715
767
|
const rawAuth = await readJsonFile(account.authPath);
|
|
716
768
|
await atomicWriteFile(this.paths.currentAuthPath, `${rawAuth.trimEnd()}\n`);
|
|
769
|
+
if ("apikey" === account.auth_mode && account.configPath) {
|
|
770
|
+
const rawConfig = await readJsonFile(account.configPath);
|
|
771
|
+
await atomicWriteFile(this.paths.currentConfigPath, rawConfig.endsWith("\n") ? rawConfig : `${rawConfig}\n`);
|
|
772
|
+
} else if ("apikey" === account.auth_mode) {
|
|
773
|
+
await this.ensureEmptyAccountConfigSnapshot(name);
|
|
774
|
+
warnings.push(`Saved apikey account "${name}" was missing config.toml snapshot. Created an empty snapshot; configure baseUrl manually if needed.`);
|
|
775
|
+
} else if (await pathExists(this.paths.currentConfigPath)) {
|
|
776
|
+
const currentRawConfig = await readJsonFile(this.paths.currentConfigPath);
|
|
777
|
+
await atomicWriteFile(this.paths.currentConfigPath, this.sanitizeConfigForAccountAuth(currentRawConfig));
|
|
778
|
+
}
|
|
717
779
|
const writtenSnapshot = await readAuthSnapshotFile(this.paths.currentAuthPath);
|
|
718
780
|
if (getSnapshotIdentity(writtenSnapshot) !== account.account_id) throw new Error(`Switch verification failed for account "${name}".`);
|
|
719
781
|
const meta = parseSnapshotMeta(await readJsonFile(account.metaPath));
|
|
@@ -871,6 +933,7 @@ class AccountStore {
|
|
|
871
933
|
const metaStat = await stat(account.metaPath);
|
|
872
934
|
if ((511 & authStat.mode) !== FILE_MODE) issues.push(`Account "${account.name}" auth permissions must be 600.`);
|
|
873
935
|
if ((511 & metaStat.mode) !== FILE_MODE) issues.push(`Account "${account.name}" metadata permissions must be 600.`);
|
|
936
|
+
if ("apikey" === account.auth_mode && !account.configPath) issues.push(`Account "${account.name}" is missing config.toml snapshot required for apikey auth.`);
|
|
874
937
|
if (account.duplicateAccountId) warnings.push(`Account "${account.name}" shares identity ${account.account_id} with another saved account.`);
|
|
875
938
|
}
|
|
876
939
|
let currentAuthPresent = false;
|