codexuse-cli 3.1.4 → 3.5.6

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 CHANGED
@@ -2021,450 +2021,147 @@ var require_toml = __commonJS({
2021
2021
  }
2022
2022
  });
2023
2023
 
2024
- // src/commands/daemon.ts
2025
- var import_node_child_process2 = require("child_process");
2026
- var import_node_crypto = require("crypto");
2027
- var import_node_fs = __toESM(require("fs"));
2028
- var import_node_net = __toESM(require("net"));
2029
- var import_node_os = __toESM(require("os"));
2030
- var import_node_path2 = __toESM(require("path"));
2024
+ // src/commands/accountPool.ts
2025
+ var import_node_crypto4 = require("crypto");
2026
+ var import_node_os3 = __toESM(require("os"));
2027
+ var import_node_path7 = __toESM(require("path"));
2031
2028
 
2032
- // ../../packages/shared/src/core/internal-js-runtime.ts
2033
- var import_node_child_process = require("child_process");
2034
- var import_node_path = __toESM(require("path"), 1);
2035
- var cachedBunBinary = null;
2036
- function hasBunRuntime() {
2037
- return typeof process.versions.bun === "string" && process.versions.bun.length > 0;
2038
- }
2039
- function buildRuntimePath(env) {
2040
- const homeDir = env.HOME ?? env.USERPROFILE ?? "";
2041
- const pathHints = (env.CODEX_PATH_HINTS ?? "").split(import_node_path.default.delimiter).map((entry) => entry.trim()).filter(Boolean);
2042
- const entries = [
2043
- env.PATH ?? "",
2044
- import_node_path.default.join(homeDir, ".bun", "bin"),
2045
- "/opt/homebrew/bin",
2046
- "/usr/local/bin",
2047
- import_node_path.default.join(homeDir, ".local", "bin"),
2048
- import_node_path.default.join(homeDir, ".fnm", "aliases", "default", "bin"),
2049
- import_node_path.default.join(homeDir, ".fnm", "current", "bin"),
2050
- ...pathHints
2051
- ];
2052
- return Array.from(
2053
- new Set(
2054
- entries.flatMap((entry) => entry.split(import_node_path.default.delimiter)).map((entry) => entry.trim()).filter(Boolean)
2055
- )
2056
- ).join(import_node_path.default.delimiter);
2057
- }
2058
- function resolveConfiguredBunBinary(env) {
2059
- const candidates = [
2060
- env.CODEXUSE_INTERNAL_BUN_BIN,
2061
- env.BUN_BIN,
2062
- process.env.CODEXUSE_INTERNAL_BUN_BIN,
2063
- process.env.BUN_BIN
2064
- ];
2065
- for (const candidate of candidates) {
2066
- const normalized = candidate?.trim() ?? "";
2067
- if (normalized.length > 0) {
2068
- return normalized;
2069
- }
2029
+ // ../../packages/contracts/src/settings/types.ts
2030
+ var DEFAULT_ACCOUNT_POOL_EXPOSED_MODELS = [
2031
+ "gpt-5.4",
2032
+ "gpt-5.4-mini",
2033
+ "gpt-5.4-nano"
2034
+ ];
2035
+ var DEFAULT_ACCOUNT_POOL_EXPOSED_REASONING_EFFORTS = [
2036
+ "none",
2037
+ "minimal",
2038
+ "low",
2039
+ "medium",
2040
+ "high",
2041
+ "xhigh"
2042
+ ];
2043
+ var DEFAULT_ACCOUNT_POOL_EXPOSED_FAST_MODE_REASONING_EFFORTS = [
2044
+ "xhigh"
2045
+ ];
2046
+
2047
+ // ../../packages/runtime-app-state/src/app/state.ts
2048
+ var import_node_fs2 = require("fs");
2049
+ var import_node_async_hooks = require("async_hooks");
2050
+ var import_node_path2 = __toESM(require("path"), 1);
2051
+ var import_node_os = __toESM(require("os"), 1);
2052
+
2053
+ // ../../packages/contracts/src/settings/chat-scrollback.ts
2054
+ var CHAT_SCROLLBACK_DEFAULT = 200;
2055
+ var CHAT_SCROLLBACK_MIN = 50;
2056
+ var CHAT_SCROLLBACK_MAX = 5e3;
2057
+ function clampChatScrollbackItems(value) {
2058
+ if (!Number.isFinite(value)) {
2059
+ return CHAT_SCROLLBACK_DEFAULT;
2070
2060
  }
2071
- return null;
2061
+ const rounded = Math.round(value);
2062
+ return Math.max(CHAT_SCROLLBACK_MIN, Math.min(CHAT_SCROLLBACK_MAX, rounded));
2072
2063
  }
2073
- function probeBunBinary(env) {
2074
- if (hasBunRuntime()) {
2075
- return process.execPath;
2064
+ function normalizeChatHistoryScrollbackItems(value) {
2065
+ if (value === null) {
2066
+ return null;
2076
2067
  }
2077
- if (cachedBunBinary) {
2078
- return cachedBunBinary;
2068
+ if (typeof value === "number" && Number.isFinite(value)) {
2069
+ return clampChatScrollbackItems(value);
2079
2070
  }
2080
- const homeDir = env.HOME ?? env.USERPROFILE ?? "";
2081
- const candidates = Array.from(
2082
- new Set(
2083
- [
2084
- env.CODEXUSE_INTERNAL_BUN_BIN?.trim(),
2085
- env.BUN_BIN?.trim(),
2086
- import_node_path.default.join(homeDir, ".bun", "bin", "bun"),
2087
- "/opt/homebrew/bin/bun",
2088
- "/usr/local/bin/bun",
2089
- "bun"
2090
- ].filter((candidate) => typeof candidate === "string" && candidate.length > 0)
2091
- )
2092
- );
2093
- for (const candidate of candidates) {
2094
- const probe = (0, import_node_child_process.spawnSync)(candidate, ["--version"], {
2095
- env,
2096
- stdio: "ignore"
2097
- });
2098
- if (!probe.error && probe.status === 0) {
2099
- cachedBunBinary = candidate;
2100
- return candidate;
2071
+ if (typeof value === "string") {
2072
+ const parsed = Number(value);
2073
+ if (Number.isFinite(parsed)) {
2074
+ return clampChatScrollbackItems(parsed);
2101
2075
  }
2102
2076
  }
2103
- return null;
2077
+ return CHAT_SCROLLBACK_DEFAULT;
2104
2078
  }
2105
- function resolveInternalBunRuntime(overrides = {}) {
2106
- const env = {
2107
- ...process.env,
2108
- ...overrides
2109
- };
2110
- env.PATH = buildRuntimePath(env);
2111
- const runtimeBin = hasBunRuntime() ? process.execPath : resolveConfiguredBunBinary(env) ?? probeBunBinary(env);
2112
- if (!runtimeBin) {
2113
- throw new Error(
2114
- "CodexUse desktop requires a Bun runtime to launch internal services. Install Bun or set CODEXUSE_INTERNAL_BUN_BIN."
2115
- );
2079
+
2080
+ // ../../packages/contracts/src/settings/commit-message-prompt.ts
2081
+ var DEFAULT_COMMIT_MESSAGE_PROMPT = `Generate a concise git commit message for the following changes. Follow conventional commit format (e.g., feat:, fix:, refactor:, docs:, etc.). Keep the summary line under 72 characters. Only output the commit message, nothing else.
2082
+
2083
+ Changes:
2084
+ {diff}`;
2085
+ function normalizeLineEndings(value) {
2086
+ return value.replace(/\r\n/g, "\n");
2087
+ }
2088
+ function normalizeCommitMessagePrompt(value) {
2089
+ if (typeof value !== "string") {
2090
+ return null;
2116
2091
  }
2117
- delete env.ELECTRON_RUN_AS_NODE;
2118
- return {
2119
- bin: runtimeBin,
2120
- env
2121
- };
2092
+ const normalized = normalizeLineEndings(value).trim();
2093
+ return normalized.length > 0 ? normalized : null;
2122
2094
  }
2123
2095
 
2124
- // src/commands/daemon.ts
2125
- function normalizeTelegramBotToken(value) {
2126
- const trimmed = value?.trim() ?? "";
2127
- return trimmed.length > 0 ? trimmed : null;
2128
- }
2129
- function hasFlag(args, flag) {
2130
- return args.includes(flag);
2096
+ // ../../packages/runtime-app-state/src/storage/documents.ts
2097
+ var import_node_fs = require("fs");
2098
+ var import_node_path = __toESM(require("path"), 1);
2099
+ var APP_STORAGE_TABLE = "app_storage_documents";
2100
+ var APP_STORAGE_DB_DIR = "t3-projects";
2101
+ var APP_STORAGE_DB_NAME = "state.sqlite";
2102
+ var SQLITE_BUSY_TIMEOUT_MS = 5e3;
2103
+ var SQLITE_BUSY_MAX_ATTEMPTS = 3;
2104
+ var SQLITE_BUSY_RETRY_DELAY_MS = 100;
2105
+ var initializedDbPaths = /* @__PURE__ */ new Set();
2106
+ function isRecord(value) {
2107
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
2131
2108
  }
2132
- function stripFlags(args) {
2133
- return args.filter((arg) => !arg.startsWith("-"));
2109
+ function clone(value) {
2110
+ return JSON.parse(JSON.stringify(value));
2134
2111
  }
2135
- function parseStringFlag(flags, name) {
2136
- const explicit = flags.find((flag) => flag.startsWith(`${name}=`));
2137
- if (!explicit) {
2112
+ function safeParseJson(raw) {
2113
+ if (typeof raw !== "string" || raw.trim().length === 0) {
2114
+ return null;
2115
+ }
2116
+ try {
2117
+ return JSON.parse(raw);
2118
+ } catch {
2138
2119
  return null;
2139
2120
  }
2140
- const value = explicit.slice(`${name}=`.length).trim();
2141
- return value.length > 0 ? value : "";
2142
2121
  }
2143
- function printDaemonHelp(version) {
2144
- console.log(`CodexUse CLI v${version}
2145
-
2146
- Usage:
2147
- codexuse daemon start [--telegram-bot-token=<token>] [--project-path=/abs/path]
2148
-
2149
- Notes:
2150
- - Runs the T3 Projects/chat server in headless mode.
2151
- - Requires Bun on PATH because the bundled Projects server runs on Bun.
2152
- - Pass --telegram-bot-token or set CODEXUSE_TELEGRAM_BOT_TOKEN to enable Telegram remote control.
2153
- - --project-path bootstraps that folder as the default project/thread.
2154
- - Stop with Ctrl+C or SIGTERM.
2155
- `);
2122
+ function sleep(ms) {
2123
+ return new Promise((resolve) => setTimeout(resolve, ms));
2156
2124
  }
2157
- function resolveServerEntry() {
2158
- const candidates = [
2159
- import_node_path2.default.resolve(__dirname, "server", "index.mjs"),
2160
- import_node_path2.default.resolve(__dirname, "../../../server/dist/index.mjs")
2161
- ];
2162
- for (const candidate of candidates) {
2163
- if (import_node_fs.default.existsSync(candidate)) {
2164
- return candidate;
2165
- }
2125
+ function isSqliteBusyError(error) {
2126
+ if (!error || typeof error !== "object") {
2127
+ return false;
2166
2128
  }
2167
- return candidates[0];
2129
+ const sqliteError = error;
2130
+ const message = typeof sqliteError.message === "string" ? sqliteError.message.toLowerCase() : "";
2131
+ return sqliteError.code === "SQLITE_BUSY" || sqliteError.errno === 5 || message.includes("sqlite_busy") || message.includes("database is locked");
2168
2132
  }
2169
- function resolveStateDir() {
2170
- return import_node_path2.default.join(import_node_os.default.homedir(), ".codexuse", "t3-daemon");
2133
+ function beginImmediate(db) {
2134
+ db.exec("BEGIN IMMEDIATE");
2171
2135
  }
2172
- function reserveLoopbackPort() {
2173
- return new Promise((resolve, reject) => {
2174
- const server = import_node_net.default.createServer();
2175
- server.unref();
2176
- server.once("error", reject);
2177
- server.listen(0, "127.0.0.1", () => {
2178
- const address = server.address();
2179
- const port = typeof address === "object" && address ? address.port : null;
2180
- server.close((error) => {
2181
- if (error) {
2182
- reject(error);
2183
- return;
2184
- }
2185
- if (typeof port !== "number") {
2186
- reject(new Error("Failed to reserve a loopback port for the Projects server."));
2187
- return;
2188
- }
2189
- resolve(port);
2190
- });
2191
- });
2192
- });
2136
+ function commit(db) {
2137
+ db.exec("COMMIT");
2193
2138
  }
2194
- async function runDaemonStart(options) {
2195
- const entry = resolveServerEntry();
2196
- if (!import_node_fs.default.existsSync(entry)) {
2197
- throw new Error(
2198
- `Missing bundled T3 server build output at ${entry}. Rebuild the CLI package with \`bun run build:cli\`.`
2199
- );
2200
- }
2201
- const port = await reserveLoopbackPort();
2202
- const authToken = (0, import_node_crypto.randomBytes)(24).toString("hex");
2203
- const stateDir = resolveStateDir();
2204
- const projectPath = options.projectPath ? import_node_path2.default.resolve(options.projectPath) : null;
2205
- const childCwd = projectPath ?? process.cwd();
2206
- const telegramBotToken = options.telegramBotToken?.trim() || null;
2207
- const runtime = resolveInternalBunRuntime({
2208
- T3CODE_MODE: "desktop",
2209
- T3CODE_HOST: "127.0.0.1",
2210
- T3CODE_PORT: String(port),
2211
- T3CODE_NO_BROWSER: "1",
2212
- T3CODE_AUTH_TOKEN: authToken,
2213
- T3CODE_STATE_DIR: stateDir,
2214
- T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD: projectPath ? "1" : "0",
2215
- CODEXUSE_TELEGRAM_RUNTIME_OWNER: "daemon",
2216
- ...telegramBotToken ? {
2217
- CODEXUSE_TELEGRAM_BOT_TOKEN: telegramBotToken,
2218
- CODEXUSE_TELEGRAM_BRIDGE_ENABLED: "1"
2219
- } : {}
2220
- });
2221
- const child = (0, import_node_child_process2.spawn)(runtime.bin, [entry], {
2222
- cwd: childCwd,
2223
- env: runtime.env,
2224
- stdio: "inherit"
2225
- });
2226
- console.log(`Daemon started. Projects server listening on 127.0.0.1:${port}`);
2227
- console.log(`State dir: ${stateDir}`);
2228
- if (projectPath) {
2229
- console.log(`Bootstrapped project path: ${projectPath}`);
2139
+ function rollback(db) {
2140
+ try {
2141
+ db.exec("ROLLBACK");
2142
+ } catch {
2230
2143
  }
2231
- if (telegramBotToken) {
2232
- console.log("Telegram remote control enabled for this daemon session.");
2144
+ }
2145
+ function applyConnectionPragmas(db) {
2146
+ db.exec(`PRAGMA busy_timeout = ${SQLITE_BUSY_TIMEOUT_MS};`);
2147
+ db.exec("PRAGMA journal_mode = WAL;");
2148
+ db.exec("PRAGMA foreign_keys = ON;");
2149
+ }
2150
+ function ensureSchema(db, dbPath) {
2151
+ if (initializedDbPaths.has(dbPath)) {
2152
+ return;
2233
2153
  }
2234
- console.log("Running until SIGINT/SIGTERM...");
2235
- await waitForChild(child);
2236
- }
2237
- function waitForChild(child) {
2238
- return new Promise((resolve, reject) => {
2239
- let stopping = false;
2240
- const stop = (signal) => {
2241
- if (stopping) {
2242
- return;
2243
- }
2244
- stopping = true;
2245
- child.kill(signal);
2246
- };
2247
- const onSigInt = () => stop("SIGINT");
2248
- const onSigTerm = () => stop("SIGTERM");
2249
- process.once("SIGINT", onSigInt);
2250
- process.once("SIGTERM", onSigTerm);
2251
- child.once("exit", (code, signal) => {
2252
- process.off("SIGINT", onSigInt);
2253
- process.off("SIGTERM", onSigTerm);
2254
- if (code === 0 || stopping) {
2255
- resolve();
2256
- return;
2257
- }
2258
- reject(
2259
- new Error(
2260
- `Projects server exited unexpectedly (code=${code ?? "null"}, signal=${signal ?? "null"}).`
2261
- )
2262
- );
2263
- });
2264
- child.once("error", (error) => {
2265
- process.off("SIGINT", onSigInt);
2266
- process.off("SIGTERM", onSigTerm);
2267
- reject(error);
2268
- });
2269
- });
2270
- }
2271
- async function handleDaemonCommand(args, version) {
2272
- const flags = args.filter((arg) => arg.startsWith("-"));
2273
- const params = stripFlags(args);
2274
- const sub = params[0];
2275
- if (!sub || hasFlag(flags, "--help") || hasFlag(flags, "-h")) {
2276
- printDaemonHelp(version);
2277
- return;
2278
- }
2279
- switch (sub) {
2280
- case "start": {
2281
- const projectPath = parseStringFlag(flags, "--project-path") ?? parseStringFlag(flags, "--project") ?? null;
2282
- const telegramBotToken = parseStringFlag(flags, "--telegram-bot-token") ?? normalizeTelegramBotToken(process.env.CODEXUSE_TELEGRAM_BOT_TOKEN) ?? null;
2283
- await runDaemonStart({
2284
- projectPath,
2285
- telegramBotToken,
2286
- version
2287
- });
2288
- return;
2289
- }
2290
- default:
2291
- printDaemonHelp(version);
2292
- return;
2293
- }
2294
- }
2295
-
2296
- // ../../packages/runtime-profiles/src/license/service.ts
2297
- var import_node_crypto3 = __toESM(require("crypto"), 1);
2298
-
2299
- // ../../packages/contracts/src/settings/auto-roll.ts
2300
- var DEFAULT_AUTO_ROLL_ENABLED = false;
2301
- var DEFAULT_AUTO_ROLL_WARNING_THRESHOLD = 85;
2302
- var DEFAULT_AUTO_ROLL_SWITCH_THRESHOLD = 95;
2303
- var AUTO_ROLL_WARNING_MIN = 50;
2304
- var AUTO_ROLL_WARNING_MAX = 99;
2305
- var AUTO_ROLL_SWITCH_MAX = 100;
2306
- function clampNumber(value, min, max) {
2307
- return Math.min(max, Math.max(min, value));
2308
- }
2309
- function resolveFiniteNumber(value, fallback) {
2310
- return Number.isFinite(value) ? value : fallback;
2311
- }
2312
- function sanitizeAutoRollWarningThreshold(value) {
2313
- const numeric = resolveFiniteNumber(value, DEFAULT_AUTO_ROLL_WARNING_THRESHOLD);
2314
- return clampNumber(numeric, AUTO_ROLL_WARNING_MIN, AUTO_ROLL_WARNING_MAX);
2315
- }
2316
- function sanitizeAutoRollSwitchThreshold(value, warningThreshold) {
2317
- const sanitizedWarning = sanitizeAutoRollWarningThreshold(warningThreshold);
2318
- const numeric = resolveFiniteNumber(value, DEFAULT_AUTO_ROLL_SWITCH_THRESHOLD);
2319
- return clampNumber(numeric, sanitizedWarning + 1, AUTO_ROLL_SWITCH_MAX);
2320
- }
2321
- function sanitizeAutoRollThresholds(warningThreshold, switchThreshold) {
2322
- const sanitizedWarning = sanitizeAutoRollWarningThreshold(warningThreshold);
2323
- const sanitizedSwitch = sanitizeAutoRollSwitchThreshold(switchThreshold, sanitizedWarning);
2324
- return {
2325
- warningThreshold: sanitizedWarning,
2326
- switchThreshold: sanitizedSwitch
2327
- };
2328
- }
2329
- function normalizeAutoRollSettings(raw) {
2330
- const enabled = typeof raw?.enabled === "boolean" ? raw.enabled : DEFAULT_AUTO_ROLL_ENABLED;
2331
- const rawWarning = resolveFiniteNumber(
2332
- typeof raw?.warningThreshold === "number" ? raw.warningThreshold : Number.NaN,
2333
- DEFAULT_AUTO_ROLL_WARNING_THRESHOLD
2334
- );
2335
- const rawSwitch = resolveFiniteNumber(
2336
- typeof raw?.switchThreshold === "number" ? raw.switchThreshold : Number.NaN,
2337
- DEFAULT_AUTO_ROLL_SWITCH_THRESHOLD
2338
- );
2339
- const {
2340
- warningThreshold: normalizedWarning,
2341
- switchThreshold: normalizedSwitch
2342
- } = sanitizeAutoRollThresholds(rawWarning, rawSwitch);
2343
- return {
2344
- enabled,
2345
- warningThreshold: normalizedWarning,
2346
- switchThreshold: normalizedSwitch
2347
- };
2348
- }
2349
-
2350
- // ../../packages/runtime-app-state/src/app/state.ts
2351
- var import_node_fs3 = require("fs");
2352
- var import_node_async_hooks = require("async_hooks");
2353
- var import_node_path4 = __toESM(require("path"), 1);
2354
- var import_node_os2 = __toESM(require("os"), 1);
2355
-
2356
- // ../../packages/contracts/src/settings/chat-scrollback.ts
2357
- var CHAT_SCROLLBACK_DEFAULT = 200;
2358
- var CHAT_SCROLLBACK_MIN = 50;
2359
- var CHAT_SCROLLBACK_MAX = 5e3;
2360
- function clampChatScrollbackItems(value) {
2361
- if (!Number.isFinite(value)) {
2362
- return CHAT_SCROLLBACK_DEFAULT;
2363
- }
2364
- const rounded = Math.round(value);
2365
- return Math.max(CHAT_SCROLLBACK_MIN, Math.min(CHAT_SCROLLBACK_MAX, rounded));
2366
- }
2367
- function normalizeChatHistoryScrollbackItems(value) {
2368
- if (value === null) {
2369
- return null;
2370
- }
2371
- if (typeof value === "number" && Number.isFinite(value)) {
2372
- return clampChatScrollbackItems(value);
2373
- }
2374
- if (typeof value === "string") {
2375
- const parsed = Number(value);
2376
- if (Number.isFinite(parsed)) {
2377
- return clampChatScrollbackItems(parsed);
2378
- }
2379
- }
2380
- return CHAT_SCROLLBACK_DEFAULT;
2381
- }
2382
-
2383
- // ../../packages/contracts/src/settings/commit-message-prompt.ts
2384
- var DEFAULT_COMMIT_MESSAGE_PROMPT = `Generate a concise git commit message for the following changes. Follow conventional commit format (e.g., feat:, fix:, refactor:, docs:, etc.). Keep the summary line under 72 characters. Only output the commit message, nothing else.
2385
-
2386
- Changes:
2387
- {diff}`;
2388
- function normalizeLineEndings(value) {
2389
- return value.replace(/\r\n/g, "\n");
2390
- }
2391
- function normalizeCommitMessagePrompt(value) {
2392
- if (typeof value !== "string") {
2393
- return null;
2394
- }
2395
- const normalized = normalizeLineEndings(value).trim();
2396
- return normalized.length > 0 ? normalized : null;
2397
- }
2398
-
2399
- // ../../packages/runtime-app-state/src/storage/documents.ts
2400
- var import_node_fs2 = require("fs");
2401
- var import_node_path3 = __toESM(require("path"), 1);
2402
- var APP_STORAGE_TABLE = "app_storage_documents";
2403
- var APP_STORAGE_DB_DIR = "t3-projects";
2404
- var APP_STORAGE_DB_NAME = "state.sqlite";
2405
- var SQLITE_BUSY_TIMEOUT_MS = 5e3;
2406
- var SQLITE_BUSY_MAX_ATTEMPTS = 3;
2407
- var SQLITE_BUSY_RETRY_DELAY_MS = 100;
2408
- var initializedDbPaths = /* @__PURE__ */ new Set();
2409
- function isRecord(value) {
2410
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
2411
- }
2412
- function clone(value) {
2413
- return JSON.parse(JSON.stringify(value));
2414
- }
2415
- function safeParseJson(raw) {
2416
- if (typeof raw !== "string" || raw.trim().length === 0) {
2417
- return null;
2418
- }
2419
- try {
2420
- return JSON.parse(raw);
2421
- } catch {
2422
- return null;
2423
- }
2424
- }
2425
- function sleep(ms) {
2426
- return new Promise((resolve) => setTimeout(resolve, ms));
2427
- }
2428
- function isSqliteBusyError(error) {
2429
- if (!error || typeof error !== "object") {
2430
- return false;
2431
- }
2432
- const sqliteError = error;
2433
- const message = typeof sqliteError.message === "string" ? sqliteError.message.toLowerCase() : "";
2434
- return sqliteError.code === "SQLITE_BUSY" || sqliteError.errno === 5 || message.includes("sqlite_busy") || message.includes("database is locked");
2435
- }
2436
- function beginImmediate(db) {
2437
- db.exec("BEGIN IMMEDIATE");
2438
- }
2439
- function commit(db) {
2440
- db.exec("COMMIT");
2441
- }
2442
- function rollback(db) {
2443
- try {
2444
- db.exec("ROLLBACK");
2445
- } catch {
2446
- }
2447
- }
2448
- function applyConnectionPragmas(db) {
2449
- db.exec(`PRAGMA busy_timeout = ${SQLITE_BUSY_TIMEOUT_MS};`);
2450
- db.exec("PRAGMA journal_mode = WAL;");
2451
- db.exec("PRAGMA foreign_keys = ON;");
2452
- }
2453
- function ensureSchema(db, dbPath) {
2454
- if (initializedDbPaths.has(dbPath)) {
2455
- return;
2456
- }
2457
- db.exec(`
2458
- CREATE TABLE IF NOT EXISTS ${APP_STORAGE_TABLE} (
2459
- namespace TEXT PRIMARY KEY,
2460
- value_json TEXT NOT NULL,
2461
- updated_at TEXT NOT NULL
2462
- )
2463
- `);
2464
- initializedDbPaths.add(dbPath);
2154
+ db.exec(`
2155
+ CREATE TABLE IF NOT EXISTS ${APP_STORAGE_TABLE} (
2156
+ namespace TEXT PRIMARY KEY,
2157
+ value_json TEXT NOT NULL,
2158
+ updated_at TEXT NOT NULL
2159
+ )
2160
+ `);
2161
+ initializedDbPaths.add(dbPath);
2465
2162
  }
2466
2163
  async function openDatabase(dbPath) {
2467
- await import_node_fs2.promises.mkdir(import_node_path3.default.dirname(dbPath), { recursive: true });
2164
+ await import_node_fs.promises.mkdir(import_node_path.default.dirname(dbPath), { recursive: true });
2468
2165
  if (process.versions.bun !== void 0) {
2469
2166
  const sqlite2 = await Function("return import('bun:sqlite')")();
2470
2167
  const database2 = new sqlite2.Database(dbPath);
@@ -2501,7 +2198,7 @@ async function withDatabase(dbPath, task) {
2501
2198
  throw new Error("App storage database remained busy after retrying.");
2502
2199
  }
2503
2200
  function resolveAppStorageDbPath(userDataDir) {
2504
- return import_node_path3.default.join(userDataDir, APP_STORAGE_DB_DIR, APP_STORAGE_DB_NAME);
2201
+ return import_node_path.default.join(userDataDir, APP_STORAGE_DB_DIR, APP_STORAGE_DB_NAME);
2505
2202
  }
2506
2203
  async function readDocument(dbPath, namespace, normalize) {
2507
2204
  return withDatabase(dbPath, (db) => {
@@ -2583,27 +2280,27 @@ function clone2(value) {
2583
2280
  return JSON.parse(JSON.stringify(value));
2584
2281
  }
2585
2282
  function resolveDefaultUserDataDir() {
2586
- const home = process.env.HOME || process.env.USERPROFILE || import_node_os2.default.homedir();
2283
+ const home = process.env.HOME || process.env.USERPROFILE || import_node_os.default.homedir();
2587
2284
  if (!home) {
2588
2285
  throw new Error("Unable to resolve home directory for app state.");
2589
2286
  }
2590
2287
  if (process.platform === "darwin") {
2591
- return import_node_path4.default.join(home, "Library", "Application Support", APP_NAME);
2288
+ return import_node_path2.default.join(home, "Library", "Application Support", APP_NAME);
2592
2289
  }
2593
2290
  if (process.platform === "win32") {
2594
2291
  const appData = process.env.APPDATA;
2595
2292
  if (appData) {
2596
- return import_node_path4.default.join(appData, APP_NAME);
2293
+ return import_node_path2.default.join(appData, APP_NAME);
2597
2294
  }
2598
- return import_node_path4.default.join(home, "AppData", "Roaming", APP_NAME);
2295
+ return import_node_path2.default.join(home, "AppData", "Roaming", APP_NAME);
2599
2296
  }
2600
- return import_node_path4.default.join(home, ".config", APP_NAME);
2297
+ return import_node_path2.default.join(home, ".config", APP_NAME);
2601
2298
  }
2602
2299
  function getUserDataDir() {
2603
2300
  return configuredUserDataDir ?? resolveDefaultUserDataDir();
2604
2301
  }
2605
2302
  function resolveLegacyAppStatePath() {
2606
- return import_node_path4.default.join(getUserDataDir(), LEGACY_APP_STATE_FILE);
2303
+ return import_node_path2.default.join(getUserDataDir(), LEGACY_APP_STATE_FILE);
2607
2304
  }
2608
2305
  function resolveStorageDbPath() {
2609
2306
  return resolveAppStorageDbPath(getUserDataDir());
@@ -2693,8 +2390,8 @@ function createDefaultAppState() {
2693
2390
  lastError: null,
2694
2391
  remoteUpdatedAt: null
2695
2392
  },
2696
- telemetry: {
2697
- installId: null,
2393
+ analytics: {
2394
+ anonymousId: null,
2698
2395
  enabled: true,
2699
2396
  lastFlushAt: null,
2700
2397
  lastError: null
@@ -2992,15 +2689,22 @@ function normalizeAppState(raw) {
2992
2689
  merged.sync.lastPullAt = asString(merged.sync.lastPullAt);
2993
2690
  merged.sync.lastError = asString(merged.sync.lastError);
2994
2691
  merged.sync.remoteUpdatedAt = asString(merged.sync.remoteUpdatedAt);
2995
- if (!isRecord2(merged.telemetry)) {
2996
- merged.telemetry = clone2(defaults.telemetry);
2692
+ if (!isRecord2(merged.analytics)) {
2693
+ merged.analytics = isRecord2(merged.telemetry) ? clone2(merged.telemetry) : clone2(defaults.analytics);
2694
+ }
2695
+ merged.analytics.anonymousId = asString(merged.analytics.anonymousId);
2696
+ if (!merged.analytics.anonymousId) {
2697
+ const legacyInstallId = asString(merged.telemetry?.installId);
2698
+ merged.analytics.anonymousId = legacyInstallId;
2699
+ }
2700
+ if (typeof merged.analytics.enabled !== "boolean") {
2701
+ merged.analytics.enabled = true;
2997
2702
  }
2998
- merged.telemetry.installId = asString(merged.telemetry.installId);
2999
- if (typeof merged.telemetry.enabled !== "boolean") {
3000
- merged.telemetry.enabled = true;
2703
+ merged.analytics.lastFlushAt = asString(merged.analytics.lastFlushAt);
2704
+ merged.analytics.lastError = asString(merged.analytics.lastError);
2705
+ if ("telemetry" in merged) {
2706
+ delete merged.telemetry;
3001
2707
  }
3002
- merged.telemetry.lastFlushAt = asString(merged.telemetry.lastFlushAt);
3003
- merged.telemetry.lastError = asString(merged.telemetry.lastError);
3004
2708
  if (!isRecord2(merged.profilesByName)) {
3005
2709
  merged.profilesByName = {};
3006
2710
  }
@@ -3050,7 +2754,7 @@ function deepMerge(base, patch) {
3050
2754
  async function readLegacyAppStateFromDisk() {
3051
2755
  const filePath = resolveLegacyAppStatePath();
3052
2756
  try {
3053
- const raw = await import_node_fs3.promises.readFile(filePath, "utf8");
2757
+ const raw = await import_node_fs2.promises.readFile(filePath, "utf8");
3054
2758
  return normalizeAppState(JSON.parse(raw));
3055
2759
  } catch (error) {
3056
2760
  const code = error.code;
@@ -3130,31 +2834,139 @@ async function patchAppState(patch) {
3130
2834
  });
3131
2835
  }
3132
2836
 
3133
- // ../../packages/runtime-codex/src/codex/settings.ts
3134
- function asString2(value) {
3135
- if (typeof value !== "string") {
3136
- return null;
2837
+ // ../../packages/runtime-app-state/src/app/runtimeSettings.ts
2838
+ var LEGACY_APP_SETTINGS_KEYS = /* @__PURE__ */ new Set([
2839
+ "backendMode",
2840
+ "theme",
2841
+ "commitMessageModelId",
2842
+ "usageShowRemaining",
2843
+ "remoteBackendProvider",
2844
+ "remoteBackendHost",
2845
+ "remoteBackendToken",
2846
+ "remoteBackends",
2847
+ "activeRemoteBackendId",
2848
+ "keepDaemonRunningAfterAppClose"
2849
+ ]);
2850
+ function asRecord(value) {
2851
+ return value && typeof value === "object" ? value : {};
2852
+ }
2853
+ function stripLegacyAppSettingsKeys(settings) {
2854
+ let changed = false;
2855
+ const next = { ...settings };
2856
+ for (const key of LEGACY_APP_SETTINGS_KEYS) {
2857
+ if (!(key in next)) {
2858
+ continue;
2859
+ }
2860
+ changed = true;
2861
+ delete next[key];
3137
2862
  }
3138
- const trimmed = value.trim();
3139
- return trimmed.length > 0 ? trimmed : null;
2863
+ return changed ? next : settings;
3140
2864
  }
3141
- function isRecord3(value) {
3142
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
2865
+ async function readAppSettings() {
2866
+ const appState = await getAppState();
2867
+ return stripLegacyAppSettingsKeys(asRecord(appState.runtimeSettings));
3143
2868
  }
3144
- function parseAutoRoll(raw) {
3145
- if (!raw) {
3146
- return null;
3147
- }
3148
- try {
3149
- return normalizeAutoRollSettings(raw);
3150
- } catch {
3151
- return null;
2869
+ async function writeAppSettings(settings, onUpdated) {
2870
+ const nextSettings = stripLegacyAppSettingsKeys(asRecord(settings));
2871
+ await updateAppState(
2872
+ (current) => ({
2873
+ ...current,
2874
+ runtimeSettings: nextSettings
2875
+ }),
2876
+ {
2877
+ mode: "replace",
2878
+ allowBeforeMigrationComplete: false
2879
+ }
2880
+ );
2881
+ if (onUpdated) {
2882
+ try {
2883
+ await onUpdated(nextSettings);
2884
+ } catch (error) {
2885
+ console.warn("Failed to apply runtime settings update hook:", error);
2886
+ }
3152
2887
  }
2888
+ return nextSettings;
3153
2889
  }
3154
- function parseStoredLicense(raw) {
3155
- if (!isRecord3(raw)) {
3156
- return null;
3157
- }
2890
+
2891
+ // ../../packages/runtime-profiles/src/license/service.ts
2892
+ var import_node_crypto2 = __toESM(require("crypto"), 1);
2893
+
2894
+ // ../../packages/contracts/src/settings/auto-roll.ts
2895
+ var DEFAULT_AUTO_ROLL_ENABLED = false;
2896
+ var DEFAULT_AUTO_ROLL_WARNING_THRESHOLD = 85;
2897
+ var DEFAULT_AUTO_ROLL_SWITCH_THRESHOLD = 95;
2898
+ var AUTO_ROLL_WARNING_MIN = 50;
2899
+ var AUTO_ROLL_WARNING_MAX = 99;
2900
+ var AUTO_ROLL_SWITCH_MAX = 100;
2901
+ function clampNumber(value, min, max) {
2902
+ return Math.min(max, Math.max(min, value));
2903
+ }
2904
+ function resolveFiniteNumber(value, fallback) {
2905
+ return Number.isFinite(value) ? value : fallback;
2906
+ }
2907
+ function sanitizeAutoRollWarningThreshold(value) {
2908
+ const numeric = resolveFiniteNumber(value, DEFAULT_AUTO_ROLL_WARNING_THRESHOLD);
2909
+ return clampNumber(numeric, AUTO_ROLL_WARNING_MIN, AUTO_ROLL_WARNING_MAX);
2910
+ }
2911
+ function sanitizeAutoRollSwitchThreshold(value, warningThreshold) {
2912
+ const sanitizedWarning = sanitizeAutoRollWarningThreshold(warningThreshold);
2913
+ const numeric = resolveFiniteNumber(value, DEFAULT_AUTO_ROLL_SWITCH_THRESHOLD);
2914
+ return clampNumber(numeric, sanitizedWarning + 1, AUTO_ROLL_SWITCH_MAX);
2915
+ }
2916
+ function sanitizeAutoRollThresholds(warningThreshold, switchThreshold) {
2917
+ const sanitizedWarning = sanitizeAutoRollWarningThreshold(warningThreshold);
2918
+ const sanitizedSwitch = sanitizeAutoRollSwitchThreshold(switchThreshold, sanitizedWarning);
2919
+ return {
2920
+ warningThreshold: sanitizedWarning,
2921
+ switchThreshold: sanitizedSwitch
2922
+ };
2923
+ }
2924
+ function normalizeAutoRollSettings(raw) {
2925
+ const enabled = typeof raw?.enabled === "boolean" ? raw.enabled : DEFAULT_AUTO_ROLL_ENABLED;
2926
+ const rawWarning = resolveFiniteNumber(
2927
+ typeof raw?.warningThreshold === "number" ? raw.warningThreshold : Number.NaN,
2928
+ DEFAULT_AUTO_ROLL_WARNING_THRESHOLD
2929
+ );
2930
+ const rawSwitch = resolveFiniteNumber(
2931
+ typeof raw?.switchThreshold === "number" ? raw.switchThreshold : Number.NaN,
2932
+ DEFAULT_AUTO_ROLL_SWITCH_THRESHOLD
2933
+ );
2934
+ const {
2935
+ warningThreshold: normalizedWarning,
2936
+ switchThreshold: normalizedSwitch
2937
+ } = sanitizeAutoRollThresholds(rawWarning, rawSwitch);
2938
+ return {
2939
+ enabled,
2940
+ warningThreshold: normalizedWarning,
2941
+ switchThreshold: normalizedSwitch
2942
+ };
2943
+ }
2944
+
2945
+ // ../../packages/runtime-codex/src/codex/settings.ts
2946
+ function asString2(value) {
2947
+ if (typeof value !== "string") {
2948
+ return null;
2949
+ }
2950
+ const trimmed = value.trim();
2951
+ return trimmed.length > 0 ? trimmed : null;
2952
+ }
2953
+ function isRecord3(value) {
2954
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
2955
+ }
2956
+ function parseAutoRoll(raw) {
2957
+ if (!raw) {
2958
+ return null;
2959
+ }
2960
+ try {
2961
+ return normalizeAutoRollSettings(raw);
2962
+ } catch {
2963
+ return null;
2964
+ }
2965
+ }
2966
+ function parseStoredLicense(raw) {
2967
+ if (!isRecord3(raw)) {
2968
+ return null;
2969
+ }
3158
2970
  const statusCandidate = asString2(raw.status);
3159
2971
  const status = ["inactive", "active", "grace", "error"].includes(statusCandidate ?? "") ? statusCandidate : void 0;
3160
2972
  const license = {
@@ -3280,25 +3092,45 @@ async function writeCodexSettingsJsonRaw(payload) {
3280
3092
  }
3281
3093
 
3282
3094
  // ../../packages/runtime-profiles/src/license/secret.ts
3283
- var import_node_crypto2 = __toESM(require("crypto"), 1);
3095
+ var import_node_fs3 = require("fs");
3096
+ var import_node_crypto = __toESM(require("crypto"), 1);
3097
+ var import_node_path3 = __toESM(require("path"), 1);
3284
3098
  var LICENSE_SECRET_DOCUMENT = "desktop.license-secret";
3099
+ var LEGACY_LICENSE_SECRET_FILE = "license.secret";
3285
3100
  async function generateSecret() {
3286
- return import_node_crypto2.default.randomBytes(32).toString("hex");
3101
+ return import_node_crypto.default.randomBytes(32).toString("hex");
3102
+ }
3103
+ function normalizeSecret(value) {
3104
+ return typeof value === "string" ? value.trim() : "";
3105
+ }
3106
+ async function readLegacyLicenseSecret() {
3107
+ const legacyPath = import_node_path3.default.join(getUserDataDir(), LEGACY_LICENSE_SECRET_FILE);
3108
+ try {
3109
+ return normalizeSecret(await import_node_fs3.promises.readFile(legacyPath, "utf8"));
3110
+ } catch (error) {
3111
+ if (error.code === "ENOENT") {
3112
+ return "";
3113
+ }
3114
+ throw error;
3115
+ }
3116
+ }
3117
+ async function persistLicenseSecret(dbPath, secret) {
3118
+ await writeDocument(dbPath, LICENSE_SECRET_DOCUMENT, secret);
3119
+ return secret;
3287
3120
  }
3288
3121
  async function getLicenseSecret() {
3289
3122
  const dbPath = resolveAppStorageDbPath(getUserDataDir());
3290
- const existing = await readDocument(
3291
- dbPath,
3292
- LICENSE_SECRET_DOCUMENT,
3293
- (value) => typeof value === "string" ? value.trim() : ""
3294
- );
3295
- const normalizedExisting = typeof existing === "string" ? existing.trim() : "";
3123
+ const existing = await readDocument(dbPath, LICENSE_SECRET_DOCUMENT, normalizeSecret);
3124
+ const normalizedExisting = normalizeSecret(existing);
3296
3125
  if (normalizedExisting.length > 0) {
3297
3126
  return normalizedExisting;
3298
3127
  }
3128
+ const legacySecret = await readLegacyLicenseSecret();
3129
+ if (legacySecret.length > 0) {
3130
+ return persistLicenseSecret(dbPath, legacySecret);
3131
+ }
3299
3132
  const secret = await generateSecret();
3300
- await writeDocument(dbPath, LICENSE_SECRET_DOCUMENT, secret);
3301
- return secret;
3133
+ return persistLicenseSecret(dbPath, secret);
3302
3134
  }
3303
3135
 
3304
3136
  // ../../packages/contracts/src/license/offer-config.ts
@@ -3403,7 +3235,7 @@ function licenseSignaturePayload(license) {
3403
3235
  return JSON.stringify(payload);
3404
3236
  }
3405
3237
  function signLicense(license, secret) {
3406
- return import_node_crypto3.default.createHmac("sha256", secret).update(licenseSignaturePayload(license)).digest("hex");
3238
+ return import_node_crypto2.default.createHmac("sha256", secret).update(licenseSignaturePayload(license)).digest("hex");
3407
3239
  }
3408
3240
  function withSignature(license, secret) {
3409
3241
  return {
@@ -3629,7 +3461,7 @@ var LicenseService = class {
3629
3461
  throw new Error("License key is required.");
3630
3462
  }
3631
3463
  const secret = await getLicenseSecret();
3632
- const digest = import_node_crypto3.default.createHash("sha256").update(trimmed).digest("hex");
3464
+ const digest = import_node_crypto2.default.createHash("sha256").update(trimmed).digest("hex");
3633
3465
  const result = await requestGumroadVerify(trimmed, "activation");
3634
3466
  ensureWithinUseLimit(result);
3635
3467
  const normalized = normalizeVerificationResult(result);
@@ -3672,149 +3504,23 @@ var LicenseService = class {
3672
3504
  };
3673
3505
  var licenseService = new LicenseService();
3674
3506
 
3675
- // src/app/help.ts
3676
- function printHelp(version) {
3677
- console.log(`CodexUse CLI v${version}
3678
-
3679
- Usage:
3680
- codexuse profile list [--no-usage] [--compact]
3681
- codexuse profile current
3682
- codexuse profile add <name> [--skip-login] [--device-auth] [--login=browser|device]
3683
- codexuse profile refresh <name> [--skip-login] [--device-auth] [--login=browser|device]
3684
- codexuse profile switch <name>
3685
- codexuse profile autoroll [--threshold=50-100] [--dry-run] [--watch] [--interval=seconds]
3686
- codexuse profile delete <name>
3687
- codexuse profile rename <old> <new>
3688
-
3689
- codexuse license status [--refresh]
3690
- codexuse license activate <license-key>
3691
-
3692
- codexuse sync status
3693
- codexuse sync pull
3694
- codexuse sync push
3695
-
3696
- codexuse daemon start [--telegram-bot-token=<token>] [--project-path=/abs/path]
3697
-
3698
- Flags:
3699
- -h, --help Show help
3700
- -v, --version Show version
3701
- --no-usage Skip rate-limit usage fetch
3702
- --compact Names only
3703
- --device-auth Use device auth for Codex login
3704
- --login=MODE Login mode: browser | device
3705
- --threshold=NN Auto-roll switch threshold percent (50-100)
3706
- --watch Keep checking and auto-switch when threshold is reached
3707
- --interval=SEC Watch interval in seconds (default: 30)
3708
- --dry-run Print planned switch without changing active profile
3709
- --telegram-bot-token=TOKEN Enable Telegram remote control for daemon mode
3710
- --project-path=PATH Optional path to bootstrap as the default Projects root in daemon mode
3711
- `);
3712
- }
3713
-
3714
- // src/platform/args.ts
3715
- var DEFAULT_AUTOROLL_INTERVAL_SECONDS = 30;
3716
- var DEFAULT_AUTOROLL_THRESHOLD = 95;
3717
- function hasFlag2(args, flag) {
3718
- return args.includes(flag);
3719
- }
3720
- function stripFlags2(args) {
3721
- return args.filter((arg) => !arg.startsWith("-"));
3722
- }
3723
- function parseLoginMode(flags) {
3724
- if (flags.includes("--device-auth")) return "device";
3725
- const explicit = flags.find((flag) => flag.startsWith("--login="));
3726
- if (!explicit) return null;
3727
- const value = explicit.split("=")[1];
3728
- if (value === "browser" || value === "device") {
3729
- return value;
3730
- }
3731
- return null;
3732
- }
3733
- function parseNumericFlag(flags, name) {
3734
- const explicit = flags.find((flag) => flag.startsWith(`${name}=`));
3735
- if (!explicit) {
3736
- return null;
3737
- }
3738
- const raw = explicit.slice(`${name}=`.length);
3739
- const value = Number.parseFloat(raw);
3740
- return Number.isFinite(value) ? value : Number.NaN;
3741
- }
3742
- function parseIntegerFlag(flags, name) {
3743
- const explicit = flags.find((flag) => flag.startsWith(`${name}=`));
3744
- if (!explicit) {
3745
- return null;
3746
- }
3747
- const raw = explicit.slice(`${name}=`.length);
3748
- const value = Number.parseInt(raw, 10);
3749
- return Number.isFinite(value) ? value : Number.NaN;
3750
- }
3751
-
3752
- // src/commands/license.ts
3753
- async function handleLicense(args, version) {
3754
- const flags = args.filter((arg) => arg.startsWith("-"));
3755
- const params = stripFlags2(args);
3756
- const sub = params[0];
3757
- if (!sub || hasFlag2(flags, "--help") || hasFlag2(flags, "-h")) {
3758
- printHelp(version);
3759
- return;
3760
- }
3761
- switch (sub) {
3762
- case "status": {
3763
- const forceRefresh = hasFlag2(flags, "--refresh");
3764
- const status = await licenseService.getStatus({ forceRefresh });
3765
- console.log(`Tier: ${status.tier}`);
3766
- console.log(`State: ${status.state}`);
3767
- if (status.profileLimit !== null) {
3768
- console.log(`Profile limit: ${status.profileLimit}`);
3769
- }
3770
- if (status.profilesRemaining !== null) {
3771
- console.log(`Profiles remaining: ${status.profilesRemaining}`);
3772
- }
3773
- if (status.purchaseEmail) {
3774
- console.log(`Email: ${status.purchaseEmail}`);
3775
- }
3776
- if (status.maskedLicenseKey) {
3777
- console.log(`License: ${status.maskedLicenseKey}`);
3778
- }
3779
- if (status.message) {
3780
- console.log(`Message: ${status.message}`);
3781
- }
3782
- if (status.error) {
3783
- console.log(`Error: ${status.error}`);
3784
- }
3785
- return;
3786
- }
3787
- case "activate": {
3788
- const key = params[1];
3789
- if (!key) {
3790
- throw new Error("License key is required.");
3791
- }
3792
- const status = await licenseService.activate(key);
3793
- console.log(status.message ?? "License activated.");
3794
- return;
3795
- }
3796
- default:
3797
- printHelp(version);
3798
- }
3799
- }
3800
-
3801
3507
  // ../../packages/runtime-profiles/src/profiles/profile-manager.ts
3802
- var import_node_crypto4 = require("crypto");
3508
+ var import_node_crypto3 = require("crypto");
3803
3509
  var import_fs = require("fs");
3804
3510
  var import_path = __toESM(require("path"), 1);
3805
3511
  var import_path2 = require("path");
3806
3512
  var import_toml = __toESM(require_toml(), 1);
3807
3513
 
3808
3514
  // ../../packages/runtime-codex/src/codex/rpc.ts
3809
- var import_node_child_process3 = require("child_process");
3515
+ var import_node_child_process2 = require("child_process");
3810
3516
  var import_node_readline = __toESM(require("readline"), 1);
3811
3517
  var import_node_fs5 = require("fs");
3812
- var import_node_os3 = __toESM(require("os"), 1);
3518
+ var import_node_os2 = __toESM(require("os"), 1);
3813
3519
  var import_node_path6 = __toESM(require("path"), 1);
3814
3520
 
3815
3521
  // ../../packages/runtime-codex/src/codex/cli.ts
3816
3522
  var import_node_fs4 = require("fs");
3817
- var import_node_path5 = __toESM(require("path"), 1);
3523
+ var import_node_path4 = __toESM(require("path"), 1);
3818
3524
  var STABLE_CHANNEL = "stable";
3819
3525
  var ENV_HINTS = ["CODEX_BINARY", "CODEX_CLI_PATH", "CODEX_PATH"];
3820
3526
  var cachedStatus = null;
@@ -3822,7 +3528,7 @@ function fileExists(candidate) {
3822
3528
  if (!candidate) {
3823
3529
  return null;
3824
3530
  }
3825
- const normalized = import_node_path5.default.resolve(candidate);
3531
+ const normalized = import_node_path4.default.resolve(candidate);
3826
3532
  try {
3827
3533
  const stats = (0, import_node_fs4.statSync)(normalized);
3828
3534
  if (stats.isFile()) {
@@ -3850,11 +3556,11 @@ function resolveCodexFromPath() {
3850
3556
  if (!pathValue) {
3851
3557
  return null;
3852
3558
  }
3853
- const entries = pathValue.split(import_node_path5.default.delimiter).map((entry) => entry.trim()).filter(Boolean);
3559
+ const entries = pathValue.split(import_node_path4.default.delimiter).map((entry) => entry.trim()).filter(Boolean);
3854
3560
  const names = process.platform === "win32" ? ["codex.exe", "codex.cmd", "codex.bat", "codex"] : ["codex"];
3855
3561
  for (const entry of entries) {
3856
3562
  for (const name of names) {
3857
- const candidate = fileExists(import_node_path5.default.join(entry, name));
3563
+ const candidate = fileExists(import_node_path4.default.join(entry, name));
3858
3564
  if (candidate) {
3859
3565
  return candidate;
3860
3566
  }
@@ -3917,6 +3623,74 @@ async function requireCodexCli() {
3917
3623
  return status.path;
3918
3624
  }
3919
3625
 
3626
+ // ../../packages/runtime-codex/src/codex/command.ts
3627
+ var import_node_child_process = require("child_process");
3628
+ var import_node_path5 = __toESM(require("path"), 1);
3629
+ var cachedNodeRuntime = null;
3630
+ function isJavaScriptEntrypoint(binaryPath) {
3631
+ const normalized = binaryPath.trim().toLowerCase();
3632
+ return normalized.endsWith(".js") || normalized.endsWith(".mjs") || normalized.endsWith(".cjs");
3633
+ }
3634
+ function resolveNodeRuntime(env) {
3635
+ const override = env?.CODEX_NODE_RUNTIME?.trim() || env?.CODEX_NODE_BIN?.trim() || env?.CODEX_NODE?.trim() || process.env.CODEX_NODE_RUNTIME?.trim() || process.env.CODEX_NODE_BIN?.trim() || process.env.CODEX_NODE?.trim() || null;
3636
+ if (override) {
3637
+ return override;
3638
+ }
3639
+ if (cachedNodeRuntime) {
3640
+ return cachedNodeRuntime;
3641
+ }
3642
+ const runtimeEnv = { ...process.env, ...env };
3643
+ const homeDir = runtimeEnv.HOME ?? runtimeEnv.USERPROFILE ?? "";
3644
+ const pathHints = (runtimeEnv.CODEX_PATH_HINTS ?? process.env.CODEX_PATH_HINTS ?? "").split(import_node_path5.default.delimiter).map((entry) => entry.trim()).filter(Boolean);
3645
+ const extraPathEntries = [
3646
+ "/usr/local/bin",
3647
+ "/opt/homebrew/bin",
3648
+ import_node_path5.default.join(homeDir, ".local", "bin"),
3649
+ import_node_path5.default.join(homeDir, ".fnm", "aliases", "default", "bin"),
3650
+ import_node_path5.default.join(homeDir, ".fnm", "current", "bin"),
3651
+ ...pathHints
3652
+ ].filter(Boolean);
3653
+ const currentPath = runtimeEnv.PATH ?? "";
3654
+ runtimeEnv.PATH = Array.from(
3655
+ /* @__PURE__ */ new Set([...extraPathEntries, ...currentPath.split(import_node_path5.default.delimiter).filter(Boolean)])
3656
+ ).join(import_node_path5.default.delimiter);
3657
+ const candidates = Array.from(
3658
+ new Set(
3659
+ [
3660
+ runtimeEnv.CODEX_NODE_RUNTIME?.trim(),
3661
+ runtimeEnv.CODEX_NODE_BIN?.trim(),
3662
+ runtimeEnv.CODEX_NODE?.trim(),
3663
+ "/opt/homebrew/bin/node",
3664
+ "/usr/local/bin/node",
3665
+ "node"
3666
+ ].filter((candidate) => typeof candidate === "string" && candidate.length > 0)
3667
+ )
3668
+ );
3669
+ for (const candidate of candidates) {
3670
+ const probe = (0, import_node_child_process.spawnSync)(candidate, ["-v"], { env: runtimeEnv, stdio: "ignore" });
3671
+ if (!probe.error && probe.status === 0) {
3672
+ cachedNodeRuntime = candidate;
3673
+ return candidate;
3674
+ }
3675
+ }
3676
+ cachedNodeRuntime = process.execPath;
3677
+ return process.execPath;
3678
+ }
3679
+ function buildCodexCommand(binaryPath, args, env) {
3680
+ if (isJavaScriptEntrypoint(binaryPath)) {
3681
+ return {
3682
+ command: resolveNodeRuntime(env),
3683
+ args: [binaryPath, ...args],
3684
+ shell: false
3685
+ };
3686
+ }
3687
+ return {
3688
+ command: binaryPath,
3689
+ args: [...args],
3690
+ shell: process.platform === "win32"
3691
+ };
3692
+ }
3693
+
3920
3694
  // ../../packages/shared/src/core/logger.ts
3921
3695
  var isTestEnv = process.env.NODE_ENV === "test" || process.env.VITEST === "true" || process.env.VITEST === "1";
3922
3696
  function isMocked(fn) {
@@ -4138,11 +3912,11 @@ function parseRateLimitSnapshotFromRpcMessage(message) {
4138
3912
  }
4139
3913
  async function fetchRateLimitsViaRpc(envOverride, options = {}) {
4140
3914
  const binaryPath = options.codexPath ?? await requireCodexCli();
4141
- const tempHome = await import_node_fs5.promises.mkdtemp(import_node_path6.default.join(import_node_os3.default.tmpdir(), "codex-rpc-"));
3915
+ const tempHome = await import_node_fs5.promises.mkdtemp(import_node_path6.default.join(import_node_os2.default.tmpdir(), "codex-rpc-"));
4142
3916
  const tempAuthPath = import_node_path6.default.join(tempHome, "auth.json");
4143
3917
  let initialSourceAuth = null;
4144
3918
  const sourceAuthPath = options.authPath ?? (envOverride?.CODEX_HOME ? import_node_path6.default.join(envOverride.CODEX_HOME, "auth.json") : import_node_path6.default.join(
4145
- envOverride?.HOME ?? process.env.HOME ?? process.env.USERPROFILE ?? import_node_os3.default.homedir(),
3919
+ envOverride?.HOME ?? process.env.HOME ?? process.env.USERPROFILE ?? import_node_os2.default.homedir(),
4146
3920
  ".codex",
4147
3921
  "auth.json"
4148
3922
  ));
@@ -4157,17 +3931,24 @@ async function fetchRateLimitsViaRpc(envOverride, options = {}) {
4157
3931
  });
4158
3932
  return null;
4159
3933
  }
4160
- const child = (0, import_node_child_process3.spawn)(process.execPath, [binaryPath, "-s", "read-only", "-a", "untrusted", "app-server"], {
3934
+ const childEnv = {
3935
+ ...process.env,
3936
+ ...envOverride ?? {},
3937
+ HOME: tempHome,
3938
+ USERPROFILE: tempHome,
3939
+ CODEX_HOME: tempHome,
3940
+ CODEX_TELEMETRY_LABEL: "codex-rpc",
3941
+ ELECTRON_RUN_AS_NODE: "1"
3942
+ };
3943
+ const command = buildCodexCommand(
3944
+ binaryPath,
3945
+ ["-s", "read-only", "-a", "untrusted", "app-server"],
3946
+ childEnv
3947
+ );
3948
+ const child = (0, import_node_child_process2.spawn)(command.command, command.args, {
4161
3949
  stdio: ["pipe", "pipe", "pipe"],
4162
- env: {
4163
- ...process.env,
4164
- ...envOverride ?? {},
4165
- HOME: tempHome,
4166
- USERPROFILE: tempHome,
4167
- CODEX_HOME: tempHome,
4168
- CODEX_TELEMETRY_LABEL: "codex-rpc",
4169
- ELECTRON_RUN_AS_NODE: "1"
4170
- }
3950
+ env: childEnv,
3951
+ shell: command.shell
4171
3952
  });
4172
3953
  const rl = import_node_readline.default.createInterface({
4173
3954
  input: child.stdout,
@@ -4467,7 +4248,7 @@ var ProfileManager = class {
4467
4248
  workspaceId: null
4468
4249
  };
4469
4250
  }
4470
- const fingerprint = (0, import_node_crypto4.createHash)("sha256").update(trimmed).digest("hex");
4251
+ const fingerprint = (0, import_node_crypto3.createHash)("sha256").update(trimmed).digest("hex");
4471
4252
  try {
4472
4253
  const parsed = JSON.parse(trimmed);
4473
4254
  const normalized = this.normalizeProfileData(parsed);
@@ -5338,7 +5119,67 @@ var ProfileManager = class {
5338
5119
  }
5339
5120
  }
5340
5121
  }
5341
- /**
5122
+ async prepareProfileRuntime(name) {
5123
+ return this.enqueueProfileOperation(
5124
+ name,
5125
+ () => this.enqueueAuthSwap(async () => {
5126
+ await this.initialize();
5127
+ const profileName = this.normalizeProfileName(name);
5128
+ const record = await this.getProfileRowByName(profileName);
5129
+ if (!record) {
5130
+ throw new Error(`Profile '${profileName}' not found!`);
5131
+ }
5132
+ let profileData = record.data;
5133
+ await this.syncProfileTokensFromActiveAuth(profileName, profileData);
5134
+ const updatedRecord = await this.getProfileRowByName(profileName);
5135
+ if (updatedRecord) {
5136
+ profileData = updatedRecord.data;
5137
+ }
5138
+ if (profileData.auth_method && profileData.auth_method !== "codex-cli") {
5139
+ throw new Error("Unsupported auth method. Codex CLI profiles only.");
5140
+ }
5141
+ const codexFormat = this.convertToCodexFormat(profileData);
5142
+ const profileHome = await this.prepareProfileHome(profileName, codexFormat);
5143
+ return {
5144
+ profileHome,
5145
+ env: {
5146
+ ...process.env,
5147
+ HOME: profileHome,
5148
+ USERPROFILE: profileHome,
5149
+ CODEX_HOME: profileHome
5150
+ }
5151
+ };
5152
+ })
5153
+ );
5154
+ }
5155
+ async syncProfileRuntime(name, profileHome) {
5156
+ return this.enqueueProfileOperation(
5157
+ name,
5158
+ () => this.enqueueAuthSwap(async () => {
5159
+ await this.initialize();
5160
+ const profileName = this.normalizeProfileName(name);
5161
+ const record = await this.getProfileRowByName(profileName);
5162
+ if (!record) {
5163
+ throw new Error(`Profile '${profileName}' not found!`);
5164
+ }
5165
+ const authPath = import_path.default.join(profileHome ?? this.getProfileHomePath(profileName), "auth.json");
5166
+ let finalAuthContent = null;
5167
+ try {
5168
+ finalAuthContent = await import_fs.promises.readFile(authPath, "utf8");
5169
+ } catch (error) {
5170
+ if (!this.isNotFoundError(error)) {
5171
+ logWarn(`Failed to read isolated auth for '${profileName}' while syncing runtime:`, error);
5172
+ }
5173
+ }
5174
+ if (finalAuthContent) {
5175
+ await this.syncProfileTokensFromAuthContent(profileName, record.data, finalAuthContent);
5176
+ return;
5177
+ }
5178
+ await this.syncProfileTokensFromActiveAuth(profileName, record.data, authPath);
5179
+ })
5180
+ );
5181
+ }
5182
+ /**
5342
5183
  * Create a new profile by running codex login
5343
5184
  */
5344
5185
  async createProfile(name) {
@@ -5699,120 +5540,1555 @@ var ProfileManager = class {
5699
5540
  }
5700
5541
  return { name: null, trusted: false };
5701
5542
  }
5702
- /**
5703
- * Export a profile to a portable format.
5704
- * Sensitive tokens are included but should be handled securely.
5705
- */
5706
- async exportProfile(name) {
5707
- await this.initialize();
5708
- const profileName = this.normalizeProfileName(name);
5709
- const record = await this.getProfileRowByName(profileName);
5710
- if (!record) {
5711
- throw new Error(`Profile '${profileName}' not found!`);
5543
+ /**
5544
+ * Export a profile to a portable format.
5545
+ * Sensitive tokens are included but should be handled securely.
5546
+ */
5547
+ async exportProfile(name) {
5548
+ await this.initialize();
5549
+ const profileName = this.normalizeProfileName(name);
5550
+ const record = await this.getProfileRowByName(profileName);
5551
+ if (!record) {
5552
+ throw new Error(`Profile '${profileName}' not found!`);
5553
+ }
5554
+ return {
5555
+ name: record.name,
5556
+ displayName: record.displayName,
5557
+ email: record.email,
5558
+ accountId: record.accountId,
5559
+ workspaceId: record.workspaceId,
5560
+ workspaceName: record.workspaceName,
5561
+ authMethod: record.authMethod,
5562
+ createdAt: record.createdAt,
5563
+ metadata: record.metadata,
5564
+ exportedAt: (/* @__PURE__ */ new Date()).toISOString()
5565
+ };
5566
+ }
5567
+ /**
5568
+ * Export profiles for cloud sync (includes token-bearing auth data).
5569
+ */
5570
+ async exportProfilesForCloudSync() {
5571
+ await this.initialize();
5572
+ const records = await this.listProfileRecords();
5573
+ return records.map((record) => ({
5574
+ name: record.name,
5575
+ displayName: record.displayName,
5576
+ data: record.data,
5577
+ metadata: record.metadata,
5578
+ accountId: record.accountId,
5579
+ workspaceId: record.workspaceId,
5580
+ workspaceName: record.workspaceName,
5581
+ email: record.email,
5582
+ authMethod: record.authMethod,
5583
+ createdAt: record.createdAt,
5584
+ updatedAt: record.updatedAt
5585
+ }));
5586
+ }
5587
+ /**
5588
+ * Replace local profiles with a full snapshot from cloud sync.
5589
+ */
5590
+ async replaceProfilesFromCloudSync(records) {
5591
+ await this.initialize();
5592
+ const now = (/* @__PURE__ */ new Date()).toISOString();
5593
+ const normalized = /* @__PURE__ */ new Map();
5594
+ const existingMap = await this.readProfilesStateMap();
5595
+ const existingNames = new Set(Object.keys(existingMap));
5596
+ for (const candidate of records ?? []) {
5597
+ try {
5598
+ const name = this.normalizeProfileName(candidate?.name ?? "");
5599
+ const data = candidate && typeof candidate.data === "object" && candidate.data !== null ? candidate.data : null;
5600
+ if (!data) {
5601
+ continue;
5602
+ }
5603
+ const metadata = candidate.metadata && typeof candidate.metadata === "object" ? candidate.metadata : void 0;
5604
+ const record = {
5605
+ name,
5606
+ displayName: typeof candidate.displayName === "string" ? candidate.displayName : null,
5607
+ data,
5608
+ metadata,
5609
+ accountId: typeof candidate.accountId === "string" ? candidate.accountId : null,
5610
+ workspaceId: typeof candidate.workspaceId === "string" ? candidate.workspaceId : null,
5611
+ workspaceName: typeof candidate.workspaceName === "string" ? candidate.workspaceName : null,
5612
+ email: typeof candidate.email === "string" ? candidate.email : null,
5613
+ authMethod: typeof candidate.authMethod === "string" ? candidate.authMethod : this.resolveAuthMethod(data),
5614
+ createdAt: typeof candidate.createdAt === "string" ? candidate.createdAt : now,
5615
+ updatedAt: typeof candidate.updatedAt === "string" ? candidate.updatedAt : now
5616
+ };
5617
+ normalized.set(name, record);
5618
+ } catch {
5619
+ }
5620
+ }
5621
+ const nextMap = {};
5622
+ for (const record of normalized.values()) {
5623
+ nextMap[record.name] = this.toStateRecord(record);
5624
+ }
5625
+ await this.writeProfilesStateMap(nextMap);
5626
+ let removed = 0;
5627
+ for (const name of existingNames) {
5628
+ if (normalized.has(name)) {
5629
+ continue;
5630
+ }
5631
+ try {
5632
+ await import_fs.promises.rm(this.getProfileHomePath(name), { recursive: true, force: true });
5633
+ removed += 1;
5634
+ } catch (error) {
5635
+ if (!this.isNotFoundError(error)) {
5636
+ logWarn(`Failed to remove profile '${name}' during cloud sync replace:`, error);
5637
+ }
5638
+ }
5639
+ }
5640
+ const preferred = await this.readPreferredProfileName();
5641
+ if (preferred) {
5642
+ try {
5643
+ const normalizedPreferred = this.normalizeProfileName(preferred);
5644
+ if (!normalized.has(normalizedPreferred)) {
5645
+ await this.persistPreferredProfileName(null);
5646
+ }
5647
+ } catch {
5648
+ await this.persistPreferredProfileName(null);
5649
+ }
5650
+ }
5651
+ return {
5652
+ imported: normalized.size,
5653
+ removed
5654
+ };
5655
+ }
5656
+ };
5657
+
5658
+ // src/app/help.ts
5659
+ function printHelp(version) {
5660
+ console.log(`CodexUse CLI v${version}
5661
+
5662
+ Usage:
5663
+ codexuse account-pool status [--runtime=desktop|daemon] [--state-dir=/abs/path] [--port=NNNN]
5664
+ codexuse account-pool enable
5665
+ codexuse account-pool disable
5666
+ codexuse account-pool routing <least-used|round-robin>
5667
+ codexuse account-pool profiles list
5668
+ codexuse account-pool profiles set <name> [more...]
5669
+ codexuse account-pool models list
5670
+ codexuse account-pool keys list [--runtime=desktop|daemon]
5671
+ codexuse account-pool keys create [--runtime=desktop|daemon]
5672
+ codexuse account-pool sessions list [--runtime=desktop|daemon]
5673
+
5674
+ codexuse profile list [--no-usage] [--compact]
5675
+ codexuse profile current
5676
+ codexuse profile add <name> [--skip-login] [--device-auth] [--login=browser|device]
5677
+ codexuse profile refresh <name> [--skip-login] [--device-auth] [--login=browser|device]
5678
+ codexuse profile switch <name>
5679
+ codexuse profile autoroll [--threshold=50-100] [--dry-run] [--watch] [--interval=seconds]
5680
+ codexuse profile delete <name>
5681
+ codexuse profile rename <old> <new>
5682
+
5683
+ codexuse license status [--refresh]
5684
+ codexuse license activate <license-key>
5685
+
5686
+ codexuse sync status
5687
+ codexuse sync pull
5688
+ codexuse sync push
5689
+
5690
+ codexuse daemon start [--port=NNNN] [--telegram-bot-token=<token>] [--project-path=/abs/path]
5691
+
5692
+ Flags:
5693
+ -h, --help Show help
5694
+ -v, --version Show version
5695
+ --no-usage Skip rate-limit usage fetch
5696
+ --compact Names only
5697
+ --device-auth Use device auth for Codex login
5698
+ --login=MODE Login mode: browser | device
5699
+ --threshold=NN Auto-roll switch threshold percent (50-100)
5700
+ --watch Keep checking and auto-switch when threshold is reached
5701
+ --interval=SEC Watch interval in seconds (default: 30)
5702
+ --dry-run Print planned switch without changing active profile
5703
+ --runtime=NAME Accounts Pool runtime store: desktop | daemon
5704
+ --state-dir=PATH Override the runtime state dir for Accounts Pool inspection
5705
+ --port=NNNN Stable daemon/API port (or use with account-pool status for daemon base URL)
5706
+ --telegram-bot-token=TOKEN Enable Telegram remote control for daemon mode
5707
+ --project-path=PATH Optional path to bootstrap as the default Projects root in daemon mode
5708
+ `);
5709
+ }
5710
+
5711
+ // src/platform/args.ts
5712
+ var DEFAULT_AUTOROLL_INTERVAL_SECONDS = 30;
5713
+ var DEFAULT_AUTOROLL_THRESHOLD = 95;
5714
+ function hasFlag(args, flag) {
5715
+ return args.includes(flag);
5716
+ }
5717
+ function stripFlags(args) {
5718
+ return args.filter((arg) => !arg.startsWith("-"));
5719
+ }
5720
+ function parseLoginMode(flags) {
5721
+ if (flags.includes("--device-auth")) return "device";
5722
+ const explicit = flags.find((flag) => flag.startsWith("--login="));
5723
+ if (!explicit) return null;
5724
+ const value = explicit.split("=")[1];
5725
+ if (value === "browser" || value === "device") {
5726
+ return value;
5727
+ }
5728
+ return null;
5729
+ }
5730
+ function parseNumericFlag(flags, name) {
5731
+ const explicit = flags.find((flag) => flag.startsWith(`${name}=`));
5732
+ if (!explicit) {
5733
+ return null;
5734
+ }
5735
+ const raw = explicit.slice(`${name}=`.length);
5736
+ const value = Number.parseFloat(raw);
5737
+ return Number.isFinite(value) ? value : Number.NaN;
5738
+ }
5739
+ function parseIntegerFlag(flags, name) {
5740
+ const explicit = flags.find((flag) => flag.startsWith(`${name}=`));
5741
+ if (!explicit) {
5742
+ return null;
5743
+ }
5744
+ const raw = explicit.slice(`${name}=`.length);
5745
+ const value = Number.parseInt(raw, 10);
5746
+ return Number.isFinite(value) ? value : Number.NaN;
5747
+ }
5748
+ function parseStringFlag(flags, name) {
5749
+ const explicit = flags.find((flag) => flag.startsWith(`${name}=`));
5750
+ if (!explicit) {
5751
+ return null;
5752
+ }
5753
+ const value = explicit.slice(`${name}=`.length).trim();
5754
+ return value.length > 0 ? value : "";
5755
+ }
5756
+
5757
+ // src/commands/accountPool.ts
5758
+ var ACCOUNT_POOL_DOCUMENT = "desktop.account-pool.v2";
5759
+ var LEGACY_BROKER_DOCUMENT = "desktop.broker.v1";
5760
+ var ACCOUNT_POOL_PRO_REQUIRED_MESSAGE = "Accounts Pool requires a Pro license.";
5761
+ var DEFAULT_DAEMON_STATE_DIR = import_node_path7.default.join(import_node_os3.default.homedir(), ".codexuse", "t3-daemon");
5762
+ var ACCOUNT_POOL_STORE_VERSION = 3;
5763
+ var MAX_RESPONSE_IDS_PER_SESSION = 64;
5764
+ var MODEL_SLUG_ALIASES = {
5765
+ "5.4": "gpt-5.4",
5766
+ "5.3": "gpt-5.3-codex",
5767
+ "gpt-5.3": "gpt-5.3-codex",
5768
+ "5.3-spark": "gpt-5.3-codex-spark",
5769
+ "gpt-5.3-spark": "gpt-5.3-codex-spark"
5770
+ };
5771
+ function asRecord2(value) {
5772
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
5773
+ }
5774
+ function asString3(value) {
5775
+ if (typeof value !== "string") {
5776
+ return null;
5777
+ }
5778
+ const trimmed = value.trim();
5779
+ return trimmed.length > 0 ? trimmed : null;
5780
+ }
5781
+ function asNumber(value) {
5782
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
5783
+ }
5784
+ function normalizeIsoString(value) {
5785
+ const normalized = asString3(value);
5786
+ if (!normalized) {
5787
+ return null;
5788
+ }
5789
+ const parsed = Date.parse(normalized);
5790
+ return Number.isNaN(parsed) ? null : new Date(parsed).toISOString();
5791
+ }
5792
+ function normalizeStringArray(value) {
5793
+ const input = Array.isArray(value) ? value : [];
5794
+ const result = [];
5795
+ const seen = /* @__PURE__ */ new Set();
5796
+ for (const entry of input) {
5797
+ const normalized = asString3(entry);
5798
+ if (!normalized || seen.has(normalized)) {
5799
+ continue;
5800
+ }
5801
+ seen.add(normalized);
5802
+ result.push(normalized);
5803
+ }
5804
+ return result;
5805
+ }
5806
+ function normalizeReasoningEffort(value) {
5807
+ const normalized = asString3(value)?.toLowerCase() ?? null;
5808
+ return normalized && normalized.length > 0 ? normalized : null;
5809
+ }
5810
+ function normalizeRoutingStrategy(value) {
5811
+ return value === "round_robin" ? "round_robin" : "least_used";
5812
+ }
5813
+ function normalizeCodexModelSlug(value) {
5814
+ const trimmed = value?.trim();
5815
+ if (!trimmed) {
5816
+ return null;
5817
+ }
5818
+ return MODEL_SLUG_ALIASES[trimmed] ?? trimmed;
5819
+ }
5820
+ function formatRoutingStrategy(value) {
5821
+ return value === "round_robin" ? "round-robin" : "least-used";
5822
+ }
5823
+ function parseRoutingStrategy(value) {
5824
+ const normalized = value?.trim().toLowerCase();
5825
+ if (!normalized) {
5826
+ return null;
5827
+ }
5828
+ if (normalized === "round-robin" || normalized === "round_robin" || normalized === "roundrobin") {
5829
+ return "round_robin";
5830
+ }
5831
+ if (normalized === "least-used" || normalized === "least_used" || normalized === "leastused") {
5832
+ return "least_used";
5833
+ }
5834
+ return null;
5835
+ }
5836
+ function normalizeAccountPoolExposedModels(value, fallbackToDefault) {
5837
+ const result = [];
5838
+ const seen = /* @__PURE__ */ new Set();
5839
+ const input = Array.isArray(value) ? value : [];
5840
+ for (const entry of input) {
5841
+ const raw = asString3(entry);
5842
+ const normalized = normalizeCodexModelSlug(raw) ?? raw;
5843
+ if (!normalized || seen.has(normalized)) {
5844
+ continue;
5845
+ }
5846
+ seen.add(normalized);
5847
+ result.push(normalized);
5848
+ }
5849
+ if (result.length > 0) {
5850
+ return result;
5851
+ }
5852
+ return fallbackToDefault ? [...DEFAULT_ACCOUNT_POOL_EXPOSED_MODELS] : [];
5853
+ }
5854
+ function normalizeAccountPoolReasoningEfforts(value, fallback) {
5855
+ const input = Array.isArray(value) ? value : [];
5856
+ const result = [];
5857
+ const seen = /* @__PURE__ */ new Set();
5858
+ for (const entry of input) {
5859
+ const normalized = normalizeReasoningEffort(entry);
5860
+ if (!normalized || seen.has(normalized)) {
5861
+ continue;
5862
+ }
5863
+ seen.add(normalized);
5864
+ result.push(normalized);
5865
+ }
5866
+ if (result.length > 0 || Array.isArray(value)) {
5867
+ return result;
5868
+ }
5869
+ return fallback ? [...fallback] : [];
5870
+ }
5871
+ function normalizeAccountPoolSettings(settings) {
5872
+ const accountPoolProfilePool = normalizeStringArray(settings.accountPoolProfilePool);
5873
+ return {
5874
+ accountPoolEnabled: settings.accountPoolEnabled === true || settings.brokerEnabled === true,
5875
+ accountPoolRoutingStrategy: normalizeRoutingStrategy(
5876
+ settings.accountPoolRoutingStrategy ?? settings.brokerRoutingStrategy
5877
+ ),
5878
+ accountPoolProfilePool: accountPoolProfilePool.length > 0 ? accountPoolProfilePool : normalizeStringArray(settings.brokerProfilePool),
5879
+ accountPoolExposedModels: normalizeAccountPoolExposedModels(
5880
+ settings.accountPoolExposedModels,
5881
+ true
5882
+ ),
5883
+ accountPoolExposedReasoningEfforts: normalizeAccountPoolReasoningEfforts(
5884
+ settings.accountPoolExposedReasoningEfforts,
5885
+ DEFAULT_ACCOUNT_POOL_EXPOSED_REASONING_EFFORTS
5886
+ ),
5887
+ accountPoolExposedFastModeReasoningEfforts: normalizeAccountPoolReasoningEfforts(
5888
+ settings.accountPoolExposedFastModeReasoningEfforts,
5889
+ DEFAULT_ACCOUNT_POOL_EXPOSED_FAST_MODE_REASONING_EFFORTS
5890
+ )
5891
+ };
5892
+ }
5893
+ function applyAccountPoolSettings(settings, nextAccountPoolSettings) {
5894
+ const next = {
5895
+ ...settings,
5896
+ accountPoolEnabled: nextAccountPoolSettings.accountPoolEnabled,
5897
+ accountPoolRoutingStrategy: nextAccountPoolSettings.accountPoolRoutingStrategy,
5898
+ accountPoolProfilePool: nextAccountPoolSettings.accountPoolProfilePool.slice(),
5899
+ accountPoolExposedModels: nextAccountPoolSettings.accountPoolExposedModels.slice(),
5900
+ accountPoolExposedReasoningEfforts: nextAccountPoolSettings.accountPoolExposedReasoningEfforts.slice(),
5901
+ accountPoolExposedFastModeReasoningEfforts: nextAccountPoolSettings.accountPoolExposedFastModeReasoningEfforts.slice()
5902
+ };
5903
+ delete next.brokerEnabled;
5904
+ delete next.brokerRoutingStrategy;
5905
+ delete next.brokerProfilePool;
5906
+ return next;
5907
+ }
5908
+ function printAccountPoolHelp(version) {
5909
+ console.log(`CodexUse CLI v${version}
5910
+
5911
+ Usage:
5912
+ codexuse account-pool status [--runtime=desktop|daemon] [--state-dir=/abs/path] [--port=NNNN]
5913
+ codexuse account-pool enable
5914
+ codexuse account-pool disable
5915
+ codexuse account-pool routing <least-used|round-robin>
5916
+
5917
+ codexuse account-pool profiles list
5918
+ codexuse account-pool profiles set <name> [more...]
5919
+ codexuse account-pool profiles add <name> [more...]
5920
+ codexuse account-pool profiles remove <name> [more...]
5921
+ codexuse account-pool profiles reset
5922
+
5923
+ codexuse account-pool models list
5924
+ codexuse account-pool models add <model> [more...]
5925
+ codexuse account-pool models remove <model> [more...]
5926
+ codexuse account-pool models reset
5927
+
5928
+ codexuse account-pool reasoning list
5929
+ codexuse account-pool reasoning add <effort> [more...]
5930
+ codexuse account-pool reasoning remove <effort> [more...]
5931
+ codexuse account-pool reasoning reset
5932
+
5933
+ codexuse account-pool fast-mode list
5934
+ codexuse account-pool fast-mode add <effort> [more...]
5935
+ codexuse account-pool fast-mode remove <effort> [more...]
5936
+ codexuse account-pool fast-mode reset
5937
+
5938
+ codexuse account-pool keys list [--runtime=desktop|daemon] [--state-dir=/abs/path]
5939
+ codexuse account-pool keys create [--runtime=desktop|daemon] [--state-dir=/abs/path]
5940
+ codexuse account-pool keys revoke <id> [--runtime=desktop|daemon] [--state-dir=/abs/path]
5941
+
5942
+ codexuse account-pool sessions list [--runtime=desktop|daemon] [--state-dir=/abs/path]
5943
+
5944
+ Notes:
5945
+ - Settings are shared with the desktop app.
5946
+ - Keys and sessions are runtime-specific: desktop and daemon each keep their own pool store.
5947
+ - Session inspection is read-only here on purpose; pooled sessions are live runtime state.
5948
+ - Use --runtime=daemon plus --state-dir or --port when you want to inspect a headless daemon.
5949
+ `);
5950
+ }
5951
+ function normalizeSessionStatus(value) {
5952
+ return value === "running" || value === "error" || value === "rollover_pending" ? value : "idle";
5953
+ }
5954
+ function normalizeApiKeyRecord(id, value) {
5955
+ const record = asRecord2(value);
5956
+ const tokenHash = asString3(record.tokenHash);
5957
+ const tokenPreview = asString3(record.tokenPreview);
5958
+ const createdAt = normalizeIsoString(record.createdAt);
5959
+ if (!tokenHash || !tokenPreview || !createdAt) {
5960
+ return null;
5961
+ }
5962
+ return {
5963
+ id,
5964
+ tokenHash,
5965
+ tokenPreview,
5966
+ createdAt,
5967
+ lastUsedAt: normalizeIsoString(record.lastUsedAt),
5968
+ revokedAt: normalizeIsoString(record.revokedAt)
5969
+ };
5970
+ }
5971
+ function normalizeLegacySessionRecord(id, value) {
5972
+ const record = asRecord2(value);
5973
+ const profileName = asString3(record.profileName);
5974
+ const createdAt = normalizeIsoString(record.createdAt);
5975
+ const lastUsedAt = normalizeIsoString(record.lastUsedAt);
5976
+ if (!profileName || !createdAt || !lastUsedAt) {
5977
+ return null;
5978
+ }
5979
+ const affinityKind = record.affinityKind === "broker_session" || record.affinityKind === "prompt_cache" ? record.affinityKind : "ephemeral";
5980
+ return {
5981
+ id,
5982
+ affinityKind,
5983
+ affinityKey: asString3(record.affinityKey),
5984
+ profileName,
5985
+ threadId: asString3(record.threadId) ?? id,
5986
+ model: asString3(record.model),
5987
+ status: normalizeSessionStatus(record.status),
5988
+ lastError: asString3(record.lastError),
5989
+ createdAt,
5990
+ lastUsedAt,
5991
+ expiresAt: normalizeIsoString(record.expiresAt),
5992
+ lastResponseId: asString3(record.lastResponseId),
5993
+ responseIds: normalizeStringArray(record.responseIds).slice(0, MAX_RESPONSE_IDS_PER_SESSION)
5994
+ };
5995
+ }
5996
+ function normalizeLegacyStore(value) {
5997
+ const record = asRecord2(value);
5998
+ const apiKeysById = {};
5999
+ for (const [id, apiKeyValue] of Object.entries(asRecord2(record.apiKeysById))) {
6000
+ const normalized = normalizeApiKeyRecord(id, apiKeyValue);
6001
+ if (normalized) {
6002
+ apiKeysById[id] = normalized;
6003
+ }
6004
+ }
6005
+ const sessionsById = {};
6006
+ for (const [id, sessionValue] of Object.entries(asRecord2(record.sessionsById))) {
6007
+ const normalized = normalizeLegacySessionRecord(id, sessionValue);
6008
+ if (normalized) {
6009
+ sessionsById[id] = normalized;
6010
+ }
6011
+ }
6012
+ const responseIndex = {};
6013
+ for (const [responseId, sessionId] of Object.entries(asRecord2(record.responseIndex))) {
6014
+ const normalizedSessionId = asString3(sessionId);
6015
+ if (normalizedSessionId) {
6016
+ responseIndex[responseId] = normalizedSessionId;
6017
+ }
6018
+ }
6019
+ return {
6020
+ version: asNumber(record.version) ?? 1,
6021
+ roundRobinCursor: asNumber(record.roundRobinCursor) ?? 0,
6022
+ apiKeysById,
6023
+ sessionsById,
6024
+ responseIndex
6025
+ };
6026
+ }
6027
+ function normalizeSessionRecord(id, value) {
6028
+ const record = asRecord2(value);
6029
+ const activeSegmentId = asString3(record.activeSegmentId);
6030
+ const createdAt = normalizeIsoString(record.createdAt);
6031
+ const lastUsedAt = normalizeIsoString(record.lastUsedAt);
6032
+ if (!activeSegmentId || !createdAt || !lastUsedAt) {
6033
+ return null;
6034
+ }
6035
+ const affinityKind = record.affinityKind === "pool_session" || record.affinityKind === "prompt_cache" ? record.affinityKind : "ephemeral";
6036
+ return {
6037
+ id,
6038
+ affinityKind,
6039
+ affinityKey: asString3(record.affinityKey),
6040
+ activeSegmentId,
6041
+ segmentIds: normalizeStringArray(record.segmentIds),
6042
+ model: asString3(record.model),
6043
+ status: normalizeSessionStatus(record.status),
6044
+ lastError: asString3(record.lastError),
6045
+ createdAt,
6046
+ lastUsedAt,
6047
+ expiresAt: normalizeIsoString(record.expiresAt),
6048
+ lastResponseId: asString3(record.lastResponseId),
6049
+ responseIds: normalizeStringArray(record.responseIds).slice(0, MAX_RESPONSE_IDS_PER_SESSION),
6050
+ rolloverCount: asNumber(record.rolloverCount) ?? 0,
6051
+ lastRolloverReason: asString3(record.lastRolloverReason)
6052
+ };
6053
+ }
6054
+ function normalizeSegmentRecord(id, value) {
6055
+ const record = asRecord2(value);
6056
+ const sessionId = asString3(record.sessionId);
6057
+ const profileName = asString3(record.profileName);
6058
+ const createdAt = normalizeIsoString(record.createdAt);
6059
+ const lastUsedAt = normalizeIsoString(record.lastUsedAt);
6060
+ if (!sessionId || !profileName || !createdAt || !lastUsedAt) {
6061
+ return null;
6062
+ }
6063
+ return {
6064
+ id,
6065
+ sessionId,
6066
+ profileName,
6067
+ createdAt,
6068
+ lastUsedAt
6069
+ };
6070
+ }
6071
+ function normalizeResponseIndexRecord(value) {
6072
+ const record = asRecord2(value);
6073
+ const sessionId = asString3(record.sessionId);
6074
+ const segmentId = asString3(record.segmentId);
6075
+ const createdAt = normalizeIsoString(record.createdAt) ?? (/* @__PURE__ */ new Date(0)).toISOString();
6076
+ if (!sessionId || !segmentId) {
6077
+ return null;
6078
+ }
6079
+ return { sessionId, segmentId, createdAt };
6080
+ }
6081
+ function normalizeAccountPoolStore(value) {
6082
+ const record = asRecord2(value);
6083
+ const apiKeysById = {};
6084
+ for (const [id, apiKeyValue] of Object.entries(asRecord2(record.apiKeysById))) {
6085
+ const normalized = normalizeApiKeyRecord(id, apiKeyValue);
6086
+ if (normalized) {
6087
+ apiKeysById[id] = normalized;
6088
+ }
6089
+ }
6090
+ const sessionsById = {};
6091
+ for (const [id, sessionValue] of Object.entries(asRecord2(record.sessionsById))) {
6092
+ const normalized = normalizeSessionRecord(id, sessionValue);
6093
+ if (normalized) {
6094
+ sessionsById[id] = normalized;
6095
+ }
6096
+ }
6097
+ const segmentsById = {};
6098
+ for (const [id, segmentValue] of Object.entries(asRecord2(record.segmentsById))) {
6099
+ const normalized = normalizeSegmentRecord(id, segmentValue);
6100
+ if (normalized) {
6101
+ segmentsById[id] = normalized;
6102
+ }
6103
+ }
6104
+ const responseIndex = {};
6105
+ for (const [responseId, responseValue] of Object.entries(asRecord2(record.responseIndex))) {
6106
+ const normalized = normalizeResponseIndexRecord(responseValue);
6107
+ if (normalized) {
6108
+ responseIndex[responseId] = normalized;
6109
+ }
6110
+ }
6111
+ return {
6112
+ version: asNumber(record.version) ?? ACCOUNT_POOL_STORE_VERSION,
6113
+ roundRobinCursor: asNumber(record.roundRobinCursor) ?? 0,
6114
+ apiKeysById,
6115
+ sessionsById,
6116
+ segmentsById,
6117
+ responseIndex
6118
+ };
6119
+ }
6120
+ function createDefaultStore() {
6121
+ return {
6122
+ version: ACCOUNT_POOL_STORE_VERSION,
6123
+ roundRobinCursor: 0,
6124
+ apiKeysById: {},
6125
+ sessionsById: {},
6126
+ segmentsById: {},
6127
+ responseIndex: {}
6128
+ };
6129
+ }
6130
+ function migrateLegacyStore(legacyStore) {
6131
+ const store = createDefaultStore();
6132
+ store.roundRobinCursor = legacyStore.roundRobinCursor;
6133
+ store.apiKeysById = { ...legacyStore.apiKeysById };
6134
+ for (const session of Object.values(legacyStore.sessionsById)) {
6135
+ const segmentId = `segment_${session.id}`;
6136
+ store.segmentsById[segmentId] = {
6137
+ id: segmentId,
6138
+ sessionId: session.id,
6139
+ profileName: session.profileName,
6140
+ createdAt: session.createdAt,
6141
+ lastUsedAt: session.lastUsedAt
6142
+ };
6143
+ store.sessionsById[session.id] = {
6144
+ id: session.id,
6145
+ affinityKind: session.affinityKind === "broker_session" ? "pool_session" : session.affinityKind,
6146
+ affinityKey: session.affinityKey,
6147
+ activeSegmentId: segmentId,
6148
+ segmentIds: [segmentId],
6149
+ model: session.model,
6150
+ status: session.status,
6151
+ lastError: session.lastError,
6152
+ createdAt: session.createdAt,
6153
+ lastUsedAt: session.lastUsedAt,
6154
+ expiresAt: session.expiresAt,
6155
+ lastResponseId: session.lastResponseId,
6156
+ responseIds: session.responseIds.slice(0, MAX_RESPONSE_IDS_PER_SESSION),
6157
+ rolloverCount: 0,
6158
+ lastRolloverReason: null
6159
+ };
6160
+ }
6161
+ for (const [responseId, sessionId] of Object.entries(legacyStore.responseIndex)) {
6162
+ const session = store.sessionsById[sessionId];
6163
+ if (!session) {
6164
+ continue;
6165
+ }
6166
+ store.responseIndex[responseId] = {
6167
+ sessionId,
6168
+ segmentId: session.activeSegmentId,
6169
+ createdAt: session.lastUsedAt
6170
+ };
6171
+ }
6172
+ return store;
6173
+ }
6174
+ function deleteSessionFromStore(store, sessionId) {
6175
+ const session = store.sessionsById[sessionId];
6176
+ if (!session) {
6177
+ return;
6178
+ }
6179
+ for (const segmentId of session.segmentIds) {
6180
+ delete store.segmentsById[segmentId];
6181
+ }
6182
+ for (const [responseId, response] of Object.entries(store.responseIndex)) {
6183
+ if (response.sessionId === sessionId) {
6184
+ delete store.responseIndex[responseId];
6185
+ }
6186
+ }
6187
+ delete store.sessionsById[sessionId];
6188
+ }
6189
+ function pruneStore(store) {
6190
+ const now = Date.now();
6191
+ for (const [sessionId, session] of Object.entries(store.sessionsById)) {
6192
+ const expiresAtMs = session.expiresAt ? Date.parse(session.expiresAt) : Number.NaN;
6193
+ if (Number.isFinite(expiresAtMs) && expiresAtMs < now) {
6194
+ deleteSessionFromStore(store, sessionId);
6195
+ }
6196
+ }
6197
+ for (const [segmentId, segment] of Object.entries(store.segmentsById)) {
6198
+ if (!store.sessionsById[segment.sessionId]) {
6199
+ delete store.segmentsById[segmentId];
6200
+ }
6201
+ }
6202
+ for (const [responseId, response] of Object.entries(store.responseIndex)) {
6203
+ if (!store.sessionsById[response.sessionId] || !store.segmentsById[response.segmentId]) {
6204
+ delete store.responseIndex[responseId];
6205
+ }
6206
+ }
6207
+ return store;
6208
+ }
6209
+ function resolveAccountPoolDbPath(stateDir) {
6210
+ return import_node_path7.default.join(stateDir, "state.sqlite");
6211
+ }
6212
+ async function readLegacyStore(dbPath) {
6213
+ return readDocument(dbPath, LEGACY_BROKER_DOCUMENT, normalizeLegacyStore);
6214
+ }
6215
+ async function readStore(stateDir) {
6216
+ const dbPath = resolveAccountPoolDbPath(stateDir);
6217
+ const store = await readDocument(
6218
+ dbPath,
6219
+ ACCOUNT_POOL_DOCUMENT,
6220
+ normalizeAccountPoolStore
6221
+ );
6222
+ if (store) {
6223
+ return pruneStore(store);
6224
+ }
6225
+ const legacyStore = await readLegacyStore(dbPath);
6226
+ if (!legacyStore) {
6227
+ return createDefaultStore();
6228
+ }
6229
+ return pruneStore(migrateLegacyStore(legacyStore));
6230
+ }
6231
+ async function updateStore(stateDir, mutate) {
6232
+ const dbPath = resolveAccountPoolDbPath(stateDir);
6233
+ const legacyStore = await readLegacyStore(dbPath);
6234
+ return updateDocument({
6235
+ dbPath,
6236
+ namespace: ACCOUNT_POOL_DOCUMENT,
6237
+ normalize: normalizeAccountPoolStore,
6238
+ fallback: () => legacyStore ? migrateLegacyStore(legacyStore) : createDefaultStore(),
6239
+ transform: (current) => {
6240
+ const next = pruneStore(current);
6241
+ mutate(next);
6242
+ return pruneStore(next);
6243
+ }
6244
+ });
6245
+ }
6246
+ function accountPoolApiKeyView(record) {
6247
+ return {
6248
+ id: record.id,
6249
+ tokenPreview: record.tokenPreview,
6250
+ createdAt: record.createdAt,
6251
+ lastUsedAt: record.lastUsedAt,
6252
+ revokedAt: record.revokedAt
6253
+ };
6254
+ }
6255
+ async function listApiKeys(stateDir) {
6256
+ const store = await readStore(stateDir);
6257
+ return Object.values(store.apiKeysById).filter((key) => key.revokedAt === null).sort((left, right) => right.createdAt.localeCompare(left.createdAt)).map(accountPoolApiKeyView);
6258
+ }
6259
+ function hashToken(token) {
6260
+ return (0, import_node_crypto4.createHash)("sha256").update(token).digest("hex");
6261
+ }
6262
+ async function createApiKey(stateDir) {
6263
+ const plaintextToken = `cux_pool_${(0, import_node_crypto4.randomBytes)(24).toString("hex")}`;
6264
+ const now = (/* @__PURE__ */ new Date()).toISOString();
6265
+ const keyRecord = {
6266
+ id: (0, import_node_crypto4.randomUUID)(),
6267
+ tokenHash: hashToken(plaintextToken),
6268
+ tokenPreview: `${plaintextToken.slice(0, 8)}...${plaintextToken.slice(-6)}`,
6269
+ createdAt: now,
6270
+ lastUsedAt: null,
6271
+ revokedAt: null
6272
+ };
6273
+ await updateStore(stateDir, (store) => {
6274
+ store.apiKeysById[keyRecord.id] = keyRecord;
6275
+ });
6276
+ return {
6277
+ key: accountPoolApiKeyView(keyRecord),
6278
+ plaintextToken
6279
+ };
6280
+ }
6281
+ async function revokeApiKey(stateDir, id) {
6282
+ let revoked = false;
6283
+ await updateStore(stateDir, (store) => {
6284
+ const existing = store.apiKeysById[id];
6285
+ if (!existing || existing.revokedAt !== null) {
6286
+ return;
6287
+ }
6288
+ existing.revokedAt = (/* @__PURE__ */ new Date()).toISOString();
6289
+ revoked = true;
6290
+ });
6291
+ return revoked;
6292
+ }
6293
+ async function listSessions(stateDir) {
6294
+ const store = await readStore(stateDir);
6295
+ return Object.values(store.sessionsById).sort((left, right) => right.lastUsedAt.localeCompare(left.lastUsedAt)).flatMap((session) => {
6296
+ const activeSegment = store.segmentsById[session.activeSegmentId];
6297
+ if (!activeSegment) {
6298
+ return [];
6299
+ }
6300
+ return [{
6301
+ id: session.id,
6302
+ affinityKind: session.affinityKind,
6303
+ affinityKey: session.affinityKey,
6304
+ activeProfileName: activeSegment.profileName,
6305
+ model: session.model,
6306
+ status: session.status,
6307
+ lastError: session.lastError,
6308
+ createdAt: session.createdAt,
6309
+ lastUsedAt: session.lastUsedAt,
6310
+ expiresAt: session.expiresAt,
6311
+ lastResponseId: session.lastResponseId,
6312
+ responseCount: session.responseIds.length,
6313
+ segmentCount: session.segmentIds.length,
6314
+ rolloverCount: session.rolloverCount,
6315
+ lastRolloverReason: session.lastRolloverReason
6316
+ }];
6317
+ });
6318
+ }
6319
+ function resolveRuntimeStateDir(runtime, explicitStateDir) {
6320
+ if (explicitStateDir) {
6321
+ return import_node_path7.default.resolve(explicitStateDir);
6322
+ }
6323
+ if (runtime === "daemon") {
6324
+ return DEFAULT_DAEMON_STATE_DIR;
6325
+ }
6326
+ return import_node_path7.default.join(getUserDataDir(), "t3-projects");
6327
+ }
6328
+ function resolveStateDir(flags) {
6329
+ const runtimeFlag = parseStringFlag(flags, "--runtime")?.trim().toLowerCase();
6330
+ const runtime = runtimeFlag === "daemon" ? "daemon" : "desktop";
6331
+ const explicitStateDir = parseStringFlag(flags, "--state-dir") ?? parseStringFlag(flags, "--state") ?? null;
6332
+ return {
6333
+ runtime,
6334
+ stateDir: resolveRuntimeStateDir(runtime, explicitStateDir)
6335
+ };
6336
+ }
6337
+ function resolveBaseUrlFromPort(runtime, port) {
6338
+ if (runtime === "desktop") {
6339
+ return null;
6340
+ }
6341
+ if (port === null) {
6342
+ return null;
6343
+ }
6344
+ if (!Number.isFinite(port) || port < 1 || port > 65535) {
6345
+ throw new Error("Daemon port must be an integer between 1 and 65535.");
6346
+ }
6347
+ return `http://127.0.0.1:${port}`;
6348
+ }
6349
+ function resolveBaseUrl(runtime, flags) {
6350
+ return resolveBaseUrlFromPort(runtime, parseIntegerFlag(flags, "--port"));
6351
+ }
6352
+ async function readAccountPoolRuntimeSummary(input = {}) {
6353
+ const runtime = input.runtime ?? "desktop";
6354
+ const stateDir = resolveRuntimeStateDir(runtime, input.stateDir ?? null);
6355
+ const [license, { normalized }, apiKeys, sessions] = await Promise.all([
6356
+ licenseService.getStatus().catch(() => null),
6357
+ readSettingsSnapshot(),
6358
+ listApiKeys(stateDir),
6359
+ listSessions(stateDir)
6360
+ ]);
6361
+ return {
6362
+ runtime,
6363
+ stateDir,
6364
+ baseUrl: resolveBaseUrlFromPort(runtime, input.port ?? null),
6365
+ enabled: normalized.accountPoolEnabled,
6366
+ hasProLicense: license?.isPro === true,
6367
+ routingStrategy: normalized.accountPoolRoutingStrategy,
6368
+ activeApiKeyCount: apiKeys.length,
6369
+ activeSessionCount: sessions.length
6370
+ };
6371
+ }
6372
+ async function readSettingsSnapshot() {
6373
+ const raw = asRecord2(await readAppSettings());
6374
+ return {
6375
+ raw,
6376
+ normalized: normalizeAccountPoolSettings(raw)
6377
+ };
6378
+ }
6379
+ async function updateSettings(transform) {
6380
+ const { raw, normalized } = await readSettingsSnapshot();
6381
+ const nextSettings = transform(normalized);
6382
+ const license = await licenseService.getStatus().catch(() => null);
6383
+ const forcedDisabledByLicense = !license?.isPro && nextSettings.accountPoolEnabled === true;
6384
+ const sanitizedSettings = {
6385
+ ...nextSettings,
6386
+ accountPoolEnabled: forcedDisabledByLicense ? false : nextSettings.accountPoolEnabled,
6387
+ accountPoolRoutingStrategy: normalizeRoutingStrategy(nextSettings.accountPoolRoutingStrategy),
6388
+ accountPoolProfilePool: normalizeStringArray(nextSettings.accountPoolProfilePool),
6389
+ accountPoolExposedModels: normalizeAccountPoolExposedModels(
6390
+ nextSettings.accountPoolExposedModels,
6391
+ true
6392
+ ),
6393
+ accountPoolExposedReasoningEfforts: normalizeAccountPoolReasoningEfforts(
6394
+ nextSettings.accountPoolExposedReasoningEfforts,
6395
+ null
6396
+ ),
6397
+ accountPoolExposedFastModeReasoningEfforts: normalizeAccountPoolReasoningEfforts(
6398
+ nextSettings.accountPoolExposedFastModeReasoningEfforts,
6399
+ null
6400
+ )
6401
+ };
6402
+ await writeAppSettings(applyAccountPoolSettings(raw, sanitizedSettings));
6403
+ return {
6404
+ settings: sanitizedSettings,
6405
+ forcedDisabledByLicense
6406
+ };
6407
+ }
6408
+ async function requireProAccess() {
6409
+ const license = await licenseService.getStatus();
6410
+ if (!license.isPro) {
6411
+ throw new Error(ACCOUNT_POOL_PRO_REQUIRED_MESSAGE);
6412
+ }
6413
+ }
6414
+ async function listSavedProfiles() {
6415
+ const manager = new ProfileManager();
6416
+ return manager.listProfiles();
6417
+ }
6418
+ function printStringList(label, values) {
6419
+ console.log(`${label}: ${values.length > 0 ? values.join(", ") : "(none)"}`);
6420
+ }
6421
+ function printSessionRow(session) {
6422
+ const fields = [
6423
+ `id=${session.id}`,
6424
+ `profile=${session.activeProfileName}`,
6425
+ `status=${session.status}`,
6426
+ `model=${session.model ?? "-"}`,
6427
+ `responses=${session.responseCount}`,
6428
+ `segments=${session.segmentCount}`
6429
+ ];
6430
+ if (session.lastResponseId) {
6431
+ fields.push(`lastResponse=${session.lastResponseId}`);
6432
+ }
6433
+ if (session.lastRolloverReason) {
6434
+ fields.push(`rollover=${session.lastRolloverReason}`);
6435
+ }
6436
+ console.log(fields.join(" "));
6437
+ }
6438
+ function printApiKeyRow(apiKey) {
6439
+ const fields = [
6440
+ `id=${apiKey.id}`,
6441
+ `preview=${apiKey.tokenPreview}`,
6442
+ `created=${apiKey.createdAt}`
6443
+ ];
6444
+ if (apiKey.lastUsedAt) {
6445
+ fields.push(`lastUsed=${apiKey.lastUsedAt}`);
6446
+ }
6447
+ console.log(fields.join(" "));
6448
+ }
6449
+ function getArrayValues(params, startIndex) {
6450
+ return params.slice(startIndex).map((value) => value.trim()).filter(Boolean);
6451
+ }
6452
+ async function handleStatus(flags) {
6453
+ const license = await licenseService.getStatus().catch(() => null);
6454
+ const { raw, normalized } = await readSettingsSnapshot();
6455
+ const { runtime, stateDir } = resolveStateDir(flags);
6456
+ const baseUrl = resolveBaseUrl(runtime, flags);
6457
+ const [profiles, apiKeys, sessions] = await Promise.all([
6458
+ listSavedProfiles(),
6459
+ listApiKeys(stateDir),
6460
+ listSessions(stateDir)
6461
+ ]);
6462
+ const explicitSelection = normalized.accountPoolProfilePool;
6463
+ const selectedProfiles = explicitSelection.length > 0 ? explicitSelection : profiles.map((profile) => profile.name);
6464
+ const missingSelectedProfiles = explicitSelection.filter(
6465
+ (profileName) => !profiles.some((profile) => profile.name === profileName)
6466
+ );
6467
+ console.log(`License: ${license ? `${license.tier} (${license.state})` : "unknown"}`);
6468
+ console.log(`Enabled: ${normalized.accountPoolEnabled ? "yes" : "no"}`);
6469
+ console.log(`Runtime: ${runtime}`);
6470
+ console.log(`State dir: ${stateDir}`);
6471
+ console.log(`Routing: ${formatRoutingStrategy(normalized.accountPoolRoutingStrategy)}`);
6472
+ console.log(`Public API ready: ${normalized.accountPoolEnabled && license?.isPro === true && apiKeys.length > 0 ? "yes" : "no"}`);
6473
+ console.log(`API keys: ${apiKeys.length}`);
6474
+ console.log(`Sessions: ${sessions.length}`);
6475
+ console.log(`Selection mode: ${explicitSelection.length > 0 ? "explicit" : "all saved profiles"}`);
6476
+ if (baseUrl) {
6477
+ console.log(`Base URL: ${baseUrl}`);
6478
+ } else {
6479
+ console.log(
6480
+ runtime === "desktop" ? "Base URL: check the running desktop app for the current Accounts Pool URL." : "Base URL: daemon port not specified here; use the URL printed by `codexuse daemon start` or pass --port=NNNN."
6481
+ );
6482
+ }
6483
+ printStringList("Selected profiles", selectedProfiles);
6484
+ if (missingSelectedProfiles.length > 0) {
6485
+ printStringList("Missing configured profiles", missingSelectedProfiles);
6486
+ }
6487
+ printStringList("Published models", normalized.accountPoolExposedModels);
6488
+ printStringList("Reasoning aliases", normalized.accountPoolExposedReasoningEfforts);
6489
+ printStringList("Fast-mode aliases", normalized.accountPoolExposedFastModeReasoningEfforts);
6490
+ if (raw.accountPoolEnabled !== true && raw.brokerEnabled === true) {
6491
+ console.log("Legacy broker setting detected: runtime is reading it through the new Accounts Pool surface.");
6492
+ }
6493
+ if (normalized.accountPoolEnabled && license?.isPro !== true) {
6494
+ console.log(ACCOUNT_POOL_PRO_REQUIRED_MESSAGE);
6495
+ }
6496
+ }
6497
+ async function handleEnable() {
6498
+ await requireProAccess();
6499
+ const result = await updateSettings((current) => ({
6500
+ ...current,
6501
+ accountPoolEnabled: true
6502
+ }));
6503
+ console.log(result.settings.accountPoolEnabled ? "Accounts Pool enabled." : "Accounts Pool is disabled.");
6504
+ }
6505
+ async function handleDisable() {
6506
+ const result = await updateSettings((current) => ({
6507
+ ...current,
6508
+ accountPoolEnabled: false
6509
+ }));
6510
+ console.log(result.settings.accountPoolEnabled ? "Accounts Pool is enabled." : "Accounts Pool disabled.");
6511
+ }
6512
+ async function handleRouting(params) {
6513
+ const value = parseRoutingStrategy(params[1]);
6514
+ if (!value) {
6515
+ throw new Error("Routing strategy must be `least-used` or `round-robin`.");
6516
+ }
6517
+ const result = await updateSettings((current) => ({
6518
+ ...current,
6519
+ accountPoolRoutingStrategy: value
6520
+ }));
6521
+ console.log(`Accounts Pool routing set to ${formatRoutingStrategy(result.settings.accountPoolRoutingStrategy)}.`);
6522
+ }
6523
+ async function handleProfiles(params) {
6524
+ const action = params[1];
6525
+ const profiles = await listSavedProfiles();
6526
+ const availableProfileNames = profiles.map((profile) => profile.name);
6527
+ const availableSet = new Set(availableProfileNames);
6528
+ const { normalized } = await readSettingsSnapshot();
6529
+ if (!action || action === "list") {
6530
+ const explicitSelection = normalized.accountPoolProfilePool;
6531
+ if (explicitSelection.length === 0) {
6532
+ console.log("Selection: all saved profiles");
6533
+ } else {
6534
+ printStringList("Explicitly selected profiles", explicitSelection);
6535
+ }
6536
+ for (const profile of profiles) {
6537
+ const selected = explicitSelection.length === 0 || explicitSelection.includes(profile.name);
6538
+ const invalidSuffix = profile.isValid ? "" : " [needs re-auth]";
6539
+ console.log(`${selected ? "*" : "-"} ${profile.name}${invalidSuffix}`);
6540
+ }
6541
+ const missing = explicitSelection.filter((name) => !availableSet.has(name));
6542
+ if (missing.length > 0) {
6543
+ printStringList("Missing configured profiles", missing);
6544
+ }
6545
+ return;
6546
+ }
6547
+ const names = getArrayValues(params, 2);
6548
+ if ((action === "set" || action === "add" || action === "remove") && names.length === 0) {
6549
+ throw new Error(`Profile names are required for \`${action}\`.`);
6550
+ }
6551
+ for (const name of names) {
6552
+ if (!availableSet.has(name)) {
6553
+ throw new Error(`Unknown profile: ${name}`);
6554
+ }
6555
+ }
6556
+ const result = await updateSettings((current) => {
6557
+ const currentSelection = current.accountPoolProfilePool.length > 0 ? current.accountPoolProfilePool.slice() : availableProfileNames.slice();
6558
+ let nextSelection = currentSelection;
6559
+ switch (action) {
6560
+ case "set":
6561
+ nextSelection = names.slice();
6562
+ break;
6563
+ case "add":
6564
+ nextSelection = Array.from(/* @__PURE__ */ new Set([...currentSelection, ...names]));
6565
+ break;
6566
+ case "remove":
6567
+ nextSelection = currentSelection.filter((name) => !names.includes(name));
6568
+ break;
6569
+ case "reset":
6570
+ nextSelection = [];
6571
+ break;
6572
+ default:
6573
+ throw new Error("Unknown profiles action.");
6574
+ }
6575
+ return {
6576
+ ...current,
6577
+ accountPoolProfilePool: action === "reset" || nextSelection.length === availableProfileNames.length ? [] : nextSelection
6578
+ };
6579
+ });
6580
+ if (result.settings.accountPoolProfilePool.length === 0) {
6581
+ console.log("Accounts Pool now uses all saved profiles.");
6582
+ return;
6583
+ }
6584
+ printStringList("Accounts Pool selected profiles", result.settings.accountPoolProfilePool);
6585
+ }
6586
+ async function handleArraySetting(input) {
6587
+ const action = input.params[1];
6588
+ const { normalized } = await readSettingsSnapshot();
6589
+ const currentValues = normalized[input.key];
6590
+ if (!action || action === "list") {
6591
+ printStringList(input.label, currentValues);
6592
+ return;
6593
+ }
6594
+ const rawValues = getArrayValues(input.params, 2);
6595
+ if ((action === "add" || action === "remove") && rawValues.length === 0) {
6596
+ throw new Error(`Values are required for \`${input.actionName} ${action}\`.`);
6597
+ }
6598
+ const normalizedValues = rawValues.map(input.normalizeValue).filter((value) => Boolean(value));
6599
+ if ((action === "add" || action === "remove") && normalizedValues.length === 0) {
6600
+ throw new Error("No valid values were provided.");
6601
+ }
6602
+ const result = await updateSettings((current) => {
6603
+ const existing = current[input.key];
6604
+ switch (action) {
6605
+ case "add":
6606
+ return {
6607
+ ...current,
6608
+ [input.key]: Array.from(/* @__PURE__ */ new Set([...existing, ...normalizedValues]))
6609
+ };
6610
+ case "remove":
6611
+ return {
6612
+ ...current,
6613
+ [input.key]: existing.filter((value) => !normalizedValues.includes(value))
6614
+ };
6615
+ case "reset":
6616
+ return {
6617
+ ...current,
6618
+ [input.key]: [...input.defaultValues]
6619
+ };
6620
+ default:
6621
+ throw new Error(`Unknown ${input.actionName} action.`);
6622
+ }
6623
+ });
6624
+ printStringList(input.label, result.settings[input.key]);
6625
+ }
6626
+ async function handleKeys(params, flags) {
6627
+ const action = params[1];
6628
+ const { runtime, stateDir } = resolveStateDir(flags);
6629
+ const baseUrl = resolveBaseUrl(runtime, flags);
6630
+ if (!action || action === "list") {
6631
+ const apiKeys = await listApiKeys(stateDir);
6632
+ if (apiKeys.length === 0) {
6633
+ console.log("No active Accounts Pool API keys.");
6634
+ return;
6635
+ }
6636
+ for (const apiKey of apiKeys) {
6637
+ printApiKeyRow(apiKey);
6638
+ }
6639
+ return;
6640
+ }
6641
+ switch (action) {
6642
+ case "create": {
6643
+ await requireProAccess();
6644
+ const created = await createApiKey(stateDir);
6645
+ console.log(`Created key: ${created.key.id}`);
6646
+ console.log(`Preview: ${created.key.tokenPreview}`);
6647
+ console.log(`Token: ${created.plaintextToken}`);
6648
+ console.log(`Runtime: ${runtime}`);
6649
+ console.log(`State dir: ${stateDir}`);
6650
+ if (baseUrl) {
6651
+ console.log(`Base URL: ${baseUrl}`);
6652
+ } else if (runtime === "desktop") {
6653
+ console.log("Base URL: use the current Accounts Pool URL shown by the running desktop app.");
6654
+ } else if (runtime === "daemon") {
6655
+ console.log("Base URL: pass --port=NNNN here or use the URL printed by `codexuse daemon start`.");
6656
+ }
6657
+ console.log("This key only works for the selected runtime state dir.");
6658
+ console.log("Store this token now. The plaintext value is only shown once.");
6659
+ return;
6660
+ }
6661
+ case "revoke": {
6662
+ const id = params[2]?.trim();
6663
+ if (!id) {
6664
+ throw new Error("API key id is required.");
6665
+ }
6666
+ const removed = await revokeApiKey(stateDir, id);
6667
+ if (!removed) {
6668
+ throw new Error(`No Accounts Pool API key found for id ${id}.`);
6669
+ }
6670
+ console.log(`Revoked key: ${id}`);
6671
+ return;
6672
+ }
6673
+ default:
6674
+ throw new Error("Unknown keys action.");
6675
+ }
6676
+ }
6677
+ async function handleSessions(params, flags) {
6678
+ const action = params[1];
6679
+ if (action && action !== "list") {
6680
+ throw new Error("Only `sessions list` is supported here. Session mutation stays runtime-owned.");
6681
+ }
6682
+ const { stateDir } = resolveStateDir(flags);
6683
+ const sessions = await listSessions(stateDir);
6684
+ if (sessions.length === 0) {
6685
+ console.log("No pooled sessions in this runtime store.");
6686
+ return;
6687
+ }
6688
+ for (const session of sessions) {
6689
+ printSessionRow(session);
6690
+ }
6691
+ console.log("Session mutation is intentionally not exposed here; pooled sessions belong to the live runtime owner.");
6692
+ }
6693
+ async function handleAccountPoolCommand(args, version) {
6694
+ const flags = args.filter((arg) => arg.startsWith("-"));
6695
+ const params = stripFlags(args);
6696
+ const sub = params[0];
6697
+ if (!sub || hasFlag(flags, "--help") || hasFlag(flags, "-h")) {
6698
+ printAccountPoolHelp(version);
6699
+ return;
6700
+ }
6701
+ switch (sub) {
6702
+ case "status":
6703
+ await handleStatus(flags);
6704
+ return;
6705
+ case "enable":
6706
+ await handleEnable();
6707
+ return;
6708
+ case "disable":
6709
+ await handleDisable();
6710
+ return;
6711
+ case "routing":
6712
+ await handleRouting(params);
6713
+ return;
6714
+ case "profiles":
6715
+ await handleProfiles(params);
6716
+ return;
6717
+ case "models":
6718
+ await handleArraySetting({
6719
+ label: "Published models",
6720
+ actionName: "models",
6721
+ params,
6722
+ key: "accountPoolExposedModels",
6723
+ defaultValues: DEFAULT_ACCOUNT_POOL_EXPOSED_MODELS,
6724
+ normalizeValue: (value) => normalizeCodexModelSlug(value) ?? asString3(value)
6725
+ });
6726
+ return;
6727
+ case "reasoning":
6728
+ await handleArraySetting({
6729
+ label: "Reasoning aliases",
6730
+ actionName: "reasoning",
6731
+ params,
6732
+ key: "accountPoolExposedReasoningEfforts",
6733
+ defaultValues: DEFAULT_ACCOUNT_POOL_EXPOSED_REASONING_EFFORTS,
6734
+ normalizeValue: (value) => normalizeReasoningEffort(value)
6735
+ });
6736
+ return;
6737
+ case "fast-mode":
6738
+ await handleArraySetting({
6739
+ label: "Fast-mode aliases",
6740
+ actionName: "fast-mode",
6741
+ params,
6742
+ key: "accountPoolExposedFastModeReasoningEfforts",
6743
+ defaultValues: DEFAULT_ACCOUNT_POOL_EXPOSED_FAST_MODE_REASONING_EFFORTS,
6744
+ normalizeValue: (value) => normalizeReasoningEffort(value)
6745
+ });
6746
+ return;
6747
+ case "keys":
6748
+ await handleKeys(params, flags);
6749
+ return;
6750
+ case "sessions":
6751
+ await handleSessions(params, flags);
6752
+ return;
6753
+ default:
6754
+ printHelp(version);
6755
+ return;
6756
+ }
6757
+ }
6758
+
6759
+ // src/commands/daemon.ts
6760
+ var import_node_child_process4 = require("child_process");
6761
+ var import_node_crypto5 = require("crypto");
6762
+ var import_node_fs6 = __toESM(require("fs"));
6763
+ var import_node_net = __toESM(require("net"));
6764
+ var import_node_os4 = __toESM(require("os"));
6765
+ var import_node_path9 = __toESM(require("path"));
6766
+
6767
+ // ../../packages/shared/src/core/internal-js-runtime.ts
6768
+ var import_node_child_process3 = require("child_process");
6769
+ var import_node_path8 = __toESM(require("path"), 1);
6770
+ var cachedBunBinary = null;
6771
+ function hasBunRuntime() {
6772
+ return typeof process.versions.bun === "string" && process.versions.bun.length > 0;
6773
+ }
6774
+ function buildRuntimePath(env) {
6775
+ const homeDir = env.HOME ?? env.USERPROFILE ?? "";
6776
+ const pathHints = (env.CODEX_PATH_HINTS ?? "").split(import_node_path8.default.delimiter).map((entry) => entry.trim()).filter(Boolean);
6777
+ const entries = [
6778
+ env.PATH ?? "",
6779
+ import_node_path8.default.join(homeDir, ".bun", "bin"),
6780
+ "/opt/homebrew/bin",
6781
+ "/usr/local/bin",
6782
+ import_node_path8.default.join(homeDir, ".local", "bin"),
6783
+ import_node_path8.default.join(homeDir, ".fnm", "aliases", "default", "bin"),
6784
+ import_node_path8.default.join(homeDir, ".fnm", "current", "bin"),
6785
+ ...pathHints
6786
+ ];
6787
+ return Array.from(
6788
+ new Set(
6789
+ entries.flatMap((entry) => entry.split(import_node_path8.default.delimiter)).map((entry) => entry.trim()).filter(Boolean)
6790
+ )
6791
+ ).join(import_node_path8.default.delimiter);
6792
+ }
6793
+ function resolveConfiguredBunBinary(env) {
6794
+ const candidates = [
6795
+ env.CODEXUSE_INTERNAL_BUN_BIN,
6796
+ env.BUN_BIN,
6797
+ process.env.CODEXUSE_INTERNAL_BUN_BIN,
6798
+ process.env.BUN_BIN
6799
+ ];
6800
+ for (const candidate of candidates) {
6801
+ const normalized = candidate?.trim() ?? "";
6802
+ if (normalized.length > 0) {
6803
+ return normalized;
6804
+ }
6805
+ }
6806
+ return null;
6807
+ }
6808
+ function probeBunBinary(env) {
6809
+ if (hasBunRuntime()) {
6810
+ return process.execPath;
6811
+ }
6812
+ if (cachedBunBinary) {
6813
+ return cachedBunBinary;
6814
+ }
6815
+ const homeDir = env.HOME ?? env.USERPROFILE ?? "";
6816
+ const candidates = Array.from(
6817
+ new Set(
6818
+ [
6819
+ env.CODEXUSE_INTERNAL_BUN_BIN?.trim(),
6820
+ env.BUN_BIN?.trim(),
6821
+ import_node_path8.default.join(homeDir, ".bun", "bin", "bun"),
6822
+ "/opt/homebrew/bin/bun",
6823
+ "/usr/local/bin/bun",
6824
+ "bun"
6825
+ ].filter((candidate) => typeof candidate === "string" && candidate.length > 0)
6826
+ )
6827
+ );
6828
+ for (const candidate of candidates) {
6829
+ const probe = (0, import_node_child_process3.spawnSync)(candidate, ["--version"], {
6830
+ env,
6831
+ stdio: "ignore"
6832
+ });
6833
+ if (!probe.error && probe.status === 0) {
6834
+ cachedBunBinary = candidate;
6835
+ return candidate;
6836
+ }
6837
+ }
6838
+ return null;
6839
+ }
6840
+ function resolveInternalBunRuntime(overrides = {}) {
6841
+ const env = {
6842
+ ...process.env,
6843
+ ...overrides
6844
+ };
6845
+ env.PATH = buildRuntimePath(env);
6846
+ const runtimeBin = hasBunRuntime() ? process.execPath : resolveConfiguredBunBinary(env) ?? probeBunBinary(env);
6847
+ if (!runtimeBin) {
6848
+ throw new Error(
6849
+ "CodexUse desktop requires a Bun runtime to launch internal services. Install Bun or set CODEXUSE_INTERNAL_BUN_BIN."
6850
+ );
6851
+ }
6852
+ delete env.ELECTRON_RUN_AS_NODE;
6853
+ return {
6854
+ bin: runtimeBin,
6855
+ env
6856
+ };
6857
+ }
6858
+
6859
+ // src/commands/daemon.ts
6860
+ function normalizeTelegramBotToken(value) {
6861
+ const trimmed = value?.trim() ?? "";
6862
+ return trimmed.length > 0 ? trimmed : null;
6863
+ }
6864
+ function printDaemonHelp(version) {
6865
+ console.log(`CodexUse CLI v${version}
6866
+
6867
+ Usage:
6868
+ codexuse daemon start [--port=NNNN] [--telegram-bot-token=<token>] [--project-path=/abs/path]
6869
+
6870
+ Notes:
6871
+ - Runs the T3 Projects/chat server in headless mode.
6872
+ - The same daemon can serve Accounts Pool and Telegram remote control.
6873
+ - Requires Bun on PATH because the bundled Projects server runs on Bun.
6874
+ - Pass --port to keep a stable local API URL for Accounts Pool or other local clients.
6875
+ - Pass --telegram-bot-token or set CODEXUSE_TELEGRAM_BOT_TOKEN to enable Telegram remote control.
6876
+ - --project-path bootstraps that folder as the default project/thread.
6877
+ - Stop with Ctrl+C or SIGTERM.
6878
+ `);
6879
+ }
6880
+ function resolveServerEntry() {
6881
+ const candidates = [
6882
+ import_node_path9.default.resolve(__dirname, "server", "index.mjs"),
6883
+ import_node_path9.default.resolve(__dirname, "../../../server/dist/index.mjs")
6884
+ ];
6885
+ for (const candidate of candidates) {
6886
+ if (import_node_fs6.default.existsSync(candidate)) {
6887
+ return candidate;
6888
+ }
6889
+ }
6890
+ return candidates[0];
6891
+ }
6892
+ function resolveStateDir2() {
6893
+ return import_node_path9.default.join(import_node_os4.default.homedir(), ".codexuse", "t3-daemon");
6894
+ }
6895
+ function reserveLoopbackPort(port = 0) {
6896
+ return new Promise((resolve, reject) => {
6897
+ const server = import_node_net.default.createServer();
6898
+ server.unref();
6899
+ server.once("error", reject);
6900
+ server.listen(port, "127.0.0.1", () => {
6901
+ const address = server.address();
6902
+ const port2 = typeof address === "object" && address ? address.port : null;
6903
+ server.close((error) => {
6904
+ if (error) {
6905
+ reject(error);
6906
+ return;
6907
+ }
6908
+ if (typeof port2 !== "number") {
6909
+ reject(new Error("Failed to reserve a loopback port for the Projects server."));
6910
+ return;
6911
+ }
6912
+ resolve(port2);
6913
+ });
6914
+ });
6915
+ });
6916
+ }
6917
+ async function runDaemonStart(options) {
6918
+ const entry = resolveServerEntry();
6919
+ if (!import_node_fs6.default.existsSync(entry)) {
6920
+ throw new Error(
6921
+ `Missing bundled T3 server build output at ${entry}. Rebuild the CLI package with \`bun run build:cli\`.`
6922
+ );
6923
+ }
6924
+ if (options.port !== null && (!Number.isFinite(options.port) || options.port < 1 || options.port > 65535)) {
6925
+ throw new Error("Daemon port must be an integer between 1 and 65535.");
6926
+ }
6927
+ const port = await reserveLoopbackPort(options.port ?? 0);
6928
+ const authToken = (0, import_node_crypto5.randomBytes)(24).toString("hex");
6929
+ const stateDir = resolveStateDir2();
6930
+ const projectPath = options.projectPath ? import_node_path9.default.resolve(options.projectPath) : null;
6931
+ const childCwd = projectPath ?? process.cwd();
6932
+ const telegramBotToken = options.telegramBotToken?.trim() || null;
6933
+ const runtime = resolveInternalBunRuntime({
6934
+ T3CODE_MODE: "desktop",
6935
+ T3CODE_HOST: "127.0.0.1",
6936
+ T3CODE_PORT: String(port),
6937
+ T3CODE_NO_BROWSER: "1",
6938
+ T3CODE_AUTH_TOKEN: authToken,
6939
+ T3CODE_STATE_DIR: stateDir,
6940
+ T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD: projectPath ? "1" : "0",
6941
+ CODEXUSE_TELEGRAM_RUNTIME_OWNER: "daemon",
6942
+ ...telegramBotToken ? {
6943
+ CODEXUSE_TELEGRAM_BOT_TOKEN: telegramBotToken,
6944
+ CODEXUSE_TELEGRAM_BRIDGE_ENABLED: "1"
6945
+ } : {}
6946
+ });
6947
+ const child = (0, import_node_child_process4.spawn)(runtime.bin, [entry], {
6948
+ cwd: childCwd,
6949
+ env: runtime.env,
6950
+ stdio: "inherit"
6951
+ });
6952
+ console.log(`Daemon started. Projects server listening on http://127.0.0.1:${port}`);
6953
+ console.log(`State dir: ${stateDir}`);
6954
+ if (projectPath) {
6955
+ console.log(`Bootstrapped project path: ${projectPath}`);
6956
+ }
6957
+ if (telegramBotToken) {
6958
+ console.log("Telegram remote control enabled for this daemon session.");
6959
+ }
6960
+ const accountPool = await readAccountPoolRuntimeSummary({
6961
+ runtime: "daemon",
6962
+ stateDir,
6963
+ port
6964
+ });
6965
+ if (accountPool.enabled && accountPool.hasProLicense) {
6966
+ console.log(`Accounts Pool base URL: ${accountPool.baseUrl ?? `http://127.0.0.1:${port}`}`);
6967
+ if (accountPool.activeApiKeyCount > 0) {
6968
+ console.log(`Accounts Pool active keys: ${accountPool.activeApiKeyCount}`);
6969
+ } else {
6970
+ console.log("Accounts Pool is enabled, but this daemon has no runtime key yet. Create one with `codexuse account-pool keys create --runtime=daemon`.");
6971
+ }
6972
+ } else if (accountPool.enabled) {
6973
+ console.log("Accounts Pool is configured, but Pro is required before this daemon can serve it.");
6974
+ } else {
6975
+ console.log("Accounts Pool is disabled for this daemon. Enable it with `codexuse account-pool enable`.");
6976
+ }
6977
+ if (options.port === null) {
6978
+ console.log("Tip: pass --port=3773 (or another fixed port) if another local client should keep one stable base URL.");
6979
+ }
6980
+ console.log("Running until SIGINT/SIGTERM...");
6981
+ await waitForChild(child);
6982
+ }
6983
+ function waitForChild(child) {
6984
+ return new Promise((resolve, reject) => {
6985
+ let stopping = false;
6986
+ const stop = (signal) => {
6987
+ if (stopping) {
6988
+ return;
6989
+ }
6990
+ stopping = true;
6991
+ child.kill(signal);
6992
+ };
6993
+ const onSigInt = () => stop("SIGINT");
6994
+ const onSigTerm = () => stop("SIGTERM");
6995
+ process.once("SIGINT", onSigInt);
6996
+ process.once("SIGTERM", onSigTerm);
6997
+ child.once("exit", (code, signal) => {
6998
+ process.off("SIGINT", onSigInt);
6999
+ process.off("SIGTERM", onSigTerm);
7000
+ if (code === 0 || stopping) {
7001
+ resolve();
7002
+ return;
7003
+ }
7004
+ reject(
7005
+ new Error(
7006
+ `Projects server exited unexpectedly (code=${code ?? "null"}, signal=${signal ?? "null"}).`
7007
+ )
7008
+ );
7009
+ });
7010
+ child.once("error", (error) => {
7011
+ process.off("SIGINT", onSigInt);
7012
+ process.off("SIGTERM", onSigTerm);
7013
+ reject(error);
7014
+ });
7015
+ });
7016
+ }
7017
+ async function handleDaemonCommand(args, version) {
7018
+ const flags = args.filter((arg) => arg.startsWith("-"));
7019
+ const params = stripFlags(args);
7020
+ const sub = params[0];
7021
+ if (!sub || hasFlag(flags, "--help") || hasFlag(flags, "-h")) {
7022
+ printDaemonHelp(version);
7023
+ return;
7024
+ }
7025
+ switch (sub) {
7026
+ case "start": {
7027
+ const port = parseIntegerFlag(flags, "--port");
7028
+ const projectPath = parseStringFlag(flags, "--project-path") ?? parseStringFlag(flags, "--project") ?? null;
7029
+ const telegramBotToken = parseStringFlag(flags, "--telegram-bot-token") ?? normalizeTelegramBotToken(process.env.CODEXUSE_TELEGRAM_BOT_TOKEN) ?? null;
7030
+ await runDaemonStart({
7031
+ projectPath,
7032
+ port,
7033
+ telegramBotToken,
7034
+ version
7035
+ });
7036
+ return;
5712
7037
  }
5713
- return {
5714
- name: record.name,
5715
- displayName: record.displayName,
5716
- email: record.email,
5717
- accountId: record.accountId,
5718
- workspaceId: record.workspaceId,
5719
- workspaceName: record.workspaceName,
5720
- authMethod: record.authMethod,
5721
- createdAt: record.createdAt,
5722
- metadata: record.metadata,
5723
- exportedAt: (/* @__PURE__ */ new Date()).toISOString()
5724
- };
7038
+ default:
7039
+ printDaemonHelp(version);
7040
+ return;
5725
7041
  }
5726
- /**
5727
- * Export profiles for cloud sync (includes token-bearing auth data).
5728
- */
5729
- async exportProfilesForCloudSync() {
5730
- await this.initialize();
5731
- const records = await this.listProfileRecords();
5732
- return records.map((record) => ({
5733
- name: record.name,
5734
- displayName: record.displayName,
5735
- data: record.data,
5736
- metadata: record.metadata,
5737
- accountId: record.accountId,
5738
- workspaceId: record.workspaceId,
5739
- workspaceName: record.workspaceName,
5740
- email: record.email,
5741
- authMethod: record.authMethod,
5742
- createdAt: record.createdAt,
5743
- updatedAt: record.updatedAt
5744
- }));
7042
+ }
7043
+
7044
+ // src/commands/license.ts
7045
+ async function handleLicense(args, version) {
7046
+ const flags = args.filter((arg) => arg.startsWith("-"));
7047
+ const params = stripFlags(args);
7048
+ const sub = params[0];
7049
+ if (!sub || hasFlag(flags, "--help") || hasFlag(flags, "-h")) {
7050
+ printHelp(version);
7051
+ return;
5745
7052
  }
5746
- /**
5747
- * Replace local profiles with a full snapshot from cloud sync.
5748
- */
5749
- async replaceProfilesFromCloudSync(records) {
5750
- await this.initialize();
5751
- const now = (/* @__PURE__ */ new Date()).toISOString();
5752
- const normalized = /* @__PURE__ */ new Map();
5753
- const existingMap = await this.readProfilesStateMap();
5754
- const existingNames = new Set(Object.keys(existingMap));
5755
- for (const candidate of records ?? []) {
5756
- try {
5757
- const name = this.normalizeProfileName(candidate?.name ?? "");
5758
- const data = candidate && typeof candidate.data === "object" && candidate.data !== null ? candidate.data : null;
5759
- if (!data) {
5760
- continue;
5761
- }
5762
- const metadata = candidate.metadata && typeof candidate.metadata === "object" ? candidate.metadata : void 0;
5763
- const record = {
5764
- name,
5765
- displayName: typeof candidate.displayName === "string" ? candidate.displayName : null,
5766
- data,
5767
- metadata,
5768
- accountId: typeof candidate.accountId === "string" ? candidate.accountId : null,
5769
- workspaceId: typeof candidate.workspaceId === "string" ? candidate.workspaceId : null,
5770
- workspaceName: typeof candidate.workspaceName === "string" ? candidate.workspaceName : null,
5771
- email: typeof candidate.email === "string" ? candidate.email : null,
5772
- authMethod: typeof candidate.authMethod === "string" ? candidate.authMethod : this.resolveAuthMethod(data),
5773
- createdAt: typeof candidate.createdAt === "string" ? candidate.createdAt : now,
5774
- updatedAt: typeof candidate.updatedAt === "string" ? candidate.updatedAt : now
5775
- };
5776
- normalized.set(name, record);
5777
- } catch {
7053
+ switch (sub) {
7054
+ case "status": {
7055
+ const forceRefresh = hasFlag(flags, "--refresh");
7056
+ const status = await licenseService.getStatus({ forceRefresh });
7057
+ console.log(`Tier: ${status.tier}`);
7058
+ console.log(`State: ${status.state}`);
7059
+ if (status.profileLimit !== null) {
7060
+ console.log(`Profile limit: ${status.profileLimit}`);
5778
7061
  }
5779
- }
5780
- const nextMap = {};
5781
- for (const record of normalized.values()) {
5782
- nextMap[record.name] = this.toStateRecord(record);
5783
- }
5784
- await this.writeProfilesStateMap(nextMap);
5785
- let removed = 0;
5786
- for (const name of existingNames) {
5787
- if (normalized.has(name)) {
5788
- continue;
7062
+ if (status.profilesRemaining !== null) {
7063
+ console.log(`Profiles remaining: ${status.profilesRemaining}`);
5789
7064
  }
5790
- try {
5791
- await import_fs.promises.rm(this.getProfileHomePath(name), { recursive: true, force: true });
5792
- removed += 1;
5793
- } catch (error) {
5794
- if (!this.isNotFoundError(error)) {
5795
- logWarn(`Failed to remove profile '${name}' during cloud sync replace:`, error);
5796
- }
7065
+ if (status.purchaseEmail) {
7066
+ console.log(`Email: ${status.purchaseEmail}`);
7067
+ }
7068
+ if (status.maskedLicenseKey) {
7069
+ console.log(`License: ${status.maskedLicenseKey}`);
7070
+ }
7071
+ if (status.message) {
7072
+ console.log(`Message: ${status.message}`);
7073
+ }
7074
+ if (status.error) {
7075
+ console.log(`Error: ${status.error}`);
5797
7076
  }
7077
+ return;
5798
7078
  }
5799
- const preferred = await this.readPreferredProfileName();
5800
- if (preferred) {
5801
- try {
5802
- const normalizedPreferred = this.normalizeProfileName(preferred);
5803
- if (!normalized.has(normalizedPreferred)) {
5804
- await this.persistPreferredProfileName(null);
5805
- }
5806
- } catch {
5807
- await this.persistPreferredProfileName(null);
7079
+ case "activate": {
7080
+ const key = params[1];
7081
+ if (!key) {
7082
+ throw new Error("License key is required.");
5808
7083
  }
7084
+ const status = await licenseService.activate(key);
7085
+ console.log(status.message ?? "License activated.");
7086
+ return;
5809
7087
  }
5810
- return {
5811
- imported: normalized.size,
5812
- removed
5813
- };
7088
+ default:
7089
+ printHelp(version);
5814
7090
  }
5815
- };
7091
+ }
5816
7092
 
5817
7093
  // ../../packages/runtime-profiles/src/license/guard.ts
5818
7094
  async function assertProfileCreationAllowed(profileManager) {
@@ -5842,15 +7118,15 @@ function maxUsedPercent(snapshot) {
5842
7118
  }
5843
7119
 
5844
7120
  // src/platform/codexCli.ts
5845
- var import_node_child_process4 = require("child_process");
5846
- var import_node_fs6 = require("fs");
5847
- var import_node_path7 = __toESM(require("path"));
7121
+ var import_node_child_process5 = require("child_process");
7122
+ var import_node_fs7 = require("fs");
7123
+ var import_node_path10 = __toESM(require("path"));
5848
7124
  var ENV_HINTS2 = ["CODEX_BINARY", "CODEX_CLI_PATH", "CODEX_PATH"];
5849
7125
  function fileExists2(candidate) {
5850
7126
  if (!candidate) return null;
5851
- const resolved = import_node_path7.default.resolve(candidate);
7127
+ const resolved = import_node_path10.default.resolve(candidate);
5852
7128
  try {
5853
- const stat2 = (0, import_node_fs6.statSync)(resolved);
7129
+ const stat2 = (0, import_node_fs7.statSync)(resolved);
5854
7130
  if (stat2.isFile()) return resolved;
5855
7131
  } catch {
5856
7132
  return null;
@@ -5868,11 +7144,11 @@ function resolveFromEnv() {
5868
7144
  }
5869
7145
  function resolveFromPath() {
5870
7146
  const pathValue = process.env.PATH ?? "";
5871
- const entries = pathValue.split(import_node_path7.default.delimiter).filter(Boolean);
7147
+ const entries = pathValue.split(import_node_path10.default.delimiter).filter(Boolean);
5872
7148
  const names = process.platform === "win32" ? ["codex.exe", "codex.cmd", "codex.bat", "codex"] : ["codex"];
5873
7149
  for (const entry of entries) {
5874
7150
  for (const name of names) {
5875
- const candidate = fileExists2(import_node_path7.default.join(entry, name));
7151
+ const candidate = fileExists2(import_node_path10.default.join(entry, name));
5876
7152
  if (candidate) return candidate;
5877
7153
  }
5878
7154
  }
@@ -5888,7 +7164,7 @@ function requireCodexBinary(context) {
5888
7164
  const message = context ? `${context} ${hint}` : hint;
5889
7165
  throw new Error(message);
5890
7166
  }
5891
- function buildCodexCommand(codexPath, args) {
7167
+ function buildCodexCommand2(codexPath, args) {
5892
7168
  const normalized = codexPath.toLowerCase();
5893
7169
  const isJs = normalized.endsWith(".js");
5894
7170
  if (isJs) {
@@ -5920,8 +7196,8 @@ async function runCodexLogin(mode) {
5920
7196
  const codexPath = requireCodexBinary("Codex CLI is required to login.");
5921
7197
  const resolvedMode = resolveLoginMode(mode ?? null);
5922
7198
  const loginArgs = resolvedMode === "device" ? ["login", "--device-auth"] : ["login"];
5923
- const { command, args, shell } = buildCodexCommand(codexPath, loginArgs);
5924
- const child = (0, import_node_child_process4.spawn)(command, args, {
7199
+ const { command, args, shell } = buildCodexCommand2(codexPath, loginArgs);
7200
+ const child = (0, import_node_child_process5.spawn)(command, args, {
5925
7201
  stdio: "inherit",
5926
7202
  env: process.env,
5927
7203
  shell
@@ -6429,9 +7705,9 @@ function delay(ms) {
6429
7705
  }
6430
7706
  async function handleProfileCommand(args, version) {
6431
7707
  const flags = args.filter((arg) => arg.startsWith("-"));
6432
- const params = stripFlags2(args);
7708
+ const params = stripFlags(args);
6433
7709
  const sub = params[0];
6434
- if (!sub || hasFlag2(flags, "--help") || hasFlag2(flags, "-h")) {
7710
+ if (!sub || hasFlag(flags, "--help") || hasFlag(flags, "-h")) {
6435
7711
  printHelp(version);
6436
7712
  return;
6437
7713
  }
@@ -6444,8 +7720,8 @@ async function handleProfileCommand(args, version) {
6444
7720
  console.log("No profiles found.");
6445
7721
  return;
6446
7722
  }
6447
- const compact = hasFlag2(flags, "--compact");
6448
- const withUsage = !hasFlag2(flags, "--no-usage");
7723
+ const compact = hasFlag(flags, "--compact");
7724
+ const withUsage = !hasFlag(flags, "--no-usage");
6449
7725
  let usageMap = null;
6450
7726
  if (withUsage) {
6451
7727
  const codexPath = resolveCodexBinary();
@@ -6491,7 +7767,7 @@ async function handleProfileCommand(args, version) {
6491
7767
  throw new Error("Profile name is required.");
6492
7768
  }
6493
7769
  await assertProfileCreationAllowed(manager);
6494
- if (!hasFlag2(flags, "--skip-login")) {
7770
+ if (!hasFlag(flags, "--skip-login")) {
6495
7771
  const loginMode = resolveLoginMode(parseLoginMode(flags));
6496
7772
  await runCodexLogin(loginMode);
6497
7773
  }
@@ -6505,7 +7781,7 @@ async function handleProfileCommand(args, version) {
6505
7781
  if (!name) {
6506
7782
  throw new Error("Profile name is required.");
6507
7783
  }
6508
- if (!hasFlag2(flags, "--skip-login")) {
7784
+ if (!hasFlag(flags, "--skip-login")) {
6509
7785
  const loginMode = resolveLoginMode(parseLoginMode(flags));
6510
7786
  await runCodexLogin(loginMode);
6511
7787
  }
@@ -6525,8 +7801,8 @@ async function handleProfileCommand(args, version) {
6525
7801
  }
6526
7802
  case "autoroll":
6527
7803
  case "auto-roll": {
6528
- const watch = hasFlag2(flags, "--watch");
6529
- const dryRun = hasFlag2(flags, "--dry-run");
7804
+ const watch = hasFlag(flags, "--watch");
7805
+ const dryRun = hasFlag(flags, "--dry-run");
6530
7806
  const settings = await getStoredAutoRollSettings().catch(() => null);
6531
7807
  const fallbackThreshold = typeof settings?.switchThreshold === "number" && Number.isFinite(settings.switchThreshold) ? Math.round(settings.switchThreshold) : DEFAULT_AUTOROLL_THRESHOLD;
6532
7808
  const threshold = parseAutoRollThreshold(flags, fallbackThreshold);
@@ -6581,8 +7857,8 @@ async function handleProfileCommand(args, version) {
6581
7857
  }
6582
7858
 
6583
7859
  // ../../packages/runtime-profiles/src/cloud-sync/service.ts
6584
- var import_node_fs7 = require("fs");
6585
- var import_node_path11 = __toESM(require("path"), 1);
7860
+ var import_node_fs8 = require("fs");
7861
+ var import_node_path14 = __toESM(require("path"), 1);
6586
7862
 
6587
7863
  // ../../packages/contracts/src/cloud-sync/types.ts
6588
7864
  var CLOUD_SYNC_SCHEMA_VERSION = 1;
@@ -6742,9 +8018,9 @@ async function pushRemoteSnapshot(licenseKey, snapshot, options) {
6742
8018
  var import_toml2 = __toESM(require_toml(), 1);
6743
8019
 
6744
8020
  // ../../packages/runtime-codex/src/codex/config-metadata.ts
6745
- var import_node_child_process5 = require("child_process");
8021
+ var import_node_child_process6 = require("child_process");
6746
8022
  var import_promises = require("fs/promises");
6747
- var import_node_path8 = __toESM(require("path"), 1);
8023
+ var import_node_path11 = __toESM(require("path"), 1);
6748
8024
  var CONFIG_SCHEMA_URL = "https://raw.githubusercontent.com/openai/codex/main/codex-rs/core/config.schema.json";
6749
8025
  var METADATA_CACHE_TTL_MS = 6e4;
6750
8026
  var FALLBACK_SCHEMA_TOP_LEVEL_KEYS = [
@@ -6854,7 +8130,7 @@ function uniqueSorted(values) {
6854
8130
  }
6855
8131
  async function runCommand(command, args, timeoutMs = 3e3) {
6856
8132
  return new Promise((resolve, reject) => {
6857
- const child = (0, import_node_child_process5.spawn)(command, args, {
8133
+ const child = (0, import_node_child_process6.spawn)(command, args, {
6858
8134
  stdio: ["ignore", "pipe", "pipe"],
6859
8135
  env: process.env
6860
8136
  });
@@ -6930,8 +8206,8 @@ function parseSchemaKeys(schemaRaw) {
6930
8206
  async function readSchemaFromLocal() {
6931
8207
  const candidates = [
6932
8208
  process.env.CODEX_CONFIG_SCHEMA_PATH,
6933
- import_node_path8.default.join(process.cwd(), "codex-rs", "core", "config.schema.json"),
6934
- import_node_path8.default.join(process.cwd(), "node_modules", "@openai", "codex", "codex-rs", "core", "config.schema.json")
8209
+ import_node_path11.default.join(process.cwd(), "codex-rs", "core", "config.schema.json"),
8210
+ import_node_path11.default.join(process.cwd(), "node_modules", "@openai", "codex", "codex-rs", "core", "config.schema.json")
6935
8211
  ].filter((candidate) => Boolean(candidate));
6936
8212
  for (const candidate of candidates) {
6937
8213
  try {
@@ -7036,20 +8312,20 @@ async function getCodexConfigMetadata(forceRefresh = false) {
7036
8312
 
7037
8313
  // ../../packages/runtime-codex/src/codex/config-io.ts
7038
8314
  var import_promises2 = require("fs/promises");
7039
- var import_node_path10 = __toESM(require("path"), 1);
8315
+ var import_node_path13 = __toESM(require("path"), 1);
7040
8316
 
7041
8317
  // ../../packages/runtime-codex/src/codex/home.ts
7042
- var import_node_os4 = __toESM(require("os"), 1);
7043
- var import_node_path9 = __toESM(require("path"), 1);
8318
+ var import_node_os5 = __toESM(require("os"), 1);
8319
+ var import_node_path12 = __toESM(require("path"), 1);
7044
8320
  function resolveHomeDir() {
7045
- return process.env.HOME || process.env.USERPROFILE || import_node_os4.default.homedir();
8321
+ return process.env.HOME || process.env.USERPROFILE || import_node_os5.default.homedir();
7046
8322
  }
7047
8323
  function expandHomePrefix(input, homeDir) {
7048
8324
  if (input === "~") {
7049
8325
  return homeDir;
7050
8326
  }
7051
8327
  if (input.startsWith("~/") || input.startsWith("~\\")) {
7052
- return import_node_path9.default.join(homeDir, input.slice(2));
8328
+ return import_node_path12.default.join(homeDir, input.slice(2));
7053
8329
  }
7054
8330
  return input;
7055
8331
  }
@@ -7065,13 +8341,13 @@ function normalizeCodexHomePath(input) {
7065
8341
  if (!configured) {
7066
8342
  return null;
7067
8343
  }
7068
- return import_node_path9.default.resolve(expandHomePrefix(configured, resolveHomeDir()));
8344
+ return import_node_path12.default.resolve(expandHomePrefix(configured, resolveHomeDir()));
7069
8345
  }
7070
8346
  function resolveDefaultCodexHomeDir() {
7071
- return import_node_path9.default.join(resolveHomeDir(), ".codex");
8347
+ return import_node_path12.default.join(resolveHomeDir(), ".codex");
7072
8348
  }
7073
8349
  function resolveProfileCodexHomeDir(profileName) {
7074
- return import_node_path9.default.join(getUserDataDir(), "profile-homes", profileName);
8350
+ return import_node_path12.default.join(getUserDataDir(), "profile-homes", profileName);
7075
8351
  }
7076
8352
  function resolveCodexHomeDir(options) {
7077
8353
  return normalizeCodexHomePath(options?.codexHomePath) ?? resolveDefaultCodexHomeDir();
@@ -7152,10 +8428,10 @@ var YOLO_PRESET = {
7152
8428
 
7153
8429
  // ../../packages/runtime-codex/src/codex/config-io.ts
7154
8430
  function getConfigPath(options) {
7155
- return import_node_path10.default.join(resolveCodexHomeDir(options), "config.toml");
8431
+ return import_node_path13.default.join(resolveCodexHomeDir(options), "config.toml");
7156
8432
  }
7157
8433
  async function ensureConfigDirExists(filePath) {
7158
- const dir = import_node_path10.default.dirname(filePath);
8434
+ const dir = import_node_path13.default.dirname(filePath);
7159
8435
  await (0, import_promises2.mkdir)(dir, { recursive: true });
7160
8436
  }
7161
8437
  function normalizeLineEndings2(content) {
@@ -7214,41 +8490,171 @@ function stripAnsi(value) {
7214
8490
  function normalizeWhitespace(value) {
7215
8491
  return value.replace(/\s+/g, " ").trim();
7216
8492
  }
7217
- function formatUserFacingError(error, options = {}) {
7218
- const fallback = options.fallback ?? "Something went wrong. Please try again.";
8493
+ function compactErrorText(value, maxLength = 220) {
8494
+ if (!value) {
8495
+ return "";
8496
+ }
8497
+ const cleaned = normalizeWhitespace(stripAnsi(String(value)));
8498
+ if (!cleaned) {
8499
+ return "";
8500
+ }
8501
+ if (cleaned.length <= maxLength) {
8502
+ return cleaned;
8503
+ }
8504
+ return `${cleaned.slice(0, maxLength).trimEnd()}\u2026`;
8505
+ }
8506
+ function createErrorReferenceId() {
8507
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`.toUpperCase();
8508
+ }
8509
+ function serializeUnknownError(value) {
8510
+ if (typeof value === "string") {
8511
+ return value;
8512
+ }
8513
+ if (value instanceof Error) {
8514
+ return value.stack ?? value.message;
8515
+ }
8516
+ try {
8517
+ return JSON.stringify(value, null, 2);
8518
+ } catch {
8519
+ return String(value);
8520
+ }
8521
+ }
8522
+ function extractRawMessage(error) {
7219
8523
  if (error === null || typeof error === "undefined") {
7220
- return fallback;
8524
+ return "";
8525
+ }
8526
+ if (typeof error === "string") {
8527
+ return error;
7221
8528
  }
7222
- const rawMessage = typeof error === "string" ? error : error instanceof Error ? error.message : String(error);
8529
+ if (error instanceof Error) {
8530
+ return error.message;
8531
+ }
8532
+ return String(error);
8533
+ }
8534
+ function normalizeRawMessage(rawMessage) {
7223
8535
  let cleaned = normalizeWhitespace(stripAnsi(rawMessage));
7224
8536
  if (!cleaned) {
7225
- return fallback;
8537
+ return "";
7226
8538
  }
7227
8539
  if (cleaned.startsWith("Error invoking remote method")) {
7228
8540
  cleaned = cleaned.replace(/^Error invoking remote method '[^']+': Error: /, "");
7229
8541
  }
8542
+ if (cleaned.toLowerCase().includes("openai codex") && cleaned.toLowerCase().includes("workdir:")) {
8543
+ const parts = cleaned.split(/error:/i);
8544
+ if (parts.length > 1) {
8545
+ return parts[parts.length - 1]?.trim() ?? cleaned;
8546
+ }
8547
+ }
8548
+ return cleaned;
8549
+ }
8550
+ function resolveCategory(cleaned, options) {
7230
8551
  const lower = cleaned.toLowerCase();
7231
8552
  if (options.notFoundFallback && (lower.includes("enoent") || lower.includes("not found"))) {
7232
- return options.notFoundFallback;
8553
+ return {
8554
+ code: "ERR_NOT_FOUND",
8555
+ title: "We couldn't find that item",
8556
+ message: options.notFoundFallback,
8557
+ recovery: "Check the path or selection, then try again.",
8558
+ retryable: true
8559
+ };
7233
8560
  }
7234
8561
  if (lower.includes("rate limit") || lower.includes("quota exceeded") || lower.includes("too many requests") || lower.includes("429")) {
7235
- cleaned = "Rate limit exceeded. Please wait a moment or upgrade your plan.";
7236
- } else if (lower.includes("timeout") || lower.includes("timed out")) {
7237
- cleaned = "Request timed out. Check your connection and try again.";
7238
- } else if (lower.includes("network") || lower.includes("econn") || lower.includes("networkerror") || lower.includes("connection")) {
7239
- cleaned = "Network issue. Check your connection and retry.";
7240
- } else if (lower.includes("permission") || lower.includes("eacces")) {
7241
- cleaned = "Permission denied. Restart CodexUse and retry.";
7242
- } else if (lower.includes("openai codex") && lower.includes("workdir:")) {
7243
- if (lower.includes("error:")) {
7244
- const parts = cleaned.split(/error:/i);
7245
- if (parts.length > 1) {
7246
- cleaned = parts[parts.length - 1].trim();
7247
- }
7248
- } else {
7249
- cleaned = "Codex CLI failed to respond. Please try again.";
7250
- }
8562
+ return {
8563
+ code: "ERR_RATE_LIMIT",
8564
+ title: "Rate limit reached",
8565
+ message: "The provider is rate limited right now.",
8566
+ recovery: "Wait a moment, switch profiles, or try again.",
8567
+ retryable: true
8568
+ };
8569
+ }
8570
+ if (lower.includes("timeout") || lower.includes("timed out")) {
8571
+ return {
8572
+ code: "ERR_TIMEOUT",
8573
+ title: "Request timed out",
8574
+ message: "The operation took too long to finish.",
8575
+ recovery: "Check the connection and try again.",
8576
+ retryable: true
8577
+ };
8578
+ }
8579
+ if (lower.includes("network") || lower.includes("econn") || lower.includes("networkerror") || lower.includes("connection refused") || lower.includes("socket hang up")) {
8580
+ return {
8581
+ code: "ERR_NETWORK",
8582
+ title: "Can't reach the service",
8583
+ message: "CodexUse couldn't connect to the required service.",
8584
+ recovery: "Check your internet or local server connection, then retry.",
8585
+ retryable: true
8586
+ };
8587
+ }
8588
+ if (lower.includes("token_invalidated") || lower.includes("session invalidated") || lower.includes("session expired") || lower.includes("refresh token") || lower.includes("unauthorized") || lower.includes("401")) {
8589
+ return {
8590
+ code: "ERR_AUTH",
8591
+ title: "Sign-in expired",
8592
+ message: "This account needs to sign in again.",
8593
+ recovery: "Reconnect the profile, then retry the action.",
8594
+ retryable: true
8595
+ };
8596
+ }
8597
+ if (lower.includes("permission") || lower.includes("eacces") || lower.includes("operation not permitted")) {
8598
+ return {
8599
+ code: "ERR_PERMISSION",
8600
+ title: "Permission denied",
8601
+ message: "CodexUse doesn't have permission to do that.",
8602
+ recovery: "Check the file permissions or restart the app and try again.",
8603
+ retryable: true
8604
+ };
8605
+ }
8606
+ if (lower.includes("enospc") || lower.includes("disk full") || lower.includes("no space left")) {
8607
+ return {
8608
+ code: "ERR_DISK_FULL",
8609
+ title: "Disk is full",
8610
+ message: "CodexUse couldn't save data because the disk is full.",
8611
+ recovery: "Free up disk space, then try again.",
8612
+ retryable: true
8613
+ };
7251
8614
  }
8615
+ if (lower.includes("codex cli") || lower.includes("command not found") || lower.includes("spawn codex") || lower.includes("could not find codex")) {
8616
+ return {
8617
+ code: "ERR_CLI_UNAVAILABLE",
8618
+ title: "Codex CLI unavailable",
8619
+ message: "CodexUse couldn't start the Codex CLI.",
8620
+ recovery: "Check Runtime > Codex and confirm the CLI is installed correctly.",
8621
+ retryable: true
8622
+ };
8623
+ }
8624
+ if (lower.includes("provider") || lower.includes("model") || lower.includes("openai") || lower.includes("anthropic")) {
8625
+ return {
8626
+ code: "ERR_PROVIDER",
8627
+ title: "Provider request failed",
8628
+ message: "The AI provider rejected or failed the request.",
8629
+ recovery: "Try again, or switch the active profile or model.",
8630
+ retryable: true
8631
+ };
8632
+ }
8633
+ return {
8634
+ code: "ERR_UNKNOWN",
8635
+ title: "Something went wrong",
8636
+ message: options.fallback,
8637
+ recovery: "Try again. If it keeps happening, report the error with the reference below.",
8638
+ retryable: true
8639
+ };
8640
+ }
8641
+ function resolveUserFacingError(error, options = {}) {
8642
+ const fallback = options.fallback ?? "Something went wrong. Please try again.";
8643
+ const rawMessage = extractRawMessage(error);
8644
+ const cleaned = normalizeRawMessage(rawMessage);
8645
+ const category = resolveCategory(cleaned, {
8646
+ fallback,
8647
+ ...options.notFoundFallback ? { notFoundFallback: options.notFoundFallback } : {}
8648
+ });
8649
+ return {
8650
+ ...category,
8651
+ referenceId: createErrorReferenceId(),
8652
+ technicalMessage: compactErrorText(cleaned || rawMessage || fallback, 400),
8653
+ technicalDetails: compactErrorText(serializeUnknownError(error), 4e3)
8654
+ };
8655
+ }
8656
+ function formatUserFacingError(error, options = {}) {
8657
+ let cleaned = resolveUserFacingError(error, options).message;
7252
8658
  const maxLength = typeof options.maxLength === "number" ? options.maxLength : 220;
7253
8659
  if (cleaned.length > maxLength) {
7254
8660
  cleaned = `${cleaned.slice(0, maxLength).trimEnd()}\u2026`;
@@ -7679,13 +9085,13 @@ function formatMegabytes(bytes) {
7679
9085
  return (bytes / MB_DIVISOR).toFixed(2);
7680
9086
  }
7681
9087
  function resolveSyncBackupsDir() {
7682
- return import_node_path11.default.join(getUserDataDir(), SYNC_BACKUP_DIR);
9088
+ return import_node_path14.default.join(getUserDataDir(), SYNC_BACKUP_DIR);
7683
9089
  }
7684
9090
  function toSafeIsoForFileName(iso) {
7685
9091
  return iso.replace(/:/g, "-");
7686
9092
  }
7687
9093
  async function pruneOldPrePullBackups(backupsDir) {
7688
- const entries = await import_node_fs7.promises.readdir(backupsDir, { withFileTypes: true });
9094
+ const entries = await import_node_fs8.promises.readdir(backupsDir, { withFileTypes: true });
7689
9095
  const files = entries.filter(
7690
9096
  (entry) => entry.isFile() && entry.name.startsWith(PRE_PULL_BACKUP_PREFIX) && entry.name.endsWith(PRE_PULL_BACKUP_SUFFIX)
7691
9097
  ).map((entry) => entry.name).sort((a, b) => b.localeCompare(a));
@@ -7693,7 +9099,7 @@ async function pruneOldPrePullBackups(backupsDir) {
7693
9099
  return;
7694
9100
  }
7695
9101
  for (const stale of files.slice(PRE_PULL_BACKUP_KEEP_COUNT)) {
7696
- await import_node_fs7.promises.rm(import_node_path11.default.join(backupsDir, stale), { force: true });
9102
+ await import_node_fs8.promises.rm(import_node_path14.default.join(backupsDir, stale), { force: true });
7697
9103
  }
7698
9104
  }
7699
9105
  async function createPrePullBackup(profileManager) {
@@ -7701,13 +9107,13 @@ async function createPrePullBackup(profileManager) {
7701
9107
  enforcePushGuards: false
7702
9108
  });
7703
9109
  const backupsDir = resolveSyncBackupsDir();
7704
- await import_node_fs7.promises.mkdir(backupsDir, { recursive: true });
9110
+ await import_node_fs8.promises.mkdir(backupsDir, { recursive: true });
7705
9111
  const timestamp = toSafeIsoForFileName((/* @__PURE__ */ new Date()).toISOString());
7706
- const backupPath = import_node_path11.default.join(
9112
+ const backupPath = import_node_path14.default.join(
7707
9113
  backupsDir,
7708
9114
  `${PRE_PULL_BACKUP_PREFIX}${timestamp}${PRE_PULL_BACKUP_SUFFIX}`
7709
9115
  );
7710
- await import_node_fs7.promises.writeFile(backupPath, `${JSON.stringify(snapshot, null, 2)}
9116
+ await import_node_fs8.promises.writeFile(backupPath, `${JSON.stringify(snapshot, null, 2)}
7711
9117
  `, "utf8");
7712
9118
  await pruneOldPrePullBackups(backupsDir);
7713
9119
  logInfo("[cloud-sync] created pre-pull backup", { backupPath });
@@ -7923,9 +9329,9 @@ function printSyncResult(result) {
7923
9329
  }
7924
9330
  async function handleSync(args, version) {
7925
9331
  const flags = args.filter((arg) => arg.startsWith("-"));
7926
- const params = stripFlags2(args);
9332
+ const params = stripFlags(args);
7927
9333
  const sub = params[0];
7928
- if (!sub || hasFlag2(flags, "--help") || hasFlag2(flags, "-h")) {
9334
+ if (!sub || hasFlag(flags, "--help") || hasFlag(flags, "-h")) {
7929
9335
  printHelp(version);
7930
9336
  return;
7931
9337
  }
@@ -7976,9 +9382,9 @@ async function handleSync(args, version) {
7976
9382
  }
7977
9383
 
7978
9384
  // ../../packages/runtime-app-state/src/storage/migrations/v1.ts
7979
- var import_node_fs8 = require("fs");
7980
- var import_node_path12 = __toESM(require("path"), 1);
7981
- var import_node_os5 = __toESM(require("os"), 1);
9385
+ var import_node_fs9 = require("fs");
9386
+ var import_node_path15 = __toESM(require("path"), 1);
9387
+ var import_node_os6 = __toESM(require("os"), 1);
7982
9388
 
7983
9389
  // ../../packages/contracts/src/settings/legacy-localstorage-keys.ts
7984
9390
  var LEGACY_LOCALSTORAGE_KEYS = [
@@ -8002,11 +9408,11 @@ var LEGACY_SKILLS_DIR = "skills";
8002
9408
  var LEGACY_SKILL_CACHE_DIR = "skill-cache";
8003
9409
  var LEGACY_SKILLS_REPOS_FILE = "repos.json";
8004
9410
  var LEGACY_SKILL_MANIFEST = ".codexuse-skill.json";
8005
- var LEGACY_LICENSE_SECRET_FILE = "license.secret";
9411
+ var LEGACY_LICENSE_SECRET_FILE2 = "license.secret";
8006
9412
  function isRecord6(value) {
8007
9413
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
8008
9414
  }
8009
- function asString3(value) {
9415
+ function asString4(value) {
8010
9416
  if (typeof value !== "string") {
8011
9417
  return null;
8012
9418
  }
@@ -8017,20 +9423,20 @@ function isMissingPathError(error) {
8017
9423
  return error.code === "ENOENT";
8018
9424
  }
8019
9425
  function resolveHomeDir2() {
8020
- const home = process.env.HOME || process.env.USERPROFILE || import_node_os5.default.homedir();
9426
+ const home = process.env.HOME || process.env.USERPROFILE || import_node_os6.default.homedir();
8021
9427
  if (!home) {
8022
9428
  throw new Error("HOME is not set.");
8023
9429
  }
8024
9430
  return home;
8025
9431
  }
8026
9432
  function resolveCodexDir() {
8027
- return import_node_path12.default.join(resolveHomeDir2(), ".codex");
9433
+ return import_node_path15.default.join(resolveHomeDir2(), ".codex");
8028
9434
  }
8029
9435
  function resolveLegacyPath(...segments) {
8030
- return import_node_path12.default.join(resolveCodexDir(), ...segments);
9436
+ return import_node_path15.default.join(resolveCodexDir(), ...segments);
8031
9437
  }
8032
9438
  async function readJsonFile(filePath) {
8033
- const raw = await import_node_fs8.promises.readFile(filePath, "utf8");
9439
+ const raw = await import_node_fs9.promises.readFile(filePath, "utf8");
8034
9440
  return JSON.parse(raw);
8035
9441
  }
8036
9442
  async function readJsonFileIfExists(filePath) {
@@ -8045,14 +9451,14 @@ async function readJsonFileIfExists(filePath) {
8045
9451
  }
8046
9452
  }
8047
9453
  async function copyDirectoryManual(source, destination) {
8048
- await import_node_fs8.promises.mkdir(destination, { recursive: true });
8049
- const entries = await import_node_fs8.promises.readdir(source, { withFileTypes: true });
9454
+ await import_node_fs9.promises.mkdir(destination, { recursive: true });
9455
+ const entries = await import_node_fs9.promises.readdir(source, { withFileTypes: true });
8050
9456
  for (const entry of entries) {
8051
- const srcPath = import_node_path12.default.join(source, entry.name);
8052
- const destPath = import_node_path12.default.join(destination, entry.name);
9457
+ const srcPath = import_node_path15.default.join(source, entry.name);
9458
+ const destPath = import_node_path15.default.join(destination, entry.name);
8053
9459
  if (entry.isSymbolicLink()) {
8054
- const linkTarget = await import_node_fs8.promises.readlink(srcPath);
8055
- await import_node_fs8.promises.symlink(linkTarget, destPath).catch((error) => {
9460
+ const linkTarget = await import_node_fs9.promises.readlink(srcPath);
9461
+ await import_node_fs9.promises.symlink(linkTarget, destPath).catch((error) => {
8056
9462
  if (error.code !== "EEXIST") {
8057
9463
  throw error;
8058
9464
  }
@@ -8064,14 +9470,14 @@ async function copyDirectoryManual(source, destination) {
8064
9470
  continue;
8065
9471
  }
8066
9472
  if (entry.isFile()) {
8067
- await import_node_fs8.promises.copyFile(srcPath, destPath);
9473
+ await import_node_fs9.promises.copyFile(srcPath, destPath);
8068
9474
  }
8069
9475
  }
8070
9476
  }
8071
9477
  async function copyDirIfExists(source, destination) {
8072
9478
  let stats = null;
8073
9479
  try {
8074
- stats = await import_node_fs8.promises.stat(source);
9480
+ stats = await import_node_fs9.promises.stat(source);
8075
9481
  } catch (error) {
8076
9482
  if (error.code === "ENOENT") {
8077
9483
  return;
@@ -8081,10 +9487,10 @@ async function copyDirIfExists(source, destination) {
8081
9487
  if (!stats.isDirectory()) {
8082
9488
  return;
8083
9489
  }
8084
- await import_node_fs8.promises.rm(destination, { recursive: true, force: true });
8085
- await import_node_fs8.promises.mkdir(import_node_path12.default.dirname(destination), { recursive: true });
9490
+ await import_node_fs9.promises.rm(destination, { recursive: true, force: true });
9491
+ await import_node_fs9.promises.mkdir(import_node_path15.default.dirname(destination), { recursive: true });
8086
9492
  try {
8087
- await import_node_fs8.promises.cp(source, destination, {
9493
+ await import_node_fs9.promises.cp(source, destination, {
8088
9494
  recursive: true,
8089
9495
  errorOnExist: false,
8090
9496
  force: true,
@@ -8101,7 +9507,7 @@ async function copyDirIfExists(source, destination) {
8101
9507
  }
8102
9508
  async function copyFileIfExists(source, destination, options) {
8103
9509
  try {
8104
- const sourceStat = await import_node_fs8.promises.stat(source);
9510
+ const sourceStat = await import_node_fs9.promises.stat(source);
8105
9511
  if (!sourceStat.isFile()) {
8106
9512
  return;
8107
9513
  }
@@ -8112,7 +9518,7 @@ async function copyFileIfExists(source, destination, options) {
8112
9518
  throw error;
8113
9519
  }
8114
9520
  try {
8115
- const destinationStat = await import_node_fs8.promises.stat(destination);
9521
+ const destinationStat = await import_node_fs9.promises.stat(destination);
8116
9522
  if (destinationStat.isFile()) {
8117
9523
  return;
8118
9524
  }
@@ -8121,17 +9527,17 @@ async function copyFileIfExists(source, destination, options) {
8121
9527
  throw error;
8122
9528
  }
8123
9529
  }
8124
- await import_node_fs8.promises.mkdir(import_node_path12.default.dirname(destination), { recursive: true });
8125
- await import_node_fs8.promises.copyFile(source, destination);
9530
+ await import_node_fs9.promises.mkdir(import_node_path15.default.dirname(destination), { recursive: true });
9531
+ await import_node_fs9.promises.copyFile(source, destination);
8126
9532
  if (typeof options?.mode === "number") {
8127
- await import_node_fs8.promises.chmod(destination, options.mode).catch(() => void 0);
9533
+ await import_node_fs9.promises.chmod(destination, options.mode).catch(() => void 0);
8128
9534
  }
8129
9535
  }
8130
9536
  async function cleanupCopiedSkillsMetadata(skillsDir) {
8131
- await removeIfExists(import_node_path12.default.join(skillsDir, LEGACY_SKILLS_REPOS_FILE));
9537
+ await removeIfExists(import_node_path15.default.join(skillsDir, LEGACY_SKILLS_REPOS_FILE));
8132
9538
  let entries = [];
8133
9539
  try {
8134
- entries = await import_node_fs8.promises.readdir(skillsDir, { withFileTypes: true });
9540
+ entries = await import_node_fs9.promises.readdir(skillsDir, { withFileTypes: true });
8135
9541
  } catch {
8136
9542
  return;
8137
9543
  }
@@ -8139,30 +9545,30 @@ async function cleanupCopiedSkillsMetadata(skillsDir) {
8139
9545
  if (!entry.isDirectory() || entry.name.startsWith(".")) {
8140
9546
  continue;
8141
9547
  }
8142
- await removeIfExists(import_node_path12.default.join(skillsDir, entry.name, LEGACY_SKILL_MANIFEST));
9548
+ await removeIfExists(import_node_path15.default.join(skillsDir, entry.name, LEGACY_SKILL_MANIFEST));
8143
9549
  }
8144
9550
  }
8145
9551
  async function migrateLegacyDirectoriesToUserData() {
8146
9552
  const userDataDir = getUserDataDir();
8147
9553
  const legacyCodexDir = resolveCodexDir();
8148
9554
  await copyDirIfExists(
8149
- import_node_path12.default.join(legacyCodexDir, LEGACY_PROFILE_HOMES_DIR),
8150
- import_node_path12.default.join(userDataDir, LEGACY_PROFILE_HOMES_DIR)
9555
+ import_node_path15.default.join(legacyCodexDir, LEGACY_PROFILE_HOMES_DIR),
9556
+ import_node_path15.default.join(userDataDir, LEGACY_PROFILE_HOMES_DIR)
8151
9557
  );
8152
9558
  await copyDirIfExists(
8153
- import_node_path12.default.join(legacyCodexDir, LEGACY_SKILLS_DIR),
8154
- import_node_path12.default.join(userDataDir, LEGACY_SKILLS_DIR)
9559
+ import_node_path15.default.join(legacyCodexDir, LEGACY_SKILLS_DIR),
9560
+ import_node_path15.default.join(userDataDir, LEGACY_SKILLS_DIR)
8155
9561
  );
8156
9562
  await copyDirIfExists(
8157
- import_node_path12.default.join(legacyCodexDir, LEGACY_SKILL_CACHE_DIR),
8158
- import_node_path12.default.join(userDataDir, LEGACY_SKILL_CACHE_DIR)
9563
+ import_node_path15.default.join(legacyCodexDir, LEGACY_SKILL_CACHE_DIR),
9564
+ import_node_path15.default.join(userDataDir, LEGACY_SKILL_CACHE_DIR)
8159
9565
  );
8160
9566
  await copyFileIfExists(
8161
- import_node_path12.default.join(legacyCodexDir, LEGACY_LICENSE_SECRET_FILE),
8162
- import_node_path12.default.join(userDataDir, LEGACY_LICENSE_SECRET_FILE),
9567
+ import_node_path15.default.join(legacyCodexDir, LEGACY_LICENSE_SECRET_FILE2),
9568
+ import_node_path15.default.join(userDataDir, LEGACY_LICENSE_SECRET_FILE2),
8163
9569
  { mode: 384 }
8164
9570
  );
8165
- await cleanupCopiedSkillsMetadata(import_node_path12.default.join(userDataDir, LEGACY_SKILLS_DIR));
9571
+ await cleanupCopiedSkillsMetadata(import_node_path15.default.join(userDataDir, LEGACY_SKILLS_DIR));
8166
9572
  }
8167
9573
  function pickAutoRoll(raw) {
8168
9574
  if (!raw) {
@@ -8186,16 +9592,16 @@ function parseLegacyLicense(raw) {
8186
9592
  signature: null
8187
9593
  };
8188
9594
  }
8189
- const statusCandidate = asString3(raw.status);
9595
+ const statusCandidate = asString4(raw.status);
8190
9596
  const status = ["inactive", "active", "grace", "error"].includes(statusCandidate ?? "") ? statusCandidate : "inactive";
8191
9597
  return {
8192
- licenseKey: asString3(raw.licenseKey ?? raw.license_key),
8193
- purchaseEmail: asString3(raw.purchaseEmail ?? raw.purchase_email),
8194
- lastVerifiedAt: asString3(raw.lastVerifiedAt ?? raw.last_verified_at),
8195
- nextCheckAt: asString3(raw.nextCheckAt ?? raw.next_check_at),
8196
- lastVerificationError: asString3(raw.lastVerificationError ?? raw.last_verification_error),
9598
+ licenseKey: asString4(raw.licenseKey ?? raw.license_key),
9599
+ purchaseEmail: asString4(raw.purchaseEmail ?? raw.purchase_email),
9600
+ lastVerifiedAt: asString4(raw.lastVerifiedAt ?? raw.last_verified_at),
9601
+ nextCheckAt: asString4(raw.nextCheckAt ?? raw.next_check_at),
9602
+ lastVerificationError: asString4(raw.lastVerificationError ?? raw.last_verification_error),
8197
9603
  status,
8198
- signature: asString3(raw.signature)
9604
+ signature: asString4(raw.signature)
8199
9605
  };
8200
9606
  }
8201
9607
  function parseLegacyProfileRecord(name, raw) {
@@ -8218,16 +9624,16 @@ function parseLegacyProfileRecord(name, raw) {
8218
9624
  }
8219
9625
  return {
8220
9626
  name,
8221
- displayName: asString3(raw.displayName ?? raw.display_name) ?? name,
9627
+ displayName: asString4(raw.displayName ?? raw.display_name) ?? name,
8222
9628
  data,
8223
9629
  metadata: isRecord6(raw.metadata) ? raw.metadata : void 0,
8224
- accountId: asString3(raw.accountId ?? raw.account_id),
8225
- workspaceId: asString3(raw.workspaceId ?? raw.workspace_id),
8226
- workspaceName: asString3(raw.workspaceName ?? raw.workspace_name),
8227
- email: asString3(raw.email),
8228
- authMethod: asString3(raw.authMethod ?? raw.auth_method),
8229
- createdAt: asString3(raw.createdAt ?? raw.created_at),
8230
- updatedAt: asString3(raw.updatedAt ?? raw.updated_at)
9630
+ accountId: asString4(raw.accountId ?? raw.account_id),
9631
+ workspaceId: asString4(raw.workspaceId ?? raw.workspace_id),
9632
+ workspaceName: asString4(raw.workspaceName ?? raw.workspace_name),
9633
+ email: asString4(raw.email),
9634
+ authMethod: asString4(raw.authMethod ?? raw.auth_method),
9635
+ createdAt: asString4(raw.createdAt ?? raw.created_at),
9636
+ updatedAt: asString4(raw.updatedAt ?? raw.updated_at)
8231
9637
  };
8232
9638
  }
8233
9639
  async function loadLegacySettingsPatch() {
@@ -8240,9 +9646,9 @@ async function loadLegacySettingsPatch() {
8240
9646
  const license = parseLegacyLicense(raw.license ?? raw.license_data ?? raw.license_state);
8241
9647
  return {
8242
9648
  app: {
8243
- lastAppVersion: asString3(raw.lastAppVersion ?? raw.last_app_version),
8244
- pendingUpdateVersion: asString3(raw.pendingUpdateVersion ?? raw.pending_update_version),
8245
- lastProfileName: asString3(raw.lastProfileName ?? raw.last_profile_name)
9649
+ lastAppVersion: asString4(raw.lastAppVersion ?? raw.last_app_version),
9650
+ pendingUpdateVersion: asString4(raw.pendingUpdateVersion ?? raw.pending_update_version),
9651
+ lastProfileName: asString4(raw.lastProfileName ?? raw.last_profile_name)
8246
9652
  },
8247
9653
  license,
8248
9654
  autoRoll: autoRoll ? {
@@ -8260,15 +9666,15 @@ async function loadLegacySyncPatch() {
8260
9666
  }
8261
9667
  return {
8262
9668
  sync: {
8263
- lastPushAt: asString3(raw.lastPushAt),
8264
- lastPullAt: asString3(raw.lastPullAt),
8265
- lastError: asString3(raw.lastError),
8266
- remoteUpdatedAt: asString3(raw.remoteUpdatedAt)
9669
+ lastPushAt: asString4(raw.lastPushAt),
9670
+ lastPullAt: asString4(raw.lastPullAt),
9671
+ lastError: asString4(raw.lastError),
9672
+ remoteUpdatedAt: asString4(raw.remoteUpdatedAt)
8267
9673
  }
8268
9674
  };
8269
9675
  }
8270
9676
  async function loadLegacyAppSettingsParityPatch() {
8271
- const filePath = import_node_path12.default.join(getUserDataDir(), LEGACY_APP_SETTINGS_PARITY_FILE);
9677
+ const filePath = import_node_path15.default.join(getUserDataDir(), LEGACY_APP_SETTINGS_PARITY_FILE);
8272
9678
  const raw = await readJsonFileIfExists(filePath);
8273
9679
  if (!isRecord6(raw)) {
8274
9680
  return {};
@@ -8281,7 +9687,7 @@ async function loadLegacyProfilesPatch() {
8281
9687
  const root = resolveLegacyPath(LEGACY_PROFILE_HOMES_DIR);
8282
9688
  let entries = [];
8283
9689
  try {
8284
- entries = await import_node_fs8.promises.readdir(root, { withFileTypes: true });
9690
+ entries = await import_node_fs9.promises.readdir(root, { withFileTypes: true });
8285
9691
  } catch (error) {
8286
9692
  if (error.code === "ENOENT") {
8287
9693
  return {};
@@ -8293,7 +9699,7 @@ async function loadLegacyProfilesPatch() {
8293
9699
  if (!entry.isDirectory() || entry.name.startsWith(".")) {
8294
9700
  continue;
8295
9701
  }
8296
- const profileFile = import_node_path12.default.join(root, entry.name, "profile.json");
9702
+ const profileFile = import_node_path15.default.join(root, entry.name, "profile.json");
8297
9703
  const raw = await readJsonFileIfExists(profileFile);
8298
9704
  if (!raw) {
8299
9705
  continue;
@@ -8313,28 +9719,28 @@ function parseSkillInstallMetadata(raw) {
8313
9719
  if (!isRecord6(raw)) {
8314
9720
  return null;
8315
9721
  }
8316
- const id = asString3(raw.id);
9722
+ const id = asString4(raw.id);
8317
9723
  if (!id) {
8318
9724
  return null;
8319
9725
  }
8320
9726
  return {
8321
9727
  id,
8322
- repo: asString3(raw.repo),
8323
- repoPath: asString3(raw.repoPath),
8324
- sourceLabel: asString3(raw.sourceLabel),
9728
+ repo: asString4(raw.repo),
9729
+ repoPath: asString4(raw.repoPath),
9730
+ sourceLabel: asString4(raw.sourceLabel),
8325
9731
  sourceType: raw.sourceType === "official" || raw.sourceType === "community" || raw.sourceType === "local" ? raw.sourceType : void 0,
8326
- viewUrl: asString3(raw.viewUrl),
8327
- createdAt: asString3(raw.createdAt)
9732
+ viewUrl: asString4(raw.viewUrl),
9733
+ createdAt: asString4(raw.createdAt)
8328
9734
  };
8329
9735
  }
8330
9736
  async function loadLegacySkillsPatch() {
8331
9737
  const skillsRoot = resolveLegacyPath(LEGACY_SKILLS_DIR);
8332
- const reposPath = import_node_path12.default.join(skillsRoot, LEGACY_SKILLS_REPOS_FILE);
9738
+ const reposPath = import_node_path15.default.join(skillsRoot, LEGACY_SKILLS_REPOS_FILE);
8333
9739
  const reposRaw = await readJsonFileIfExists(reposPath);
8334
9740
  const installsBySlug = {};
8335
9741
  let dirEntries = [];
8336
9742
  try {
8337
- dirEntries = await import_node_fs8.promises.readdir(skillsRoot, { withFileTypes: true });
9743
+ dirEntries = await import_node_fs9.promises.readdir(skillsRoot, { withFileTypes: true });
8338
9744
  } catch (error) {
8339
9745
  if (error.code !== "ENOENT") {
8340
9746
  throw error;
@@ -8347,7 +9753,7 @@ async function loadLegacySkillsPatch() {
8347
9753
  if (entry.name.startsWith(".")) {
8348
9754
  continue;
8349
9755
  }
8350
- const manifestPath = import_node_path12.default.join(skillsRoot, entry.name, LEGACY_SKILL_MANIFEST);
9756
+ const manifestPath = import_node_path15.default.join(skillsRoot, entry.name, LEGACY_SKILL_MANIFEST);
8351
9757
  const manifestRaw = await readJsonFileIfExists(manifestPath);
8352
9758
  const parsed = parseSkillInstallMetadata(manifestRaw);
8353
9759
  if (!parsed) {
@@ -8457,29 +9863,29 @@ function mergeLegacyLocalStoragePatch(payload) {
8457
9863
  };
8458
9864
  }
8459
9865
  async function removeIfExists(target) {
8460
- await import_node_fs8.promises.rm(target, { recursive: true, force: true });
9866
+ await import_node_fs9.promises.rm(target, { recursive: true, force: true });
8461
9867
  }
8462
9868
  async function cleanupLegacyCanonicalSources() {
8463
9869
  const homeDir = resolveHomeDir2();
8464
9870
  await removeIfExists(resolveLegacyAppStatePath());
8465
- await removeIfExists(import_node_path12.default.join(getUserDataDir(), LEGACY_APP_SETTINGS_PARITY_FILE));
8466
- await removeIfExists(import_node_path12.default.join(homeDir, SQLITE_STORAGE_DIR));
9871
+ await removeIfExists(import_node_path15.default.join(getUserDataDir(), LEGACY_APP_SETTINGS_PARITY_FILE));
9872
+ await removeIfExists(import_node_path15.default.join(homeDir, SQLITE_STORAGE_DIR));
8467
9873
  await removeIfExists(resolveLegacyPath(LEGACY_SETTINGS_FILE));
8468
9874
  await removeIfExists(resolveLegacyPath(LEGACY_SETTINGS_BACKUP_FILE));
8469
9875
  await removeIfExists(resolveLegacyPath(LEGACY_SYNC_STATE_FILE));
8470
- await removeIfExists(resolveLegacyPath(LEGACY_LICENSE_SECRET_FILE));
9876
+ await removeIfExists(resolveLegacyPath(LEGACY_LICENSE_SECRET_FILE2));
8471
9877
  await removeIfExists(resolveLegacyPath(LEGACY_SKILLS_DIR, LEGACY_SKILLS_REPOS_FILE));
8472
9878
  const profileHomesRoot = resolveLegacyPath(LEGACY_PROFILE_HOMES_DIR);
8473
9879
  try {
8474
- const profileHomes = await import_node_fs8.promises.readdir(profileHomesRoot, { withFileTypes: true });
9880
+ const profileHomes = await import_node_fs9.promises.readdir(profileHomesRoot, { withFileTypes: true });
8475
9881
  for (const profileHome of profileHomes) {
8476
9882
  if (!profileHome.isDirectory()) {
8477
9883
  continue;
8478
9884
  }
8479
- const profileDir = import_node_path12.default.join(profileHomesRoot, profileHome.name);
9885
+ const profileDir = import_node_path15.default.join(profileHomesRoot, profileHome.name);
8480
9886
  let files = [];
8481
9887
  try {
8482
- files = await import_node_fs8.promises.readdir(profileDir);
9888
+ files = await import_node_fs9.promises.readdir(profileDir);
8483
9889
  } catch {
8484
9890
  continue;
8485
9891
  }
@@ -8487,7 +9893,7 @@ async function cleanupLegacyCanonicalSources() {
8487
9893
  if (!file.startsWith("profile.json")) {
8488
9894
  continue;
8489
9895
  }
8490
- await removeIfExists(import_node_path12.default.join(profileDir, file));
9896
+ await removeIfExists(import_node_path15.default.join(profileDir, file));
8491
9897
  }
8492
9898
  }
8493
9899
  } catch (error) {
@@ -8500,12 +9906,12 @@ async function cleanupLegacyCanonicalSources() {
8500
9906
  }
8501
9907
  const skillsRoot = resolveLegacyPath(LEGACY_SKILLS_DIR);
8502
9908
  try {
8503
- const skillDirs = await import_node_fs8.promises.readdir(skillsRoot, { withFileTypes: true });
9909
+ const skillDirs = await import_node_fs9.promises.readdir(skillsRoot, { withFileTypes: true });
8504
9910
  for (const entry of skillDirs) {
8505
9911
  if (!entry.isDirectory() || entry.name.startsWith(".")) {
8506
9912
  continue;
8507
9913
  }
8508
- await removeIfExists(import_node_path12.default.join(skillsRoot, entry.name, LEGACY_SKILL_MANIFEST));
9914
+ await removeIfExists(import_node_path15.default.join(skillsRoot, entry.name, LEGACY_SKILL_MANIFEST));
8509
9915
  }
8510
9916
  } catch (error) {
8511
9917
  if (!isMissingPathError(error)) {
@@ -8718,24 +10124,29 @@ async function ensureCliStorageReady() {
8718
10124
  }
8719
10125
 
8720
10126
  // src/app/main.ts
8721
- var VERSION = true ? "3.1.4" : "0.0.0";
10127
+ var VERSION = true ? "3.5.6" : "0.0.0";
8722
10128
  async function runCli() {
8723
10129
  const args = process.argv.slice(2);
8724
10130
  if (args.length === 0) {
8725
10131
  printHelp(VERSION);
8726
10132
  return;
8727
10133
  }
8728
- if (args[0]?.startsWith("-") && (hasFlag2(args, "--help") || hasFlag2(args, "-h"))) {
10134
+ if (args[0]?.startsWith("-") && (hasFlag(args, "--help") || hasFlag(args, "-h"))) {
8729
10135
  printHelp(VERSION);
8730
10136
  return;
8731
10137
  }
8732
- if (args[0]?.startsWith("-") && (hasFlag2(args, "--version") || hasFlag2(args, "-v"))) {
10138
+ if (args[0]?.startsWith("-") && (hasFlag(args, "--version") || hasFlag(args, "-v"))) {
8733
10139
  console.log(VERSION);
8734
10140
  return;
8735
10141
  }
8736
10142
  const command = args[0];
8737
10143
  const rest = args.slice(1);
8738
10144
  switch (command) {
10145
+ case "account-pool":
10146
+ case "accounts-pool":
10147
+ await ensureCliStorageReady();
10148
+ await handleAccountPoolCommand(rest, VERSION);
10149
+ return;
8739
10150
  case "profile":
8740
10151
  await ensureCliStorageReady();
8741
10152
  await handleProfileCommand(rest, VERSION);