Spaces:
Sleeping
Sleeping
File size: 4,097 Bytes
476094d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | #!/usr/bin/env node
import fs from "node:fs/promises";
import path from "node:path";
const DEFAULT_TOKENS_DIR = path.resolve(process.cwd(), "acc_pool");
function parseArgs(argv) {
const args = {};
for (const part of argv) {
if (!part.startsWith("--")) continue;
const raw = part.slice(2);
const idx = raw.indexOf("=");
if (idx === -1) {
args[raw] = "true";
continue;
}
args[raw.slice(0, idx)] = raw.slice(idx + 1);
}
return args;
}
function nowStamp() {
const date = new Date();
const pad = (value) => String(value).padStart(2, "0");
return [
date.getFullYear(),
pad(date.getMonth() + 1),
pad(date.getDate()),
"-",
pad(date.getHours()),
pad(date.getMinutes()),
pad(date.getSeconds()),
].join("");
}
function normalizeEntries(raw) {
if (Array.isArray(raw)) {
return raw.filter((entry) => entry && typeof entry === "object");
}
if (raw && typeof raw === "object") {
return [raw];
}
return [];
}
function getTokenSource(entry) {
return entry?.tokens && typeof entry.tokens === "object" ? entry.tokens : entry;
}
function isUsableCodexEntry(entry) {
const tokenSource = getTokenSource(entry);
return Boolean(
(entry.type || "codex") === "codex" &&
tokenSource.access_token &&
tokenSource.account_id &&
tokenSource.id_token &&
tokenSource.refresh_token,
);
}
function normalizeCodexEntry(entry) {
const tokenSource = getTokenSource(entry);
return {
OPENAI_API_KEY: entry.OPENAI_API_KEY || "",
auth_mode: entry.auth_mode || "chatgpt",
type: entry.type || "codex",
disabled: Boolean(entry.disabled),
email: entry.email || "",
name: entry.name || "",
last_refresh: entry.last_refresh || new Date().toISOString(),
expired: entry.expired || null,
tokens: {
access_token: tokenSource.access_token || "",
account_id: tokenSource.account_id || "",
id_token: tokenSource.id_token || "",
refresh_token: tokenSource.refresh_token || "",
},
};
}
export async function migrateCodexAccountPool({ tokensDir = DEFAULT_TOKENS_DIR } = {}) {
const dirEntries = await fs.readdir(tokensDir, { withFileTypes: true });
const files = dirEntries
.filter((entry) => entry.isFile() && entry.name.endsWith(".json") && entry.name !== "pool.json")
.map((entry) => path.join(tokensDir, entry.name))
.sort((left, right) => left.localeCompare(right));
const merged = [];
const seen = new Set();
for (const filePath of files) {
let raw;
try {
raw = JSON.parse(await fs.readFile(filePath, "utf8"));
} catch {
continue;
}
for (const entry of normalizeEntries(raw)) {
if (!isUsableCodexEntry(entry)) continue;
const normalized = normalizeCodexEntry(entry);
const accountId = normalized.tokens.account_id;
if (!accountId || seen.has(accountId)) continue;
seen.add(accountId);
merged.push(normalized);
}
}
const poolPath = path.join(tokensDir, "pool.json");
await fs.writeFile(poolPath, `${JSON.stringify(merged, null, 2)}\n`, "utf8");
let backupDir = null;
if (files.length > 0) {
backupDir = path.join(tokensDir, "_backup", nowStamp());
await fs.mkdir(backupDir, { recursive: true });
for (const filePath of files) {
await fs.rename(filePath, path.join(backupDir, path.basename(filePath)));
}
}
return {
poolPath,
backupDir,
count: merged.length,
};
}
async function main() {
const args = parseArgs(process.argv.slice(2));
const tokensDir = path.resolve(args["tokens-dir"] || DEFAULT_TOKENS_DIR);
const result = await migrateCodexAccountPool({ tokensDir });
console.log(`Pool written: ${result.poolPath}`);
console.log(`Accounts merged: ${result.count}`);
console.log(`Backup dir: ${result.backupDir || "(none)"}`);
}
const directRun =
process.argv[1] && path.resolve(process.argv[1]) === path.resolve(new URL(import.meta.url).pathname);
if (directRun) {
main().catch((error) => {
console.error(error?.message || error);
process.exitCode = 1;
});
}
|