codexapp 0.1.48 → 0.1.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/index-Batjlg_n.js +1428 -0
- package/dist/assets/index-uy5QW_sJ.css +1 -0
- package/dist/index.html +2 -2
- package/dist-cli/index.js +233 -2
- package/dist-cli/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/assets/index-C55jtDWV.css +0 -1
- package/dist/assets/index-DhcIXLYw.js +0 -1428
package/dist-cli/index.js
CHANGED
|
@@ -1273,6 +1273,53 @@ function scoreFileCandidate(path, query) {
|
|
|
1273
1273
|
if (lowerPath.includes(lowerQuery)) return 4;
|
|
1274
1274
|
return 10;
|
|
1275
1275
|
}
|
|
1276
|
+
function decodeHtmlEntities(value) {
|
|
1277
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(///gi, "/");
|
|
1278
|
+
}
|
|
1279
|
+
function stripHtml(value) {
|
|
1280
|
+
return decodeHtmlEntities(value.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim());
|
|
1281
|
+
}
|
|
1282
|
+
function parseGithubTrendingHtml(html, limit) {
|
|
1283
|
+
const rows = html.match(/<article[\s\S]*?<\/article>/g) ?? [];
|
|
1284
|
+
const items = [];
|
|
1285
|
+
let seq = Date.now();
|
|
1286
|
+
for (const row of rows) {
|
|
1287
|
+
const hrefMatch = row.match(/href="\/([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+)"/);
|
|
1288
|
+
if (!hrefMatch) continue;
|
|
1289
|
+
const fullName = hrefMatch[1] ?? "";
|
|
1290
|
+
if (!fullName || items.some((item) => item.fullName === fullName)) continue;
|
|
1291
|
+
const descriptionMatch = row.match(/<p[^>]*>([\s\S]*?)<\/p>/);
|
|
1292
|
+
const languageMatch = row.match(/programmingLanguage[^>]*>\s*([\s\S]*?)\s*<\/span>/);
|
|
1293
|
+
const starsMatch = row.match(/href="\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+\/stargazers"[\s\S]*?>([\s\S]*?)<\/a>/);
|
|
1294
|
+
const starsText = stripHtml(starsMatch?.[1] ?? "").replace(/,/g, "");
|
|
1295
|
+
const stars = Number.parseInt(starsText, 10);
|
|
1296
|
+
items.push({
|
|
1297
|
+
id: seq,
|
|
1298
|
+
fullName,
|
|
1299
|
+
url: `https://github.com/${fullName}`,
|
|
1300
|
+
description: stripHtml(descriptionMatch?.[1] ?? ""),
|
|
1301
|
+
language: stripHtml(languageMatch?.[1] ?? ""),
|
|
1302
|
+
stars: Number.isFinite(stars) ? stars : 0
|
|
1303
|
+
});
|
|
1304
|
+
seq += 1;
|
|
1305
|
+
if (items.length >= limit) break;
|
|
1306
|
+
}
|
|
1307
|
+
return items;
|
|
1308
|
+
}
|
|
1309
|
+
async function fetchGithubTrending(since, limit) {
|
|
1310
|
+
const endpoint = `https://github.com/trending?since=${since}`;
|
|
1311
|
+
const response = await fetch(endpoint, {
|
|
1312
|
+
headers: {
|
|
1313
|
+
"User-Agent": "codex-web-local",
|
|
1314
|
+
Accept: "text/html"
|
|
1315
|
+
}
|
|
1316
|
+
});
|
|
1317
|
+
if (!response.ok) {
|
|
1318
|
+
throw new Error(`GitHub trending fetch failed (${response.status})`);
|
|
1319
|
+
}
|
|
1320
|
+
const html = await response.text();
|
|
1321
|
+
return parseGithubTrendingHtml(html, limit);
|
|
1322
|
+
}
|
|
1276
1323
|
async function listFilesWithRipgrep(cwd) {
|
|
1277
1324
|
return await new Promise((resolve3, reject) => {
|
|
1278
1325
|
const proc = spawn2("rg", ["--files", "--hidden", "-g", "!.git", "-g", "!node_modules"], {
|
|
@@ -1380,6 +1427,33 @@ async function runCommandCapture(command, args, options = {}) {
|
|
|
1380
1427
|
});
|
|
1381
1428
|
});
|
|
1382
1429
|
}
|
|
1430
|
+
async function runCommandWithOutput2(command, args, options = {}) {
|
|
1431
|
+
return await new Promise((resolve3, reject) => {
|
|
1432
|
+
const proc = spawn2(command, args, {
|
|
1433
|
+
cwd: options.cwd,
|
|
1434
|
+
env: process.env,
|
|
1435
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1436
|
+
});
|
|
1437
|
+
let stdout = "";
|
|
1438
|
+
let stderr = "";
|
|
1439
|
+
proc.stdout.on("data", (chunk) => {
|
|
1440
|
+
stdout += chunk.toString();
|
|
1441
|
+
});
|
|
1442
|
+
proc.stderr.on("data", (chunk) => {
|
|
1443
|
+
stderr += chunk.toString();
|
|
1444
|
+
});
|
|
1445
|
+
proc.on("error", reject);
|
|
1446
|
+
proc.on("close", (code) => {
|
|
1447
|
+
if (code === 0) {
|
|
1448
|
+
resolve3(stdout.trim());
|
|
1449
|
+
return;
|
|
1450
|
+
}
|
|
1451
|
+
const details = [stderr.trim(), stdout.trim()].filter(Boolean).join("\n");
|
|
1452
|
+
const suffix = details.length > 0 ? `: ${details}` : "";
|
|
1453
|
+
reject(new Error(`Command failed (${command} ${args.join(" ")})${suffix}`));
|
|
1454
|
+
});
|
|
1455
|
+
});
|
|
1456
|
+
}
|
|
1383
1457
|
function normalizeStringArray(value) {
|
|
1384
1458
|
if (!Array.isArray(value)) return [];
|
|
1385
1459
|
const normalized = [];
|
|
@@ -1400,6 +1474,30 @@ function normalizeStringRecord(value) {
|
|
|
1400
1474
|
}
|
|
1401
1475
|
return next;
|
|
1402
1476
|
}
|
|
1477
|
+
function normalizeCommitMessage(value) {
|
|
1478
|
+
if (typeof value !== "string") return "";
|
|
1479
|
+
const normalized = value.replace(/\r\n?/gu, "\n").split("\n").map((line) => line.trim()).filter((line) => line.length > 0).join("\n").trim();
|
|
1480
|
+
return normalized.slice(0, 2e3);
|
|
1481
|
+
}
|
|
1482
|
+
async function hasGitWorkingTreeChanges(cwd) {
|
|
1483
|
+
const status = await runCommandWithOutput2("git", ["status", "--porcelain"], { cwd });
|
|
1484
|
+
return status.trim().length > 0;
|
|
1485
|
+
}
|
|
1486
|
+
async function findCommitByExactMessage(cwd, message) {
|
|
1487
|
+
const normalizedTarget = normalizeCommitMessage(message);
|
|
1488
|
+
if (!normalizedTarget) return "";
|
|
1489
|
+
const raw = await runCommandWithOutput2("git", ["log", "--format=%H%x1f%B%x1e"], { cwd });
|
|
1490
|
+
const entries = raw.split("");
|
|
1491
|
+
for (const entry of entries) {
|
|
1492
|
+
if (!entry.trim()) continue;
|
|
1493
|
+
const [shaRaw, bodyRaw] = entry.split("");
|
|
1494
|
+
const sha = (shaRaw ?? "").trim();
|
|
1495
|
+
const body = normalizeCommitMessage(bodyRaw ?? "");
|
|
1496
|
+
if (!sha) continue;
|
|
1497
|
+
if (body === normalizedTarget) return sha;
|
|
1498
|
+
}
|
|
1499
|
+
return "";
|
|
1500
|
+
}
|
|
1403
1501
|
function getCodexAuthPath() {
|
|
1404
1502
|
return join3(getCodexHomeDir2(), "auth.json");
|
|
1405
1503
|
}
|
|
@@ -2251,6 +2349,19 @@ function createCodexBridgeMiddleware() {
|
|
|
2251
2349
|
setJson2(res, 200, { data: { path: homedir3() } });
|
|
2252
2350
|
return;
|
|
2253
2351
|
}
|
|
2352
|
+
if (req.method === "GET" && url.pathname === "/codex-api/github-trending") {
|
|
2353
|
+
const sinceRaw = (url.searchParams.get("since") ?? "").trim().toLowerCase();
|
|
2354
|
+
const since = sinceRaw === "weekly" ? "weekly" : sinceRaw === "monthly" ? "monthly" : "daily";
|
|
2355
|
+
const limitRaw = Number.parseInt((url.searchParams.get("limit") ?? "6").trim(), 10);
|
|
2356
|
+
const limit = Number.isFinite(limitRaw) ? Math.max(1, Math.min(10, limitRaw)) : 6;
|
|
2357
|
+
try {
|
|
2358
|
+
const data = await fetchGithubTrending(since, limit);
|
|
2359
|
+
setJson2(res, 200, { data });
|
|
2360
|
+
} catch (error) {
|
|
2361
|
+
setJson2(res, 502, { error: getErrorMessage2(error, "Failed to fetch GitHub trending") });
|
|
2362
|
+
}
|
|
2363
|
+
return;
|
|
2364
|
+
}
|
|
2254
2365
|
if (req.method === "POST" && url.pathname === "/codex-api/worktree/create") {
|
|
2255
2366
|
const payload = asRecord2(await readJsonBody(req));
|
|
2256
2367
|
const rawSourceCwd = typeof payload?.sourceCwd === "string" ? payload.sourceCwd.trim() : "";
|
|
@@ -2321,6 +2432,99 @@ function createCodexBridgeMiddleware() {
|
|
|
2321
2432
|
}
|
|
2322
2433
|
return;
|
|
2323
2434
|
}
|
|
2435
|
+
if (req.method === "POST" && url.pathname === "/codex-api/worktree/auto-commit") {
|
|
2436
|
+
const payload = asRecord2(await readJsonBody(req));
|
|
2437
|
+
const rawCwd = typeof payload?.cwd === "string" ? payload.cwd.trim() : "";
|
|
2438
|
+
const commitMessage = normalizeCommitMessage(payload?.message);
|
|
2439
|
+
if (!rawCwd) {
|
|
2440
|
+
setJson2(res, 400, { error: "Missing cwd" });
|
|
2441
|
+
return;
|
|
2442
|
+
}
|
|
2443
|
+
if (!commitMessage) {
|
|
2444
|
+
setJson2(res, 400, { error: "Missing message" });
|
|
2445
|
+
return;
|
|
2446
|
+
}
|
|
2447
|
+
const cwd = isAbsolute(rawCwd) ? rawCwd : resolve(rawCwd);
|
|
2448
|
+
try {
|
|
2449
|
+
const cwdInfo = await stat2(cwd);
|
|
2450
|
+
if (!cwdInfo.isDirectory()) {
|
|
2451
|
+
setJson2(res, 400, { error: "cwd is not a directory" });
|
|
2452
|
+
return;
|
|
2453
|
+
}
|
|
2454
|
+
} catch {
|
|
2455
|
+
setJson2(res, 404, { error: "cwd does not exist" });
|
|
2456
|
+
return;
|
|
2457
|
+
}
|
|
2458
|
+
try {
|
|
2459
|
+
await runCommandCapture("git", ["rev-parse", "--is-inside-work-tree"], { cwd });
|
|
2460
|
+
const beforeStatus = await runCommandWithOutput2("git", ["status", "--porcelain"], { cwd });
|
|
2461
|
+
if (!beforeStatus.trim()) {
|
|
2462
|
+
setJson2(res, 200, { data: { committed: false } });
|
|
2463
|
+
return;
|
|
2464
|
+
}
|
|
2465
|
+
await runCommand2("git", ["add", "-A"], { cwd });
|
|
2466
|
+
const stagedStatus = await runCommandWithOutput2("git", ["diff", "--cached", "--name-only"], { cwd });
|
|
2467
|
+
if (!stagedStatus.trim()) {
|
|
2468
|
+
setJson2(res, 200, { data: { committed: false } });
|
|
2469
|
+
return;
|
|
2470
|
+
}
|
|
2471
|
+
await runCommand2("git", ["commit", "-m", commitMessage], { cwd });
|
|
2472
|
+
setJson2(res, 200, { data: { committed: true } });
|
|
2473
|
+
} catch (error) {
|
|
2474
|
+
setJson2(res, 500, { error: getErrorMessage2(error, "Failed to auto-commit worktree changes") });
|
|
2475
|
+
}
|
|
2476
|
+
return;
|
|
2477
|
+
}
|
|
2478
|
+
if (req.method === "POST" && url.pathname === "/codex-api/worktree/rollback-to-message") {
|
|
2479
|
+
const payload = asRecord2(await readJsonBody(req));
|
|
2480
|
+
const rawCwd = typeof payload?.cwd === "string" ? payload.cwd.trim() : "";
|
|
2481
|
+
const commitMessage = normalizeCommitMessage(payload?.message);
|
|
2482
|
+
if (!rawCwd) {
|
|
2483
|
+
setJson2(res, 400, { error: "Missing cwd" });
|
|
2484
|
+
return;
|
|
2485
|
+
}
|
|
2486
|
+
if (!commitMessage) {
|
|
2487
|
+
setJson2(res, 400, { error: "Missing message" });
|
|
2488
|
+
return;
|
|
2489
|
+
}
|
|
2490
|
+
const cwd = isAbsolute(rawCwd) ? rawCwd : resolve(rawCwd);
|
|
2491
|
+
try {
|
|
2492
|
+
const cwdInfo = await stat2(cwd);
|
|
2493
|
+
if (!cwdInfo.isDirectory()) {
|
|
2494
|
+
setJson2(res, 400, { error: "cwd is not a directory" });
|
|
2495
|
+
return;
|
|
2496
|
+
}
|
|
2497
|
+
} catch {
|
|
2498
|
+
setJson2(res, 404, { error: "cwd does not exist" });
|
|
2499
|
+
return;
|
|
2500
|
+
}
|
|
2501
|
+
try {
|
|
2502
|
+
await runCommandCapture("git", ["rev-parse", "--is-inside-work-tree"], { cwd });
|
|
2503
|
+
const commitSha = await findCommitByExactMessage(cwd, commitMessage);
|
|
2504
|
+
if (!commitSha) {
|
|
2505
|
+
setJson2(res, 404, { error: "No matching commit found for this user message" });
|
|
2506
|
+
return;
|
|
2507
|
+
}
|
|
2508
|
+
let resetTargetSha = "";
|
|
2509
|
+
try {
|
|
2510
|
+
resetTargetSha = await runCommandCapture("git", ["rev-parse", `${commitSha}^`], { cwd });
|
|
2511
|
+
} catch {
|
|
2512
|
+
setJson2(res, 409, { error: "Cannot rollback: matched commit has no parent commit" });
|
|
2513
|
+
return;
|
|
2514
|
+
}
|
|
2515
|
+
let stashed = false;
|
|
2516
|
+
if (await hasGitWorkingTreeChanges(cwd)) {
|
|
2517
|
+
const stashMessage = `codex-auto-stash-before-rollback-${Date.now()}`;
|
|
2518
|
+
await runCommand2("git", ["stash", "push", "-u", "-m", stashMessage], { cwd });
|
|
2519
|
+
stashed = true;
|
|
2520
|
+
}
|
|
2521
|
+
await runCommand2("git", ["reset", "--hard", resetTargetSha], { cwd });
|
|
2522
|
+
setJson2(res, 200, { data: { reset: true, commitSha, resetTargetSha, stashed } });
|
|
2523
|
+
} catch (error) {
|
|
2524
|
+
setJson2(res, 500, { error: getErrorMessage2(error, "Failed to rollback worktree to user message commit") });
|
|
2525
|
+
}
|
|
2526
|
+
return;
|
|
2527
|
+
}
|
|
2324
2528
|
if (req.method === "PUT" && url.pathname === "/codex-api/workspace-roots-state") {
|
|
2325
2529
|
const payload = await readJsonBody(req);
|
|
2326
2530
|
const record = asRecord2(payload);
|
|
@@ -3185,6 +3389,22 @@ function generatePassword() {
|
|
|
3185
3389
|
// src/cli/index.ts
|
|
3186
3390
|
var program = new Command().name("codexui").description("Web interface for Codex app-server");
|
|
3187
3391
|
var __dirname2 = dirname3(fileURLToPath2(import.meta.url));
|
|
3392
|
+
var hasPromptedCloudflaredInstall = false;
|
|
3393
|
+
function getCodexHomePath() {
|
|
3394
|
+
return process.env.CODEX_HOME?.trim() || join6(homedir4(), ".codex");
|
|
3395
|
+
}
|
|
3396
|
+
function getCloudflaredPromptMarkerPath() {
|
|
3397
|
+
return join6(getCodexHomePath(), ".cloudflared-install-prompted");
|
|
3398
|
+
}
|
|
3399
|
+
function hasPromptedCloudflaredInstallPersisted() {
|
|
3400
|
+
return existsSync5(getCloudflaredPromptMarkerPath());
|
|
3401
|
+
}
|
|
3402
|
+
async function persistCloudflaredInstallPrompted() {
|
|
3403
|
+
const codexHome = getCodexHomePath();
|
|
3404
|
+
mkdirSync(codexHome, { recursive: true });
|
|
3405
|
+
await writeFile4(getCloudflaredPromptMarkerPath(), `${Date.now()}
|
|
3406
|
+
`, "utf8");
|
|
3407
|
+
}
|
|
3188
3408
|
async function readCliVersion() {
|
|
3189
3409
|
try {
|
|
3190
3410
|
const packageJsonPath = join6(__dirname2, "..", "package.json");
|
|
@@ -3286,6 +3506,14 @@ async function ensureCloudflaredInstalledLinux() {
|
|
|
3286
3506
|
return installed;
|
|
3287
3507
|
}
|
|
3288
3508
|
async function shouldInstallCloudflaredInteractively() {
|
|
3509
|
+
if (hasPromptedCloudflaredInstall || hasPromptedCloudflaredInstallPersisted()) {
|
|
3510
|
+
return false;
|
|
3511
|
+
}
|
|
3512
|
+
hasPromptedCloudflaredInstall = true;
|
|
3513
|
+
await persistCloudflaredInstallPrompted();
|
|
3514
|
+
if (process.platform === "win32") {
|
|
3515
|
+
return false;
|
|
3516
|
+
}
|
|
3289
3517
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
3290
3518
|
console.warn("\n[cloudflared] cloudflared is missing and terminal is non-interactive, skipping install.");
|
|
3291
3519
|
return false;
|
|
@@ -3304,6 +3532,9 @@ async function resolveCloudflaredForTunnel() {
|
|
|
3304
3532
|
if (current) {
|
|
3305
3533
|
return current;
|
|
3306
3534
|
}
|
|
3535
|
+
if (process.platform === "win32") {
|
|
3536
|
+
return null;
|
|
3537
|
+
}
|
|
3307
3538
|
const installApproved = await shouldInstallCloudflaredInteractively();
|
|
3308
3539
|
if (!installApproved) {
|
|
3309
3540
|
return null;
|
|
@@ -3311,7 +3542,7 @@ async function resolveCloudflaredForTunnel() {
|
|
|
3311
3542
|
return ensureCloudflaredInstalledLinux();
|
|
3312
3543
|
}
|
|
3313
3544
|
function hasCodexAuth() {
|
|
3314
|
-
const codexHome =
|
|
3545
|
+
const codexHome = getCodexHomePath();
|
|
3315
3546
|
return existsSync5(join6(codexHome, "auth.json"));
|
|
3316
3547
|
}
|
|
3317
3548
|
function ensureCodexInstalled() {
|
|
@@ -3471,7 +3702,7 @@ function listenWithFallback(server, startPort) {
|
|
|
3471
3702
|
});
|
|
3472
3703
|
}
|
|
3473
3704
|
function getCodexGlobalStatePath2() {
|
|
3474
|
-
const codexHome =
|
|
3705
|
+
const codexHome = getCodexHomePath();
|
|
3475
3706
|
return join6(codexHome, ".codex-global-state.json");
|
|
3476
3707
|
}
|
|
3477
3708
|
function normalizeUniqueStrings(value) {
|