codexuse-cli 3.1.5 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -3
- package/dist/index.js +2463 -950
- 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 +8157 -5525
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2021,428 +2021,143 @@ 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 writeQueueByDbPath = /* @__PURE__ */ new Map();
|
|
2106
|
+
var initializedDbPaths = /* @__PURE__ */ new Set();
|
|
2107
|
+
function isRecord(value) {
|
|
2108
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
2131
2109
|
}
|
|
2132
|
-
function
|
|
2133
|
-
return
|
|
2110
|
+
function clone(value) {
|
|
2111
|
+
return JSON.parse(JSON.stringify(value));
|
|
2134
2112
|
}
|
|
2135
|
-
function
|
|
2136
|
-
|
|
2137
|
-
|
|
2113
|
+
function safeParseJson(raw) {
|
|
2114
|
+
if (typeof raw !== "string" || raw.trim().length === 0) {
|
|
2115
|
+
return null;
|
|
2116
|
+
}
|
|
2117
|
+
try {
|
|
2118
|
+
return JSON.parse(raw);
|
|
2119
|
+
} catch {
|
|
2138
2120
|
return null;
|
|
2139
2121
|
}
|
|
2140
|
-
const value = explicit.slice(`${name}=`.length).trim();
|
|
2141
|
-
return value.length > 0 ? value : "";
|
|
2142
2122
|
}
|
|
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
|
-
`);
|
|
2123
|
+
function sleep(ms) {
|
|
2124
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2156
2125
|
}
|
|
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
|
-
}
|
|
2126
|
+
function isSqliteBusyError(error) {
|
|
2127
|
+
if (!error || typeof error !== "object") {
|
|
2128
|
+
return false;
|
|
2166
2129
|
}
|
|
2167
|
-
|
|
2130
|
+
const sqliteError = error;
|
|
2131
|
+
const message = typeof sqliteError.message === "string" ? sqliteError.message.toLowerCase() : "";
|
|
2132
|
+
return sqliteError.code === "SQLITE_BUSY" || sqliteError.errno === 5 || message.includes("sqlite_busy") || message.includes("database is locked");
|
|
2168
2133
|
}
|
|
2169
|
-
function
|
|
2170
|
-
|
|
2134
|
+
function beginImmediate(db) {
|
|
2135
|
+
db.exec("BEGIN IMMEDIATE");
|
|
2171
2136
|
}
|
|
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
|
-
});
|
|
2137
|
+
function commit(db) {
|
|
2138
|
+
db.exec("COMMIT");
|
|
2193
2139
|
}
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
`Missing bundled T3 server build output at ${entry}. Rebuild the CLI package with \`bun run build:cli\`.`
|
|
2199
|
-
);
|
|
2140
|
+
function rollback(db) {
|
|
2141
|
+
try {
|
|
2142
|
+
db.exec("ROLLBACK");
|
|
2143
|
+
} catch {
|
|
2200
2144
|
}
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
const
|
|
2204
|
-
|
|
2205
|
-
const
|
|
2206
|
-
|
|
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"
|
|
2145
|
+
}
|
|
2146
|
+
async function withWriteQueue(dbPath, task) {
|
|
2147
|
+
const previous = writeQueueByDbPath.get(dbPath) ?? Promise.resolve();
|
|
2148
|
+
let release;
|
|
2149
|
+
const current = new Promise((resolve) => {
|
|
2150
|
+
release = resolve;
|
|
2225
2151
|
});
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
}
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
await waitForChild(child);
|
|
2236
|
-
}
|
|
2237
|
-
function waitForChild(child) {
|
|
2238
|
-
return new Promise((resolve, reject) => {
|
|
2239
|
-
let stopping = false;
|
|
2240
|
-
const stop = (signal) => {
|
|
2241
|
-
if (stopping) {
|
|
2242
|
-
return;
|
|
2243
|
-
}
|
|
2244
|
-
stopping = true;
|
|
2245
|
-
child.kill(signal);
|
|
2246
|
-
};
|
|
2247
|
-
const onSigInt = () => stop("SIGINT");
|
|
2248
|
-
const onSigTerm = () => stop("SIGTERM");
|
|
2249
|
-
process.once("SIGINT", onSigInt);
|
|
2250
|
-
process.once("SIGTERM", onSigTerm);
|
|
2251
|
-
child.once("exit", (code, signal) => {
|
|
2252
|
-
process.off("SIGINT", onSigInt);
|
|
2253
|
-
process.off("SIGTERM", onSigTerm);
|
|
2254
|
-
if (code === 0 || stopping) {
|
|
2255
|
-
resolve();
|
|
2256
|
-
return;
|
|
2257
|
-
}
|
|
2258
|
-
reject(
|
|
2259
|
-
new Error(
|
|
2260
|
-
`Projects server exited unexpectedly (code=${code ?? "null"}, signal=${signal ?? "null"}).`
|
|
2261
|
-
)
|
|
2262
|
-
);
|
|
2263
|
-
});
|
|
2264
|
-
child.once("error", (error) => {
|
|
2265
|
-
process.off("SIGINT", onSigInt);
|
|
2266
|
-
process.off("SIGTERM", onSigTerm);
|
|
2267
|
-
reject(error);
|
|
2268
|
-
});
|
|
2269
|
-
});
|
|
2270
|
-
}
|
|
2271
|
-
async function handleDaemonCommand(args, version) {
|
|
2272
|
-
const flags = args.filter((arg) => arg.startsWith("-"));
|
|
2273
|
-
const params = stripFlags(args);
|
|
2274
|
-
const sub = params[0];
|
|
2275
|
-
if (!sub || hasFlag(flags, "--help") || hasFlag(flags, "-h")) {
|
|
2276
|
-
printDaemonHelp(version);
|
|
2277
|
-
return;
|
|
2278
|
-
}
|
|
2279
|
-
switch (sub) {
|
|
2280
|
-
case "start": {
|
|
2281
|
-
const projectPath = parseStringFlag(flags, "--project-path") ?? parseStringFlag(flags, "--project") ?? null;
|
|
2282
|
-
const telegramBotToken = parseStringFlag(flags, "--telegram-bot-token") ?? normalizeTelegramBotToken(process.env.CODEXUSE_TELEGRAM_BOT_TOKEN) ?? null;
|
|
2283
|
-
await runDaemonStart({
|
|
2284
|
-
projectPath,
|
|
2285
|
-
telegramBotToken,
|
|
2286
|
-
version
|
|
2287
|
-
});
|
|
2288
|
-
return;
|
|
2289
|
-
}
|
|
2290
|
-
default:
|
|
2291
|
-
printDaemonHelp(version);
|
|
2292
|
-
return;
|
|
2293
|
-
}
|
|
2294
|
-
}
|
|
2295
|
-
|
|
2296
|
-
// ../../packages/runtime-profiles/src/license/service.ts
|
|
2297
|
-
var import_node_crypto3 = __toESM(require("crypto"), 1);
|
|
2298
|
-
|
|
2299
|
-
// ../../packages/contracts/src/settings/auto-roll.ts
|
|
2300
|
-
var DEFAULT_AUTO_ROLL_ENABLED = false;
|
|
2301
|
-
var DEFAULT_AUTO_ROLL_WARNING_THRESHOLD = 85;
|
|
2302
|
-
var DEFAULT_AUTO_ROLL_SWITCH_THRESHOLD = 95;
|
|
2303
|
-
var AUTO_ROLL_WARNING_MIN = 50;
|
|
2304
|
-
var AUTO_ROLL_WARNING_MAX = 99;
|
|
2305
|
-
var AUTO_ROLL_SWITCH_MAX = 100;
|
|
2306
|
-
function clampNumber(value, min, max) {
|
|
2307
|
-
return Math.min(max, Math.max(min, value));
|
|
2308
|
-
}
|
|
2309
|
-
function resolveFiniteNumber(value, fallback) {
|
|
2310
|
-
return Number.isFinite(value) ? value : fallback;
|
|
2311
|
-
}
|
|
2312
|
-
function sanitizeAutoRollWarningThreshold(value) {
|
|
2313
|
-
const numeric = resolveFiniteNumber(value, DEFAULT_AUTO_ROLL_WARNING_THRESHOLD);
|
|
2314
|
-
return clampNumber(numeric, AUTO_ROLL_WARNING_MIN, AUTO_ROLL_WARNING_MAX);
|
|
2315
|
-
}
|
|
2316
|
-
function sanitizeAutoRollSwitchThreshold(value, warningThreshold) {
|
|
2317
|
-
const sanitizedWarning = sanitizeAutoRollWarningThreshold(warningThreshold);
|
|
2318
|
-
const numeric = resolveFiniteNumber(value, DEFAULT_AUTO_ROLL_SWITCH_THRESHOLD);
|
|
2319
|
-
return clampNumber(numeric, sanitizedWarning + 1, AUTO_ROLL_SWITCH_MAX);
|
|
2320
|
-
}
|
|
2321
|
-
function sanitizeAutoRollThresholds(warningThreshold, switchThreshold) {
|
|
2322
|
-
const sanitizedWarning = sanitizeAutoRollWarningThreshold(warningThreshold);
|
|
2323
|
-
const sanitizedSwitch = sanitizeAutoRollSwitchThreshold(switchThreshold, sanitizedWarning);
|
|
2324
|
-
return {
|
|
2325
|
-
warningThreshold: sanitizedWarning,
|
|
2326
|
-
switchThreshold: sanitizedSwitch
|
|
2327
|
-
};
|
|
2328
|
-
}
|
|
2329
|
-
function normalizeAutoRollSettings(raw) {
|
|
2330
|
-
const enabled = typeof raw?.enabled === "boolean" ? raw.enabled : DEFAULT_AUTO_ROLL_ENABLED;
|
|
2331
|
-
const rawWarning = resolveFiniteNumber(
|
|
2332
|
-
typeof raw?.warningThreshold === "number" ? raw.warningThreshold : Number.NaN,
|
|
2333
|
-
DEFAULT_AUTO_ROLL_WARNING_THRESHOLD
|
|
2334
|
-
);
|
|
2335
|
-
const rawSwitch = resolveFiniteNumber(
|
|
2336
|
-
typeof raw?.switchThreshold === "number" ? raw.switchThreshold : Number.NaN,
|
|
2337
|
-
DEFAULT_AUTO_ROLL_SWITCH_THRESHOLD
|
|
2338
|
-
);
|
|
2339
|
-
const {
|
|
2340
|
-
warningThreshold: normalizedWarning,
|
|
2341
|
-
switchThreshold: normalizedSwitch
|
|
2342
|
-
} = sanitizeAutoRollThresholds(rawWarning, rawSwitch);
|
|
2343
|
-
return {
|
|
2344
|
-
enabled,
|
|
2345
|
-
warningThreshold: normalizedWarning,
|
|
2346
|
-
switchThreshold: normalizedSwitch
|
|
2347
|
-
};
|
|
2348
|
-
}
|
|
2349
|
-
|
|
2350
|
-
// ../../packages/runtime-app-state/src/app/state.ts
|
|
2351
|
-
var import_node_fs3 = require("fs");
|
|
2352
|
-
var import_node_async_hooks = require("async_hooks");
|
|
2353
|
-
var import_node_path4 = __toESM(require("path"), 1);
|
|
2354
|
-
var import_node_os2 = __toESM(require("os"), 1);
|
|
2355
|
-
|
|
2356
|
-
// ../../packages/contracts/src/settings/chat-scrollback.ts
|
|
2357
|
-
var CHAT_SCROLLBACK_DEFAULT = 200;
|
|
2358
|
-
var CHAT_SCROLLBACK_MIN = 50;
|
|
2359
|
-
var CHAT_SCROLLBACK_MAX = 5e3;
|
|
2360
|
-
function clampChatScrollbackItems(value) {
|
|
2361
|
-
if (!Number.isFinite(value)) {
|
|
2362
|
-
return CHAT_SCROLLBACK_DEFAULT;
|
|
2363
|
-
}
|
|
2364
|
-
const rounded = Math.round(value);
|
|
2365
|
-
return Math.max(CHAT_SCROLLBACK_MIN, Math.min(CHAT_SCROLLBACK_MAX, rounded));
|
|
2366
|
-
}
|
|
2367
|
-
function normalizeChatHistoryScrollbackItems(value) {
|
|
2368
|
-
if (value === null) {
|
|
2369
|
-
return null;
|
|
2370
|
-
}
|
|
2371
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
2372
|
-
return clampChatScrollbackItems(value);
|
|
2373
|
-
}
|
|
2374
|
-
if (typeof value === "string") {
|
|
2375
|
-
const parsed = Number(value);
|
|
2376
|
-
if (Number.isFinite(parsed)) {
|
|
2377
|
-
return clampChatScrollbackItems(parsed);
|
|
2378
|
-
}
|
|
2379
|
-
}
|
|
2380
|
-
return CHAT_SCROLLBACK_DEFAULT;
|
|
2381
|
-
}
|
|
2382
|
-
|
|
2383
|
-
// ../../packages/contracts/src/settings/commit-message-prompt.ts
|
|
2384
|
-
var DEFAULT_COMMIT_MESSAGE_PROMPT = `Generate a concise git commit message for the following changes. Follow conventional commit format (e.g., feat:, fix:, refactor:, docs:, etc.). Keep the summary line under 72 characters. Only output the commit message, nothing else.
|
|
2385
|
-
|
|
2386
|
-
Changes:
|
|
2387
|
-
{diff}`;
|
|
2388
|
-
function normalizeLineEndings(value) {
|
|
2389
|
-
return value.replace(/\r\n/g, "\n");
|
|
2390
|
-
}
|
|
2391
|
-
function normalizeCommitMessagePrompt(value) {
|
|
2392
|
-
if (typeof value !== "string") {
|
|
2393
|
-
return null;
|
|
2394
|
-
}
|
|
2395
|
-
const normalized = normalizeLineEndings(value).trim();
|
|
2396
|
-
return normalized.length > 0 ? normalized : null;
|
|
2397
|
-
}
|
|
2398
|
-
|
|
2399
|
-
// ../../packages/runtime-app-state/src/storage/documents.ts
|
|
2400
|
-
var import_node_fs2 = require("fs");
|
|
2401
|
-
var import_node_path3 = __toESM(require("path"), 1);
|
|
2402
|
-
var APP_STORAGE_TABLE = "app_storage_documents";
|
|
2403
|
-
var APP_STORAGE_DB_DIR = "t3-projects";
|
|
2404
|
-
var APP_STORAGE_DB_NAME = "state.sqlite";
|
|
2405
|
-
var SQLITE_BUSY_TIMEOUT_MS = 5e3;
|
|
2406
|
-
var SQLITE_BUSY_MAX_ATTEMPTS = 3;
|
|
2407
|
-
var SQLITE_BUSY_RETRY_DELAY_MS = 100;
|
|
2408
|
-
var initializedDbPaths = /* @__PURE__ */ new Set();
|
|
2409
|
-
function isRecord(value) {
|
|
2410
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
2411
|
-
}
|
|
2412
|
-
function clone(value) {
|
|
2413
|
-
return JSON.parse(JSON.stringify(value));
|
|
2414
|
-
}
|
|
2415
|
-
function safeParseJson(raw) {
|
|
2416
|
-
if (typeof raw !== "string" || raw.trim().length === 0) {
|
|
2417
|
-
return null;
|
|
2418
|
-
}
|
|
2419
|
-
try {
|
|
2420
|
-
return JSON.parse(raw);
|
|
2421
|
-
} catch {
|
|
2422
|
-
return null;
|
|
2423
|
-
}
|
|
2424
|
-
}
|
|
2425
|
-
function sleep(ms) {
|
|
2426
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2427
|
-
}
|
|
2428
|
-
function isSqliteBusyError(error) {
|
|
2429
|
-
if (!error || typeof error !== "object") {
|
|
2430
|
-
return false;
|
|
2431
|
-
}
|
|
2432
|
-
const sqliteError = error;
|
|
2433
|
-
const message = typeof sqliteError.message === "string" ? sqliteError.message.toLowerCase() : "";
|
|
2434
|
-
return sqliteError.code === "SQLITE_BUSY" || sqliteError.errno === 5 || message.includes("sqlite_busy") || message.includes("database is locked");
|
|
2435
|
-
}
|
|
2436
|
-
function beginImmediate(db) {
|
|
2437
|
-
db.exec("BEGIN IMMEDIATE");
|
|
2438
|
-
}
|
|
2439
|
-
function commit(db) {
|
|
2440
|
-
db.exec("COMMIT");
|
|
2441
|
-
}
|
|
2442
|
-
function rollback(db) {
|
|
2443
|
-
try {
|
|
2444
|
-
db.exec("ROLLBACK");
|
|
2445
|
-
} catch {
|
|
2152
|
+
writeQueueByDbPath.set(dbPath, current);
|
|
2153
|
+
await previous;
|
|
2154
|
+
try {
|
|
2155
|
+
return await task();
|
|
2156
|
+
} finally {
|
|
2157
|
+
release?.();
|
|
2158
|
+
if (writeQueueByDbPath.get(dbPath) === current) {
|
|
2159
|
+
writeQueueByDbPath.delete(dbPath);
|
|
2160
|
+
}
|
|
2446
2161
|
}
|
|
2447
2162
|
}
|
|
2448
2163
|
function applyConnectionPragmas(db) {
|
|
@@ -2464,7 +2179,7 @@ function ensureSchema(db, dbPath) {
|
|
|
2464
2179
|
initializedDbPaths.add(dbPath);
|
|
2465
2180
|
}
|
|
2466
2181
|
async function openDatabase(dbPath) {
|
|
2467
|
-
await
|
|
2182
|
+
await import_node_fs.promises.mkdir(import_node_path.default.dirname(dbPath), { recursive: true });
|
|
2468
2183
|
if (process.versions.bun !== void 0) {
|
|
2469
2184
|
const sqlite2 = await Function("return import('bun:sqlite')")();
|
|
2470
2185
|
const database2 = new sqlite2.Database(dbPath);
|
|
@@ -2501,7 +2216,7 @@ async function withDatabase(dbPath, task) {
|
|
|
2501
2216
|
throw new Error("App storage database remained busy after retrying.");
|
|
2502
2217
|
}
|
|
2503
2218
|
function resolveAppStorageDbPath(userDataDir) {
|
|
2504
|
-
return
|
|
2219
|
+
return import_node_path.default.join(userDataDir, APP_STORAGE_DB_DIR, APP_STORAGE_DB_NAME);
|
|
2505
2220
|
}
|
|
2506
2221
|
async function readDocument(dbPath, namespace, normalize) {
|
|
2507
2222
|
return withDatabase(dbPath, (db) => {
|
|
@@ -2516,56 +2231,62 @@ async function readDocument(dbPath, namespace, normalize) {
|
|
|
2516
2231
|
});
|
|
2517
2232
|
}
|
|
2518
2233
|
async function writeDocument(dbPath, namespace, value) {
|
|
2519
|
-
return
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
db
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2234
|
+
return withWriteQueue(
|
|
2235
|
+
dbPath,
|
|
2236
|
+
async () => withDatabase(dbPath, (db) => {
|
|
2237
|
+
const normalizedValue = clone(value);
|
|
2238
|
+
beginImmediate(db);
|
|
2239
|
+
try {
|
|
2240
|
+
db.prepare(
|
|
2241
|
+
`
|
|
2242
|
+
INSERT INTO ${APP_STORAGE_TABLE} (namespace, value_json, updated_at)
|
|
2243
|
+
VALUES (?, ?, ?)
|
|
2244
|
+
ON CONFLICT(namespace)
|
|
2245
|
+
DO UPDATE SET
|
|
2246
|
+
value_json = excluded.value_json,
|
|
2247
|
+
updated_at = excluded.updated_at
|
|
2248
|
+
`
|
|
2249
|
+
).run(namespace, JSON.stringify(normalizedValue), (/* @__PURE__ */ new Date()).toISOString());
|
|
2250
|
+
commit(db);
|
|
2251
|
+
return normalizedValue;
|
|
2252
|
+
} catch (error) {
|
|
2253
|
+
rollback(db);
|
|
2254
|
+
throw error;
|
|
2255
|
+
}
|
|
2256
|
+
})
|
|
2257
|
+
);
|
|
2540
2258
|
}
|
|
2541
2259
|
async function updateDocument(input) {
|
|
2542
|
-
return
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2260
|
+
return withWriteQueue(
|
|
2261
|
+
input.dbPath,
|
|
2262
|
+
async () => withDatabase(input.dbPath, (db) => {
|
|
2263
|
+
beginImmediate(db);
|
|
2264
|
+
try {
|
|
2265
|
+
const row = db.prepare(
|
|
2266
|
+
`SELECT value_json AS valueJson FROM ${APP_STORAGE_TABLE} WHERE namespace = ?`
|
|
2267
|
+
).get(input.namespace);
|
|
2268
|
+
const current = isRecord(row) ? input.normalize(
|
|
2269
|
+
safeParseJson(row.valueJson) ?? input.fallback()
|
|
2270
|
+
) : input.fallback();
|
|
2271
|
+
const next = input.normalize(input.transform(clone(current)));
|
|
2272
|
+
db.prepare(
|
|
2273
|
+
`
|
|
2274
|
+
INSERT INTO ${APP_STORAGE_TABLE} (namespace, value_json, updated_at)
|
|
2275
|
+
VALUES (?, ?, ?)
|
|
2276
|
+
ON CONFLICT(namespace)
|
|
2277
|
+
DO UPDATE SET
|
|
2278
|
+
value_json = excluded.value_json,
|
|
2279
|
+
updated_at = excluded.updated_at
|
|
2280
|
+
`
|
|
2281
|
+
).run(input.namespace, JSON.stringify(next), (/* @__PURE__ */ new Date()).toISOString());
|
|
2282
|
+
commit(db);
|
|
2283
|
+
return clone(next);
|
|
2284
|
+
} catch (error) {
|
|
2285
|
+
rollback(db);
|
|
2286
|
+
throw error;
|
|
2287
|
+
}
|
|
2288
|
+
})
|
|
2289
|
+
);
|
|
2569
2290
|
}
|
|
2570
2291
|
|
|
2571
2292
|
// ../../packages/runtime-app-state/src/app/state.ts
|
|
@@ -2583,27 +2304,27 @@ function clone2(value) {
|
|
|
2583
2304
|
return JSON.parse(JSON.stringify(value));
|
|
2584
2305
|
}
|
|
2585
2306
|
function resolveDefaultUserDataDir() {
|
|
2586
|
-
const home = process.env.HOME || process.env.USERPROFILE ||
|
|
2307
|
+
const home = process.env.HOME || process.env.USERPROFILE || import_node_os.default.homedir();
|
|
2587
2308
|
if (!home) {
|
|
2588
2309
|
throw new Error("Unable to resolve home directory for app state.");
|
|
2589
2310
|
}
|
|
2590
2311
|
if (process.platform === "darwin") {
|
|
2591
|
-
return
|
|
2312
|
+
return import_node_path2.default.join(home, "Library", "Application Support", APP_NAME);
|
|
2592
2313
|
}
|
|
2593
2314
|
if (process.platform === "win32") {
|
|
2594
2315
|
const appData = process.env.APPDATA;
|
|
2595
2316
|
if (appData) {
|
|
2596
|
-
return
|
|
2317
|
+
return import_node_path2.default.join(appData, APP_NAME);
|
|
2597
2318
|
}
|
|
2598
|
-
return
|
|
2319
|
+
return import_node_path2.default.join(home, "AppData", "Roaming", APP_NAME);
|
|
2599
2320
|
}
|
|
2600
|
-
return
|
|
2321
|
+
return import_node_path2.default.join(home, ".config", APP_NAME);
|
|
2601
2322
|
}
|
|
2602
2323
|
function getUserDataDir() {
|
|
2603
2324
|
return configuredUserDataDir ?? resolveDefaultUserDataDir();
|
|
2604
2325
|
}
|
|
2605
2326
|
function resolveLegacyAppStatePath() {
|
|
2606
|
-
return
|
|
2327
|
+
return import_node_path2.default.join(getUserDataDir(), LEGACY_APP_STATE_FILE);
|
|
2607
2328
|
}
|
|
2608
2329
|
function resolveStorageDbPath() {
|
|
2609
2330
|
return resolveAppStorageDbPath(getUserDataDir());
|
|
@@ -2693,8 +2414,8 @@ function createDefaultAppState() {
|
|
|
2693
2414
|
lastError: null,
|
|
2694
2415
|
remoteUpdatedAt: null
|
|
2695
2416
|
},
|
|
2696
|
-
|
|
2697
|
-
|
|
2417
|
+
analytics: {
|
|
2418
|
+
anonymousId: null,
|
|
2698
2419
|
enabled: true,
|
|
2699
2420
|
lastFlushAt: null,
|
|
2700
2421
|
lastError: null
|
|
@@ -2992,15 +2713,22 @@ function normalizeAppState(raw) {
|
|
|
2992
2713
|
merged.sync.lastPullAt = asString(merged.sync.lastPullAt);
|
|
2993
2714
|
merged.sync.lastError = asString(merged.sync.lastError);
|
|
2994
2715
|
merged.sync.remoteUpdatedAt = asString(merged.sync.remoteUpdatedAt);
|
|
2995
|
-
if (!isRecord2(merged.
|
|
2996
|
-
merged.
|
|
2716
|
+
if (!isRecord2(merged.analytics)) {
|
|
2717
|
+
merged.analytics = isRecord2(merged.telemetry) ? clone2(merged.telemetry) : clone2(defaults.analytics);
|
|
2718
|
+
}
|
|
2719
|
+
merged.analytics.anonymousId = asString(merged.analytics.anonymousId);
|
|
2720
|
+
if (!merged.analytics.anonymousId) {
|
|
2721
|
+
const legacyInstallId = asString(merged.telemetry?.installId);
|
|
2722
|
+
merged.analytics.anonymousId = legacyInstallId;
|
|
2997
2723
|
}
|
|
2998
|
-
merged.
|
|
2999
|
-
|
|
3000
|
-
|
|
2724
|
+
if (typeof merged.analytics.enabled !== "boolean") {
|
|
2725
|
+
merged.analytics.enabled = true;
|
|
2726
|
+
}
|
|
2727
|
+
merged.analytics.lastFlushAt = asString(merged.analytics.lastFlushAt);
|
|
2728
|
+
merged.analytics.lastError = asString(merged.analytics.lastError);
|
|
2729
|
+
if ("telemetry" in merged) {
|
|
2730
|
+
delete merged.telemetry;
|
|
3001
2731
|
}
|
|
3002
|
-
merged.telemetry.lastFlushAt = asString(merged.telemetry.lastFlushAt);
|
|
3003
|
-
merged.telemetry.lastError = asString(merged.telemetry.lastError);
|
|
3004
2732
|
if (!isRecord2(merged.profilesByName)) {
|
|
3005
2733
|
merged.profilesByName = {};
|
|
3006
2734
|
}
|
|
@@ -3050,7 +2778,7 @@ function deepMerge(base, patch) {
|
|
|
3050
2778
|
async function readLegacyAppStateFromDisk() {
|
|
3051
2779
|
const filePath = resolveLegacyAppStatePath();
|
|
3052
2780
|
try {
|
|
3053
|
-
const raw = await
|
|
2781
|
+
const raw = await import_node_fs2.promises.readFile(filePath, "utf8");
|
|
3054
2782
|
return normalizeAppState(JSON.parse(raw));
|
|
3055
2783
|
} catch (error) {
|
|
3056
2784
|
const code = error.code;
|
|
@@ -3130,38 +2858,146 @@ async function patchAppState(patch) {
|
|
|
3130
2858
|
});
|
|
3131
2859
|
}
|
|
3132
2860
|
|
|
3133
|
-
// ../../packages/runtime-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
2861
|
+
// ../../packages/runtime-app-state/src/app/runtimeSettings.ts
|
|
2862
|
+
var LEGACY_APP_SETTINGS_KEYS = /* @__PURE__ */ new Set([
|
|
2863
|
+
"backendMode",
|
|
2864
|
+
"theme",
|
|
2865
|
+
"commitMessageModelId",
|
|
2866
|
+
"usageShowRemaining",
|
|
2867
|
+
"remoteBackendProvider",
|
|
2868
|
+
"remoteBackendHost",
|
|
2869
|
+
"remoteBackendToken",
|
|
2870
|
+
"remoteBackends",
|
|
2871
|
+
"activeRemoteBackendId",
|
|
2872
|
+
"keepDaemonRunningAfterAppClose"
|
|
2873
|
+
]);
|
|
2874
|
+
function asRecord(value) {
|
|
2875
|
+
return value && typeof value === "object" ? value : {};
|
|
2876
|
+
}
|
|
2877
|
+
function stripLegacyAppSettingsKeys(settings) {
|
|
2878
|
+
let changed = false;
|
|
2879
|
+
const next = { ...settings };
|
|
2880
|
+
for (const key of LEGACY_APP_SETTINGS_KEYS) {
|
|
2881
|
+
if (!(key in next)) {
|
|
2882
|
+
continue;
|
|
2883
|
+
}
|
|
2884
|
+
changed = true;
|
|
2885
|
+
delete next[key];
|
|
3137
2886
|
}
|
|
3138
|
-
|
|
3139
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
2887
|
+
return changed ? next : settings;
|
|
3140
2888
|
}
|
|
3141
|
-
function
|
|
3142
|
-
|
|
2889
|
+
async function readAppSettings() {
|
|
2890
|
+
const appState = await getAppState();
|
|
2891
|
+
return stripLegacyAppSettingsKeys(asRecord(appState.runtimeSettings));
|
|
3143
2892
|
}
|
|
3144
|
-
function
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
2893
|
+
async function writeAppSettings(settings, onUpdated) {
|
|
2894
|
+
const nextSettings = stripLegacyAppSettingsKeys(asRecord(settings));
|
|
2895
|
+
await updateAppState(
|
|
2896
|
+
(current) => ({
|
|
2897
|
+
...current,
|
|
2898
|
+
runtimeSettings: nextSettings
|
|
2899
|
+
}),
|
|
2900
|
+
{
|
|
2901
|
+
mode: "replace",
|
|
2902
|
+
allowBeforeMigrationComplete: false
|
|
2903
|
+
}
|
|
2904
|
+
);
|
|
2905
|
+
if (onUpdated) {
|
|
2906
|
+
try {
|
|
2907
|
+
await onUpdated(nextSettings);
|
|
2908
|
+
} catch (error) {
|
|
2909
|
+
console.warn("Failed to apply runtime settings update hook:", error);
|
|
2910
|
+
}
|
|
3152
2911
|
}
|
|
2912
|
+
return nextSettings;
|
|
3153
2913
|
}
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
2914
|
+
|
|
2915
|
+
// ../../packages/runtime-profiles/src/license/service.ts
|
|
2916
|
+
var import_node_crypto2 = __toESM(require("crypto"), 1);
|
|
2917
|
+
|
|
2918
|
+
// ../../packages/contracts/src/settings/auto-roll.ts
|
|
2919
|
+
var DEFAULT_AUTO_ROLL_ENABLED = false;
|
|
2920
|
+
var DEFAULT_AUTO_ROLL_WARNING_THRESHOLD = 85;
|
|
2921
|
+
var DEFAULT_AUTO_ROLL_SWITCH_THRESHOLD = 95;
|
|
2922
|
+
var AUTO_ROLL_WARNING_MIN = 50;
|
|
2923
|
+
var AUTO_ROLL_WARNING_MAX = 99;
|
|
2924
|
+
var AUTO_ROLL_SWITCH_MAX = 100;
|
|
2925
|
+
function clampNumber(value, min, max) {
|
|
2926
|
+
return Math.min(max, Math.max(min, value));
|
|
2927
|
+
}
|
|
2928
|
+
function resolveFiniteNumber(value, fallback) {
|
|
2929
|
+
return Number.isFinite(value) ? value : fallback;
|
|
2930
|
+
}
|
|
2931
|
+
function sanitizeAutoRollWarningThreshold(value) {
|
|
2932
|
+
const numeric = resolveFiniteNumber(value, DEFAULT_AUTO_ROLL_WARNING_THRESHOLD);
|
|
2933
|
+
return clampNumber(numeric, AUTO_ROLL_WARNING_MIN, AUTO_ROLL_WARNING_MAX);
|
|
2934
|
+
}
|
|
2935
|
+
function sanitizeAutoRollSwitchThreshold(value, warningThreshold) {
|
|
2936
|
+
const sanitizedWarning = sanitizeAutoRollWarningThreshold(warningThreshold);
|
|
2937
|
+
const numeric = resolveFiniteNumber(value, DEFAULT_AUTO_ROLL_SWITCH_THRESHOLD);
|
|
2938
|
+
return clampNumber(numeric, sanitizedWarning + 1, AUTO_ROLL_SWITCH_MAX);
|
|
2939
|
+
}
|
|
2940
|
+
function sanitizeAutoRollThresholds(warningThreshold, switchThreshold) {
|
|
2941
|
+
const sanitizedWarning = sanitizeAutoRollWarningThreshold(warningThreshold);
|
|
2942
|
+
const sanitizedSwitch = sanitizeAutoRollSwitchThreshold(switchThreshold, sanitizedWarning);
|
|
2943
|
+
return {
|
|
2944
|
+
warningThreshold: sanitizedWarning,
|
|
2945
|
+
switchThreshold: sanitizedSwitch
|
|
2946
|
+
};
|
|
2947
|
+
}
|
|
2948
|
+
function normalizeAutoRollSettings(raw) {
|
|
2949
|
+
const enabled = typeof raw?.enabled === "boolean" ? raw.enabled : DEFAULT_AUTO_ROLL_ENABLED;
|
|
2950
|
+
const rawWarning = resolveFiniteNumber(
|
|
2951
|
+
typeof raw?.warningThreshold === "number" ? raw.warningThreshold : Number.NaN,
|
|
2952
|
+
DEFAULT_AUTO_ROLL_WARNING_THRESHOLD
|
|
2953
|
+
);
|
|
2954
|
+
const rawSwitch = resolveFiniteNumber(
|
|
2955
|
+
typeof raw?.switchThreshold === "number" ? raw.switchThreshold : Number.NaN,
|
|
2956
|
+
DEFAULT_AUTO_ROLL_SWITCH_THRESHOLD
|
|
2957
|
+
);
|
|
2958
|
+
const {
|
|
2959
|
+
warningThreshold: normalizedWarning,
|
|
2960
|
+
switchThreshold: normalizedSwitch
|
|
2961
|
+
} = sanitizeAutoRollThresholds(rawWarning, rawSwitch);
|
|
2962
|
+
return {
|
|
2963
|
+
enabled,
|
|
2964
|
+
warningThreshold: normalizedWarning,
|
|
2965
|
+
switchThreshold: normalizedSwitch
|
|
2966
|
+
};
|
|
2967
|
+
}
|
|
2968
|
+
|
|
2969
|
+
// ../../packages/runtime-codex/src/codex/settings.ts
|
|
2970
|
+
function asString2(value) {
|
|
2971
|
+
if (typeof value !== "string") {
|
|
2972
|
+
return null;
|
|
2973
|
+
}
|
|
2974
|
+
const trimmed = value.trim();
|
|
2975
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
2976
|
+
}
|
|
2977
|
+
function isRecord3(value) {
|
|
2978
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
2979
|
+
}
|
|
2980
|
+
function parseAutoRoll(raw) {
|
|
2981
|
+
if (!raw) {
|
|
2982
|
+
return null;
|
|
2983
|
+
}
|
|
2984
|
+
try {
|
|
2985
|
+
return normalizeAutoRollSettings(raw);
|
|
2986
|
+
} catch {
|
|
2987
|
+
return null;
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
function parseStoredLicense(raw) {
|
|
2991
|
+
if (!isRecord3(raw)) {
|
|
2992
|
+
return null;
|
|
2993
|
+
}
|
|
2994
|
+
const statusCandidate = asString2(raw.status);
|
|
2995
|
+
const status = ["inactive", "active", "grace", "error"].includes(statusCandidate ?? "") ? statusCandidate : void 0;
|
|
2996
|
+
const license = {
|
|
2997
|
+
licenseKey: asString2(raw.licenseKey ?? raw.license_key),
|
|
2998
|
+
purchaseEmail: asString2(raw.purchaseEmail ?? raw.purchase_email),
|
|
2999
|
+
lastVerifiedAt: asString2(raw.lastVerifiedAt ?? raw.last_verified_at),
|
|
3000
|
+
nextCheckAt: asString2(raw.nextCheckAt ?? raw.next_check_at),
|
|
3165
3001
|
lastVerificationError: asString2(raw.lastVerificationError ?? raw.last_verification_error),
|
|
3166
3002
|
status,
|
|
3167
3003
|
signature: asString2(raw.signature)
|
|
@@ -3280,25 +3116,45 @@ async function writeCodexSettingsJsonRaw(payload) {
|
|
|
3280
3116
|
}
|
|
3281
3117
|
|
|
3282
3118
|
// ../../packages/runtime-profiles/src/license/secret.ts
|
|
3283
|
-
var
|
|
3119
|
+
var import_node_fs3 = require("fs");
|
|
3120
|
+
var import_node_crypto = __toESM(require("crypto"), 1);
|
|
3121
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
3284
3122
|
var LICENSE_SECRET_DOCUMENT = "desktop.license-secret";
|
|
3123
|
+
var LEGACY_LICENSE_SECRET_FILE = "license.secret";
|
|
3285
3124
|
async function generateSecret() {
|
|
3286
|
-
return
|
|
3125
|
+
return import_node_crypto.default.randomBytes(32).toString("hex");
|
|
3126
|
+
}
|
|
3127
|
+
function normalizeSecret(value) {
|
|
3128
|
+
return typeof value === "string" ? value.trim() : "";
|
|
3129
|
+
}
|
|
3130
|
+
async function readLegacyLicenseSecret() {
|
|
3131
|
+
const legacyPath = import_node_path3.default.join(getUserDataDir(), LEGACY_LICENSE_SECRET_FILE);
|
|
3132
|
+
try {
|
|
3133
|
+
return normalizeSecret(await import_node_fs3.promises.readFile(legacyPath, "utf8"));
|
|
3134
|
+
} catch (error) {
|
|
3135
|
+
if (error.code === "ENOENT") {
|
|
3136
|
+
return "";
|
|
3137
|
+
}
|
|
3138
|
+
throw error;
|
|
3139
|
+
}
|
|
3140
|
+
}
|
|
3141
|
+
async function persistLicenseSecret(dbPath, secret) {
|
|
3142
|
+
await writeDocument(dbPath, LICENSE_SECRET_DOCUMENT, secret);
|
|
3143
|
+
return secret;
|
|
3287
3144
|
}
|
|
3288
3145
|
async function getLicenseSecret() {
|
|
3289
3146
|
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() : "";
|
|
3147
|
+
const existing = await readDocument(dbPath, LICENSE_SECRET_DOCUMENT, normalizeSecret);
|
|
3148
|
+
const normalizedExisting = normalizeSecret(existing);
|
|
3296
3149
|
if (normalizedExisting.length > 0) {
|
|
3297
3150
|
return normalizedExisting;
|
|
3298
3151
|
}
|
|
3152
|
+
const legacySecret = await readLegacyLicenseSecret();
|
|
3153
|
+
if (legacySecret.length > 0) {
|
|
3154
|
+
return persistLicenseSecret(dbPath, legacySecret);
|
|
3155
|
+
}
|
|
3299
3156
|
const secret = await generateSecret();
|
|
3300
|
-
|
|
3301
|
-
return secret;
|
|
3157
|
+
return persistLicenseSecret(dbPath, secret);
|
|
3302
3158
|
}
|
|
3303
3159
|
|
|
3304
3160
|
// ../../packages/contracts/src/license/offer-config.ts
|
|
@@ -3403,7 +3259,7 @@ function licenseSignaturePayload(license) {
|
|
|
3403
3259
|
return JSON.stringify(payload);
|
|
3404
3260
|
}
|
|
3405
3261
|
function signLicense(license, secret) {
|
|
3406
|
-
return
|
|
3262
|
+
return import_node_crypto2.default.createHmac("sha256", secret).update(licenseSignaturePayload(license)).digest("hex");
|
|
3407
3263
|
}
|
|
3408
3264
|
function withSignature(license, secret) {
|
|
3409
3265
|
return {
|
|
@@ -3629,7 +3485,7 @@ var LicenseService = class {
|
|
|
3629
3485
|
throw new Error("License key is required.");
|
|
3630
3486
|
}
|
|
3631
3487
|
const secret = await getLicenseSecret();
|
|
3632
|
-
const digest =
|
|
3488
|
+
const digest = import_node_crypto2.default.createHash("sha256").update(trimmed).digest("hex");
|
|
3633
3489
|
const result = await requestGumroadVerify(trimmed, "activation");
|
|
3634
3490
|
ensureWithinUseLimit(result);
|
|
3635
3491
|
const normalized = normalizeVerificationResult(result);
|
|
@@ -3672,149 +3528,80 @@ var LicenseService = class {
|
|
|
3672
3528
|
};
|
|
3673
3529
|
var licenseService = new LicenseService();
|
|
3674
3530
|
|
|
3675
|
-
// src/
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
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]
|
|
3531
|
+
// ../../packages/runtime-profiles/src/profiles/profile-manager.ts
|
|
3532
|
+
var import_node_crypto3 = require("crypto");
|
|
3533
|
+
var import_fs = require("fs");
|
|
3534
|
+
var import_path = __toESM(require("path"), 1);
|
|
3535
|
+
var import_path2 = require("path");
|
|
3536
|
+
var import_toml = __toESM(require_toml(), 1);
|
|
3697
3537
|
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
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
|
-
`);
|
|
3538
|
+
// ../../packages/contracts/src/profiles/identity.ts
|
|
3539
|
+
function normalizeValue(value) {
|
|
3540
|
+
if (typeof value !== "string") {
|
|
3541
|
+
return null;
|
|
3542
|
+
}
|
|
3543
|
+
const trimmed = value.trim();
|
|
3544
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
3712
3545
|
}
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
var DEFAULT_AUTOROLL_THRESHOLD = 95;
|
|
3717
|
-
function hasFlag2(args, flag) {
|
|
3718
|
-
return args.includes(flag);
|
|
3546
|
+
function normalizeEmail(value) {
|
|
3547
|
+
const normalized = normalizeValue(value);
|
|
3548
|
+
return normalized ? normalized.toLowerCase() : null;
|
|
3719
3549
|
}
|
|
3720
|
-
function
|
|
3721
|
-
return
|
|
3550
|
+
function encodeIdentityPart(value) {
|
|
3551
|
+
return encodeURIComponent(value);
|
|
3722
3552
|
}
|
|
3723
|
-
function
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
const value = explicit.split("=")[1];
|
|
3728
|
-
if (value === "browser" || value === "device") {
|
|
3729
|
-
return value;
|
|
3553
|
+
function resolveLegacyProfileAccountKey(profile) {
|
|
3554
|
+
const accountId = normalizeValue(profile.accountId);
|
|
3555
|
+
if (accountId) {
|
|
3556
|
+
return accountId;
|
|
3730
3557
|
}
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
const explicit = flags.find((flag) => flag.startsWith(`${name}=`));
|
|
3735
|
-
if (!explicit) {
|
|
3736
|
-
return null;
|
|
3558
|
+
const email = normalizeValue(profile.email ?? profile.metadata?.email);
|
|
3559
|
+
if (email) {
|
|
3560
|
+
return email;
|
|
3737
3561
|
}
|
|
3738
|
-
const
|
|
3739
|
-
|
|
3740
|
-
return Number.isFinite(value) ? value : Number.NaN;
|
|
3562
|
+
const profileName = normalizeValue(profile.name);
|
|
3563
|
+
return profileName ? `profile:${profileName}` : null;
|
|
3741
3564
|
}
|
|
3742
|
-
function
|
|
3743
|
-
const
|
|
3744
|
-
|
|
3745
|
-
|
|
3565
|
+
function resolveProfileIdentityKey(profile) {
|
|
3566
|
+
const workspaceId = normalizeValue(profile.workspaceId);
|
|
3567
|
+
const accountUserId = normalizeValue(profile.metadata?.chatgptAccountUserId);
|
|
3568
|
+
if (accountUserId) {
|
|
3569
|
+
return `seat:${encodeIdentityPart(accountUserId)}`;
|
|
3746
3570
|
}
|
|
3747
|
-
const
|
|
3748
|
-
|
|
3749
|
-
|
|
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;
|
|
3571
|
+
const userId = normalizeValue(profile.metadata?.userId) ?? normalizeValue(profile.metadata?.chatgptUserId);
|
|
3572
|
+
if (workspaceId && userId) {
|
|
3573
|
+
return `workspace-user:${encodeIdentityPart(workspaceId)}|${encodeIdentityPart(userId)}`;
|
|
3760
3574
|
}
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
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);
|
|
3575
|
+
const accountId = normalizeValue(profile.accountId);
|
|
3576
|
+
if (accountId) {
|
|
3577
|
+
return accountId;
|
|
3578
|
+
}
|
|
3579
|
+
const email = normalizeEmail(profile.email ?? profile.metadata?.email);
|
|
3580
|
+
if (workspaceId && email) {
|
|
3581
|
+
return `workspace-email:${encodeIdentityPart(workspaceId)}|${encodeIdentityPart(email)}`;
|
|
3582
|
+
}
|
|
3583
|
+
if (userId) {
|
|
3584
|
+
return `user:${encodeIdentityPart(userId)}`;
|
|
3585
|
+
}
|
|
3586
|
+
if (email) {
|
|
3587
|
+
return `email:${encodeIdentityPart(email)}`;
|
|
3798
3588
|
}
|
|
3589
|
+
return resolveLegacyProfileAccountKey(profile);
|
|
3590
|
+
}
|
|
3591
|
+
function resolveProfileIdentityKeyFromProfile(profile) {
|
|
3592
|
+
return resolveProfileIdentityKey(profile) ?? `profile:${profile.name}`;
|
|
3799
3593
|
}
|
|
3800
|
-
|
|
3801
|
-
// ../../packages/runtime-profiles/src/profiles/profile-manager.ts
|
|
3802
|
-
var import_node_crypto4 = require("crypto");
|
|
3803
|
-
var import_fs = require("fs");
|
|
3804
|
-
var import_path = __toESM(require("path"), 1);
|
|
3805
|
-
var import_path2 = require("path");
|
|
3806
|
-
var import_toml = __toESM(require_toml(), 1);
|
|
3807
3594
|
|
|
3808
3595
|
// ../../packages/runtime-codex/src/codex/rpc.ts
|
|
3809
|
-
var
|
|
3596
|
+
var import_node_child_process2 = require("child_process");
|
|
3810
3597
|
var import_node_readline = __toESM(require("readline"), 1);
|
|
3811
3598
|
var import_node_fs5 = require("fs");
|
|
3812
|
-
var
|
|
3599
|
+
var import_node_os2 = __toESM(require("os"), 1);
|
|
3813
3600
|
var import_node_path6 = __toESM(require("path"), 1);
|
|
3814
3601
|
|
|
3815
3602
|
// ../../packages/runtime-codex/src/codex/cli.ts
|
|
3816
3603
|
var import_node_fs4 = require("fs");
|
|
3817
|
-
var
|
|
3604
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
3818
3605
|
var STABLE_CHANNEL = "stable";
|
|
3819
3606
|
var ENV_HINTS = ["CODEX_BINARY", "CODEX_CLI_PATH", "CODEX_PATH"];
|
|
3820
3607
|
var cachedStatus = null;
|
|
@@ -3822,7 +3609,7 @@ function fileExists(candidate) {
|
|
|
3822
3609
|
if (!candidate) {
|
|
3823
3610
|
return null;
|
|
3824
3611
|
}
|
|
3825
|
-
const normalized =
|
|
3612
|
+
const normalized = import_node_path4.default.resolve(candidate);
|
|
3826
3613
|
try {
|
|
3827
3614
|
const stats = (0, import_node_fs4.statSync)(normalized);
|
|
3828
3615
|
if (stats.isFile()) {
|
|
@@ -3850,11 +3637,11 @@ function resolveCodexFromPath() {
|
|
|
3850
3637
|
if (!pathValue) {
|
|
3851
3638
|
return null;
|
|
3852
3639
|
}
|
|
3853
|
-
const entries = pathValue.split(
|
|
3640
|
+
const entries = pathValue.split(import_node_path4.default.delimiter).map((entry) => entry.trim()).filter(Boolean);
|
|
3854
3641
|
const names = process.platform === "win32" ? ["codex.exe", "codex.cmd", "codex.bat", "codex"] : ["codex"];
|
|
3855
3642
|
for (const entry of entries) {
|
|
3856
3643
|
for (const name of names) {
|
|
3857
|
-
const candidate = fileExists(
|
|
3644
|
+
const candidate = fileExists(import_node_path4.default.join(entry, name));
|
|
3858
3645
|
if (candidate) {
|
|
3859
3646
|
return candidate;
|
|
3860
3647
|
}
|
|
@@ -3917,21 +3704,89 @@ async function requireCodexCli() {
|
|
|
3917
3704
|
return status.path;
|
|
3918
3705
|
}
|
|
3919
3706
|
|
|
3920
|
-
// ../../packages/
|
|
3921
|
-
var
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3707
|
+
// ../../packages/runtime-codex/src/codex/command.ts
|
|
3708
|
+
var import_node_child_process = require("child_process");
|
|
3709
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
3710
|
+
var cachedNodeRuntime = null;
|
|
3711
|
+
function isJavaScriptEntrypoint(binaryPath) {
|
|
3712
|
+
const normalized = binaryPath.trim().toLowerCase();
|
|
3713
|
+
return normalized.endsWith(".js") || normalized.endsWith(".mjs") || normalized.endsWith(".cjs");
|
|
3714
|
+
}
|
|
3715
|
+
function resolveNodeRuntime(env) {
|
|
3716
|
+
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;
|
|
3717
|
+
if (override) {
|
|
3718
|
+
return override;
|
|
3719
|
+
}
|
|
3720
|
+
if (cachedNodeRuntime) {
|
|
3721
|
+
return cachedNodeRuntime;
|
|
3722
|
+
}
|
|
3723
|
+
const runtimeEnv = { ...process.env, ...env };
|
|
3724
|
+
const homeDir = runtimeEnv.HOME ?? runtimeEnv.USERPROFILE ?? "";
|
|
3725
|
+
const pathHints = (runtimeEnv.CODEX_PATH_HINTS ?? process.env.CODEX_PATH_HINTS ?? "").split(import_node_path5.default.delimiter).map((entry) => entry.trim()).filter(Boolean);
|
|
3726
|
+
const extraPathEntries = [
|
|
3727
|
+
"/usr/local/bin",
|
|
3728
|
+
"/opt/homebrew/bin",
|
|
3729
|
+
import_node_path5.default.join(homeDir, ".local", "bin"),
|
|
3730
|
+
import_node_path5.default.join(homeDir, ".fnm", "aliases", "default", "bin"),
|
|
3731
|
+
import_node_path5.default.join(homeDir, ".fnm", "current", "bin"),
|
|
3732
|
+
...pathHints
|
|
3733
|
+
].filter(Boolean);
|
|
3734
|
+
const currentPath = runtimeEnv.PATH ?? "";
|
|
3735
|
+
runtimeEnv.PATH = Array.from(
|
|
3736
|
+
/* @__PURE__ */ new Set([...extraPathEntries, ...currentPath.split(import_node_path5.default.delimiter).filter(Boolean)])
|
|
3737
|
+
).join(import_node_path5.default.delimiter);
|
|
3738
|
+
const candidates = Array.from(
|
|
3739
|
+
new Set(
|
|
3740
|
+
[
|
|
3741
|
+
runtimeEnv.CODEX_NODE_RUNTIME?.trim(),
|
|
3742
|
+
runtimeEnv.CODEX_NODE_BIN?.trim(),
|
|
3743
|
+
runtimeEnv.CODEX_NODE?.trim(),
|
|
3744
|
+
"/opt/homebrew/bin/node",
|
|
3745
|
+
"/usr/local/bin/node",
|
|
3746
|
+
"node"
|
|
3747
|
+
].filter((candidate) => typeof candidate === "string" && candidate.length > 0)
|
|
3748
|
+
)
|
|
3749
|
+
);
|
|
3750
|
+
for (const candidate of candidates) {
|
|
3751
|
+
const probe = (0, import_node_child_process.spawnSync)(candidate, ["-v"], { env: runtimeEnv, stdio: "ignore" });
|
|
3752
|
+
if (!probe.error && probe.status === 0) {
|
|
3753
|
+
cachedNodeRuntime = candidate;
|
|
3754
|
+
return candidate;
|
|
3755
|
+
}
|
|
3756
|
+
}
|
|
3757
|
+
cachedNodeRuntime = process.execPath;
|
|
3758
|
+
return process.execPath;
|
|
3759
|
+
}
|
|
3760
|
+
function buildCodexCommand(binaryPath, args, env) {
|
|
3761
|
+
if (isJavaScriptEntrypoint(binaryPath)) {
|
|
3762
|
+
return {
|
|
3763
|
+
command: resolveNodeRuntime(env),
|
|
3764
|
+
args: [binaryPath, ...args],
|
|
3765
|
+
shell: false
|
|
3766
|
+
};
|
|
3767
|
+
}
|
|
3768
|
+
return {
|
|
3769
|
+
command: binaryPath,
|
|
3770
|
+
args: [...args],
|
|
3771
|
+
shell: process.platform === "win32"
|
|
3772
|
+
};
|
|
3773
|
+
}
|
|
3774
|
+
|
|
3775
|
+
// ../../packages/shared/src/core/logger.ts
|
|
3776
|
+
var isTestEnv = process.env.NODE_ENV === "test" || process.env.VITEST === "true" || process.env.VITEST === "1";
|
|
3777
|
+
function isMocked(fn) {
|
|
3778
|
+
return Boolean(fn && typeof fn === "function" && "mock" in fn);
|
|
3779
|
+
}
|
|
3780
|
+
function logWarn(...args) {
|
|
3781
|
+
if (isTestEnv && !isMocked(console.warn)) {
|
|
3782
|
+
return;
|
|
3783
|
+
}
|
|
3784
|
+
console.warn(...args);
|
|
3785
|
+
}
|
|
3786
|
+
function logError(...args) {
|
|
3787
|
+
if (isTestEnv && !isMocked(console.error)) {
|
|
3788
|
+
return;
|
|
3789
|
+
}
|
|
3935
3790
|
console.error(...args);
|
|
3936
3791
|
}
|
|
3937
3792
|
function logInfo(...args) {
|
|
@@ -4138,11 +3993,11 @@ function parseRateLimitSnapshotFromRpcMessage(message) {
|
|
|
4138
3993
|
}
|
|
4139
3994
|
async function fetchRateLimitsViaRpc(envOverride, options = {}) {
|
|
4140
3995
|
const binaryPath = options.codexPath ?? await requireCodexCli();
|
|
4141
|
-
const tempHome = await import_node_fs5.promises.mkdtemp(import_node_path6.default.join(
|
|
3996
|
+
const tempHome = await import_node_fs5.promises.mkdtemp(import_node_path6.default.join(import_node_os2.default.tmpdir(), "codex-rpc-"));
|
|
4142
3997
|
const tempAuthPath = import_node_path6.default.join(tempHome, "auth.json");
|
|
4143
3998
|
let initialSourceAuth = null;
|
|
4144
3999
|
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 ??
|
|
4000
|
+
envOverride?.HOME ?? process.env.HOME ?? process.env.USERPROFILE ?? import_node_os2.default.homedir(),
|
|
4146
4001
|
".codex",
|
|
4147
4002
|
"auth.json"
|
|
4148
4003
|
));
|
|
@@ -4157,17 +4012,24 @@ async function fetchRateLimitsViaRpc(envOverride, options = {}) {
|
|
|
4157
4012
|
});
|
|
4158
4013
|
return null;
|
|
4159
4014
|
}
|
|
4160
|
-
const
|
|
4015
|
+
const childEnv = {
|
|
4016
|
+
...process.env,
|
|
4017
|
+
...envOverride ?? {},
|
|
4018
|
+
HOME: tempHome,
|
|
4019
|
+
USERPROFILE: tempHome,
|
|
4020
|
+
CODEX_HOME: tempHome,
|
|
4021
|
+
CODEX_TELEMETRY_LABEL: "codex-rpc",
|
|
4022
|
+
ELECTRON_RUN_AS_NODE: "1"
|
|
4023
|
+
};
|
|
4024
|
+
const command = buildCodexCommand(
|
|
4025
|
+
binaryPath,
|
|
4026
|
+
["-s", "read-only", "-a", "untrusted", "app-server"],
|
|
4027
|
+
childEnv
|
|
4028
|
+
);
|
|
4029
|
+
const child = (0, import_node_child_process2.spawn)(command.command, command.args, {
|
|
4161
4030
|
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
|
-
}
|
|
4031
|
+
env: childEnv,
|
|
4032
|
+
shell: command.shell
|
|
4171
4033
|
});
|
|
4172
4034
|
const rl = import_node_readline.default.createInterface({
|
|
4173
4035
|
input: child.stdout,
|
|
@@ -4467,7 +4329,7 @@ var ProfileManager = class {
|
|
|
4467
4329
|
workspaceId: null
|
|
4468
4330
|
};
|
|
4469
4331
|
}
|
|
4470
|
-
const fingerprint = (0,
|
|
4332
|
+
const fingerprint = (0, import_node_crypto3.createHash)("sha256").update(trimmed).digest("hex");
|
|
4471
4333
|
try {
|
|
4472
4334
|
const parsed = JSON.parse(trimmed);
|
|
4473
4335
|
const normalized = this.normalizeProfileData(parsed);
|
|
@@ -4735,6 +4597,7 @@ var ProfileManager = class {
|
|
|
4735
4597
|
groups,
|
|
4736
4598
|
userId,
|
|
4737
4599
|
chatgptUserId,
|
|
4600
|
+
chatgptAccountUserId,
|
|
4738
4601
|
emailVerified,
|
|
4739
4602
|
tokenExpiresAt: this.toIsoStringFromSeconds(exp),
|
|
4740
4603
|
tokenIssuedAt: this.toIsoStringFromSeconds(iat),
|
|
@@ -4742,7 +4605,7 @@ var ProfileManager = class {
|
|
|
4742
4605
|
};
|
|
4743
4606
|
const hasMeaningfulData = Boolean(metadata.planType) || Boolean(
|
|
4744
4607
|
metadata.subscription && (metadata.subscription.activeStart || metadata.subscription.activeUntil || metadata.subscription.lastChecked)
|
|
4745
|
-
) || Boolean(metadata.organizations && metadata.organizations.length > 0) || Boolean(metadata.groups && metadata.groups.length > 0) || Boolean(metadata.userId) || Boolean(metadata.chatgptUserId) || Boolean(metadata.email) || typeof metadata.emailVerified === "boolean" || Boolean(metadata.tokenExpiresAt) || Boolean(metadata.tokenIssuedAt) || Boolean(metadata.tokenAuthTime);
|
|
4608
|
+
) || Boolean(metadata.organizations && metadata.organizations.length > 0) || Boolean(metadata.groups && metadata.groups.length > 0) || Boolean(metadata.userId) || Boolean(metadata.chatgptUserId) || Boolean(metadata.chatgptAccountUserId) || Boolean(metadata.email) || typeof metadata.emailVerified === "boolean" || Boolean(metadata.tokenExpiresAt) || Boolean(metadata.tokenIssuedAt) || Boolean(metadata.tokenAuthTime);
|
|
4746
4609
|
return hasMeaningfulData ? metadata : void 0;
|
|
4747
4610
|
}
|
|
4748
4611
|
/**
|
|
@@ -4858,13 +4721,35 @@ var ProfileManager = class {
|
|
|
4858
4721
|
return accountId.trim();
|
|
4859
4722
|
}
|
|
4860
4723
|
}
|
|
4861
|
-
const projectId = "project_id" in data && typeof data.project_id === "string" ? data.project_id?.trim() : void 0;
|
|
4862
|
-
if (projectId) {
|
|
4863
|
-
return projectId;
|
|
4864
|
-
}
|
|
4865
4724
|
const email = "email" in data && typeof data.email === "string" ? data.email?.trim() : void 0;
|
|
4866
4725
|
return email || void 0;
|
|
4867
4726
|
}
|
|
4727
|
+
resolveProfileIdentityKey(args) {
|
|
4728
|
+
return resolveProfileIdentityKey(args);
|
|
4729
|
+
}
|
|
4730
|
+
resolveLegacyAccountKey(args) {
|
|
4731
|
+
return resolveLegacyProfileAccountKey(args);
|
|
4732
|
+
}
|
|
4733
|
+
resolveProfileIdentityFromData(name, data, metadata) {
|
|
4734
|
+
const resolvedMetadata = metadata ?? this.extractProfileMetadata(data);
|
|
4735
|
+
const workspace = this.resolveWorkspaceIdentity(data, resolvedMetadata);
|
|
4736
|
+
const email = this.resolveProfileEmail(data, resolvedMetadata) ?? null;
|
|
4737
|
+
return this.resolveProfileIdentityKey({
|
|
4738
|
+
name,
|
|
4739
|
+
accountId: this.getAccountIdFromData(data) ?? null,
|
|
4740
|
+
workspaceId: workspace.id ?? null,
|
|
4741
|
+
email,
|
|
4742
|
+
metadata: resolvedMetadata
|
|
4743
|
+
});
|
|
4744
|
+
}
|
|
4745
|
+
resolveProfileIdentityFromRecord(record) {
|
|
4746
|
+
return this.resolveProfileIdentityFromData(record.name, record.data, record.metadata) ?? this.resolveLegacyAccountKey({
|
|
4747
|
+
name: record.name,
|
|
4748
|
+
accountId: record.accountId,
|
|
4749
|
+
email: record.email,
|
|
4750
|
+
metadata: record.metadata
|
|
4751
|
+
}) ?? resolveFallbackAccountKey(record.name);
|
|
4752
|
+
}
|
|
4868
4753
|
resolveWorkspaceIdentity(data, metadata) {
|
|
4869
4754
|
const directId = "workspace_id" in data && typeof data.workspace_id === "string" ? data.workspace_id.trim() : void 0;
|
|
4870
4755
|
const directName = "workspace_name" in data && typeof data.workspace_name === "string" ? data.workspace_name.trim() : void 0;
|
|
@@ -4885,12 +4770,9 @@ var ProfileManager = class {
|
|
|
4885
4770
|
const record = await this.readProfileRecord(normalized);
|
|
4886
4771
|
return record ?? void 0;
|
|
4887
4772
|
}
|
|
4888
|
-
async
|
|
4773
|
+
async getProfileRowByIdentityKey(identityKey, options) {
|
|
4889
4774
|
const records = await this.listProfileRecords();
|
|
4890
|
-
const matches = records.filter((record) =>
|
|
4891
|
-
const storedAccountId = record.accountId ?? this.getAccountIdFromData(record.data);
|
|
4892
|
-
return storedAccountId === accountId;
|
|
4893
|
-
});
|
|
4775
|
+
const matches = records.filter((record) => this.resolveProfileIdentityFromRecord(record) === identityKey);
|
|
4894
4776
|
if (matches.length === 0) {
|
|
4895
4777
|
return void 0;
|
|
4896
4778
|
}
|
|
@@ -4904,12 +4786,11 @@ var ProfileManager = class {
|
|
|
4904
4786
|
}
|
|
4905
4787
|
return matches[0];
|
|
4906
4788
|
}
|
|
4907
|
-
async
|
|
4789
|
+
async getProfileRowByIdentityAndWorkspace(identityKey, workspaceId, options) {
|
|
4908
4790
|
const workspaceKey = workspaceId && workspaceId.trim().length > 0 ? workspaceId.trim() : DEFAULT_WORKSPACE_ID;
|
|
4909
4791
|
const records = await this.listProfileRecords();
|
|
4910
4792
|
const matches = records.filter((record) => {
|
|
4911
|
-
|
|
4912
|
-
if (storedAccountId !== accountId) {
|
|
4793
|
+
if (this.resolveProfileIdentityFromRecord(record) !== identityKey) {
|
|
4913
4794
|
return false;
|
|
4914
4795
|
}
|
|
4915
4796
|
const storedWorkspace = record.workspaceId ?? record.data.workspace_id ?? DEFAULT_WORKSPACE_ID;
|
|
@@ -5187,11 +5068,17 @@ var ProfileManager = class {
|
|
|
5187
5068
|
if (latestRow) {
|
|
5188
5069
|
existing = latestRow.data;
|
|
5189
5070
|
}
|
|
5190
|
-
const
|
|
5191
|
-
const
|
|
5192
|
-
|
|
5071
|
+
const existingMetadata = this.extractProfileMetadata(existing);
|
|
5072
|
+
const existingWorkspace = this.resolveWorkspaceIdentity(existing, existingMetadata);
|
|
5073
|
+
existing.workspace_id = existing.workspace_id ?? existingWorkspace.id ?? DEFAULT_WORKSPACE_ID;
|
|
5074
|
+
if (existingWorkspace.name) {
|
|
5075
|
+
existing.workspace_name = existing.workspace_name ?? existingWorkspace.name;
|
|
5076
|
+
}
|
|
5077
|
+
const existingIdentity = this.resolveProfileIdentityFromData(profileName, existing, existingMetadata);
|
|
5078
|
+
const nextIdentity = this.resolveProfileIdentityFromData(profileName, normalized, metadata);
|
|
5079
|
+
if (existingIdentity && nextIdentity && existingIdentity !== nextIdentity) {
|
|
5193
5080
|
logWarn(
|
|
5194
|
-
`Skipped syncing tokens for profile '${profileName}' because Codex auth switched to
|
|
5081
|
+
`Skipped syncing tokens for profile '${profileName}' because Codex auth switched to a different OpenAI identity.`
|
|
5195
5082
|
);
|
|
5196
5083
|
return;
|
|
5197
5084
|
}
|
|
@@ -5338,6 +5225,66 @@ var ProfileManager = class {
|
|
|
5338
5225
|
}
|
|
5339
5226
|
}
|
|
5340
5227
|
}
|
|
5228
|
+
async prepareProfileRuntime(name) {
|
|
5229
|
+
return this.enqueueProfileOperation(
|
|
5230
|
+
name,
|
|
5231
|
+
() => this.enqueueAuthSwap(async () => {
|
|
5232
|
+
await this.initialize();
|
|
5233
|
+
const profileName = this.normalizeProfileName(name);
|
|
5234
|
+
const record = await this.getProfileRowByName(profileName);
|
|
5235
|
+
if (!record) {
|
|
5236
|
+
throw new Error(`Profile '${profileName}' not found!`);
|
|
5237
|
+
}
|
|
5238
|
+
let profileData = record.data;
|
|
5239
|
+
await this.syncProfileTokensFromActiveAuth(profileName, profileData);
|
|
5240
|
+
const updatedRecord = await this.getProfileRowByName(profileName);
|
|
5241
|
+
if (updatedRecord) {
|
|
5242
|
+
profileData = updatedRecord.data;
|
|
5243
|
+
}
|
|
5244
|
+
if (profileData.auth_method && profileData.auth_method !== "codex-cli") {
|
|
5245
|
+
throw new Error("Unsupported auth method. Codex CLI profiles only.");
|
|
5246
|
+
}
|
|
5247
|
+
const codexFormat = this.convertToCodexFormat(profileData);
|
|
5248
|
+
const profileHome = await this.prepareProfileHome(profileName, codexFormat);
|
|
5249
|
+
return {
|
|
5250
|
+
profileHome,
|
|
5251
|
+
env: {
|
|
5252
|
+
...process.env,
|
|
5253
|
+
HOME: profileHome,
|
|
5254
|
+
USERPROFILE: profileHome,
|
|
5255
|
+
CODEX_HOME: profileHome
|
|
5256
|
+
}
|
|
5257
|
+
};
|
|
5258
|
+
})
|
|
5259
|
+
);
|
|
5260
|
+
}
|
|
5261
|
+
async syncProfileRuntime(name, profileHome) {
|
|
5262
|
+
return this.enqueueProfileOperation(
|
|
5263
|
+
name,
|
|
5264
|
+
() => this.enqueueAuthSwap(async () => {
|
|
5265
|
+
await this.initialize();
|
|
5266
|
+
const profileName = this.normalizeProfileName(name);
|
|
5267
|
+
const record = await this.getProfileRowByName(profileName);
|
|
5268
|
+
if (!record) {
|
|
5269
|
+
throw new Error(`Profile '${profileName}' not found!`);
|
|
5270
|
+
}
|
|
5271
|
+
const authPath = import_path.default.join(profileHome ?? this.getProfileHomePath(profileName), "auth.json");
|
|
5272
|
+
let finalAuthContent = null;
|
|
5273
|
+
try {
|
|
5274
|
+
finalAuthContent = await import_fs.promises.readFile(authPath, "utf8");
|
|
5275
|
+
} catch (error) {
|
|
5276
|
+
if (!this.isNotFoundError(error)) {
|
|
5277
|
+
logWarn(`Failed to read isolated auth for '${profileName}' while syncing runtime:`, error);
|
|
5278
|
+
}
|
|
5279
|
+
}
|
|
5280
|
+
if (finalAuthContent) {
|
|
5281
|
+
await this.syncProfileTokensFromAuthContent(profileName, record.data, finalAuthContent);
|
|
5282
|
+
return;
|
|
5283
|
+
}
|
|
5284
|
+
await this.syncProfileTokensFromActiveAuth(profileName, record.data, authPath);
|
|
5285
|
+
})
|
|
5286
|
+
);
|
|
5287
|
+
}
|
|
5341
5288
|
/**
|
|
5342
5289
|
* Create a new profile by running codex login
|
|
5343
5290
|
*/
|
|
@@ -5367,15 +5314,6 @@ var ProfileManager = class {
|
|
|
5367
5314
|
if (workspace.name) {
|
|
5368
5315
|
normalizedProfile.workspace_name = normalizedProfile.workspace_name ?? workspace.name;
|
|
5369
5316
|
}
|
|
5370
|
-
const accountId = this.getAccountIdFromData(normalizedProfile);
|
|
5371
|
-
if (accountId) {
|
|
5372
|
-
const requestedWorkspace = normalizedProfile.workspace_id ?? DEFAULT_WORKSPACE_ID;
|
|
5373
|
-
const duplicate = await this.getProfileRowByAccountAndWorkspace(accountId, requestedWorkspace, { authMethod: "codex-cli" });
|
|
5374
|
-
if (duplicate) {
|
|
5375
|
-
normalizedProfile.workspace_id = `${requestedWorkspace}:${profileName}`;
|
|
5376
|
-
normalizedProfile.workspace_name = normalizedProfile.workspace_name ?? profileName;
|
|
5377
|
-
}
|
|
5378
|
-
}
|
|
5379
5317
|
const resolvedEmail = this.resolveProfileEmail(normalizedProfile, metadata);
|
|
5380
5318
|
if (resolvedEmail) {
|
|
5381
5319
|
normalizedProfile.email = resolvedEmail;
|
|
@@ -5430,25 +5368,30 @@ var ProfileManager = class {
|
|
|
5430
5368
|
throw new Error("Failed to read Codex auth file. Complete the login and try again.");
|
|
5431
5369
|
}
|
|
5432
5370
|
const normalized = this.normalizeProfileData(activeAuth);
|
|
5433
|
-
const
|
|
5434
|
-
const
|
|
5371
|
+
const existingMetadata = record.metadata ?? this.extractProfileMetadata(existing);
|
|
5372
|
+
const currentWorkspace = this.resolveWorkspaceIdentity(existing, existingMetadata);
|
|
5373
|
+
existing.workspace_id = existing.workspace_id ?? currentWorkspace.id ?? DEFAULT_WORKSPACE_ID;
|
|
5374
|
+
if (currentWorkspace.name) {
|
|
5375
|
+
existing.workspace_name = existing.workspace_name ?? currentWorkspace.name;
|
|
5376
|
+
}
|
|
5435
5377
|
const normalizedMetadata = this.extractProfileMetadata(normalized);
|
|
5436
5378
|
const normalizedWorkspace = this.resolveWorkspaceIdentity(normalized, normalizedMetadata);
|
|
5437
5379
|
normalized.workspace_id = normalized.workspace_id ?? normalizedWorkspace.id ?? DEFAULT_WORKSPACE_ID;
|
|
5438
5380
|
if (normalizedWorkspace.name) {
|
|
5439
5381
|
normalized.workspace_name = normalized.workspace_name ?? normalizedWorkspace.name;
|
|
5440
5382
|
}
|
|
5441
|
-
|
|
5383
|
+
const existingIdentity = this.resolveProfileIdentityFromData(profileName, existing, existingMetadata);
|
|
5384
|
+
const newIdentity = this.resolveProfileIdentityFromData(profileName, normalized, normalizedMetadata);
|
|
5385
|
+
if (existingIdentity && newIdentity && existingIdentity !== newIdentity) {
|
|
5442
5386
|
throw new Error(
|
|
5443
|
-
`Active Codex login is for
|
|
5387
|
+
`Active Codex login is for a different OpenAI identity than profile '${profileName}'. Create a new profile for that login instead.`
|
|
5444
5388
|
);
|
|
5445
5389
|
}
|
|
5446
|
-
if (
|
|
5447
|
-
const currentWorkspace = this.resolveWorkspaceIdentity(existing, this.extractProfileMetadata(existing));
|
|
5390
|
+
if (existingIdentity && normalized.workspace_id) {
|
|
5448
5391
|
const targetWorkspaceId = normalized.workspace_id;
|
|
5449
5392
|
const currentWorkspaceId = currentWorkspace.id ?? DEFAULT_WORKSPACE_ID;
|
|
5450
5393
|
if (targetWorkspaceId !== currentWorkspaceId) {
|
|
5451
|
-
const duplicate = await this.
|
|
5394
|
+
const duplicate = await this.getProfileRowByIdentityAndWorkspace(existingIdentity, targetWorkspaceId, {
|
|
5452
5395
|
authMethod: this.resolveAuthMethod(existing)
|
|
5453
5396
|
});
|
|
5454
5397
|
if (duplicate && duplicate.name !== profileName) {
|
|
@@ -5665,9 +5608,9 @@ var ProfileManager = class {
|
|
|
5665
5608
|
const normalizedAuth = this.normalizeProfileData(activeAuth);
|
|
5666
5609
|
const authMetadata = this.extractProfileMetadata(normalizedAuth);
|
|
5667
5610
|
const workspace = this.resolveWorkspaceIdentity(normalizedAuth, authMetadata);
|
|
5668
|
-
const
|
|
5669
|
-
if (
|
|
5670
|
-
const scoped = await this.
|
|
5611
|
+
const activeIdentityKey = this.resolveProfileIdentityFromData(null, normalizedAuth, authMetadata);
|
|
5612
|
+
if (activeIdentityKey) {
|
|
5613
|
+
const scoped = await this.getProfileRowByIdentityAndWorkspace(activeIdentityKey, workspace.id ?? DEFAULT_WORKSPACE_ID, {
|
|
5671
5614
|
preferAuthMethod: "codex-cli"
|
|
5672
5615
|
});
|
|
5673
5616
|
if (scoped) {
|
|
@@ -5675,7 +5618,7 @@ var ProfileManager = class {
|
|
|
5675
5618
|
await this.persistPreferredProfileName(normalized);
|
|
5676
5619
|
return { name: normalized, trusted: true };
|
|
5677
5620
|
}
|
|
5678
|
-
const matching = await this.
|
|
5621
|
+
const matching = await this.getProfileRowByIdentityKey(activeIdentityKey, { preferAuthMethod: "codex-cli" });
|
|
5679
5622
|
if (matching) {
|
|
5680
5623
|
const normalized = this.normalizeProfileName(matching.name);
|
|
5681
5624
|
await this.persistPreferredProfileName(normalized);
|
|
@@ -5699,120 +5642,1555 @@ var ProfileManager = class {
|
|
|
5699
5642
|
}
|
|
5700
5643
|
return { name: null, trusted: false };
|
|
5701
5644
|
}
|
|
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!`);
|
|
5645
|
+
/**
|
|
5646
|
+
* Export a profile to a portable format.
|
|
5647
|
+
* Sensitive tokens are included but should be handled securely.
|
|
5648
|
+
*/
|
|
5649
|
+
async exportProfile(name) {
|
|
5650
|
+
await this.initialize();
|
|
5651
|
+
const profileName = this.normalizeProfileName(name);
|
|
5652
|
+
const record = await this.getProfileRowByName(profileName);
|
|
5653
|
+
if (!record) {
|
|
5654
|
+
throw new Error(`Profile '${profileName}' not found!`);
|
|
5655
|
+
}
|
|
5656
|
+
return {
|
|
5657
|
+
name: record.name,
|
|
5658
|
+
displayName: record.displayName,
|
|
5659
|
+
email: record.email,
|
|
5660
|
+
accountId: record.accountId,
|
|
5661
|
+
workspaceId: record.workspaceId,
|
|
5662
|
+
workspaceName: record.workspaceName,
|
|
5663
|
+
authMethod: record.authMethod,
|
|
5664
|
+
createdAt: record.createdAt,
|
|
5665
|
+
metadata: record.metadata,
|
|
5666
|
+
exportedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5667
|
+
};
|
|
5668
|
+
}
|
|
5669
|
+
/**
|
|
5670
|
+
* Export profiles for cloud sync (includes token-bearing auth data).
|
|
5671
|
+
*/
|
|
5672
|
+
async exportProfilesForCloudSync() {
|
|
5673
|
+
await this.initialize();
|
|
5674
|
+
const records = await this.listProfileRecords();
|
|
5675
|
+
return records.map((record) => ({
|
|
5676
|
+
name: record.name,
|
|
5677
|
+
displayName: record.displayName,
|
|
5678
|
+
data: record.data,
|
|
5679
|
+
metadata: record.metadata,
|
|
5680
|
+
accountId: record.accountId,
|
|
5681
|
+
workspaceId: record.workspaceId,
|
|
5682
|
+
workspaceName: record.workspaceName,
|
|
5683
|
+
email: record.email,
|
|
5684
|
+
authMethod: record.authMethod,
|
|
5685
|
+
createdAt: record.createdAt,
|
|
5686
|
+
updatedAt: record.updatedAt
|
|
5687
|
+
}));
|
|
5688
|
+
}
|
|
5689
|
+
/**
|
|
5690
|
+
* Replace local profiles with a full snapshot from cloud sync.
|
|
5691
|
+
*/
|
|
5692
|
+
async replaceProfilesFromCloudSync(records) {
|
|
5693
|
+
await this.initialize();
|
|
5694
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5695
|
+
const normalized = /* @__PURE__ */ new Map();
|
|
5696
|
+
const existingMap = await this.readProfilesStateMap();
|
|
5697
|
+
const existingNames = new Set(Object.keys(existingMap));
|
|
5698
|
+
for (const candidate of records ?? []) {
|
|
5699
|
+
try {
|
|
5700
|
+
const name = this.normalizeProfileName(candidate?.name ?? "");
|
|
5701
|
+
const data = candidate && typeof candidate.data === "object" && candidate.data !== null ? candidate.data : null;
|
|
5702
|
+
if (!data) {
|
|
5703
|
+
continue;
|
|
5704
|
+
}
|
|
5705
|
+
const metadata = candidate.metadata && typeof candidate.metadata === "object" ? candidate.metadata : void 0;
|
|
5706
|
+
const record = {
|
|
5707
|
+
name,
|
|
5708
|
+
displayName: typeof candidate.displayName === "string" ? candidate.displayName : null,
|
|
5709
|
+
data,
|
|
5710
|
+
metadata,
|
|
5711
|
+
accountId: typeof candidate.accountId === "string" ? candidate.accountId : null,
|
|
5712
|
+
workspaceId: typeof candidate.workspaceId === "string" ? candidate.workspaceId : null,
|
|
5713
|
+
workspaceName: typeof candidate.workspaceName === "string" ? candidate.workspaceName : null,
|
|
5714
|
+
email: typeof candidate.email === "string" ? candidate.email : null,
|
|
5715
|
+
authMethod: typeof candidate.authMethod === "string" ? candidate.authMethod : this.resolveAuthMethod(data),
|
|
5716
|
+
createdAt: typeof candidate.createdAt === "string" ? candidate.createdAt : now,
|
|
5717
|
+
updatedAt: typeof candidate.updatedAt === "string" ? candidate.updatedAt : now
|
|
5718
|
+
};
|
|
5719
|
+
normalized.set(name, record);
|
|
5720
|
+
} catch {
|
|
5721
|
+
}
|
|
5722
|
+
}
|
|
5723
|
+
const nextMap = {};
|
|
5724
|
+
for (const record of normalized.values()) {
|
|
5725
|
+
nextMap[record.name] = this.toStateRecord(record);
|
|
5726
|
+
}
|
|
5727
|
+
await this.writeProfilesStateMap(nextMap);
|
|
5728
|
+
let removed = 0;
|
|
5729
|
+
for (const name of existingNames) {
|
|
5730
|
+
if (normalized.has(name)) {
|
|
5731
|
+
continue;
|
|
5732
|
+
}
|
|
5733
|
+
try {
|
|
5734
|
+
await import_fs.promises.rm(this.getProfileHomePath(name), { recursive: true, force: true });
|
|
5735
|
+
removed += 1;
|
|
5736
|
+
} catch (error) {
|
|
5737
|
+
if (!this.isNotFoundError(error)) {
|
|
5738
|
+
logWarn(`Failed to remove profile '${name}' during cloud sync replace:`, error);
|
|
5739
|
+
}
|
|
5740
|
+
}
|
|
5741
|
+
}
|
|
5742
|
+
const preferred = await this.readPreferredProfileName();
|
|
5743
|
+
if (preferred) {
|
|
5744
|
+
try {
|
|
5745
|
+
const normalizedPreferred = this.normalizeProfileName(preferred);
|
|
5746
|
+
if (!normalized.has(normalizedPreferred)) {
|
|
5747
|
+
await this.persistPreferredProfileName(null);
|
|
5748
|
+
}
|
|
5749
|
+
} catch {
|
|
5750
|
+
await this.persistPreferredProfileName(null);
|
|
5751
|
+
}
|
|
5752
|
+
}
|
|
5753
|
+
return {
|
|
5754
|
+
imported: normalized.size,
|
|
5755
|
+
removed
|
|
5756
|
+
};
|
|
5757
|
+
}
|
|
5758
|
+
};
|
|
5759
|
+
|
|
5760
|
+
// src/app/help.ts
|
|
5761
|
+
function printHelp(version) {
|
|
5762
|
+
console.log(`CodexUse CLI v${version}
|
|
5763
|
+
|
|
5764
|
+
Usage:
|
|
5765
|
+
codexuse account-pool status [--runtime=desktop|daemon] [--state-dir=/abs/path] [--port=NNNN]
|
|
5766
|
+
codexuse account-pool enable
|
|
5767
|
+
codexuse account-pool disable
|
|
5768
|
+
codexuse account-pool routing <least-used|round-robin>
|
|
5769
|
+
codexuse account-pool profiles list
|
|
5770
|
+
codexuse account-pool profiles set <name> [more...]
|
|
5771
|
+
codexuse account-pool models list
|
|
5772
|
+
codexuse account-pool keys list [--runtime=desktop|daemon]
|
|
5773
|
+
codexuse account-pool keys create [--runtime=desktop|daemon]
|
|
5774
|
+
codexuse account-pool sessions list [--runtime=desktop|daemon]
|
|
5775
|
+
|
|
5776
|
+
codexuse profile list [--no-usage] [--compact]
|
|
5777
|
+
codexuse profile current
|
|
5778
|
+
codexuse profile add <name> [--skip-login] [--device-auth] [--login=browser|device]
|
|
5779
|
+
codexuse profile refresh <name> [--skip-login] [--device-auth] [--login=browser|device]
|
|
5780
|
+
codexuse profile switch <name>
|
|
5781
|
+
codexuse profile autoroll [--threshold=50-100] [--dry-run] [--watch] [--interval=seconds]
|
|
5782
|
+
codexuse profile delete <name>
|
|
5783
|
+
codexuse profile rename <old> <new>
|
|
5784
|
+
|
|
5785
|
+
codexuse license status [--refresh]
|
|
5786
|
+
codexuse license activate <license-key>
|
|
5787
|
+
|
|
5788
|
+
codexuse sync status
|
|
5789
|
+
codexuse sync pull
|
|
5790
|
+
codexuse sync push
|
|
5791
|
+
|
|
5792
|
+
codexuse daemon start [--port=NNNN] [--telegram-bot-token=<token>] [--project-path=/abs/path]
|
|
5793
|
+
|
|
5794
|
+
Flags:
|
|
5795
|
+
-h, --help Show help
|
|
5796
|
+
-v, --version Show version
|
|
5797
|
+
--no-usage Skip rate-limit usage fetch
|
|
5798
|
+
--compact Names only
|
|
5799
|
+
--device-auth Use device auth for Codex login
|
|
5800
|
+
--login=MODE Login mode: browser | device
|
|
5801
|
+
--threshold=NN Auto-roll switch threshold percent (50-100)
|
|
5802
|
+
--watch Keep checking and auto-switch when threshold is reached
|
|
5803
|
+
--interval=SEC Watch interval in seconds (default: 30)
|
|
5804
|
+
--dry-run Print planned switch without changing active profile
|
|
5805
|
+
--runtime=NAME Accounts Pool runtime store: desktop | daemon
|
|
5806
|
+
--state-dir=PATH Override the runtime state dir for Accounts Pool inspection
|
|
5807
|
+
--port=NNNN Stable daemon/API port (or use with account-pool status for daemon base URL)
|
|
5808
|
+
--telegram-bot-token=TOKEN Enable Telegram remote control for daemon mode
|
|
5809
|
+
--project-path=PATH Optional path to bootstrap as the default Projects root in daemon mode
|
|
5810
|
+
`);
|
|
5811
|
+
}
|
|
5812
|
+
|
|
5813
|
+
// src/platform/args.ts
|
|
5814
|
+
var DEFAULT_AUTOROLL_INTERVAL_SECONDS = 30;
|
|
5815
|
+
var DEFAULT_AUTOROLL_THRESHOLD = 95;
|
|
5816
|
+
function hasFlag(args, flag) {
|
|
5817
|
+
return args.includes(flag);
|
|
5818
|
+
}
|
|
5819
|
+
function stripFlags(args) {
|
|
5820
|
+
return args.filter((arg) => !arg.startsWith("-"));
|
|
5821
|
+
}
|
|
5822
|
+
function parseLoginMode(flags) {
|
|
5823
|
+
if (flags.includes("--device-auth")) return "device";
|
|
5824
|
+
const explicit = flags.find((flag) => flag.startsWith("--login="));
|
|
5825
|
+
if (!explicit) return null;
|
|
5826
|
+
const value = explicit.split("=")[1];
|
|
5827
|
+
if (value === "browser" || value === "device") {
|
|
5828
|
+
return value;
|
|
5829
|
+
}
|
|
5830
|
+
return null;
|
|
5831
|
+
}
|
|
5832
|
+
function parseNumericFlag(flags, name) {
|
|
5833
|
+
const explicit = flags.find((flag) => flag.startsWith(`${name}=`));
|
|
5834
|
+
if (!explicit) {
|
|
5835
|
+
return null;
|
|
5836
|
+
}
|
|
5837
|
+
const raw = explicit.slice(`${name}=`.length);
|
|
5838
|
+
const value = Number.parseFloat(raw);
|
|
5839
|
+
return Number.isFinite(value) ? value : Number.NaN;
|
|
5840
|
+
}
|
|
5841
|
+
function parseIntegerFlag(flags, name) {
|
|
5842
|
+
const explicit = flags.find((flag) => flag.startsWith(`${name}=`));
|
|
5843
|
+
if (!explicit) {
|
|
5844
|
+
return null;
|
|
5845
|
+
}
|
|
5846
|
+
const raw = explicit.slice(`${name}=`.length);
|
|
5847
|
+
const value = Number.parseInt(raw, 10);
|
|
5848
|
+
return Number.isFinite(value) ? value : Number.NaN;
|
|
5849
|
+
}
|
|
5850
|
+
function parseStringFlag(flags, name) {
|
|
5851
|
+
const explicit = flags.find((flag) => flag.startsWith(`${name}=`));
|
|
5852
|
+
if (!explicit) {
|
|
5853
|
+
return null;
|
|
5854
|
+
}
|
|
5855
|
+
const value = explicit.slice(`${name}=`.length).trim();
|
|
5856
|
+
return value.length > 0 ? value : "";
|
|
5857
|
+
}
|
|
5858
|
+
|
|
5859
|
+
// src/commands/accountPool.ts
|
|
5860
|
+
var ACCOUNT_POOL_DOCUMENT = "desktop.account-pool.v2";
|
|
5861
|
+
var LEGACY_BROKER_DOCUMENT = "desktop.broker.v1";
|
|
5862
|
+
var ACCOUNT_POOL_PRO_REQUIRED_MESSAGE = "Accounts Pool requires a Pro license.";
|
|
5863
|
+
var DEFAULT_DAEMON_STATE_DIR = import_node_path7.default.join(import_node_os3.default.homedir(), ".codexuse", "t3-daemon");
|
|
5864
|
+
var ACCOUNT_POOL_STORE_VERSION = 3;
|
|
5865
|
+
var MAX_RESPONSE_IDS_PER_SESSION = 64;
|
|
5866
|
+
var MODEL_SLUG_ALIASES = {
|
|
5867
|
+
"5.4": "gpt-5.4",
|
|
5868
|
+
"5.3": "gpt-5.3-codex",
|
|
5869
|
+
"gpt-5.3": "gpt-5.3-codex",
|
|
5870
|
+
"5.3-spark": "gpt-5.3-codex-spark",
|
|
5871
|
+
"gpt-5.3-spark": "gpt-5.3-codex-spark"
|
|
5872
|
+
};
|
|
5873
|
+
function asRecord2(value) {
|
|
5874
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
5875
|
+
}
|
|
5876
|
+
function asString3(value) {
|
|
5877
|
+
if (typeof value !== "string") {
|
|
5878
|
+
return null;
|
|
5879
|
+
}
|
|
5880
|
+
const trimmed = value.trim();
|
|
5881
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
5882
|
+
}
|
|
5883
|
+
function asNumber(value) {
|
|
5884
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
5885
|
+
}
|
|
5886
|
+
function normalizeIsoString(value) {
|
|
5887
|
+
const normalized = asString3(value);
|
|
5888
|
+
if (!normalized) {
|
|
5889
|
+
return null;
|
|
5890
|
+
}
|
|
5891
|
+
const parsed = Date.parse(normalized);
|
|
5892
|
+
return Number.isNaN(parsed) ? null : new Date(parsed).toISOString();
|
|
5893
|
+
}
|
|
5894
|
+
function normalizeStringArray(value) {
|
|
5895
|
+
const input = Array.isArray(value) ? value : [];
|
|
5896
|
+
const result = [];
|
|
5897
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5898
|
+
for (const entry of input) {
|
|
5899
|
+
const normalized = asString3(entry);
|
|
5900
|
+
if (!normalized || seen.has(normalized)) {
|
|
5901
|
+
continue;
|
|
5902
|
+
}
|
|
5903
|
+
seen.add(normalized);
|
|
5904
|
+
result.push(normalized);
|
|
5905
|
+
}
|
|
5906
|
+
return result;
|
|
5907
|
+
}
|
|
5908
|
+
function normalizeReasoningEffort(value) {
|
|
5909
|
+
const normalized = asString3(value)?.toLowerCase() ?? null;
|
|
5910
|
+
return normalized && normalized.length > 0 ? normalized : null;
|
|
5911
|
+
}
|
|
5912
|
+
function normalizeRoutingStrategy(value) {
|
|
5913
|
+
return value === "round_robin" ? "round_robin" : "least_used";
|
|
5914
|
+
}
|
|
5915
|
+
function normalizeCodexModelSlug(value) {
|
|
5916
|
+
const trimmed = value?.trim();
|
|
5917
|
+
if (!trimmed) {
|
|
5918
|
+
return null;
|
|
5919
|
+
}
|
|
5920
|
+
return MODEL_SLUG_ALIASES[trimmed] ?? trimmed;
|
|
5921
|
+
}
|
|
5922
|
+
function formatRoutingStrategy(value) {
|
|
5923
|
+
return value === "round_robin" ? "round-robin" : "least-used";
|
|
5924
|
+
}
|
|
5925
|
+
function parseRoutingStrategy(value) {
|
|
5926
|
+
const normalized = value?.trim().toLowerCase();
|
|
5927
|
+
if (!normalized) {
|
|
5928
|
+
return null;
|
|
5929
|
+
}
|
|
5930
|
+
if (normalized === "round-robin" || normalized === "round_robin" || normalized === "roundrobin") {
|
|
5931
|
+
return "round_robin";
|
|
5932
|
+
}
|
|
5933
|
+
if (normalized === "least-used" || normalized === "least_used" || normalized === "leastused") {
|
|
5934
|
+
return "least_used";
|
|
5935
|
+
}
|
|
5936
|
+
return null;
|
|
5937
|
+
}
|
|
5938
|
+
function normalizeAccountPoolExposedModels(value, fallbackToDefault) {
|
|
5939
|
+
const result = [];
|
|
5940
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5941
|
+
const input = Array.isArray(value) ? value : [];
|
|
5942
|
+
for (const entry of input) {
|
|
5943
|
+
const raw = asString3(entry);
|
|
5944
|
+
const normalized = normalizeCodexModelSlug(raw) ?? raw;
|
|
5945
|
+
if (!normalized || seen.has(normalized)) {
|
|
5946
|
+
continue;
|
|
5947
|
+
}
|
|
5948
|
+
seen.add(normalized);
|
|
5949
|
+
result.push(normalized);
|
|
5950
|
+
}
|
|
5951
|
+
if (result.length > 0) {
|
|
5952
|
+
return result;
|
|
5953
|
+
}
|
|
5954
|
+
return fallbackToDefault ? [...DEFAULT_ACCOUNT_POOL_EXPOSED_MODELS] : [];
|
|
5955
|
+
}
|
|
5956
|
+
function normalizeAccountPoolReasoningEfforts(value, fallback) {
|
|
5957
|
+
const input = Array.isArray(value) ? value : [];
|
|
5958
|
+
const result = [];
|
|
5959
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5960
|
+
for (const entry of input) {
|
|
5961
|
+
const normalized = normalizeReasoningEffort(entry);
|
|
5962
|
+
if (!normalized || seen.has(normalized)) {
|
|
5963
|
+
continue;
|
|
5964
|
+
}
|
|
5965
|
+
seen.add(normalized);
|
|
5966
|
+
result.push(normalized);
|
|
5967
|
+
}
|
|
5968
|
+
if (result.length > 0 || Array.isArray(value)) {
|
|
5969
|
+
return result;
|
|
5970
|
+
}
|
|
5971
|
+
return fallback ? [...fallback] : [];
|
|
5972
|
+
}
|
|
5973
|
+
function normalizeAccountPoolSettings(settings) {
|
|
5974
|
+
const accountPoolProfilePool = normalizeStringArray(settings.accountPoolProfilePool);
|
|
5975
|
+
return {
|
|
5976
|
+
accountPoolEnabled: settings.accountPoolEnabled === true || settings.brokerEnabled === true,
|
|
5977
|
+
accountPoolRoutingStrategy: normalizeRoutingStrategy(
|
|
5978
|
+
settings.accountPoolRoutingStrategy ?? settings.brokerRoutingStrategy
|
|
5979
|
+
),
|
|
5980
|
+
accountPoolProfilePool: accountPoolProfilePool.length > 0 ? accountPoolProfilePool : normalizeStringArray(settings.brokerProfilePool),
|
|
5981
|
+
accountPoolExposedModels: normalizeAccountPoolExposedModels(
|
|
5982
|
+
settings.accountPoolExposedModels,
|
|
5983
|
+
true
|
|
5984
|
+
),
|
|
5985
|
+
accountPoolExposedReasoningEfforts: normalizeAccountPoolReasoningEfforts(
|
|
5986
|
+
settings.accountPoolExposedReasoningEfforts,
|
|
5987
|
+
DEFAULT_ACCOUNT_POOL_EXPOSED_REASONING_EFFORTS
|
|
5988
|
+
),
|
|
5989
|
+
accountPoolExposedFastModeReasoningEfforts: normalizeAccountPoolReasoningEfforts(
|
|
5990
|
+
settings.accountPoolExposedFastModeReasoningEfforts,
|
|
5991
|
+
DEFAULT_ACCOUNT_POOL_EXPOSED_FAST_MODE_REASONING_EFFORTS
|
|
5992
|
+
)
|
|
5993
|
+
};
|
|
5994
|
+
}
|
|
5995
|
+
function applyAccountPoolSettings(settings, nextAccountPoolSettings) {
|
|
5996
|
+
const next = {
|
|
5997
|
+
...settings,
|
|
5998
|
+
accountPoolEnabled: nextAccountPoolSettings.accountPoolEnabled,
|
|
5999
|
+
accountPoolRoutingStrategy: nextAccountPoolSettings.accountPoolRoutingStrategy,
|
|
6000
|
+
accountPoolProfilePool: nextAccountPoolSettings.accountPoolProfilePool.slice(),
|
|
6001
|
+
accountPoolExposedModels: nextAccountPoolSettings.accountPoolExposedModels.slice(),
|
|
6002
|
+
accountPoolExposedReasoningEfforts: nextAccountPoolSettings.accountPoolExposedReasoningEfforts.slice(),
|
|
6003
|
+
accountPoolExposedFastModeReasoningEfforts: nextAccountPoolSettings.accountPoolExposedFastModeReasoningEfforts.slice()
|
|
6004
|
+
};
|
|
6005
|
+
delete next.brokerEnabled;
|
|
6006
|
+
delete next.brokerRoutingStrategy;
|
|
6007
|
+
delete next.brokerProfilePool;
|
|
6008
|
+
return next;
|
|
6009
|
+
}
|
|
6010
|
+
function printAccountPoolHelp(version) {
|
|
6011
|
+
console.log(`CodexUse CLI v${version}
|
|
6012
|
+
|
|
6013
|
+
Usage:
|
|
6014
|
+
codexuse account-pool status [--runtime=desktop|daemon] [--state-dir=/abs/path] [--port=NNNN]
|
|
6015
|
+
codexuse account-pool enable
|
|
6016
|
+
codexuse account-pool disable
|
|
6017
|
+
codexuse account-pool routing <least-used|round-robin>
|
|
6018
|
+
|
|
6019
|
+
codexuse account-pool profiles list
|
|
6020
|
+
codexuse account-pool profiles set <name> [more...]
|
|
6021
|
+
codexuse account-pool profiles add <name> [more...]
|
|
6022
|
+
codexuse account-pool profiles remove <name> [more...]
|
|
6023
|
+
codexuse account-pool profiles reset
|
|
6024
|
+
|
|
6025
|
+
codexuse account-pool models list
|
|
6026
|
+
codexuse account-pool models add <model> [more...]
|
|
6027
|
+
codexuse account-pool models remove <model> [more...]
|
|
6028
|
+
codexuse account-pool models reset
|
|
6029
|
+
|
|
6030
|
+
codexuse account-pool reasoning list
|
|
6031
|
+
codexuse account-pool reasoning add <effort> [more...]
|
|
6032
|
+
codexuse account-pool reasoning remove <effort> [more...]
|
|
6033
|
+
codexuse account-pool reasoning reset
|
|
6034
|
+
|
|
6035
|
+
codexuse account-pool fast-mode list
|
|
6036
|
+
codexuse account-pool fast-mode add <effort> [more...]
|
|
6037
|
+
codexuse account-pool fast-mode remove <effort> [more...]
|
|
6038
|
+
codexuse account-pool fast-mode reset
|
|
6039
|
+
|
|
6040
|
+
codexuse account-pool keys list [--runtime=desktop|daemon] [--state-dir=/abs/path]
|
|
6041
|
+
codexuse account-pool keys create [--runtime=desktop|daemon] [--state-dir=/abs/path]
|
|
6042
|
+
codexuse account-pool keys revoke <id> [--runtime=desktop|daemon] [--state-dir=/abs/path]
|
|
6043
|
+
|
|
6044
|
+
codexuse account-pool sessions list [--runtime=desktop|daemon] [--state-dir=/abs/path]
|
|
6045
|
+
|
|
6046
|
+
Notes:
|
|
6047
|
+
- Settings are shared with the desktop app.
|
|
6048
|
+
- Keys and sessions are runtime-specific: desktop and daemon each keep their own pool store.
|
|
6049
|
+
- Session inspection is read-only here on purpose; pooled sessions are live runtime state.
|
|
6050
|
+
- Use --runtime=daemon plus --state-dir or --port when you want to inspect a headless daemon.
|
|
6051
|
+
`);
|
|
6052
|
+
}
|
|
6053
|
+
function normalizeSessionStatus(value) {
|
|
6054
|
+
return value === "running" || value === "error" || value === "rollover_pending" ? value : "idle";
|
|
6055
|
+
}
|
|
6056
|
+
function normalizeApiKeyRecord(id, value) {
|
|
6057
|
+
const record = asRecord2(value);
|
|
6058
|
+
const tokenHash = asString3(record.tokenHash);
|
|
6059
|
+
const tokenPreview = asString3(record.tokenPreview);
|
|
6060
|
+
const createdAt = normalizeIsoString(record.createdAt);
|
|
6061
|
+
if (!tokenHash || !tokenPreview || !createdAt) {
|
|
6062
|
+
return null;
|
|
6063
|
+
}
|
|
6064
|
+
return {
|
|
6065
|
+
id,
|
|
6066
|
+
tokenHash,
|
|
6067
|
+
tokenPreview,
|
|
6068
|
+
createdAt,
|
|
6069
|
+
lastUsedAt: normalizeIsoString(record.lastUsedAt),
|
|
6070
|
+
revokedAt: normalizeIsoString(record.revokedAt)
|
|
6071
|
+
};
|
|
6072
|
+
}
|
|
6073
|
+
function normalizeLegacySessionRecord(id, value) {
|
|
6074
|
+
const record = asRecord2(value);
|
|
6075
|
+
const profileName = asString3(record.profileName);
|
|
6076
|
+
const createdAt = normalizeIsoString(record.createdAt);
|
|
6077
|
+
const lastUsedAt = normalizeIsoString(record.lastUsedAt);
|
|
6078
|
+
if (!profileName || !createdAt || !lastUsedAt) {
|
|
6079
|
+
return null;
|
|
6080
|
+
}
|
|
6081
|
+
const affinityKind = record.affinityKind === "broker_session" || record.affinityKind === "prompt_cache" ? record.affinityKind : "ephemeral";
|
|
6082
|
+
return {
|
|
6083
|
+
id,
|
|
6084
|
+
affinityKind,
|
|
6085
|
+
affinityKey: asString3(record.affinityKey),
|
|
6086
|
+
profileName,
|
|
6087
|
+
threadId: asString3(record.threadId) ?? id,
|
|
6088
|
+
model: asString3(record.model),
|
|
6089
|
+
status: normalizeSessionStatus(record.status),
|
|
6090
|
+
lastError: asString3(record.lastError),
|
|
6091
|
+
createdAt,
|
|
6092
|
+
lastUsedAt,
|
|
6093
|
+
expiresAt: normalizeIsoString(record.expiresAt),
|
|
6094
|
+
lastResponseId: asString3(record.lastResponseId),
|
|
6095
|
+
responseIds: normalizeStringArray(record.responseIds).slice(0, MAX_RESPONSE_IDS_PER_SESSION)
|
|
6096
|
+
};
|
|
6097
|
+
}
|
|
6098
|
+
function normalizeLegacyStore(value) {
|
|
6099
|
+
const record = asRecord2(value);
|
|
6100
|
+
const apiKeysById = {};
|
|
6101
|
+
for (const [id, apiKeyValue] of Object.entries(asRecord2(record.apiKeysById))) {
|
|
6102
|
+
const normalized = normalizeApiKeyRecord(id, apiKeyValue);
|
|
6103
|
+
if (normalized) {
|
|
6104
|
+
apiKeysById[id] = normalized;
|
|
6105
|
+
}
|
|
6106
|
+
}
|
|
6107
|
+
const sessionsById = {};
|
|
6108
|
+
for (const [id, sessionValue] of Object.entries(asRecord2(record.sessionsById))) {
|
|
6109
|
+
const normalized = normalizeLegacySessionRecord(id, sessionValue);
|
|
6110
|
+
if (normalized) {
|
|
6111
|
+
sessionsById[id] = normalized;
|
|
6112
|
+
}
|
|
6113
|
+
}
|
|
6114
|
+
const responseIndex = {};
|
|
6115
|
+
for (const [responseId, sessionId] of Object.entries(asRecord2(record.responseIndex))) {
|
|
6116
|
+
const normalizedSessionId = asString3(sessionId);
|
|
6117
|
+
if (normalizedSessionId) {
|
|
6118
|
+
responseIndex[responseId] = normalizedSessionId;
|
|
6119
|
+
}
|
|
6120
|
+
}
|
|
6121
|
+
return {
|
|
6122
|
+
version: asNumber(record.version) ?? 1,
|
|
6123
|
+
roundRobinCursor: asNumber(record.roundRobinCursor) ?? 0,
|
|
6124
|
+
apiKeysById,
|
|
6125
|
+
sessionsById,
|
|
6126
|
+
responseIndex
|
|
6127
|
+
};
|
|
6128
|
+
}
|
|
6129
|
+
function normalizeSessionRecord(id, value) {
|
|
6130
|
+
const record = asRecord2(value);
|
|
6131
|
+
const activeSegmentId = asString3(record.activeSegmentId);
|
|
6132
|
+
const createdAt = normalizeIsoString(record.createdAt);
|
|
6133
|
+
const lastUsedAt = normalizeIsoString(record.lastUsedAt);
|
|
6134
|
+
if (!activeSegmentId || !createdAt || !lastUsedAt) {
|
|
6135
|
+
return null;
|
|
6136
|
+
}
|
|
6137
|
+
const affinityKind = record.affinityKind === "pool_session" || record.affinityKind === "prompt_cache" ? record.affinityKind : "ephemeral";
|
|
6138
|
+
return {
|
|
6139
|
+
id,
|
|
6140
|
+
affinityKind,
|
|
6141
|
+
affinityKey: asString3(record.affinityKey),
|
|
6142
|
+
activeSegmentId,
|
|
6143
|
+
segmentIds: normalizeStringArray(record.segmentIds),
|
|
6144
|
+
model: asString3(record.model),
|
|
6145
|
+
status: normalizeSessionStatus(record.status),
|
|
6146
|
+
lastError: asString3(record.lastError),
|
|
6147
|
+
createdAt,
|
|
6148
|
+
lastUsedAt,
|
|
6149
|
+
expiresAt: normalizeIsoString(record.expiresAt),
|
|
6150
|
+
lastResponseId: asString3(record.lastResponseId),
|
|
6151
|
+
responseIds: normalizeStringArray(record.responseIds).slice(0, MAX_RESPONSE_IDS_PER_SESSION),
|
|
6152
|
+
rolloverCount: asNumber(record.rolloverCount) ?? 0,
|
|
6153
|
+
lastRolloverReason: asString3(record.lastRolloverReason)
|
|
6154
|
+
};
|
|
6155
|
+
}
|
|
6156
|
+
function normalizeSegmentRecord(id, value) {
|
|
6157
|
+
const record = asRecord2(value);
|
|
6158
|
+
const sessionId = asString3(record.sessionId);
|
|
6159
|
+
const profileName = asString3(record.profileName);
|
|
6160
|
+
const createdAt = normalizeIsoString(record.createdAt);
|
|
6161
|
+
const lastUsedAt = normalizeIsoString(record.lastUsedAt);
|
|
6162
|
+
if (!sessionId || !profileName || !createdAt || !lastUsedAt) {
|
|
6163
|
+
return null;
|
|
6164
|
+
}
|
|
6165
|
+
return {
|
|
6166
|
+
id,
|
|
6167
|
+
sessionId,
|
|
6168
|
+
profileName,
|
|
6169
|
+
createdAt,
|
|
6170
|
+
lastUsedAt
|
|
6171
|
+
};
|
|
6172
|
+
}
|
|
6173
|
+
function normalizeResponseIndexRecord(value) {
|
|
6174
|
+
const record = asRecord2(value);
|
|
6175
|
+
const sessionId = asString3(record.sessionId);
|
|
6176
|
+
const segmentId = asString3(record.segmentId);
|
|
6177
|
+
const createdAt = normalizeIsoString(record.createdAt) ?? (/* @__PURE__ */ new Date(0)).toISOString();
|
|
6178
|
+
if (!sessionId || !segmentId) {
|
|
6179
|
+
return null;
|
|
6180
|
+
}
|
|
6181
|
+
return { sessionId, segmentId, createdAt };
|
|
6182
|
+
}
|
|
6183
|
+
function normalizeAccountPoolStore(value) {
|
|
6184
|
+
const record = asRecord2(value);
|
|
6185
|
+
const apiKeysById = {};
|
|
6186
|
+
for (const [id, apiKeyValue] of Object.entries(asRecord2(record.apiKeysById))) {
|
|
6187
|
+
const normalized = normalizeApiKeyRecord(id, apiKeyValue);
|
|
6188
|
+
if (normalized) {
|
|
6189
|
+
apiKeysById[id] = normalized;
|
|
6190
|
+
}
|
|
6191
|
+
}
|
|
6192
|
+
const sessionsById = {};
|
|
6193
|
+
for (const [id, sessionValue] of Object.entries(asRecord2(record.sessionsById))) {
|
|
6194
|
+
const normalized = normalizeSessionRecord(id, sessionValue);
|
|
6195
|
+
if (normalized) {
|
|
6196
|
+
sessionsById[id] = normalized;
|
|
6197
|
+
}
|
|
6198
|
+
}
|
|
6199
|
+
const segmentsById = {};
|
|
6200
|
+
for (const [id, segmentValue] of Object.entries(asRecord2(record.segmentsById))) {
|
|
6201
|
+
const normalized = normalizeSegmentRecord(id, segmentValue);
|
|
6202
|
+
if (normalized) {
|
|
6203
|
+
segmentsById[id] = normalized;
|
|
6204
|
+
}
|
|
6205
|
+
}
|
|
6206
|
+
const responseIndex = {};
|
|
6207
|
+
for (const [responseId, responseValue] of Object.entries(asRecord2(record.responseIndex))) {
|
|
6208
|
+
const normalized = normalizeResponseIndexRecord(responseValue);
|
|
6209
|
+
if (normalized) {
|
|
6210
|
+
responseIndex[responseId] = normalized;
|
|
6211
|
+
}
|
|
6212
|
+
}
|
|
6213
|
+
return {
|
|
6214
|
+
version: asNumber(record.version) ?? ACCOUNT_POOL_STORE_VERSION,
|
|
6215
|
+
roundRobinCursor: asNumber(record.roundRobinCursor) ?? 0,
|
|
6216
|
+
apiKeysById,
|
|
6217
|
+
sessionsById,
|
|
6218
|
+
segmentsById,
|
|
6219
|
+
responseIndex
|
|
6220
|
+
};
|
|
6221
|
+
}
|
|
6222
|
+
function createDefaultStore() {
|
|
6223
|
+
return {
|
|
6224
|
+
version: ACCOUNT_POOL_STORE_VERSION,
|
|
6225
|
+
roundRobinCursor: 0,
|
|
6226
|
+
apiKeysById: {},
|
|
6227
|
+
sessionsById: {},
|
|
6228
|
+
segmentsById: {},
|
|
6229
|
+
responseIndex: {}
|
|
6230
|
+
};
|
|
6231
|
+
}
|
|
6232
|
+
function migrateLegacyStore(legacyStore) {
|
|
6233
|
+
const store = createDefaultStore();
|
|
6234
|
+
store.roundRobinCursor = legacyStore.roundRobinCursor;
|
|
6235
|
+
store.apiKeysById = { ...legacyStore.apiKeysById };
|
|
6236
|
+
for (const session of Object.values(legacyStore.sessionsById)) {
|
|
6237
|
+
const segmentId = `segment_${session.id}`;
|
|
6238
|
+
store.segmentsById[segmentId] = {
|
|
6239
|
+
id: segmentId,
|
|
6240
|
+
sessionId: session.id,
|
|
6241
|
+
profileName: session.profileName,
|
|
6242
|
+
createdAt: session.createdAt,
|
|
6243
|
+
lastUsedAt: session.lastUsedAt
|
|
6244
|
+
};
|
|
6245
|
+
store.sessionsById[session.id] = {
|
|
6246
|
+
id: session.id,
|
|
6247
|
+
affinityKind: session.affinityKind === "broker_session" ? "pool_session" : session.affinityKind,
|
|
6248
|
+
affinityKey: session.affinityKey,
|
|
6249
|
+
activeSegmentId: segmentId,
|
|
6250
|
+
segmentIds: [segmentId],
|
|
6251
|
+
model: session.model,
|
|
6252
|
+
status: session.status,
|
|
6253
|
+
lastError: session.lastError,
|
|
6254
|
+
createdAt: session.createdAt,
|
|
6255
|
+
lastUsedAt: session.lastUsedAt,
|
|
6256
|
+
expiresAt: session.expiresAt,
|
|
6257
|
+
lastResponseId: session.lastResponseId,
|
|
6258
|
+
responseIds: session.responseIds.slice(0, MAX_RESPONSE_IDS_PER_SESSION),
|
|
6259
|
+
rolloverCount: 0,
|
|
6260
|
+
lastRolloverReason: null
|
|
6261
|
+
};
|
|
6262
|
+
}
|
|
6263
|
+
for (const [responseId, sessionId] of Object.entries(legacyStore.responseIndex)) {
|
|
6264
|
+
const session = store.sessionsById[sessionId];
|
|
6265
|
+
if (!session) {
|
|
6266
|
+
continue;
|
|
6267
|
+
}
|
|
6268
|
+
store.responseIndex[responseId] = {
|
|
6269
|
+
sessionId,
|
|
6270
|
+
segmentId: session.activeSegmentId,
|
|
6271
|
+
createdAt: session.lastUsedAt
|
|
6272
|
+
};
|
|
6273
|
+
}
|
|
6274
|
+
return store;
|
|
6275
|
+
}
|
|
6276
|
+
function deleteSessionFromStore(store, sessionId) {
|
|
6277
|
+
const session = store.sessionsById[sessionId];
|
|
6278
|
+
if (!session) {
|
|
6279
|
+
return;
|
|
6280
|
+
}
|
|
6281
|
+
for (const segmentId of session.segmentIds) {
|
|
6282
|
+
delete store.segmentsById[segmentId];
|
|
6283
|
+
}
|
|
6284
|
+
for (const [responseId, response] of Object.entries(store.responseIndex)) {
|
|
6285
|
+
if (response.sessionId === sessionId) {
|
|
6286
|
+
delete store.responseIndex[responseId];
|
|
6287
|
+
}
|
|
6288
|
+
}
|
|
6289
|
+
delete store.sessionsById[sessionId];
|
|
6290
|
+
}
|
|
6291
|
+
function pruneStore(store) {
|
|
6292
|
+
const now = Date.now();
|
|
6293
|
+
for (const [sessionId, session] of Object.entries(store.sessionsById)) {
|
|
6294
|
+
const expiresAtMs = session.expiresAt ? Date.parse(session.expiresAt) : Number.NaN;
|
|
6295
|
+
if (Number.isFinite(expiresAtMs) && expiresAtMs < now) {
|
|
6296
|
+
deleteSessionFromStore(store, sessionId);
|
|
6297
|
+
}
|
|
6298
|
+
}
|
|
6299
|
+
for (const [segmentId, segment] of Object.entries(store.segmentsById)) {
|
|
6300
|
+
if (!store.sessionsById[segment.sessionId]) {
|
|
6301
|
+
delete store.segmentsById[segmentId];
|
|
6302
|
+
}
|
|
6303
|
+
}
|
|
6304
|
+
for (const [responseId, response] of Object.entries(store.responseIndex)) {
|
|
6305
|
+
if (!store.sessionsById[response.sessionId] || !store.segmentsById[response.segmentId]) {
|
|
6306
|
+
delete store.responseIndex[responseId];
|
|
6307
|
+
}
|
|
6308
|
+
}
|
|
6309
|
+
return store;
|
|
6310
|
+
}
|
|
6311
|
+
function resolveAccountPoolDbPath(stateDir) {
|
|
6312
|
+
return import_node_path7.default.join(stateDir, "state.sqlite");
|
|
6313
|
+
}
|
|
6314
|
+
async function readLegacyStore(dbPath) {
|
|
6315
|
+
return readDocument(dbPath, LEGACY_BROKER_DOCUMENT, normalizeLegacyStore);
|
|
6316
|
+
}
|
|
6317
|
+
async function readStore(stateDir) {
|
|
6318
|
+
const dbPath = resolveAccountPoolDbPath(stateDir);
|
|
6319
|
+
const store = await readDocument(
|
|
6320
|
+
dbPath,
|
|
6321
|
+
ACCOUNT_POOL_DOCUMENT,
|
|
6322
|
+
normalizeAccountPoolStore
|
|
6323
|
+
);
|
|
6324
|
+
if (store) {
|
|
6325
|
+
return pruneStore(store);
|
|
6326
|
+
}
|
|
6327
|
+
const legacyStore = await readLegacyStore(dbPath);
|
|
6328
|
+
if (!legacyStore) {
|
|
6329
|
+
return createDefaultStore();
|
|
6330
|
+
}
|
|
6331
|
+
return pruneStore(migrateLegacyStore(legacyStore));
|
|
6332
|
+
}
|
|
6333
|
+
async function updateStore(stateDir, mutate) {
|
|
6334
|
+
const dbPath = resolveAccountPoolDbPath(stateDir);
|
|
6335
|
+
const legacyStore = await readLegacyStore(dbPath);
|
|
6336
|
+
return updateDocument({
|
|
6337
|
+
dbPath,
|
|
6338
|
+
namespace: ACCOUNT_POOL_DOCUMENT,
|
|
6339
|
+
normalize: normalizeAccountPoolStore,
|
|
6340
|
+
fallback: () => legacyStore ? migrateLegacyStore(legacyStore) : createDefaultStore(),
|
|
6341
|
+
transform: (current) => {
|
|
6342
|
+
const next = pruneStore(current);
|
|
6343
|
+
mutate(next);
|
|
6344
|
+
return pruneStore(next);
|
|
6345
|
+
}
|
|
6346
|
+
});
|
|
6347
|
+
}
|
|
6348
|
+
function accountPoolApiKeyView(record) {
|
|
6349
|
+
return {
|
|
6350
|
+
id: record.id,
|
|
6351
|
+
tokenPreview: record.tokenPreview,
|
|
6352
|
+
createdAt: record.createdAt,
|
|
6353
|
+
lastUsedAt: record.lastUsedAt,
|
|
6354
|
+
revokedAt: record.revokedAt
|
|
6355
|
+
};
|
|
6356
|
+
}
|
|
6357
|
+
async function listApiKeys(stateDir) {
|
|
6358
|
+
const store = await readStore(stateDir);
|
|
6359
|
+
return Object.values(store.apiKeysById).filter((key) => key.revokedAt === null).sort((left, right) => right.createdAt.localeCompare(left.createdAt)).map(accountPoolApiKeyView);
|
|
6360
|
+
}
|
|
6361
|
+
function hashToken(token) {
|
|
6362
|
+
return (0, import_node_crypto4.createHash)("sha256").update(token).digest("hex");
|
|
6363
|
+
}
|
|
6364
|
+
async function createApiKey(stateDir) {
|
|
6365
|
+
const plaintextToken = `cux_pool_${(0, import_node_crypto4.randomBytes)(24).toString("hex")}`;
|
|
6366
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6367
|
+
const keyRecord = {
|
|
6368
|
+
id: (0, import_node_crypto4.randomUUID)(),
|
|
6369
|
+
tokenHash: hashToken(plaintextToken),
|
|
6370
|
+
tokenPreview: `${plaintextToken.slice(0, 8)}...${plaintextToken.slice(-6)}`,
|
|
6371
|
+
createdAt: now,
|
|
6372
|
+
lastUsedAt: null,
|
|
6373
|
+
revokedAt: null
|
|
6374
|
+
};
|
|
6375
|
+
await updateStore(stateDir, (store) => {
|
|
6376
|
+
store.apiKeysById[keyRecord.id] = keyRecord;
|
|
6377
|
+
});
|
|
6378
|
+
return {
|
|
6379
|
+
key: accountPoolApiKeyView(keyRecord),
|
|
6380
|
+
plaintextToken
|
|
6381
|
+
};
|
|
6382
|
+
}
|
|
6383
|
+
async function revokeApiKey(stateDir, id) {
|
|
6384
|
+
let revoked = false;
|
|
6385
|
+
await updateStore(stateDir, (store) => {
|
|
6386
|
+
const existing = store.apiKeysById[id];
|
|
6387
|
+
if (!existing || existing.revokedAt !== null) {
|
|
6388
|
+
return;
|
|
6389
|
+
}
|
|
6390
|
+
existing.revokedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6391
|
+
revoked = true;
|
|
6392
|
+
});
|
|
6393
|
+
return revoked;
|
|
6394
|
+
}
|
|
6395
|
+
async function listSessions(stateDir) {
|
|
6396
|
+
const store = await readStore(stateDir);
|
|
6397
|
+
return Object.values(store.sessionsById).sort((left, right) => right.lastUsedAt.localeCompare(left.lastUsedAt)).flatMap((session) => {
|
|
6398
|
+
const activeSegment = store.segmentsById[session.activeSegmentId];
|
|
6399
|
+
if (!activeSegment) {
|
|
6400
|
+
return [];
|
|
6401
|
+
}
|
|
6402
|
+
return [{
|
|
6403
|
+
id: session.id,
|
|
6404
|
+
affinityKind: session.affinityKind,
|
|
6405
|
+
affinityKey: session.affinityKey,
|
|
6406
|
+
activeProfileName: activeSegment.profileName,
|
|
6407
|
+
model: session.model,
|
|
6408
|
+
status: session.status,
|
|
6409
|
+
lastError: session.lastError,
|
|
6410
|
+
createdAt: session.createdAt,
|
|
6411
|
+
lastUsedAt: session.lastUsedAt,
|
|
6412
|
+
expiresAt: session.expiresAt,
|
|
6413
|
+
lastResponseId: session.lastResponseId,
|
|
6414
|
+
responseCount: session.responseIds.length,
|
|
6415
|
+
segmentCount: session.segmentIds.length,
|
|
6416
|
+
rolloverCount: session.rolloverCount,
|
|
6417
|
+
lastRolloverReason: session.lastRolloverReason
|
|
6418
|
+
}];
|
|
6419
|
+
});
|
|
6420
|
+
}
|
|
6421
|
+
function resolveRuntimeStateDir(runtime, explicitStateDir) {
|
|
6422
|
+
if (explicitStateDir) {
|
|
6423
|
+
return import_node_path7.default.resolve(explicitStateDir);
|
|
6424
|
+
}
|
|
6425
|
+
if (runtime === "daemon") {
|
|
6426
|
+
return DEFAULT_DAEMON_STATE_DIR;
|
|
6427
|
+
}
|
|
6428
|
+
return import_node_path7.default.join(getUserDataDir(), "t3-projects");
|
|
6429
|
+
}
|
|
6430
|
+
function resolveStateDir(flags) {
|
|
6431
|
+
const runtimeFlag = parseStringFlag(flags, "--runtime")?.trim().toLowerCase();
|
|
6432
|
+
const runtime = runtimeFlag === "daemon" ? "daemon" : "desktop";
|
|
6433
|
+
const explicitStateDir = parseStringFlag(flags, "--state-dir") ?? parseStringFlag(flags, "--state") ?? null;
|
|
6434
|
+
return {
|
|
6435
|
+
runtime,
|
|
6436
|
+
stateDir: resolveRuntimeStateDir(runtime, explicitStateDir)
|
|
6437
|
+
};
|
|
6438
|
+
}
|
|
6439
|
+
function resolveBaseUrlFromPort(runtime, port) {
|
|
6440
|
+
if (runtime === "desktop") {
|
|
6441
|
+
return null;
|
|
6442
|
+
}
|
|
6443
|
+
if (port === null) {
|
|
6444
|
+
return null;
|
|
6445
|
+
}
|
|
6446
|
+
if (!Number.isFinite(port) || port < 1 || port > 65535) {
|
|
6447
|
+
throw new Error("Daemon port must be an integer between 1 and 65535.");
|
|
6448
|
+
}
|
|
6449
|
+
return `http://127.0.0.1:${port}`;
|
|
6450
|
+
}
|
|
6451
|
+
function resolveBaseUrl(runtime, flags) {
|
|
6452
|
+
return resolveBaseUrlFromPort(runtime, parseIntegerFlag(flags, "--port"));
|
|
6453
|
+
}
|
|
6454
|
+
async function readAccountPoolRuntimeSummary(input = {}) {
|
|
6455
|
+
const runtime = input.runtime ?? "desktop";
|
|
6456
|
+
const stateDir = resolveRuntimeStateDir(runtime, input.stateDir ?? null);
|
|
6457
|
+
const [license, { normalized }, apiKeys, sessions] = await Promise.all([
|
|
6458
|
+
licenseService.getStatus().catch(() => null),
|
|
6459
|
+
readSettingsSnapshot(),
|
|
6460
|
+
listApiKeys(stateDir),
|
|
6461
|
+
listSessions(stateDir)
|
|
6462
|
+
]);
|
|
6463
|
+
return {
|
|
6464
|
+
runtime,
|
|
6465
|
+
stateDir,
|
|
6466
|
+
baseUrl: resolveBaseUrlFromPort(runtime, input.port ?? null),
|
|
6467
|
+
enabled: normalized.accountPoolEnabled,
|
|
6468
|
+
hasProLicense: license?.isPro === true,
|
|
6469
|
+
routingStrategy: normalized.accountPoolRoutingStrategy,
|
|
6470
|
+
activeApiKeyCount: apiKeys.length,
|
|
6471
|
+
activeSessionCount: sessions.length
|
|
6472
|
+
};
|
|
6473
|
+
}
|
|
6474
|
+
async function readSettingsSnapshot() {
|
|
6475
|
+
const raw = asRecord2(await readAppSettings());
|
|
6476
|
+
return {
|
|
6477
|
+
raw,
|
|
6478
|
+
normalized: normalizeAccountPoolSettings(raw)
|
|
6479
|
+
};
|
|
6480
|
+
}
|
|
6481
|
+
async function updateSettings(transform) {
|
|
6482
|
+
const { raw, normalized } = await readSettingsSnapshot();
|
|
6483
|
+
const nextSettings = transform(normalized);
|
|
6484
|
+
const license = await licenseService.getStatus().catch(() => null);
|
|
6485
|
+
const forcedDisabledByLicense = !license?.isPro && nextSettings.accountPoolEnabled === true;
|
|
6486
|
+
const sanitizedSettings = {
|
|
6487
|
+
...nextSettings,
|
|
6488
|
+
accountPoolEnabled: forcedDisabledByLicense ? false : nextSettings.accountPoolEnabled,
|
|
6489
|
+
accountPoolRoutingStrategy: normalizeRoutingStrategy(nextSettings.accountPoolRoutingStrategy),
|
|
6490
|
+
accountPoolProfilePool: normalizeStringArray(nextSettings.accountPoolProfilePool),
|
|
6491
|
+
accountPoolExposedModels: normalizeAccountPoolExposedModels(
|
|
6492
|
+
nextSettings.accountPoolExposedModels,
|
|
6493
|
+
true
|
|
6494
|
+
),
|
|
6495
|
+
accountPoolExposedReasoningEfforts: normalizeAccountPoolReasoningEfforts(
|
|
6496
|
+
nextSettings.accountPoolExposedReasoningEfforts,
|
|
6497
|
+
null
|
|
6498
|
+
),
|
|
6499
|
+
accountPoolExposedFastModeReasoningEfforts: normalizeAccountPoolReasoningEfforts(
|
|
6500
|
+
nextSettings.accountPoolExposedFastModeReasoningEfforts,
|
|
6501
|
+
null
|
|
6502
|
+
)
|
|
6503
|
+
};
|
|
6504
|
+
await writeAppSettings(applyAccountPoolSettings(raw, sanitizedSettings));
|
|
6505
|
+
return {
|
|
6506
|
+
settings: sanitizedSettings,
|
|
6507
|
+
forcedDisabledByLicense
|
|
6508
|
+
};
|
|
6509
|
+
}
|
|
6510
|
+
async function requireProAccess() {
|
|
6511
|
+
const license = await licenseService.getStatus();
|
|
6512
|
+
if (!license.isPro) {
|
|
6513
|
+
throw new Error(ACCOUNT_POOL_PRO_REQUIRED_MESSAGE);
|
|
6514
|
+
}
|
|
6515
|
+
}
|
|
6516
|
+
async function listSavedProfiles() {
|
|
6517
|
+
const manager = new ProfileManager();
|
|
6518
|
+
return manager.listProfiles();
|
|
6519
|
+
}
|
|
6520
|
+
function printStringList(label, values) {
|
|
6521
|
+
console.log(`${label}: ${values.length > 0 ? values.join(", ") : "(none)"}`);
|
|
6522
|
+
}
|
|
6523
|
+
function printSessionRow(session) {
|
|
6524
|
+
const fields = [
|
|
6525
|
+
`id=${session.id}`,
|
|
6526
|
+
`profile=${session.activeProfileName}`,
|
|
6527
|
+
`status=${session.status}`,
|
|
6528
|
+
`model=${session.model ?? "-"}`,
|
|
6529
|
+
`responses=${session.responseCount}`,
|
|
6530
|
+
`segments=${session.segmentCount}`
|
|
6531
|
+
];
|
|
6532
|
+
if (session.lastResponseId) {
|
|
6533
|
+
fields.push(`lastResponse=${session.lastResponseId}`);
|
|
6534
|
+
}
|
|
6535
|
+
if (session.lastRolloverReason) {
|
|
6536
|
+
fields.push(`rollover=${session.lastRolloverReason}`);
|
|
6537
|
+
}
|
|
6538
|
+
console.log(fields.join(" "));
|
|
6539
|
+
}
|
|
6540
|
+
function printApiKeyRow(apiKey) {
|
|
6541
|
+
const fields = [
|
|
6542
|
+
`id=${apiKey.id}`,
|
|
6543
|
+
`preview=${apiKey.tokenPreview}`,
|
|
6544
|
+
`created=${apiKey.createdAt}`
|
|
6545
|
+
];
|
|
6546
|
+
if (apiKey.lastUsedAt) {
|
|
6547
|
+
fields.push(`lastUsed=${apiKey.lastUsedAt}`);
|
|
6548
|
+
}
|
|
6549
|
+
console.log(fields.join(" "));
|
|
6550
|
+
}
|
|
6551
|
+
function getArrayValues(params, startIndex) {
|
|
6552
|
+
return params.slice(startIndex).map((value) => value.trim()).filter(Boolean);
|
|
6553
|
+
}
|
|
6554
|
+
async function handleStatus(flags) {
|
|
6555
|
+
const license = await licenseService.getStatus().catch(() => null);
|
|
6556
|
+
const { raw, normalized } = await readSettingsSnapshot();
|
|
6557
|
+
const { runtime, stateDir } = resolveStateDir(flags);
|
|
6558
|
+
const baseUrl = resolveBaseUrl(runtime, flags);
|
|
6559
|
+
const [profiles, apiKeys, sessions] = await Promise.all([
|
|
6560
|
+
listSavedProfiles(),
|
|
6561
|
+
listApiKeys(stateDir),
|
|
6562
|
+
listSessions(stateDir)
|
|
6563
|
+
]);
|
|
6564
|
+
const explicitSelection = normalized.accountPoolProfilePool;
|
|
6565
|
+
const selectedProfiles = explicitSelection.length > 0 ? explicitSelection : profiles.map((profile) => profile.name);
|
|
6566
|
+
const missingSelectedProfiles = explicitSelection.filter(
|
|
6567
|
+
(profileName) => !profiles.some((profile) => profile.name === profileName)
|
|
6568
|
+
);
|
|
6569
|
+
console.log(`License: ${license ? `${license.tier} (${license.state})` : "unknown"}`);
|
|
6570
|
+
console.log(`Enabled: ${normalized.accountPoolEnabled ? "yes" : "no"}`);
|
|
6571
|
+
console.log(`Runtime: ${runtime}`);
|
|
6572
|
+
console.log(`State dir: ${stateDir}`);
|
|
6573
|
+
console.log(`Routing: ${formatRoutingStrategy(normalized.accountPoolRoutingStrategy)}`);
|
|
6574
|
+
console.log(`Public API ready: ${normalized.accountPoolEnabled && license?.isPro === true && apiKeys.length > 0 ? "yes" : "no"}`);
|
|
6575
|
+
console.log(`API keys: ${apiKeys.length}`);
|
|
6576
|
+
console.log(`Sessions: ${sessions.length}`);
|
|
6577
|
+
console.log(`Selection mode: ${explicitSelection.length > 0 ? "explicit" : "all saved profiles"}`);
|
|
6578
|
+
if (baseUrl) {
|
|
6579
|
+
console.log(`Base URL: ${baseUrl}`);
|
|
6580
|
+
} else {
|
|
6581
|
+
console.log(
|
|
6582
|
+
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."
|
|
6583
|
+
);
|
|
6584
|
+
}
|
|
6585
|
+
printStringList("Selected profiles", selectedProfiles);
|
|
6586
|
+
if (missingSelectedProfiles.length > 0) {
|
|
6587
|
+
printStringList("Missing configured profiles", missingSelectedProfiles);
|
|
6588
|
+
}
|
|
6589
|
+
printStringList("Published models", normalized.accountPoolExposedModels);
|
|
6590
|
+
printStringList("Reasoning aliases", normalized.accountPoolExposedReasoningEfforts);
|
|
6591
|
+
printStringList("Fast-mode aliases", normalized.accountPoolExposedFastModeReasoningEfforts);
|
|
6592
|
+
if (raw.accountPoolEnabled !== true && raw.brokerEnabled === true) {
|
|
6593
|
+
console.log("Legacy broker setting detected: runtime is reading it through the new Accounts Pool surface.");
|
|
6594
|
+
}
|
|
6595
|
+
if (normalized.accountPoolEnabled && license?.isPro !== true) {
|
|
6596
|
+
console.log(ACCOUNT_POOL_PRO_REQUIRED_MESSAGE);
|
|
6597
|
+
}
|
|
6598
|
+
}
|
|
6599
|
+
async function handleEnable() {
|
|
6600
|
+
await requireProAccess();
|
|
6601
|
+
const result = await updateSettings((current) => ({
|
|
6602
|
+
...current,
|
|
6603
|
+
accountPoolEnabled: true
|
|
6604
|
+
}));
|
|
6605
|
+
console.log(result.settings.accountPoolEnabled ? "Accounts Pool enabled." : "Accounts Pool is disabled.");
|
|
6606
|
+
}
|
|
6607
|
+
async function handleDisable() {
|
|
6608
|
+
const result = await updateSettings((current) => ({
|
|
6609
|
+
...current,
|
|
6610
|
+
accountPoolEnabled: false
|
|
6611
|
+
}));
|
|
6612
|
+
console.log(result.settings.accountPoolEnabled ? "Accounts Pool is enabled." : "Accounts Pool disabled.");
|
|
6613
|
+
}
|
|
6614
|
+
async function handleRouting(params) {
|
|
6615
|
+
const value = parseRoutingStrategy(params[1]);
|
|
6616
|
+
if (!value) {
|
|
6617
|
+
throw new Error("Routing strategy must be `least-used` or `round-robin`.");
|
|
6618
|
+
}
|
|
6619
|
+
const result = await updateSettings((current) => ({
|
|
6620
|
+
...current,
|
|
6621
|
+
accountPoolRoutingStrategy: value
|
|
6622
|
+
}));
|
|
6623
|
+
console.log(`Accounts Pool routing set to ${formatRoutingStrategy(result.settings.accountPoolRoutingStrategy)}.`);
|
|
6624
|
+
}
|
|
6625
|
+
async function handleProfiles(params) {
|
|
6626
|
+
const action = params[1];
|
|
6627
|
+
const profiles = await listSavedProfiles();
|
|
6628
|
+
const availableProfileNames = profiles.map((profile) => profile.name);
|
|
6629
|
+
const availableSet = new Set(availableProfileNames);
|
|
6630
|
+
const { normalized } = await readSettingsSnapshot();
|
|
6631
|
+
if (!action || action === "list") {
|
|
6632
|
+
const explicitSelection = normalized.accountPoolProfilePool;
|
|
6633
|
+
if (explicitSelection.length === 0) {
|
|
6634
|
+
console.log("Selection: all saved profiles");
|
|
6635
|
+
} else {
|
|
6636
|
+
printStringList("Explicitly selected profiles", explicitSelection);
|
|
6637
|
+
}
|
|
6638
|
+
for (const profile of profiles) {
|
|
6639
|
+
const selected = explicitSelection.length === 0 || explicitSelection.includes(profile.name);
|
|
6640
|
+
const invalidSuffix = profile.isValid ? "" : " [needs re-auth]";
|
|
6641
|
+
console.log(`${selected ? "*" : "-"} ${profile.name}${invalidSuffix}`);
|
|
6642
|
+
}
|
|
6643
|
+
const missing = explicitSelection.filter((name) => !availableSet.has(name));
|
|
6644
|
+
if (missing.length > 0) {
|
|
6645
|
+
printStringList("Missing configured profiles", missing);
|
|
6646
|
+
}
|
|
6647
|
+
return;
|
|
6648
|
+
}
|
|
6649
|
+
const names = getArrayValues(params, 2);
|
|
6650
|
+
if ((action === "set" || action === "add" || action === "remove") && names.length === 0) {
|
|
6651
|
+
throw new Error(`Profile names are required for \`${action}\`.`);
|
|
6652
|
+
}
|
|
6653
|
+
for (const name of names) {
|
|
6654
|
+
if (!availableSet.has(name)) {
|
|
6655
|
+
throw new Error(`Unknown profile: ${name}`);
|
|
6656
|
+
}
|
|
6657
|
+
}
|
|
6658
|
+
const result = await updateSettings((current) => {
|
|
6659
|
+
const currentSelection = current.accountPoolProfilePool.length > 0 ? current.accountPoolProfilePool.slice() : availableProfileNames.slice();
|
|
6660
|
+
let nextSelection = currentSelection;
|
|
6661
|
+
switch (action) {
|
|
6662
|
+
case "set":
|
|
6663
|
+
nextSelection = names.slice();
|
|
6664
|
+
break;
|
|
6665
|
+
case "add":
|
|
6666
|
+
nextSelection = Array.from(/* @__PURE__ */ new Set([...currentSelection, ...names]));
|
|
6667
|
+
break;
|
|
6668
|
+
case "remove":
|
|
6669
|
+
nextSelection = currentSelection.filter((name) => !names.includes(name));
|
|
6670
|
+
break;
|
|
6671
|
+
case "reset":
|
|
6672
|
+
nextSelection = [];
|
|
6673
|
+
break;
|
|
6674
|
+
default:
|
|
6675
|
+
throw new Error("Unknown profiles action.");
|
|
6676
|
+
}
|
|
6677
|
+
return {
|
|
6678
|
+
...current,
|
|
6679
|
+
accountPoolProfilePool: action === "reset" || nextSelection.length === availableProfileNames.length ? [] : nextSelection
|
|
6680
|
+
};
|
|
6681
|
+
});
|
|
6682
|
+
if (result.settings.accountPoolProfilePool.length === 0) {
|
|
6683
|
+
console.log("Accounts Pool now uses all saved profiles.");
|
|
6684
|
+
return;
|
|
6685
|
+
}
|
|
6686
|
+
printStringList("Accounts Pool selected profiles", result.settings.accountPoolProfilePool);
|
|
6687
|
+
}
|
|
6688
|
+
async function handleArraySetting(input) {
|
|
6689
|
+
const action = input.params[1];
|
|
6690
|
+
const { normalized } = await readSettingsSnapshot();
|
|
6691
|
+
const currentValues = normalized[input.key];
|
|
6692
|
+
if (!action || action === "list") {
|
|
6693
|
+
printStringList(input.label, currentValues);
|
|
6694
|
+
return;
|
|
6695
|
+
}
|
|
6696
|
+
const rawValues = getArrayValues(input.params, 2);
|
|
6697
|
+
if ((action === "add" || action === "remove") && rawValues.length === 0) {
|
|
6698
|
+
throw new Error(`Values are required for \`${input.actionName} ${action}\`.`);
|
|
6699
|
+
}
|
|
6700
|
+
const normalizedValues = rawValues.map(input.normalizeValue).filter((value) => Boolean(value));
|
|
6701
|
+
if ((action === "add" || action === "remove") && normalizedValues.length === 0) {
|
|
6702
|
+
throw new Error("No valid values were provided.");
|
|
6703
|
+
}
|
|
6704
|
+
const result = await updateSettings((current) => {
|
|
6705
|
+
const existing = current[input.key];
|
|
6706
|
+
switch (action) {
|
|
6707
|
+
case "add":
|
|
6708
|
+
return {
|
|
6709
|
+
...current,
|
|
6710
|
+
[input.key]: Array.from(/* @__PURE__ */ new Set([...existing, ...normalizedValues]))
|
|
6711
|
+
};
|
|
6712
|
+
case "remove":
|
|
6713
|
+
return {
|
|
6714
|
+
...current,
|
|
6715
|
+
[input.key]: existing.filter((value) => !normalizedValues.includes(value))
|
|
6716
|
+
};
|
|
6717
|
+
case "reset":
|
|
6718
|
+
return {
|
|
6719
|
+
...current,
|
|
6720
|
+
[input.key]: [...input.defaultValues]
|
|
6721
|
+
};
|
|
6722
|
+
default:
|
|
6723
|
+
throw new Error(`Unknown ${input.actionName} action.`);
|
|
6724
|
+
}
|
|
6725
|
+
});
|
|
6726
|
+
printStringList(input.label, result.settings[input.key]);
|
|
6727
|
+
}
|
|
6728
|
+
async function handleKeys(params, flags) {
|
|
6729
|
+
const action = params[1];
|
|
6730
|
+
const { runtime, stateDir } = resolveStateDir(flags);
|
|
6731
|
+
const baseUrl = resolveBaseUrl(runtime, flags);
|
|
6732
|
+
if (!action || action === "list") {
|
|
6733
|
+
const apiKeys = await listApiKeys(stateDir);
|
|
6734
|
+
if (apiKeys.length === 0) {
|
|
6735
|
+
console.log("No active Accounts Pool API keys.");
|
|
6736
|
+
return;
|
|
6737
|
+
}
|
|
6738
|
+
for (const apiKey of apiKeys) {
|
|
6739
|
+
printApiKeyRow(apiKey);
|
|
6740
|
+
}
|
|
6741
|
+
return;
|
|
6742
|
+
}
|
|
6743
|
+
switch (action) {
|
|
6744
|
+
case "create": {
|
|
6745
|
+
await requireProAccess();
|
|
6746
|
+
const created = await createApiKey(stateDir);
|
|
6747
|
+
console.log(`Created key: ${created.key.id}`);
|
|
6748
|
+
console.log(`Preview: ${created.key.tokenPreview}`);
|
|
6749
|
+
console.log(`Token: ${created.plaintextToken}`);
|
|
6750
|
+
console.log(`Runtime: ${runtime}`);
|
|
6751
|
+
console.log(`State dir: ${stateDir}`);
|
|
6752
|
+
if (baseUrl) {
|
|
6753
|
+
console.log(`Base URL: ${baseUrl}`);
|
|
6754
|
+
} else if (runtime === "desktop") {
|
|
6755
|
+
console.log("Base URL: use the current Accounts Pool URL shown by the running desktop app.");
|
|
6756
|
+
} else if (runtime === "daemon") {
|
|
6757
|
+
console.log("Base URL: pass --port=NNNN here or use the URL printed by `codexuse daemon start`.");
|
|
6758
|
+
}
|
|
6759
|
+
console.log("This key only works for the selected runtime state dir.");
|
|
6760
|
+
console.log("Store this token now. The plaintext value is only shown once.");
|
|
6761
|
+
return;
|
|
6762
|
+
}
|
|
6763
|
+
case "revoke": {
|
|
6764
|
+
const id = params[2]?.trim();
|
|
6765
|
+
if (!id) {
|
|
6766
|
+
throw new Error("API key id is required.");
|
|
6767
|
+
}
|
|
6768
|
+
const removed = await revokeApiKey(stateDir, id);
|
|
6769
|
+
if (!removed) {
|
|
6770
|
+
throw new Error(`No Accounts Pool API key found for id ${id}.`);
|
|
6771
|
+
}
|
|
6772
|
+
console.log(`Revoked key: ${id}`);
|
|
6773
|
+
return;
|
|
6774
|
+
}
|
|
6775
|
+
default:
|
|
6776
|
+
throw new Error("Unknown keys action.");
|
|
6777
|
+
}
|
|
6778
|
+
}
|
|
6779
|
+
async function handleSessions(params, flags) {
|
|
6780
|
+
const action = params[1];
|
|
6781
|
+
if (action && action !== "list") {
|
|
6782
|
+
throw new Error("Only `sessions list` is supported here. Session mutation stays runtime-owned.");
|
|
6783
|
+
}
|
|
6784
|
+
const { stateDir } = resolveStateDir(flags);
|
|
6785
|
+
const sessions = await listSessions(stateDir);
|
|
6786
|
+
if (sessions.length === 0) {
|
|
6787
|
+
console.log("No pooled sessions in this runtime store.");
|
|
6788
|
+
return;
|
|
6789
|
+
}
|
|
6790
|
+
for (const session of sessions) {
|
|
6791
|
+
printSessionRow(session);
|
|
6792
|
+
}
|
|
6793
|
+
console.log("Session mutation is intentionally not exposed here; pooled sessions belong to the live runtime owner.");
|
|
6794
|
+
}
|
|
6795
|
+
async function handleAccountPoolCommand(args, version) {
|
|
6796
|
+
const flags = args.filter((arg) => arg.startsWith("-"));
|
|
6797
|
+
const params = stripFlags(args);
|
|
6798
|
+
const sub = params[0];
|
|
6799
|
+
if (!sub || hasFlag(flags, "--help") || hasFlag(flags, "-h")) {
|
|
6800
|
+
printAccountPoolHelp(version);
|
|
6801
|
+
return;
|
|
6802
|
+
}
|
|
6803
|
+
switch (sub) {
|
|
6804
|
+
case "status":
|
|
6805
|
+
await handleStatus(flags);
|
|
6806
|
+
return;
|
|
6807
|
+
case "enable":
|
|
6808
|
+
await handleEnable();
|
|
6809
|
+
return;
|
|
6810
|
+
case "disable":
|
|
6811
|
+
await handleDisable();
|
|
6812
|
+
return;
|
|
6813
|
+
case "routing":
|
|
6814
|
+
await handleRouting(params);
|
|
6815
|
+
return;
|
|
6816
|
+
case "profiles":
|
|
6817
|
+
await handleProfiles(params);
|
|
6818
|
+
return;
|
|
6819
|
+
case "models":
|
|
6820
|
+
await handleArraySetting({
|
|
6821
|
+
label: "Published models",
|
|
6822
|
+
actionName: "models",
|
|
6823
|
+
params,
|
|
6824
|
+
key: "accountPoolExposedModels",
|
|
6825
|
+
defaultValues: DEFAULT_ACCOUNT_POOL_EXPOSED_MODELS,
|
|
6826
|
+
normalizeValue: (value) => normalizeCodexModelSlug(value) ?? asString3(value)
|
|
6827
|
+
});
|
|
6828
|
+
return;
|
|
6829
|
+
case "reasoning":
|
|
6830
|
+
await handleArraySetting({
|
|
6831
|
+
label: "Reasoning aliases",
|
|
6832
|
+
actionName: "reasoning",
|
|
6833
|
+
params,
|
|
6834
|
+
key: "accountPoolExposedReasoningEfforts",
|
|
6835
|
+
defaultValues: DEFAULT_ACCOUNT_POOL_EXPOSED_REASONING_EFFORTS,
|
|
6836
|
+
normalizeValue: (value) => normalizeReasoningEffort(value)
|
|
6837
|
+
});
|
|
6838
|
+
return;
|
|
6839
|
+
case "fast-mode":
|
|
6840
|
+
await handleArraySetting({
|
|
6841
|
+
label: "Fast-mode aliases",
|
|
6842
|
+
actionName: "fast-mode",
|
|
6843
|
+
params,
|
|
6844
|
+
key: "accountPoolExposedFastModeReasoningEfforts",
|
|
6845
|
+
defaultValues: DEFAULT_ACCOUNT_POOL_EXPOSED_FAST_MODE_REASONING_EFFORTS,
|
|
6846
|
+
normalizeValue: (value) => normalizeReasoningEffort(value)
|
|
6847
|
+
});
|
|
6848
|
+
return;
|
|
6849
|
+
case "keys":
|
|
6850
|
+
await handleKeys(params, flags);
|
|
6851
|
+
return;
|
|
6852
|
+
case "sessions":
|
|
6853
|
+
await handleSessions(params, flags);
|
|
6854
|
+
return;
|
|
6855
|
+
default:
|
|
6856
|
+
printHelp(version);
|
|
6857
|
+
return;
|
|
6858
|
+
}
|
|
6859
|
+
}
|
|
6860
|
+
|
|
6861
|
+
// src/commands/daemon.ts
|
|
6862
|
+
var import_node_child_process4 = require("child_process");
|
|
6863
|
+
var import_node_crypto5 = require("crypto");
|
|
6864
|
+
var import_node_fs6 = __toESM(require("fs"));
|
|
6865
|
+
var import_node_net = __toESM(require("net"));
|
|
6866
|
+
var import_node_os4 = __toESM(require("os"));
|
|
6867
|
+
var import_node_path9 = __toESM(require("path"));
|
|
6868
|
+
|
|
6869
|
+
// ../../packages/shared/src/core/internal-js-runtime.ts
|
|
6870
|
+
var import_node_child_process3 = require("child_process");
|
|
6871
|
+
var import_node_path8 = __toESM(require("path"), 1);
|
|
6872
|
+
var cachedBunBinary = null;
|
|
6873
|
+
function hasBunRuntime() {
|
|
6874
|
+
return typeof process.versions.bun === "string" && process.versions.bun.length > 0;
|
|
6875
|
+
}
|
|
6876
|
+
function buildRuntimePath(env) {
|
|
6877
|
+
const homeDir = env.HOME ?? env.USERPROFILE ?? "";
|
|
6878
|
+
const pathHints = (env.CODEX_PATH_HINTS ?? "").split(import_node_path8.default.delimiter).map((entry) => entry.trim()).filter(Boolean);
|
|
6879
|
+
const entries = [
|
|
6880
|
+
env.PATH ?? "",
|
|
6881
|
+
import_node_path8.default.join(homeDir, ".bun", "bin"),
|
|
6882
|
+
"/opt/homebrew/bin",
|
|
6883
|
+
"/usr/local/bin",
|
|
6884
|
+
import_node_path8.default.join(homeDir, ".local", "bin"),
|
|
6885
|
+
import_node_path8.default.join(homeDir, ".fnm", "aliases", "default", "bin"),
|
|
6886
|
+
import_node_path8.default.join(homeDir, ".fnm", "current", "bin"),
|
|
6887
|
+
...pathHints
|
|
6888
|
+
];
|
|
6889
|
+
return Array.from(
|
|
6890
|
+
new Set(
|
|
6891
|
+
entries.flatMap((entry) => entry.split(import_node_path8.default.delimiter)).map((entry) => entry.trim()).filter(Boolean)
|
|
6892
|
+
)
|
|
6893
|
+
).join(import_node_path8.default.delimiter);
|
|
6894
|
+
}
|
|
6895
|
+
function resolveConfiguredBunBinary(env) {
|
|
6896
|
+
const candidates = [
|
|
6897
|
+
env.CODEXUSE_INTERNAL_BUN_BIN,
|
|
6898
|
+
env.BUN_BIN,
|
|
6899
|
+
process.env.CODEXUSE_INTERNAL_BUN_BIN,
|
|
6900
|
+
process.env.BUN_BIN
|
|
6901
|
+
];
|
|
6902
|
+
for (const candidate of candidates) {
|
|
6903
|
+
const normalized = candidate?.trim() ?? "";
|
|
6904
|
+
if (normalized.length > 0) {
|
|
6905
|
+
return normalized;
|
|
6906
|
+
}
|
|
6907
|
+
}
|
|
6908
|
+
return null;
|
|
6909
|
+
}
|
|
6910
|
+
function probeBunBinary(env) {
|
|
6911
|
+
if (hasBunRuntime()) {
|
|
6912
|
+
return process.execPath;
|
|
6913
|
+
}
|
|
6914
|
+
if (cachedBunBinary) {
|
|
6915
|
+
return cachedBunBinary;
|
|
6916
|
+
}
|
|
6917
|
+
const homeDir = env.HOME ?? env.USERPROFILE ?? "";
|
|
6918
|
+
const candidates = Array.from(
|
|
6919
|
+
new Set(
|
|
6920
|
+
[
|
|
6921
|
+
env.CODEXUSE_INTERNAL_BUN_BIN?.trim(),
|
|
6922
|
+
env.BUN_BIN?.trim(),
|
|
6923
|
+
import_node_path8.default.join(homeDir, ".bun", "bin", "bun"),
|
|
6924
|
+
"/opt/homebrew/bin/bun",
|
|
6925
|
+
"/usr/local/bin/bun",
|
|
6926
|
+
"bun"
|
|
6927
|
+
].filter((candidate) => typeof candidate === "string" && candidate.length > 0)
|
|
6928
|
+
)
|
|
6929
|
+
);
|
|
6930
|
+
for (const candidate of candidates) {
|
|
6931
|
+
const probe = (0, import_node_child_process3.spawnSync)(candidate, ["--version"], {
|
|
6932
|
+
env,
|
|
6933
|
+
stdio: "ignore"
|
|
6934
|
+
});
|
|
6935
|
+
if (!probe.error && probe.status === 0) {
|
|
6936
|
+
cachedBunBinary = candidate;
|
|
6937
|
+
return candidate;
|
|
6938
|
+
}
|
|
6939
|
+
}
|
|
6940
|
+
return null;
|
|
6941
|
+
}
|
|
6942
|
+
function resolveInternalBunRuntime(overrides = {}) {
|
|
6943
|
+
const env = {
|
|
6944
|
+
...process.env,
|
|
6945
|
+
...overrides
|
|
6946
|
+
};
|
|
6947
|
+
env.PATH = buildRuntimePath(env);
|
|
6948
|
+
const runtimeBin = hasBunRuntime() ? process.execPath : resolveConfiguredBunBinary(env) ?? probeBunBinary(env);
|
|
6949
|
+
if (!runtimeBin) {
|
|
6950
|
+
throw new Error(
|
|
6951
|
+
"CodexUse desktop requires a Bun runtime to launch internal services. Install Bun or set CODEXUSE_INTERNAL_BUN_BIN."
|
|
6952
|
+
);
|
|
6953
|
+
}
|
|
6954
|
+
delete env.ELECTRON_RUN_AS_NODE;
|
|
6955
|
+
return {
|
|
6956
|
+
bin: runtimeBin,
|
|
6957
|
+
env
|
|
6958
|
+
};
|
|
6959
|
+
}
|
|
6960
|
+
|
|
6961
|
+
// src/commands/daemon.ts
|
|
6962
|
+
function normalizeTelegramBotToken(value) {
|
|
6963
|
+
const trimmed = value?.trim() ?? "";
|
|
6964
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
6965
|
+
}
|
|
6966
|
+
function printDaemonHelp(version) {
|
|
6967
|
+
console.log(`CodexUse CLI v${version}
|
|
6968
|
+
|
|
6969
|
+
Usage:
|
|
6970
|
+
codexuse daemon start [--port=NNNN] [--telegram-bot-token=<token>] [--project-path=/abs/path]
|
|
6971
|
+
|
|
6972
|
+
Notes:
|
|
6973
|
+
- Runs the T3 Projects/chat server in headless mode.
|
|
6974
|
+
- The same daemon can serve Accounts Pool and Telegram remote control.
|
|
6975
|
+
- Requires Bun on PATH because the bundled Projects server runs on Bun.
|
|
6976
|
+
- Pass --port to keep a stable local API URL for Accounts Pool or other local clients.
|
|
6977
|
+
- Pass --telegram-bot-token or set CODEXUSE_TELEGRAM_BOT_TOKEN to enable Telegram remote control.
|
|
6978
|
+
- --project-path bootstraps that folder as the default project/thread.
|
|
6979
|
+
- Stop with Ctrl+C or SIGTERM.
|
|
6980
|
+
`);
|
|
6981
|
+
}
|
|
6982
|
+
function resolveServerEntry() {
|
|
6983
|
+
const candidates = [
|
|
6984
|
+
import_node_path9.default.resolve(__dirname, "server", "index.mjs"),
|
|
6985
|
+
import_node_path9.default.resolve(__dirname, "../../../server/dist/index.mjs")
|
|
6986
|
+
];
|
|
6987
|
+
for (const candidate of candidates) {
|
|
6988
|
+
if (import_node_fs6.default.existsSync(candidate)) {
|
|
6989
|
+
return candidate;
|
|
6990
|
+
}
|
|
6991
|
+
}
|
|
6992
|
+
return candidates[0];
|
|
6993
|
+
}
|
|
6994
|
+
function resolveStateDir2() {
|
|
6995
|
+
return import_node_path9.default.join(import_node_os4.default.homedir(), ".codexuse", "t3-daemon");
|
|
6996
|
+
}
|
|
6997
|
+
function reserveLoopbackPort(port = 0) {
|
|
6998
|
+
return new Promise((resolve, reject) => {
|
|
6999
|
+
const server = import_node_net.default.createServer();
|
|
7000
|
+
server.unref();
|
|
7001
|
+
server.once("error", reject);
|
|
7002
|
+
server.listen(port, "127.0.0.1", () => {
|
|
7003
|
+
const address = server.address();
|
|
7004
|
+
const port2 = typeof address === "object" && address ? address.port : null;
|
|
7005
|
+
server.close((error) => {
|
|
7006
|
+
if (error) {
|
|
7007
|
+
reject(error);
|
|
7008
|
+
return;
|
|
7009
|
+
}
|
|
7010
|
+
if (typeof port2 !== "number") {
|
|
7011
|
+
reject(new Error("Failed to reserve a loopback port for the Projects server."));
|
|
7012
|
+
return;
|
|
7013
|
+
}
|
|
7014
|
+
resolve(port2);
|
|
7015
|
+
});
|
|
7016
|
+
});
|
|
7017
|
+
});
|
|
7018
|
+
}
|
|
7019
|
+
async function runDaemonStart(options) {
|
|
7020
|
+
const entry = resolveServerEntry();
|
|
7021
|
+
if (!import_node_fs6.default.existsSync(entry)) {
|
|
7022
|
+
throw new Error(
|
|
7023
|
+
`Missing bundled T3 server build output at ${entry}. Rebuild the CLI package with \`bun run build:cli\`.`
|
|
7024
|
+
);
|
|
7025
|
+
}
|
|
7026
|
+
if (options.port !== null && (!Number.isFinite(options.port) || options.port < 1 || options.port > 65535)) {
|
|
7027
|
+
throw new Error("Daemon port must be an integer between 1 and 65535.");
|
|
7028
|
+
}
|
|
7029
|
+
const port = await reserveLoopbackPort(options.port ?? 0);
|
|
7030
|
+
const authToken = (0, import_node_crypto5.randomBytes)(24).toString("hex");
|
|
7031
|
+
const stateDir = resolveStateDir2();
|
|
7032
|
+
const projectPath = options.projectPath ? import_node_path9.default.resolve(options.projectPath) : null;
|
|
7033
|
+
const childCwd = projectPath ?? process.cwd();
|
|
7034
|
+
const telegramBotToken = options.telegramBotToken?.trim() || null;
|
|
7035
|
+
const runtime = resolveInternalBunRuntime({
|
|
7036
|
+
T3CODE_MODE: "desktop",
|
|
7037
|
+
T3CODE_HOST: "127.0.0.1",
|
|
7038
|
+
T3CODE_PORT: String(port),
|
|
7039
|
+
T3CODE_NO_BROWSER: "1",
|
|
7040
|
+
T3CODE_AUTH_TOKEN: authToken,
|
|
7041
|
+
T3CODE_STATE_DIR: stateDir,
|
|
7042
|
+
T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD: projectPath ? "1" : "0",
|
|
7043
|
+
CODEXUSE_TELEGRAM_RUNTIME_OWNER: "daemon",
|
|
7044
|
+
...telegramBotToken ? {
|
|
7045
|
+
CODEXUSE_TELEGRAM_BOT_TOKEN: telegramBotToken,
|
|
7046
|
+
CODEXUSE_TELEGRAM_BRIDGE_ENABLED: "1"
|
|
7047
|
+
} : {}
|
|
7048
|
+
});
|
|
7049
|
+
const child = (0, import_node_child_process4.spawn)(runtime.bin, [entry], {
|
|
7050
|
+
cwd: childCwd,
|
|
7051
|
+
env: runtime.env,
|
|
7052
|
+
stdio: "inherit"
|
|
7053
|
+
});
|
|
7054
|
+
console.log(`Daemon started. Projects server listening on http://127.0.0.1:${port}`);
|
|
7055
|
+
console.log(`State dir: ${stateDir}`);
|
|
7056
|
+
if (projectPath) {
|
|
7057
|
+
console.log(`Bootstrapped project path: ${projectPath}`);
|
|
7058
|
+
}
|
|
7059
|
+
if (telegramBotToken) {
|
|
7060
|
+
console.log("Telegram remote control enabled for this daemon session.");
|
|
7061
|
+
}
|
|
7062
|
+
const accountPool = await readAccountPoolRuntimeSummary({
|
|
7063
|
+
runtime: "daemon",
|
|
7064
|
+
stateDir,
|
|
7065
|
+
port
|
|
7066
|
+
});
|
|
7067
|
+
if (accountPool.enabled && accountPool.hasProLicense) {
|
|
7068
|
+
console.log(`Accounts Pool base URL: ${accountPool.baseUrl ?? `http://127.0.0.1:${port}`}`);
|
|
7069
|
+
if (accountPool.activeApiKeyCount > 0) {
|
|
7070
|
+
console.log(`Accounts Pool active keys: ${accountPool.activeApiKeyCount}`);
|
|
7071
|
+
} else {
|
|
7072
|
+
console.log("Accounts Pool is enabled, but this daemon has no runtime key yet. Create one with `codexuse account-pool keys create --runtime=daemon`.");
|
|
7073
|
+
}
|
|
7074
|
+
} else if (accountPool.enabled) {
|
|
7075
|
+
console.log("Accounts Pool is configured, but Pro is required before this daemon can serve it.");
|
|
7076
|
+
} else {
|
|
7077
|
+
console.log("Accounts Pool is disabled for this daemon. Enable it with `codexuse account-pool enable`.");
|
|
7078
|
+
}
|
|
7079
|
+
if (options.port === null) {
|
|
7080
|
+
console.log("Tip: pass --port=3773 (or another fixed port) if another local client should keep one stable base URL.");
|
|
7081
|
+
}
|
|
7082
|
+
console.log("Running until SIGINT/SIGTERM...");
|
|
7083
|
+
await waitForChild(child);
|
|
7084
|
+
}
|
|
7085
|
+
function waitForChild(child) {
|
|
7086
|
+
return new Promise((resolve, reject) => {
|
|
7087
|
+
let stopping = false;
|
|
7088
|
+
const stop = (signal) => {
|
|
7089
|
+
if (stopping) {
|
|
7090
|
+
return;
|
|
7091
|
+
}
|
|
7092
|
+
stopping = true;
|
|
7093
|
+
child.kill(signal);
|
|
7094
|
+
};
|
|
7095
|
+
const onSigInt = () => stop("SIGINT");
|
|
7096
|
+
const onSigTerm = () => stop("SIGTERM");
|
|
7097
|
+
process.once("SIGINT", onSigInt);
|
|
7098
|
+
process.once("SIGTERM", onSigTerm);
|
|
7099
|
+
child.once("exit", (code, signal) => {
|
|
7100
|
+
process.off("SIGINT", onSigInt);
|
|
7101
|
+
process.off("SIGTERM", onSigTerm);
|
|
7102
|
+
if (code === 0 || stopping) {
|
|
7103
|
+
resolve();
|
|
7104
|
+
return;
|
|
7105
|
+
}
|
|
7106
|
+
reject(
|
|
7107
|
+
new Error(
|
|
7108
|
+
`Projects server exited unexpectedly (code=${code ?? "null"}, signal=${signal ?? "null"}).`
|
|
7109
|
+
)
|
|
7110
|
+
);
|
|
7111
|
+
});
|
|
7112
|
+
child.once("error", (error) => {
|
|
7113
|
+
process.off("SIGINT", onSigInt);
|
|
7114
|
+
process.off("SIGTERM", onSigTerm);
|
|
7115
|
+
reject(error);
|
|
7116
|
+
});
|
|
7117
|
+
});
|
|
7118
|
+
}
|
|
7119
|
+
async function handleDaemonCommand(args, version) {
|
|
7120
|
+
const flags = args.filter((arg) => arg.startsWith("-"));
|
|
7121
|
+
const params = stripFlags(args);
|
|
7122
|
+
const sub = params[0];
|
|
7123
|
+
if (!sub || hasFlag(flags, "--help") || hasFlag(flags, "-h")) {
|
|
7124
|
+
printDaemonHelp(version);
|
|
7125
|
+
return;
|
|
7126
|
+
}
|
|
7127
|
+
switch (sub) {
|
|
7128
|
+
case "start": {
|
|
7129
|
+
const port = parseIntegerFlag(flags, "--port");
|
|
7130
|
+
const projectPath = parseStringFlag(flags, "--project-path") ?? parseStringFlag(flags, "--project") ?? null;
|
|
7131
|
+
const telegramBotToken = parseStringFlag(flags, "--telegram-bot-token") ?? normalizeTelegramBotToken(process.env.CODEXUSE_TELEGRAM_BOT_TOKEN) ?? null;
|
|
7132
|
+
await runDaemonStart({
|
|
7133
|
+
projectPath,
|
|
7134
|
+
port,
|
|
7135
|
+
telegramBotToken,
|
|
7136
|
+
version
|
|
7137
|
+
});
|
|
7138
|
+
return;
|
|
5712
7139
|
}
|
|
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
|
-
};
|
|
7140
|
+
default:
|
|
7141
|
+
printDaemonHelp(version);
|
|
7142
|
+
return;
|
|
5725
7143
|
}
|
|
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
|
-
}));
|
|
7144
|
+
}
|
|
7145
|
+
|
|
7146
|
+
// src/commands/license.ts
|
|
7147
|
+
async function handleLicense(args, version) {
|
|
7148
|
+
const flags = args.filter((arg) => arg.startsWith("-"));
|
|
7149
|
+
const params = stripFlags(args);
|
|
7150
|
+
const sub = params[0];
|
|
7151
|
+
if (!sub || hasFlag(flags, "--help") || hasFlag(flags, "-h")) {
|
|
7152
|
+
printHelp(version);
|
|
7153
|
+
return;
|
|
5745
7154
|
}
|
|
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 {
|
|
7155
|
+
switch (sub) {
|
|
7156
|
+
case "status": {
|
|
7157
|
+
const forceRefresh = hasFlag(flags, "--refresh");
|
|
7158
|
+
const status = await licenseService.getStatus({ forceRefresh });
|
|
7159
|
+
console.log(`Tier: ${status.tier}`);
|
|
7160
|
+
console.log(`State: ${status.state}`);
|
|
7161
|
+
if (status.profileLimit !== null) {
|
|
7162
|
+
console.log(`Profile limit: ${status.profileLimit}`);
|
|
5778
7163
|
}
|
|
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;
|
|
7164
|
+
if (status.profilesRemaining !== null) {
|
|
7165
|
+
console.log(`Profiles remaining: ${status.profilesRemaining}`);
|
|
5789
7166
|
}
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
7167
|
+
if (status.purchaseEmail) {
|
|
7168
|
+
console.log(`Email: ${status.purchaseEmail}`);
|
|
7169
|
+
}
|
|
7170
|
+
if (status.maskedLicenseKey) {
|
|
7171
|
+
console.log(`License: ${status.maskedLicenseKey}`);
|
|
7172
|
+
}
|
|
7173
|
+
if (status.message) {
|
|
7174
|
+
console.log(`Message: ${status.message}`);
|
|
7175
|
+
}
|
|
7176
|
+
if (status.error) {
|
|
7177
|
+
console.log(`Error: ${status.error}`);
|
|
5797
7178
|
}
|
|
7179
|
+
return;
|
|
5798
7180
|
}
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
if (!normalized.has(normalizedPreferred)) {
|
|
5804
|
-
await this.persistPreferredProfileName(null);
|
|
5805
|
-
}
|
|
5806
|
-
} catch {
|
|
5807
|
-
await this.persistPreferredProfileName(null);
|
|
7181
|
+
case "activate": {
|
|
7182
|
+
const key = params[1];
|
|
7183
|
+
if (!key) {
|
|
7184
|
+
throw new Error("License key is required.");
|
|
5808
7185
|
}
|
|
7186
|
+
const status = await licenseService.activate(key);
|
|
7187
|
+
console.log(status.message ?? "License activated.");
|
|
7188
|
+
return;
|
|
5809
7189
|
}
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
removed
|
|
5813
|
-
};
|
|
7190
|
+
default:
|
|
7191
|
+
printHelp(version);
|
|
5814
7192
|
}
|
|
5815
|
-
}
|
|
7193
|
+
}
|
|
5816
7194
|
|
|
5817
7195
|
// ../../packages/runtime-profiles/src/license/guard.ts
|
|
5818
7196
|
async function assertProfileCreationAllowed(profileManager) {
|
|
@@ -5842,15 +7220,15 @@ function maxUsedPercent(snapshot) {
|
|
|
5842
7220
|
}
|
|
5843
7221
|
|
|
5844
7222
|
// src/platform/codexCli.ts
|
|
5845
|
-
var
|
|
5846
|
-
var
|
|
5847
|
-
var
|
|
7223
|
+
var import_node_child_process5 = require("child_process");
|
|
7224
|
+
var import_node_fs7 = require("fs");
|
|
7225
|
+
var import_node_path10 = __toESM(require("path"));
|
|
5848
7226
|
var ENV_HINTS2 = ["CODEX_BINARY", "CODEX_CLI_PATH", "CODEX_PATH"];
|
|
5849
7227
|
function fileExists2(candidate) {
|
|
5850
7228
|
if (!candidate) return null;
|
|
5851
|
-
const resolved =
|
|
7229
|
+
const resolved = import_node_path10.default.resolve(candidate);
|
|
5852
7230
|
try {
|
|
5853
|
-
const stat2 = (0,
|
|
7231
|
+
const stat2 = (0, import_node_fs7.statSync)(resolved);
|
|
5854
7232
|
if (stat2.isFile()) return resolved;
|
|
5855
7233
|
} catch {
|
|
5856
7234
|
return null;
|
|
@@ -5868,11 +7246,11 @@ function resolveFromEnv() {
|
|
|
5868
7246
|
}
|
|
5869
7247
|
function resolveFromPath() {
|
|
5870
7248
|
const pathValue = process.env.PATH ?? "";
|
|
5871
|
-
const entries = pathValue.split(
|
|
7249
|
+
const entries = pathValue.split(import_node_path10.default.delimiter).filter(Boolean);
|
|
5872
7250
|
const names = process.platform === "win32" ? ["codex.exe", "codex.cmd", "codex.bat", "codex"] : ["codex"];
|
|
5873
7251
|
for (const entry of entries) {
|
|
5874
7252
|
for (const name of names) {
|
|
5875
|
-
const candidate = fileExists2(
|
|
7253
|
+
const candidate = fileExists2(import_node_path10.default.join(entry, name));
|
|
5876
7254
|
if (candidate) return candidate;
|
|
5877
7255
|
}
|
|
5878
7256
|
}
|
|
@@ -5888,7 +7266,7 @@ function requireCodexBinary(context) {
|
|
|
5888
7266
|
const message = context ? `${context} ${hint}` : hint;
|
|
5889
7267
|
throw new Error(message);
|
|
5890
7268
|
}
|
|
5891
|
-
function
|
|
7269
|
+
function buildCodexCommand2(codexPath, args) {
|
|
5892
7270
|
const normalized = codexPath.toLowerCase();
|
|
5893
7271
|
const isJs = normalized.endsWith(".js");
|
|
5894
7272
|
if (isJs) {
|
|
@@ -5920,8 +7298,8 @@ async function runCodexLogin(mode) {
|
|
|
5920
7298
|
const codexPath = requireCodexBinary("Codex CLI is required to login.");
|
|
5921
7299
|
const resolvedMode = resolveLoginMode(mode ?? null);
|
|
5922
7300
|
const loginArgs = resolvedMode === "device" ? ["login", "--device-auth"] : ["login"];
|
|
5923
|
-
const { command, args, shell } =
|
|
5924
|
-
const child = (0,
|
|
7301
|
+
const { command, args, shell } = buildCodexCommand2(codexPath, loginArgs);
|
|
7302
|
+
const child = (0, import_node_child_process5.spawn)(command, args, {
|
|
5925
7303
|
stdio: "inherit",
|
|
5926
7304
|
env: process.env,
|
|
5927
7305
|
shell
|
|
@@ -5942,7 +7320,7 @@ function formatProfileLabel(name, displayName) {
|
|
|
5942
7320
|
return name;
|
|
5943
7321
|
}
|
|
5944
7322
|
function profileRateLimitKey(profile) {
|
|
5945
|
-
return profile
|
|
7323
|
+
return resolveProfileIdentityKeyFromProfile(profile);
|
|
5946
7324
|
}
|
|
5947
7325
|
function toTitleCase(value) {
|
|
5948
7326
|
return value.replace(/\w\S*/g, (word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase());
|
|
@@ -6429,9 +7807,9 @@ function delay(ms) {
|
|
|
6429
7807
|
}
|
|
6430
7808
|
async function handleProfileCommand(args, version) {
|
|
6431
7809
|
const flags = args.filter((arg) => arg.startsWith("-"));
|
|
6432
|
-
const params =
|
|
7810
|
+
const params = stripFlags(args);
|
|
6433
7811
|
const sub = params[0];
|
|
6434
|
-
if (!sub ||
|
|
7812
|
+
if (!sub || hasFlag(flags, "--help") || hasFlag(flags, "-h")) {
|
|
6435
7813
|
printHelp(version);
|
|
6436
7814
|
return;
|
|
6437
7815
|
}
|
|
@@ -6444,8 +7822,8 @@ async function handleProfileCommand(args, version) {
|
|
|
6444
7822
|
console.log("No profiles found.");
|
|
6445
7823
|
return;
|
|
6446
7824
|
}
|
|
6447
|
-
const compact =
|
|
6448
|
-
const withUsage = !
|
|
7825
|
+
const compact = hasFlag(flags, "--compact");
|
|
7826
|
+
const withUsage = !hasFlag(flags, "--no-usage");
|
|
6449
7827
|
let usageMap = null;
|
|
6450
7828
|
if (withUsage) {
|
|
6451
7829
|
const codexPath = resolveCodexBinary();
|
|
@@ -6491,7 +7869,7 @@ async function handleProfileCommand(args, version) {
|
|
|
6491
7869
|
throw new Error("Profile name is required.");
|
|
6492
7870
|
}
|
|
6493
7871
|
await assertProfileCreationAllowed(manager);
|
|
6494
|
-
if (!
|
|
7872
|
+
if (!hasFlag(flags, "--skip-login")) {
|
|
6495
7873
|
const loginMode = resolveLoginMode(parseLoginMode(flags));
|
|
6496
7874
|
await runCodexLogin(loginMode);
|
|
6497
7875
|
}
|
|
@@ -6505,7 +7883,7 @@ async function handleProfileCommand(args, version) {
|
|
|
6505
7883
|
if (!name) {
|
|
6506
7884
|
throw new Error("Profile name is required.");
|
|
6507
7885
|
}
|
|
6508
|
-
if (!
|
|
7886
|
+
if (!hasFlag(flags, "--skip-login")) {
|
|
6509
7887
|
const loginMode = resolveLoginMode(parseLoginMode(flags));
|
|
6510
7888
|
await runCodexLogin(loginMode);
|
|
6511
7889
|
}
|
|
@@ -6525,8 +7903,8 @@ async function handleProfileCommand(args, version) {
|
|
|
6525
7903
|
}
|
|
6526
7904
|
case "autoroll":
|
|
6527
7905
|
case "auto-roll": {
|
|
6528
|
-
const watch =
|
|
6529
|
-
const dryRun =
|
|
7906
|
+
const watch = hasFlag(flags, "--watch");
|
|
7907
|
+
const dryRun = hasFlag(flags, "--dry-run");
|
|
6530
7908
|
const settings = await getStoredAutoRollSettings().catch(() => null);
|
|
6531
7909
|
const fallbackThreshold = typeof settings?.switchThreshold === "number" && Number.isFinite(settings.switchThreshold) ? Math.round(settings.switchThreshold) : DEFAULT_AUTOROLL_THRESHOLD;
|
|
6532
7910
|
const threshold = parseAutoRollThreshold(flags, fallbackThreshold);
|
|
@@ -6581,8 +7959,8 @@ async function handleProfileCommand(args, version) {
|
|
|
6581
7959
|
}
|
|
6582
7960
|
|
|
6583
7961
|
// ../../packages/runtime-profiles/src/cloud-sync/service.ts
|
|
6584
|
-
var
|
|
6585
|
-
var
|
|
7962
|
+
var import_node_fs8 = require("fs");
|
|
7963
|
+
var import_node_path14 = __toESM(require("path"), 1);
|
|
6586
7964
|
|
|
6587
7965
|
// ../../packages/contracts/src/cloud-sync/types.ts
|
|
6588
7966
|
var CLOUD_SYNC_SCHEMA_VERSION = 1;
|
|
@@ -6742,9 +8120,9 @@ async function pushRemoteSnapshot(licenseKey, snapshot, options) {
|
|
|
6742
8120
|
var import_toml2 = __toESM(require_toml(), 1);
|
|
6743
8121
|
|
|
6744
8122
|
// ../../packages/runtime-codex/src/codex/config-metadata.ts
|
|
6745
|
-
var
|
|
8123
|
+
var import_node_child_process6 = require("child_process");
|
|
6746
8124
|
var import_promises = require("fs/promises");
|
|
6747
|
-
var
|
|
8125
|
+
var import_node_path11 = __toESM(require("path"), 1);
|
|
6748
8126
|
var CONFIG_SCHEMA_URL = "https://raw.githubusercontent.com/openai/codex/main/codex-rs/core/config.schema.json";
|
|
6749
8127
|
var METADATA_CACHE_TTL_MS = 6e4;
|
|
6750
8128
|
var FALLBACK_SCHEMA_TOP_LEVEL_KEYS = [
|
|
@@ -6854,7 +8232,7 @@ function uniqueSorted(values) {
|
|
|
6854
8232
|
}
|
|
6855
8233
|
async function runCommand(command, args, timeoutMs = 3e3) {
|
|
6856
8234
|
return new Promise((resolve, reject) => {
|
|
6857
|
-
const child = (0,
|
|
8235
|
+
const child = (0, import_node_child_process6.spawn)(command, args, {
|
|
6858
8236
|
stdio: ["ignore", "pipe", "pipe"],
|
|
6859
8237
|
env: process.env
|
|
6860
8238
|
});
|
|
@@ -6930,8 +8308,8 @@ function parseSchemaKeys(schemaRaw) {
|
|
|
6930
8308
|
async function readSchemaFromLocal() {
|
|
6931
8309
|
const candidates = [
|
|
6932
8310
|
process.env.CODEX_CONFIG_SCHEMA_PATH,
|
|
6933
|
-
|
|
6934
|
-
|
|
8311
|
+
import_node_path11.default.join(process.cwd(), "codex-rs", "core", "config.schema.json"),
|
|
8312
|
+
import_node_path11.default.join(process.cwd(), "node_modules", "@openai", "codex", "codex-rs", "core", "config.schema.json")
|
|
6935
8313
|
].filter((candidate) => Boolean(candidate));
|
|
6936
8314
|
for (const candidate of candidates) {
|
|
6937
8315
|
try {
|
|
@@ -7036,20 +8414,20 @@ async function getCodexConfigMetadata(forceRefresh = false) {
|
|
|
7036
8414
|
|
|
7037
8415
|
// ../../packages/runtime-codex/src/codex/config-io.ts
|
|
7038
8416
|
var import_promises2 = require("fs/promises");
|
|
7039
|
-
var
|
|
8417
|
+
var import_node_path13 = __toESM(require("path"), 1);
|
|
7040
8418
|
|
|
7041
8419
|
// ../../packages/runtime-codex/src/codex/home.ts
|
|
7042
|
-
var
|
|
7043
|
-
var
|
|
8420
|
+
var import_node_os5 = __toESM(require("os"), 1);
|
|
8421
|
+
var import_node_path12 = __toESM(require("path"), 1);
|
|
7044
8422
|
function resolveHomeDir() {
|
|
7045
|
-
return process.env.HOME || process.env.USERPROFILE ||
|
|
8423
|
+
return process.env.HOME || process.env.USERPROFILE || import_node_os5.default.homedir();
|
|
7046
8424
|
}
|
|
7047
8425
|
function expandHomePrefix(input, homeDir) {
|
|
7048
8426
|
if (input === "~") {
|
|
7049
8427
|
return homeDir;
|
|
7050
8428
|
}
|
|
7051
8429
|
if (input.startsWith("~/") || input.startsWith("~\\")) {
|
|
7052
|
-
return
|
|
8430
|
+
return import_node_path12.default.join(homeDir, input.slice(2));
|
|
7053
8431
|
}
|
|
7054
8432
|
return input;
|
|
7055
8433
|
}
|
|
@@ -7065,13 +8443,13 @@ function normalizeCodexHomePath(input) {
|
|
|
7065
8443
|
if (!configured) {
|
|
7066
8444
|
return null;
|
|
7067
8445
|
}
|
|
7068
|
-
return
|
|
8446
|
+
return import_node_path12.default.resolve(expandHomePrefix(configured, resolveHomeDir()));
|
|
7069
8447
|
}
|
|
7070
8448
|
function resolveDefaultCodexHomeDir() {
|
|
7071
|
-
return
|
|
8449
|
+
return import_node_path12.default.join(resolveHomeDir(), ".codex");
|
|
7072
8450
|
}
|
|
7073
8451
|
function resolveProfileCodexHomeDir(profileName) {
|
|
7074
|
-
return
|
|
8452
|
+
return import_node_path12.default.join(getUserDataDir(), "profile-homes", profileName);
|
|
7075
8453
|
}
|
|
7076
8454
|
function resolveCodexHomeDir(options) {
|
|
7077
8455
|
return normalizeCodexHomePath(options?.codexHomePath) ?? resolveDefaultCodexHomeDir();
|
|
@@ -7152,10 +8530,10 @@ var YOLO_PRESET = {
|
|
|
7152
8530
|
|
|
7153
8531
|
// ../../packages/runtime-codex/src/codex/config-io.ts
|
|
7154
8532
|
function getConfigPath(options) {
|
|
7155
|
-
return
|
|
8533
|
+
return import_node_path13.default.join(resolveCodexHomeDir(options), "config.toml");
|
|
7156
8534
|
}
|
|
7157
8535
|
async function ensureConfigDirExists(filePath) {
|
|
7158
|
-
const dir =
|
|
8536
|
+
const dir = import_node_path13.default.dirname(filePath);
|
|
7159
8537
|
await (0, import_promises2.mkdir)(dir, { recursive: true });
|
|
7160
8538
|
}
|
|
7161
8539
|
function normalizeLineEndings2(content) {
|
|
@@ -7214,41 +8592,171 @@ function stripAnsi(value) {
|
|
|
7214
8592
|
function normalizeWhitespace(value) {
|
|
7215
8593
|
return value.replace(/\s+/g, " ").trim();
|
|
7216
8594
|
}
|
|
7217
|
-
function
|
|
7218
|
-
|
|
8595
|
+
function compactErrorText(value, maxLength = 220) {
|
|
8596
|
+
if (!value) {
|
|
8597
|
+
return "";
|
|
8598
|
+
}
|
|
8599
|
+
const cleaned = normalizeWhitespace(stripAnsi(String(value)));
|
|
8600
|
+
if (!cleaned) {
|
|
8601
|
+
return "";
|
|
8602
|
+
}
|
|
8603
|
+
if (cleaned.length <= maxLength) {
|
|
8604
|
+
return cleaned;
|
|
8605
|
+
}
|
|
8606
|
+
return `${cleaned.slice(0, maxLength).trimEnd()}\u2026`;
|
|
8607
|
+
}
|
|
8608
|
+
function createErrorReferenceId() {
|
|
8609
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`.toUpperCase();
|
|
8610
|
+
}
|
|
8611
|
+
function serializeUnknownError(value) {
|
|
8612
|
+
if (typeof value === "string") {
|
|
8613
|
+
return value;
|
|
8614
|
+
}
|
|
8615
|
+
if (value instanceof Error) {
|
|
8616
|
+
return value.stack ?? value.message;
|
|
8617
|
+
}
|
|
8618
|
+
try {
|
|
8619
|
+
return JSON.stringify(value, null, 2);
|
|
8620
|
+
} catch {
|
|
8621
|
+
return String(value);
|
|
8622
|
+
}
|
|
8623
|
+
}
|
|
8624
|
+
function extractRawMessage(error) {
|
|
7219
8625
|
if (error === null || typeof error === "undefined") {
|
|
7220
|
-
return
|
|
8626
|
+
return "";
|
|
8627
|
+
}
|
|
8628
|
+
if (typeof error === "string") {
|
|
8629
|
+
return error;
|
|
7221
8630
|
}
|
|
7222
|
-
|
|
8631
|
+
if (error instanceof Error) {
|
|
8632
|
+
return error.message;
|
|
8633
|
+
}
|
|
8634
|
+
return String(error);
|
|
8635
|
+
}
|
|
8636
|
+
function normalizeRawMessage(rawMessage) {
|
|
7223
8637
|
let cleaned = normalizeWhitespace(stripAnsi(rawMessage));
|
|
7224
8638
|
if (!cleaned) {
|
|
7225
|
-
return
|
|
8639
|
+
return "";
|
|
7226
8640
|
}
|
|
7227
8641
|
if (cleaned.startsWith("Error invoking remote method")) {
|
|
7228
8642
|
cleaned = cleaned.replace(/^Error invoking remote method '[^']+': Error: /, "");
|
|
7229
8643
|
}
|
|
8644
|
+
if (cleaned.toLowerCase().includes("openai codex") && cleaned.toLowerCase().includes("workdir:")) {
|
|
8645
|
+
const parts = cleaned.split(/error:/i);
|
|
8646
|
+
if (parts.length > 1) {
|
|
8647
|
+
return parts[parts.length - 1]?.trim() ?? cleaned;
|
|
8648
|
+
}
|
|
8649
|
+
}
|
|
8650
|
+
return cleaned;
|
|
8651
|
+
}
|
|
8652
|
+
function resolveCategory(cleaned, options) {
|
|
7230
8653
|
const lower = cleaned.toLowerCase();
|
|
7231
8654
|
if (options.notFoundFallback && (lower.includes("enoent") || lower.includes("not found"))) {
|
|
7232
|
-
return
|
|
8655
|
+
return {
|
|
8656
|
+
code: "ERR_NOT_FOUND",
|
|
8657
|
+
title: "We couldn't find that item",
|
|
8658
|
+
message: options.notFoundFallback,
|
|
8659
|
+
recovery: "Check the path or selection, then try again.",
|
|
8660
|
+
retryable: true
|
|
8661
|
+
};
|
|
7233
8662
|
}
|
|
7234
8663
|
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
|
-
}
|
|
8664
|
+
return {
|
|
8665
|
+
code: "ERR_RATE_LIMIT",
|
|
8666
|
+
title: "Rate limit reached",
|
|
8667
|
+
message: "The provider is rate limited right now.",
|
|
8668
|
+
recovery: "Wait a moment, switch profiles, or try again.",
|
|
8669
|
+
retryable: true
|
|
8670
|
+
};
|
|
8671
|
+
}
|
|
8672
|
+
if (lower.includes("timeout") || lower.includes("timed out")) {
|
|
8673
|
+
return {
|
|
8674
|
+
code: "ERR_TIMEOUT",
|
|
8675
|
+
title: "Request timed out",
|
|
8676
|
+
message: "The operation took too long to finish.",
|
|
8677
|
+
recovery: "Check the connection and try again.",
|
|
8678
|
+
retryable: true
|
|
8679
|
+
};
|
|
8680
|
+
}
|
|
8681
|
+
if (lower.includes("network") || lower.includes("econn") || lower.includes("networkerror") || lower.includes("connection refused") || lower.includes("socket hang up")) {
|
|
8682
|
+
return {
|
|
8683
|
+
code: "ERR_NETWORK",
|
|
8684
|
+
title: "Can't reach the service",
|
|
8685
|
+
message: "CodexUse couldn't connect to the required service.",
|
|
8686
|
+
recovery: "Check your internet or local server connection, then retry.",
|
|
8687
|
+
retryable: true
|
|
8688
|
+
};
|
|
8689
|
+
}
|
|
8690
|
+
if (lower.includes("token_invalidated") || lower.includes("session invalidated") || lower.includes("session expired") || lower.includes("refresh token") || lower.includes("unauthorized") || lower.includes("401")) {
|
|
8691
|
+
return {
|
|
8692
|
+
code: "ERR_AUTH",
|
|
8693
|
+
title: "Sign-in expired",
|
|
8694
|
+
message: "This account needs to sign in again.",
|
|
8695
|
+
recovery: "Reconnect the profile, then retry the action.",
|
|
8696
|
+
retryable: true
|
|
8697
|
+
};
|
|
8698
|
+
}
|
|
8699
|
+
if (lower.includes("permission") || lower.includes("eacces") || lower.includes("operation not permitted")) {
|
|
8700
|
+
return {
|
|
8701
|
+
code: "ERR_PERMISSION",
|
|
8702
|
+
title: "Permission denied",
|
|
8703
|
+
message: "CodexUse doesn't have permission to do that.",
|
|
8704
|
+
recovery: "Check the file permissions or restart the app and try again.",
|
|
8705
|
+
retryable: true
|
|
8706
|
+
};
|
|
8707
|
+
}
|
|
8708
|
+
if (lower.includes("enospc") || lower.includes("disk full") || lower.includes("no space left")) {
|
|
8709
|
+
return {
|
|
8710
|
+
code: "ERR_DISK_FULL",
|
|
8711
|
+
title: "Disk is full",
|
|
8712
|
+
message: "CodexUse couldn't save data because the disk is full.",
|
|
8713
|
+
recovery: "Free up disk space, then try again.",
|
|
8714
|
+
retryable: true
|
|
8715
|
+
};
|
|
7251
8716
|
}
|
|
8717
|
+
if (lower.includes("codex cli") || lower.includes("command not found") || lower.includes("spawn codex") || lower.includes("could not find codex")) {
|
|
8718
|
+
return {
|
|
8719
|
+
code: "ERR_CLI_UNAVAILABLE",
|
|
8720
|
+
title: "Codex CLI unavailable",
|
|
8721
|
+
message: "CodexUse couldn't start the Codex CLI.",
|
|
8722
|
+
recovery: "Check Runtime > Codex and confirm the CLI is installed correctly.",
|
|
8723
|
+
retryable: true
|
|
8724
|
+
};
|
|
8725
|
+
}
|
|
8726
|
+
if (lower.includes("provider") || lower.includes("model") || lower.includes("openai") || lower.includes("anthropic")) {
|
|
8727
|
+
return {
|
|
8728
|
+
code: "ERR_PROVIDER",
|
|
8729
|
+
title: "Provider request failed",
|
|
8730
|
+
message: "The AI provider rejected or failed the request.",
|
|
8731
|
+
recovery: "Try again, or switch the active profile or model.",
|
|
8732
|
+
retryable: true
|
|
8733
|
+
};
|
|
8734
|
+
}
|
|
8735
|
+
return {
|
|
8736
|
+
code: "ERR_UNKNOWN",
|
|
8737
|
+
title: "Something went wrong",
|
|
8738
|
+
message: options.fallback,
|
|
8739
|
+
recovery: "Try again. If it keeps happening, report the error with the reference below.",
|
|
8740
|
+
retryable: true
|
|
8741
|
+
};
|
|
8742
|
+
}
|
|
8743
|
+
function resolveUserFacingError(error, options = {}) {
|
|
8744
|
+
const fallback = options.fallback ?? "Something went wrong. Please try again.";
|
|
8745
|
+
const rawMessage = extractRawMessage(error);
|
|
8746
|
+
const cleaned = normalizeRawMessage(rawMessage);
|
|
8747
|
+
const category = resolveCategory(cleaned, {
|
|
8748
|
+
fallback,
|
|
8749
|
+
...options.notFoundFallback ? { notFoundFallback: options.notFoundFallback } : {}
|
|
8750
|
+
});
|
|
8751
|
+
return {
|
|
8752
|
+
...category,
|
|
8753
|
+
referenceId: createErrorReferenceId(),
|
|
8754
|
+
technicalMessage: compactErrorText(cleaned || rawMessage || fallback, 400),
|
|
8755
|
+
technicalDetails: compactErrorText(serializeUnknownError(error), 4e3)
|
|
8756
|
+
};
|
|
8757
|
+
}
|
|
8758
|
+
function formatUserFacingError(error, options = {}) {
|
|
8759
|
+
let cleaned = resolveUserFacingError(error, options).message;
|
|
7252
8760
|
const maxLength = typeof options.maxLength === "number" ? options.maxLength : 220;
|
|
7253
8761
|
if (cleaned.length > maxLength) {
|
|
7254
8762
|
cleaned = `${cleaned.slice(0, maxLength).trimEnd()}\u2026`;
|
|
@@ -7679,13 +9187,13 @@ function formatMegabytes(bytes) {
|
|
|
7679
9187
|
return (bytes / MB_DIVISOR).toFixed(2);
|
|
7680
9188
|
}
|
|
7681
9189
|
function resolveSyncBackupsDir() {
|
|
7682
|
-
return
|
|
9190
|
+
return import_node_path14.default.join(getUserDataDir(), SYNC_BACKUP_DIR);
|
|
7683
9191
|
}
|
|
7684
9192
|
function toSafeIsoForFileName(iso) {
|
|
7685
9193
|
return iso.replace(/:/g, "-");
|
|
7686
9194
|
}
|
|
7687
9195
|
async function pruneOldPrePullBackups(backupsDir) {
|
|
7688
|
-
const entries = await
|
|
9196
|
+
const entries = await import_node_fs8.promises.readdir(backupsDir, { withFileTypes: true });
|
|
7689
9197
|
const files = entries.filter(
|
|
7690
9198
|
(entry) => entry.isFile() && entry.name.startsWith(PRE_PULL_BACKUP_PREFIX) && entry.name.endsWith(PRE_PULL_BACKUP_SUFFIX)
|
|
7691
9199
|
).map((entry) => entry.name).sort((a, b) => b.localeCompare(a));
|
|
@@ -7693,7 +9201,7 @@ async function pruneOldPrePullBackups(backupsDir) {
|
|
|
7693
9201
|
return;
|
|
7694
9202
|
}
|
|
7695
9203
|
for (const stale of files.slice(PRE_PULL_BACKUP_KEEP_COUNT)) {
|
|
7696
|
-
await
|
|
9204
|
+
await import_node_fs8.promises.rm(import_node_path14.default.join(backupsDir, stale), { force: true });
|
|
7697
9205
|
}
|
|
7698
9206
|
}
|
|
7699
9207
|
async function createPrePullBackup(profileManager) {
|
|
@@ -7701,13 +9209,13 @@ async function createPrePullBackup(profileManager) {
|
|
|
7701
9209
|
enforcePushGuards: false
|
|
7702
9210
|
});
|
|
7703
9211
|
const backupsDir = resolveSyncBackupsDir();
|
|
7704
|
-
await
|
|
9212
|
+
await import_node_fs8.promises.mkdir(backupsDir, { recursive: true });
|
|
7705
9213
|
const timestamp = toSafeIsoForFileName((/* @__PURE__ */ new Date()).toISOString());
|
|
7706
|
-
const backupPath =
|
|
9214
|
+
const backupPath = import_node_path14.default.join(
|
|
7707
9215
|
backupsDir,
|
|
7708
9216
|
`${PRE_PULL_BACKUP_PREFIX}${timestamp}${PRE_PULL_BACKUP_SUFFIX}`
|
|
7709
9217
|
);
|
|
7710
|
-
await
|
|
9218
|
+
await import_node_fs8.promises.writeFile(backupPath, `${JSON.stringify(snapshot, null, 2)}
|
|
7711
9219
|
`, "utf8");
|
|
7712
9220
|
await pruneOldPrePullBackups(backupsDir);
|
|
7713
9221
|
logInfo("[cloud-sync] created pre-pull backup", { backupPath });
|
|
@@ -7923,9 +9431,9 @@ function printSyncResult(result) {
|
|
|
7923
9431
|
}
|
|
7924
9432
|
async function handleSync(args, version) {
|
|
7925
9433
|
const flags = args.filter((arg) => arg.startsWith("-"));
|
|
7926
|
-
const params =
|
|
9434
|
+
const params = stripFlags(args);
|
|
7927
9435
|
const sub = params[0];
|
|
7928
|
-
if (!sub ||
|
|
9436
|
+
if (!sub || hasFlag(flags, "--help") || hasFlag(flags, "-h")) {
|
|
7929
9437
|
printHelp(version);
|
|
7930
9438
|
return;
|
|
7931
9439
|
}
|
|
@@ -7976,9 +9484,9 @@ async function handleSync(args, version) {
|
|
|
7976
9484
|
}
|
|
7977
9485
|
|
|
7978
9486
|
// ../../packages/runtime-app-state/src/storage/migrations/v1.ts
|
|
7979
|
-
var
|
|
7980
|
-
var
|
|
7981
|
-
var
|
|
9487
|
+
var import_node_fs9 = require("fs");
|
|
9488
|
+
var import_node_path15 = __toESM(require("path"), 1);
|
|
9489
|
+
var import_node_os6 = __toESM(require("os"), 1);
|
|
7982
9490
|
|
|
7983
9491
|
// ../../packages/contracts/src/settings/legacy-localstorage-keys.ts
|
|
7984
9492
|
var LEGACY_LOCALSTORAGE_KEYS = [
|
|
@@ -8002,11 +9510,11 @@ var LEGACY_SKILLS_DIR = "skills";
|
|
|
8002
9510
|
var LEGACY_SKILL_CACHE_DIR = "skill-cache";
|
|
8003
9511
|
var LEGACY_SKILLS_REPOS_FILE = "repos.json";
|
|
8004
9512
|
var LEGACY_SKILL_MANIFEST = ".codexuse-skill.json";
|
|
8005
|
-
var
|
|
9513
|
+
var LEGACY_LICENSE_SECRET_FILE2 = "license.secret";
|
|
8006
9514
|
function isRecord6(value) {
|
|
8007
9515
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
8008
9516
|
}
|
|
8009
|
-
function
|
|
9517
|
+
function asString4(value) {
|
|
8010
9518
|
if (typeof value !== "string") {
|
|
8011
9519
|
return null;
|
|
8012
9520
|
}
|
|
@@ -8017,20 +9525,20 @@ function isMissingPathError(error) {
|
|
|
8017
9525
|
return error.code === "ENOENT";
|
|
8018
9526
|
}
|
|
8019
9527
|
function resolveHomeDir2() {
|
|
8020
|
-
const home = process.env.HOME || process.env.USERPROFILE ||
|
|
9528
|
+
const home = process.env.HOME || process.env.USERPROFILE || import_node_os6.default.homedir();
|
|
8021
9529
|
if (!home) {
|
|
8022
9530
|
throw new Error("HOME is not set.");
|
|
8023
9531
|
}
|
|
8024
9532
|
return home;
|
|
8025
9533
|
}
|
|
8026
9534
|
function resolveCodexDir() {
|
|
8027
|
-
return
|
|
9535
|
+
return import_node_path15.default.join(resolveHomeDir2(), ".codex");
|
|
8028
9536
|
}
|
|
8029
9537
|
function resolveLegacyPath(...segments) {
|
|
8030
|
-
return
|
|
9538
|
+
return import_node_path15.default.join(resolveCodexDir(), ...segments);
|
|
8031
9539
|
}
|
|
8032
9540
|
async function readJsonFile(filePath) {
|
|
8033
|
-
const raw = await
|
|
9541
|
+
const raw = await import_node_fs9.promises.readFile(filePath, "utf8");
|
|
8034
9542
|
return JSON.parse(raw);
|
|
8035
9543
|
}
|
|
8036
9544
|
async function readJsonFileIfExists(filePath) {
|
|
@@ -8045,14 +9553,14 @@ async function readJsonFileIfExists(filePath) {
|
|
|
8045
9553
|
}
|
|
8046
9554
|
}
|
|
8047
9555
|
async function copyDirectoryManual(source, destination) {
|
|
8048
|
-
await
|
|
8049
|
-
const entries = await
|
|
9556
|
+
await import_node_fs9.promises.mkdir(destination, { recursive: true });
|
|
9557
|
+
const entries = await import_node_fs9.promises.readdir(source, { withFileTypes: true });
|
|
8050
9558
|
for (const entry of entries) {
|
|
8051
|
-
const srcPath =
|
|
8052
|
-
const destPath =
|
|
9559
|
+
const srcPath = import_node_path15.default.join(source, entry.name);
|
|
9560
|
+
const destPath = import_node_path15.default.join(destination, entry.name);
|
|
8053
9561
|
if (entry.isSymbolicLink()) {
|
|
8054
|
-
const linkTarget = await
|
|
8055
|
-
await
|
|
9562
|
+
const linkTarget = await import_node_fs9.promises.readlink(srcPath);
|
|
9563
|
+
await import_node_fs9.promises.symlink(linkTarget, destPath).catch((error) => {
|
|
8056
9564
|
if (error.code !== "EEXIST") {
|
|
8057
9565
|
throw error;
|
|
8058
9566
|
}
|
|
@@ -8064,14 +9572,14 @@ async function copyDirectoryManual(source, destination) {
|
|
|
8064
9572
|
continue;
|
|
8065
9573
|
}
|
|
8066
9574
|
if (entry.isFile()) {
|
|
8067
|
-
await
|
|
9575
|
+
await import_node_fs9.promises.copyFile(srcPath, destPath);
|
|
8068
9576
|
}
|
|
8069
9577
|
}
|
|
8070
9578
|
}
|
|
8071
9579
|
async function copyDirIfExists(source, destination) {
|
|
8072
9580
|
let stats = null;
|
|
8073
9581
|
try {
|
|
8074
|
-
stats = await
|
|
9582
|
+
stats = await import_node_fs9.promises.stat(source);
|
|
8075
9583
|
} catch (error) {
|
|
8076
9584
|
if (error.code === "ENOENT") {
|
|
8077
9585
|
return;
|
|
@@ -8081,10 +9589,10 @@ async function copyDirIfExists(source, destination) {
|
|
|
8081
9589
|
if (!stats.isDirectory()) {
|
|
8082
9590
|
return;
|
|
8083
9591
|
}
|
|
8084
|
-
await
|
|
8085
|
-
await
|
|
9592
|
+
await import_node_fs9.promises.rm(destination, { recursive: true, force: true });
|
|
9593
|
+
await import_node_fs9.promises.mkdir(import_node_path15.default.dirname(destination), { recursive: true });
|
|
8086
9594
|
try {
|
|
8087
|
-
await
|
|
9595
|
+
await import_node_fs9.promises.cp(source, destination, {
|
|
8088
9596
|
recursive: true,
|
|
8089
9597
|
errorOnExist: false,
|
|
8090
9598
|
force: true,
|
|
@@ -8101,7 +9609,7 @@ async function copyDirIfExists(source, destination) {
|
|
|
8101
9609
|
}
|
|
8102
9610
|
async function copyFileIfExists(source, destination, options) {
|
|
8103
9611
|
try {
|
|
8104
|
-
const sourceStat = await
|
|
9612
|
+
const sourceStat = await import_node_fs9.promises.stat(source);
|
|
8105
9613
|
if (!sourceStat.isFile()) {
|
|
8106
9614
|
return;
|
|
8107
9615
|
}
|
|
@@ -8112,7 +9620,7 @@ async function copyFileIfExists(source, destination, options) {
|
|
|
8112
9620
|
throw error;
|
|
8113
9621
|
}
|
|
8114
9622
|
try {
|
|
8115
|
-
const destinationStat = await
|
|
9623
|
+
const destinationStat = await import_node_fs9.promises.stat(destination);
|
|
8116
9624
|
if (destinationStat.isFile()) {
|
|
8117
9625
|
return;
|
|
8118
9626
|
}
|
|
@@ -8121,17 +9629,17 @@ async function copyFileIfExists(source, destination, options) {
|
|
|
8121
9629
|
throw error;
|
|
8122
9630
|
}
|
|
8123
9631
|
}
|
|
8124
|
-
await
|
|
8125
|
-
await
|
|
9632
|
+
await import_node_fs9.promises.mkdir(import_node_path15.default.dirname(destination), { recursive: true });
|
|
9633
|
+
await import_node_fs9.promises.copyFile(source, destination);
|
|
8126
9634
|
if (typeof options?.mode === "number") {
|
|
8127
|
-
await
|
|
9635
|
+
await import_node_fs9.promises.chmod(destination, options.mode).catch(() => void 0);
|
|
8128
9636
|
}
|
|
8129
9637
|
}
|
|
8130
9638
|
async function cleanupCopiedSkillsMetadata(skillsDir) {
|
|
8131
|
-
await removeIfExists(
|
|
9639
|
+
await removeIfExists(import_node_path15.default.join(skillsDir, LEGACY_SKILLS_REPOS_FILE));
|
|
8132
9640
|
let entries = [];
|
|
8133
9641
|
try {
|
|
8134
|
-
entries = await
|
|
9642
|
+
entries = await import_node_fs9.promises.readdir(skillsDir, { withFileTypes: true });
|
|
8135
9643
|
} catch {
|
|
8136
9644
|
return;
|
|
8137
9645
|
}
|
|
@@ -8139,30 +9647,30 @@ async function cleanupCopiedSkillsMetadata(skillsDir) {
|
|
|
8139
9647
|
if (!entry.isDirectory() || entry.name.startsWith(".")) {
|
|
8140
9648
|
continue;
|
|
8141
9649
|
}
|
|
8142
|
-
await removeIfExists(
|
|
9650
|
+
await removeIfExists(import_node_path15.default.join(skillsDir, entry.name, LEGACY_SKILL_MANIFEST));
|
|
8143
9651
|
}
|
|
8144
9652
|
}
|
|
8145
9653
|
async function migrateLegacyDirectoriesToUserData() {
|
|
8146
9654
|
const userDataDir = getUserDataDir();
|
|
8147
9655
|
const legacyCodexDir = resolveCodexDir();
|
|
8148
9656
|
await copyDirIfExists(
|
|
8149
|
-
|
|
8150
|
-
|
|
9657
|
+
import_node_path15.default.join(legacyCodexDir, LEGACY_PROFILE_HOMES_DIR),
|
|
9658
|
+
import_node_path15.default.join(userDataDir, LEGACY_PROFILE_HOMES_DIR)
|
|
8151
9659
|
);
|
|
8152
9660
|
await copyDirIfExists(
|
|
8153
|
-
|
|
8154
|
-
|
|
9661
|
+
import_node_path15.default.join(legacyCodexDir, LEGACY_SKILLS_DIR),
|
|
9662
|
+
import_node_path15.default.join(userDataDir, LEGACY_SKILLS_DIR)
|
|
8155
9663
|
);
|
|
8156
9664
|
await copyDirIfExists(
|
|
8157
|
-
|
|
8158
|
-
|
|
9665
|
+
import_node_path15.default.join(legacyCodexDir, LEGACY_SKILL_CACHE_DIR),
|
|
9666
|
+
import_node_path15.default.join(userDataDir, LEGACY_SKILL_CACHE_DIR)
|
|
8159
9667
|
);
|
|
8160
9668
|
await copyFileIfExists(
|
|
8161
|
-
|
|
8162
|
-
|
|
9669
|
+
import_node_path15.default.join(legacyCodexDir, LEGACY_LICENSE_SECRET_FILE2),
|
|
9670
|
+
import_node_path15.default.join(userDataDir, LEGACY_LICENSE_SECRET_FILE2),
|
|
8163
9671
|
{ mode: 384 }
|
|
8164
9672
|
);
|
|
8165
|
-
await cleanupCopiedSkillsMetadata(
|
|
9673
|
+
await cleanupCopiedSkillsMetadata(import_node_path15.default.join(userDataDir, LEGACY_SKILLS_DIR));
|
|
8166
9674
|
}
|
|
8167
9675
|
function pickAutoRoll(raw) {
|
|
8168
9676
|
if (!raw) {
|
|
@@ -8186,16 +9694,16 @@ function parseLegacyLicense(raw) {
|
|
|
8186
9694
|
signature: null
|
|
8187
9695
|
};
|
|
8188
9696
|
}
|
|
8189
|
-
const statusCandidate =
|
|
9697
|
+
const statusCandidate = asString4(raw.status);
|
|
8190
9698
|
const status = ["inactive", "active", "grace", "error"].includes(statusCandidate ?? "") ? statusCandidate : "inactive";
|
|
8191
9699
|
return {
|
|
8192
|
-
licenseKey:
|
|
8193
|
-
purchaseEmail:
|
|
8194
|
-
lastVerifiedAt:
|
|
8195
|
-
nextCheckAt:
|
|
8196
|
-
lastVerificationError:
|
|
9700
|
+
licenseKey: asString4(raw.licenseKey ?? raw.license_key),
|
|
9701
|
+
purchaseEmail: asString4(raw.purchaseEmail ?? raw.purchase_email),
|
|
9702
|
+
lastVerifiedAt: asString4(raw.lastVerifiedAt ?? raw.last_verified_at),
|
|
9703
|
+
nextCheckAt: asString4(raw.nextCheckAt ?? raw.next_check_at),
|
|
9704
|
+
lastVerificationError: asString4(raw.lastVerificationError ?? raw.last_verification_error),
|
|
8197
9705
|
status,
|
|
8198
|
-
signature:
|
|
9706
|
+
signature: asString4(raw.signature)
|
|
8199
9707
|
};
|
|
8200
9708
|
}
|
|
8201
9709
|
function parseLegacyProfileRecord(name, raw) {
|
|
@@ -8218,16 +9726,16 @@ function parseLegacyProfileRecord(name, raw) {
|
|
|
8218
9726
|
}
|
|
8219
9727
|
return {
|
|
8220
9728
|
name,
|
|
8221
|
-
displayName:
|
|
9729
|
+
displayName: asString4(raw.displayName ?? raw.display_name) ?? name,
|
|
8222
9730
|
data,
|
|
8223
9731
|
metadata: isRecord6(raw.metadata) ? raw.metadata : void 0,
|
|
8224
|
-
accountId:
|
|
8225
|
-
workspaceId:
|
|
8226
|
-
workspaceName:
|
|
8227
|
-
email:
|
|
8228
|
-
authMethod:
|
|
8229
|
-
createdAt:
|
|
8230
|
-
updatedAt:
|
|
9732
|
+
accountId: asString4(raw.accountId ?? raw.account_id),
|
|
9733
|
+
workspaceId: asString4(raw.workspaceId ?? raw.workspace_id),
|
|
9734
|
+
workspaceName: asString4(raw.workspaceName ?? raw.workspace_name),
|
|
9735
|
+
email: asString4(raw.email),
|
|
9736
|
+
authMethod: asString4(raw.authMethod ?? raw.auth_method),
|
|
9737
|
+
createdAt: asString4(raw.createdAt ?? raw.created_at),
|
|
9738
|
+
updatedAt: asString4(raw.updatedAt ?? raw.updated_at)
|
|
8231
9739
|
};
|
|
8232
9740
|
}
|
|
8233
9741
|
async function loadLegacySettingsPatch() {
|
|
@@ -8240,9 +9748,9 @@ async function loadLegacySettingsPatch() {
|
|
|
8240
9748
|
const license = parseLegacyLicense(raw.license ?? raw.license_data ?? raw.license_state);
|
|
8241
9749
|
return {
|
|
8242
9750
|
app: {
|
|
8243
|
-
lastAppVersion:
|
|
8244
|
-
pendingUpdateVersion:
|
|
8245
|
-
lastProfileName:
|
|
9751
|
+
lastAppVersion: asString4(raw.lastAppVersion ?? raw.last_app_version),
|
|
9752
|
+
pendingUpdateVersion: asString4(raw.pendingUpdateVersion ?? raw.pending_update_version),
|
|
9753
|
+
lastProfileName: asString4(raw.lastProfileName ?? raw.last_profile_name)
|
|
8246
9754
|
},
|
|
8247
9755
|
license,
|
|
8248
9756
|
autoRoll: autoRoll ? {
|
|
@@ -8260,15 +9768,15 @@ async function loadLegacySyncPatch() {
|
|
|
8260
9768
|
}
|
|
8261
9769
|
return {
|
|
8262
9770
|
sync: {
|
|
8263
|
-
lastPushAt:
|
|
8264
|
-
lastPullAt:
|
|
8265
|
-
lastError:
|
|
8266
|
-
remoteUpdatedAt:
|
|
9771
|
+
lastPushAt: asString4(raw.lastPushAt),
|
|
9772
|
+
lastPullAt: asString4(raw.lastPullAt),
|
|
9773
|
+
lastError: asString4(raw.lastError),
|
|
9774
|
+
remoteUpdatedAt: asString4(raw.remoteUpdatedAt)
|
|
8267
9775
|
}
|
|
8268
9776
|
};
|
|
8269
9777
|
}
|
|
8270
9778
|
async function loadLegacyAppSettingsParityPatch() {
|
|
8271
|
-
const filePath =
|
|
9779
|
+
const filePath = import_node_path15.default.join(getUserDataDir(), LEGACY_APP_SETTINGS_PARITY_FILE);
|
|
8272
9780
|
const raw = await readJsonFileIfExists(filePath);
|
|
8273
9781
|
if (!isRecord6(raw)) {
|
|
8274
9782
|
return {};
|
|
@@ -8281,7 +9789,7 @@ async function loadLegacyProfilesPatch() {
|
|
|
8281
9789
|
const root = resolveLegacyPath(LEGACY_PROFILE_HOMES_DIR);
|
|
8282
9790
|
let entries = [];
|
|
8283
9791
|
try {
|
|
8284
|
-
entries = await
|
|
9792
|
+
entries = await import_node_fs9.promises.readdir(root, { withFileTypes: true });
|
|
8285
9793
|
} catch (error) {
|
|
8286
9794
|
if (error.code === "ENOENT") {
|
|
8287
9795
|
return {};
|
|
@@ -8293,7 +9801,7 @@ async function loadLegacyProfilesPatch() {
|
|
|
8293
9801
|
if (!entry.isDirectory() || entry.name.startsWith(".")) {
|
|
8294
9802
|
continue;
|
|
8295
9803
|
}
|
|
8296
|
-
const profileFile =
|
|
9804
|
+
const profileFile = import_node_path15.default.join(root, entry.name, "profile.json");
|
|
8297
9805
|
const raw = await readJsonFileIfExists(profileFile);
|
|
8298
9806
|
if (!raw) {
|
|
8299
9807
|
continue;
|
|
@@ -8313,28 +9821,28 @@ function parseSkillInstallMetadata(raw) {
|
|
|
8313
9821
|
if (!isRecord6(raw)) {
|
|
8314
9822
|
return null;
|
|
8315
9823
|
}
|
|
8316
|
-
const id =
|
|
9824
|
+
const id = asString4(raw.id);
|
|
8317
9825
|
if (!id) {
|
|
8318
9826
|
return null;
|
|
8319
9827
|
}
|
|
8320
9828
|
return {
|
|
8321
9829
|
id,
|
|
8322
|
-
repo:
|
|
8323
|
-
repoPath:
|
|
8324
|
-
sourceLabel:
|
|
9830
|
+
repo: asString4(raw.repo),
|
|
9831
|
+
repoPath: asString4(raw.repoPath),
|
|
9832
|
+
sourceLabel: asString4(raw.sourceLabel),
|
|
8325
9833
|
sourceType: raw.sourceType === "official" || raw.sourceType === "community" || raw.sourceType === "local" ? raw.sourceType : void 0,
|
|
8326
|
-
viewUrl:
|
|
8327
|
-
createdAt:
|
|
9834
|
+
viewUrl: asString4(raw.viewUrl),
|
|
9835
|
+
createdAt: asString4(raw.createdAt)
|
|
8328
9836
|
};
|
|
8329
9837
|
}
|
|
8330
9838
|
async function loadLegacySkillsPatch() {
|
|
8331
9839
|
const skillsRoot = resolveLegacyPath(LEGACY_SKILLS_DIR);
|
|
8332
|
-
const reposPath =
|
|
9840
|
+
const reposPath = import_node_path15.default.join(skillsRoot, LEGACY_SKILLS_REPOS_FILE);
|
|
8333
9841
|
const reposRaw = await readJsonFileIfExists(reposPath);
|
|
8334
9842
|
const installsBySlug = {};
|
|
8335
9843
|
let dirEntries = [];
|
|
8336
9844
|
try {
|
|
8337
|
-
dirEntries = await
|
|
9845
|
+
dirEntries = await import_node_fs9.promises.readdir(skillsRoot, { withFileTypes: true });
|
|
8338
9846
|
} catch (error) {
|
|
8339
9847
|
if (error.code !== "ENOENT") {
|
|
8340
9848
|
throw error;
|
|
@@ -8347,7 +9855,7 @@ async function loadLegacySkillsPatch() {
|
|
|
8347
9855
|
if (entry.name.startsWith(".")) {
|
|
8348
9856
|
continue;
|
|
8349
9857
|
}
|
|
8350
|
-
const manifestPath =
|
|
9858
|
+
const manifestPath = import_node_path15.default.join(skillsRoot, entry.name, LEGACY_SKILL_MANIFEST);
|
|
8351
9859
|
const manifestRaw = await readJsonFileIfExists(manifestPath);
|
|
8352
9860
|
const parsed = parseSkillInstallMetadata(manifestRaw);
|
|
8353
9861
|
if (!parsed) {
|
|
@@ -8457,29 +9965,29 @@ function mergeLegacyLocalStoragePatch(payload) {
|
|
|
8457
9965
|
};
|
|
8458
9966
|
}
|
|
8459
9967
|
async function removeIfExists(target) {
|
|
8460
|
-
await
|
|
9968
|
+
await import_node_fs9.promises.rm(target, { recursive: true, force: true });
|
|
8461
9969
|
}
|
|
8462
9970
|
async function cleanupLegacyCanonicalSources() {
|
|
8463
9971
|
const homeDir = resolveHomeDir2();
|
|
8464
9972
|
await removeIfExists(resolveLegacyAppStatePath());
|
|
8465
|
-
await removeIfExists(
|
|
8466
|
-
await removeIfExists(
|
|
9973
|
+
await removeIfExists(import_node_path15.default.join(getUserDataDir(), LEGACY_APP_SETTINGS_PARITY_FILE));
|
|
9974
|
+
await removeIfExists(import_node_path15.default.join(homeDir, SQLITE_STORAGE_DIR));
|
|
8467
9975
|
await removeIfExists(resolveLegacyPath(LEGACY_SETTINGS_FILE));
|
|
8468
9976
|
await removeIfExists(resolveLegacyPath(LEGACY_SETTINGS_BACKUP_FILE));
|
|
8469
9977
|
await removeIfExists(resolveLegacyPath(LEGACY_SYNC_STATE_FILE));
|
|
8470
|
-
await removeIfExists(resolveLegacyPath(
|
|
9978
|
+
await removeIfExists(resolveLegacyPath(LEGACY_LICENSE_SECRET_FILE2));
|
|
8471
9979
|
await removeIfExists(resolveLegacyPath(LEGACY_SKILLS_DIR, LEGACY_SKILLS_REPOS_FILE));
|
|
8472
9980
|
const profileHomesRoot = resolveLegacyPath(LEGACY_PROFILE_HOMES_DIR);
|
|
8473
9981
|
try {
|
|
8474
|
-
const profileHomes = await
|
|
9982
|
+
const profileHomes = await import_node_fs9.promises.readdir(profileHomesRoot, { withFileTypes: true });
|
|
8475
9983
|
for (const profileHome of profileHomes) {
|
|
8476
9984
|
if (!profileHome.isDirectory()) {
|
|
8477
9985
|
continue;
|
|
8478
9986
|
}
|
|
8479
|
-
const profileDir =
|
|
9987
|
+
const profileDir = import_node_path15.default.join(profileHomesRoot, profileHome.name);
|
|
8480
9988
|
let files = [];
|
|
8481
9989
|
try {
|
|
8482
|
-
files = await
|
|
9990
|
+
files = await import_node_fs9.promises.readdir(profileDir);
|
|
8483
9991
|
} catch {
|
|
8484
9992
|
continue;
|
|
8485
9993
|
}
|
|
@@ -8487,7 +9995,7 @@ async function cleanupLegacyCanonicalSources() {
|
|
|
8487
9995
|
if (!file.startsWith("profile.json")) {
|
|
8488
9996
|
continue;
|
|
8489
9997
|
}
|
|
8490
|
-
await removeIfExists(
|
|
9998
|
+
await removeIfExists(import_node_path15.default.join(profileDir, file));
|
|
8491
9999
|
}
|
|
8492
10000
|
}
|
|
8493
10001
|
} catch (error) {
|
|
@@ -8500,12 +10008,12 @@ async function cleanupLegacyCanonicalSources() {
|
|
|
8500
10008
|
}
|
|
8501
10009
|
const skillsRoot = resolveLegacyPath(LEGACY_SKILLS_DIR);
|
|
8502
10010
|
try {
|
|
8503
|
-
const skillDirs = await
|
|
10011
|
+
const skillDirs = await import_node_fs9.promises.readdir(skillsRoot, { withFileTypes: true });
|
|
8504
10012
|
for (const entry of skillDirs) {
|
|
8505
10013
|
if (!entry.isDirectory() || entry.name.startsWith(".")) {
|
|
8506
10014
|
continue;
|
|
8507
10015
|
}
|
|
8508
|
-
await removeIfExists(
|
|
10016
|
+
await removeIfExists(import_node_path15.default.join(skillsRoot, entry.name, LEGACY_SKILL_MANIFEST));
|
|
8509
10017
|
}
|
|
8510
10018
|
} catch (error) {
|
|
8511
10019
|
if (!isMissingPathError(error)) {
|
|
@@ -8718,24 +10226,29 @@ async function ensureCliStorageReady() {
|
|
|
8718
10226
|
}
|
|
8719
10227
|
|
|
8720
10228
|
// src/app/main.ts
|
|
8721
|
-
var VERSION = true ? "3.
|
|
10229
|
+
var VERSION = true ? "3.6.0" : "0.0.0";
|
|
8722
10230
|
async function runCli() {
|
|
8723
10231
|
const args = process.argv.slice(2);
|
|
8724
10232
|
if (args.length === 0) {
|
|
8725
10233
|
printHelp(VERSION);
|
|
8726
10234
|
return;
|
|
8727
10235
|
}
|
|
8728
|
-
if (args[0]?.startsWith("-") && (
|
|
10236
|
+
if (args[0]?.startsWith("-") && (hasFlag(args, "--help") || hasFlag(args, "-h"))) {
|
|
8729
10237
|
printHelp(VERSION);
|
|
8730
10238
|
return;
|
|
8731
10239
|
}
|
|
8732
|
-
if (args[0]?.startsWith("-") && (
|
|
10240
|
+
if (args[0]?.startsWith("-") && (hasFlag(args, "--version") || hasFlag(args, "-v"))) {
|
|
8733
10241
|
console.log(VERSION);
|
|
8734
10242
|
return;
|
|
8735
10243
|
}
|
|
8736
10244
|
const command = args[0];
|
|
8737
10245
|
const rest = args.slice(1);
|
|
8738
10246
|
switch (command) {
|
|
10247
|
+
case "account-pool":
|
|
10248
|
+
case "accounts-pool":
|
|
10249
|
+
await ensureCliStorageReady();
|
|
10250
|
+
await handleAccountPoolCommand(rest, VERSION);
|
|
10251
|
+
return;
|
|
8739
10252
|
case "profile":
|
|
8740
10253
|
await ensureCliStorageReady();
|
|
8741
10254
|
await handleProfileCommand(rest, VERSION);
|