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/README.md +17 -3
- package/dist/index.js +2279 -868
- package/dist/index.js.map +1 -1
- package/dist/server/{NodeSqliteClient-BMOt9qz2.mjs → NodeSqliteClient-C3G_wOtl.mjs} +3 -3
- package/dist/server/{SqlClient-CB8oPtuH.mjs → SqlClient-DExl4DLk.mjs} +11 -160
- package/dist/server/{SqlError-3qccgSvc.mjs → SqlError-CJg2cz5q.mjs} +2 -2
- package/dist/server/{SqliteClient-ChChXcv_.mjs → SqliteClient-DG8tJKWU.mjs} +3 -3
- package/dist/server/index.mjs +8273 -6051
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2021,450 +2021,147 @@ var require_toml = __commonJS({
|
|
|
2021
2021
|
}
|
|
2022
2022
|
});
|
|
2023
2023
|
|
|
2024
|
-
// src/commands/
|
|
2025
|
-
var
|
|
2026
|
-
var
|
|
2027
|
-
var
|
|
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/
|
|
2033
|
-
var
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
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
|
-
|
|
2061
|
+
const rounded = Math.round(value);
|
|
2062
|
+
return Math.max(CHAT_SCROLLBACK_MIN, Math.min(CHAT_SCROLLBACK_MAX, rounded));
|
|
2072
2063
|
}
|
|
2073
|
-
function
|
|
2074
|
-
if (
|
|
2075
|
-
return
|
|
2064
|
+
function normalizeChatHistoryScrollbackItems(value) {
|
|
2065
|
+
if (value === null) {
|
|
2066
|
+
return null;
|
|
2076
2067
|
}
|
|
2077
|
-
if (
|
|
2078
|
-
return
|
|
2068
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
2069
|
+
return clampChatScrollbackItems(value);
|
|
2079
2070
|
}
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
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
|
|
2077
|
+
return CHAT_SCROLLBACK_DEFAULT;
|
|
2104
2078
|
}
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
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
|
-
|
|
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/
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
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
|
|
2133
|
-
return
|
|
2109
|
+
function clone(value) {
|
|
2110
|
+
return JSON.parse(JSON.stringify(value));
|
|
2134
2111
|
}
|
|
2135
|
-
function
|
|
2136
|
-
|
|
2137
|
-
|
|
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
|
|
2144
|
-
|
|
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
|
|
2158
|
-
|
|
2159
|
-
|
|
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
|
-
|
|
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
|
|
2170
|
-
|
|
2133
|
+
function beginImmediate(db) {
|
|
2134
|
+
db.exec("BEGIN IMMEDIATE");
|
|
2171
2135
|
}
|
|
2172
|
-
function
|
|
2173
|
-
|
|
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
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
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
|
-
|
|
2232
|
-
|
|
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
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
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
|
|
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
|
|
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 ||
|
|
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
|
|
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
|
|
2293
|
+
return import_node_path2.default.join(appData, APP_NAME);
|
|
2597
2294
|
}
|
|
2598
|
-
return
|
|
2295
|
+
return import_node_path2.default.join(home, "AppData", "Roaming", APP_NAME);
|
|
2599
2296
|
}
|
|
2600
|
-
return
|
|
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
|
|
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
|
-
|
|
2697
|
-
|
|
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.
|
|
2996
|
-
merged.
|
|
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.
|
|
2999
|
-
|
|
3000
|
-
|
|
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
|
|
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-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
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
|
-
|
|
3139
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
2863
|
+
return changed ? next : settings;
|
|
3140
2864
|
}
|
|
3141
|
-
function
|
|
3142
|
-
|
|
2865
|
+
async function readAppSettings() {
|
|
2866
|
+
const appState = await getAppState();
|
|
2867
|
+
return stripLegacyAppSettingsKeys(asRecord(appState.runtimeSettings));
|
|
3143
2868
|
}
|
|
3144
|
-
function
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
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
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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 ??
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
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
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
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
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
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
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
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
|
-
|
|
5811
|
-
|
|
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
|
|
5846
|
-
var
|
|
5847
|
-
var
|
|
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 =
|
|
7127
|
+
const resolved = import_node_path10.default.resolve(candidate);
|
|
5852
7128
|
try {
|
|
5853
|
-
const stat2 = (0,
|
|
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(
|
|
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(
|
|
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
|
|
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 } =
|
|
5924
|
-
const child = (0,
|
|
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 =
|
|
7708
|
+
const params = stripFlags(args);
|
|
6433
7709
|
const sub = params[0];
|
|
6434
|
-
if (!sub ||
|
|
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 =
|
|
6448
|
-
const withUsage = !
|
|
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 (!
|
|
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 (!
|
|
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 =
|
|
6529
|
-
const dryRun =
|
|
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
|
|
6585
|
-
var
|
|
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
|
|
8021
|
+
var import_node_child_process6 = require("child_process");
|
|
6746
8022
|
var import_promises = require("fs/promises");
|
|
6747
|
-
var
|
|
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,
|
|
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
|
-
|
|
6934
|
-
|
|
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
|
|
8315
|
+
var import_node_path13 = __toESM(require("path"), 1);
|
|
7040
8316
|
|
|
7041
8317
|
// ../../packages/runtime-codex/src/codex/home.ts
|
|
7042
|
-
var
|
|
7043
|
-
var
|
|
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 ||
|
|
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
|
|
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
|
|
8344
|
+
return import_node_path12.default.resolve(expandHomePrefix(configured, resolveHomeDir()));
|
|
7069
8345
|
}
|
|
7070
8346
|
function resolveDefaultCodexHomeDir() {
|
|
7071
|
-
return
|
|
8347
|
+
return import_node_path12.default.join(resolveHomeDir(), ".codex");
|
|
7072
8348
|
}
|
|
7073
8349
|
function resolveProfileCodexHomeDir(profileName) {
|
|
7074
|
-
return
|
|
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
|
|
8431
|
+
return import_node_path13.default.join(resolveCodexHomeDir(options), "config.toml");
|
|
7156
8432
|
}
|
|
7157
8433
|
async function ensureConfigDirExists(filePath) {
|
|
7158
|
-
const dir =
|
|
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
|
|
7218
|
-
|
|
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
|
|
8524
|
+
return "";
|
|
8525
|
+
}
|
|
8526
|
+
if (typeof error === "string") {
|
|
8527
|
+
return error;
|
|
7221
8528
|
}
|
|
7222
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
|
|
7239
|
-
|
|
7240
|
-
|
|
7241
|
-
|
|
7242
|
-
}
|
|
7243
|
-
|
|
7244
|
-
|
|
7245
|
-
|
|
7246
|
-
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
9110
|
+
await import_node_fs8.promises.mkdir(backupsDir, { recursive: true });
|
|
7705
9111
|
const timestamp = toSafeIsoForFileName((/* @__PURE__ */ new Date()).toISOString());
|
|
7706
|
-
const backupPath =
|
|
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
|
|
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 =
|
|
9332
|
+
const params = stripFlags(args);
|
|
7927
9333
|
const sub = params[0];
|
|
7928
|
-
if (!sub ||
|
|
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
|
|
7980
|
-
var
|
|
7981
|
-
var
|
|
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
|
|
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
|
|
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 ||
|
|
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
|
|
9433
|
+
return import_node_path15.default.join(resolveHomeDir2(), ".codex");
|
|
8028
9434
|
}
|
|
8029
9435
|
function resolveLegacyPath(...segments) {
|
|
8030
|
-
return
|
|
9436
|
+
return import_node_path15.default.join(resolveCodexDir(), ...segments);
|
|
8031
9437
|
}
|
|
8032
9438
|
async function readJsonFile(filePath) {
|
|
8033
|
-
const raw = await
|
|
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
|
|
8049
|
-
const entries = await
|
|
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 =
|
|
8052
|
-
const destPath =
|
|
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
|
|
8055
|
-
await
|
|
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
|
|
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
|
|
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
|
|
8085
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
8125
|
-
await
|
|
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
|
|
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(
|
|
9537
|
+
await removeIfExists(import_node_path15.default.join(skillsDir, LEGACY_SKILLS_REPOS_FILE));
|
|
8132
9538
|
let entries = [];
|
|
8133
9539
|
try {
|
|
8134
|
-
entries = await
|
|
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(
|
|
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
|
-
|
|
8150
|
-
|
|
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
|
-
|
|
8154
|
-
|
|
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
|
-
|
|
8158
|
-
|
|
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
|
-
|
|
8162
|
-
|
|
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(
|
|
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 =
|
|
9595
|
+
const statusCandidate = asString4(raw.status);
|
|
8190
9596
|
const status = ["inactive", "active", "grace", "error"].includes(statusCandidate ?? "") ? statusCandidate : "inactive";
|
|
8191
9597
|
return {
|
|
8192
|
-
licenseKey:
|
|
8193
|
-
purchaseEmail:
|
|
8194
|
-
lastVerifiedAt:
|
|
8195
|
-
nextCheckAt:
|
|
8196
|
-
lastVerificationError:
|
|
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:
|
|
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:
|
|
9627
|
+
displayName: asString4(raw.displayName ?? raw.display_name) ?? name,
|
|
8222
9628
|
data,
|
|
8223
9629
|
metadata: isRecord6(raw.metadata) ? raw.metadata : void 0,
|
|
8224
|
-
accountId:
|
|
8225
|
-
workspaceId:
|
|
8226
|
-
workspaceName:
|
|
8227
|
-
email:
|
|
8228
|
-
authMethod:
|
|
8229
|
-
createdAt:
|
|
8230
|
-
updatedAt:
|
|
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:
|
|
8244
|
-
pendingUpdateVersion:
|
|
8245
|
-
lastProfileName:
|
|
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:
|
|
8264
|
-
lastPullAt:
|
|
8265
|
-
lastError:
|
|
8266
|
-
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
9722
|
+
const id = asString4(raw.id);
|
|
8317
9723
|
if (!id) {
|
|
8318
9724
|
return null;
|
|
8319
9725
|
}
|
|
8320
9726
|
return {
|
|
8321
9727
|
id,
|
|
8322
|
-
repo:
|
|
8323
|
-
repoPath:
|
|
8324
|
-
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:
|
|
8327
|
-
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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(
|
|
8466
|
-
await removeIfExists(
|
|
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(
|
|
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
|
|
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 =
|
|
9885
|
+
const profileDir = import_node_path15.default.join(profileHomesRoot, profileHome.name);
|
|
8480
9886
|
let files = [];
|
|
8481
9887
|
try {
|
|
8482
|
-
files = await
|
|
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(
|
|
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
|
|
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(
|
|
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.
|
|
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("-") && (
|
|
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("-") && (
|
|
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);
|