codexui-android 0.1.108 → 0.1.109
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/{DirectoryHub-BjsbwciN.css → DirectoryHub-BuG9gBQw.css} +1 -1
- package/dist/assets/DirectoryHub-DvExS-9c.js +2 -0
- package/dist/assets/ReviewPane-AzgWKh85.css +1 -0
- package/dist/assets/ReviewPane-Ckuqkl-S.js +1 -0
- package/dist/assets/{ThreadConversation-BwA4BCDu.css → ThreadConversation-BVrT3Y63.css} +1 -1
- package/dist/assets/{ThreadConversation-DKfW7vC7.js → ThreadConversation-DjMDsjfX.js} +18 -18
- package/dist/assets/{ThreadTerminalPanel-nYqGBGVO.css → ThreadTerminalPanel-BcVTRuCH.css} +1 -1
- package/dist/assets/{ThreadTerminalPanel-CrJKkAvB.js → ThreadTerminalPanel-DWgrDp2e.js} +1 -1
- package/dist/assets/index-AFrh-w0S.css +1 -0
- package/dist/assets/index-ZXDbBStq.js +63 -0
- package/dist/assets/{index.esm-Bh22Jgzp.js → index.esm--s_sgTDk.js} +2 -2
- package/dist/assets/{index.esm-D7eMWmxV.js → index.esm-D_uN0D-X.js} +1 -1
- package/dist/assets/{index.esm-aDL62PRZ.js → index.esm-DvadSfWE.js} +2 -2
- package/dist/index.html +2 -2
- package/dist-cli/index.js +263 -84
- package/dist-cli/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/assets/DirectoryHub-BeJqaP4D.js +0 -2
- package/dist/assets/ReviewPane-BAfo9D_N.css +0 -1
- package/dist/assets/ReviewPane-IP5BRZ-3.js +0 -1
- package/dist/assets/index-DfEYiJAK.js +0 -63
- package/dist/assets/index-KQ_3KeZN.css +0 -1
package/dist-cli/index.js
CHANGED
|
@@ -2047,6 +2047,9 @@ function deriveSkillPathInfo(skillPath, knownPaths = /* @__PURE__ */ new Set())
|
|
|
2047
2047
|
function getSkillsInstallDir() {
|
|
2048
2048
|
return join4(getCodexHomeDir2(), "skills");
|
|
2049
2049
|
}
|
|
2050
|
+
function getSharedSkillsInstallDir() {
|
|
2051
|
+
return join4(getSkillsInstallDir(), "shared_skills");
|
|
2052
|
+
}
|
|
2050
2053
|
var DEFAULT_COMMAND_TIMEOUT_MS = 12e4;
|
|
2051
2054
|
var SKILL_SEARCH_METADATA_LIMIT = 20;
|
|
2052
2055
|
var SKILL_SEARCH_METADATA_CONCURRENCY = 4;
|
|
@@ -2421,9 +2424,8 @@ var startupSyncStatus = {
|
|
|
2421
2424
|
lastSuccessAtIso: "",
|
|
2422
2425
|
lastError: ""
|
|
2423
2426
|
};
|
|
2424
|
-
async function
|
|
2427
|
+
async function scanInstalledSkillsFromDir(skillsDir) {
|
|
2425
2428
|
const map = /* @__PURE__ */ new Map();
|
|
2426
|
-
const skillsDir = getSkillsInstallDir();
|
|
2427
2429
|
try {
|
|
2428
2430
|
const entries = await readdir(skillsDir, { withFileTypes: true });
|
|
2429
2431
|
for (const entry of entries) {
|
|
@@ -2439,6 +2441,9 @@ async function scanInstalledSkillsFromDisk() {
|
|
|
2439
2441
|
}
|
|
2440
2442
|
return map;
|
|
2441
2443
|
}
|
|
2444
|
+
async function scanInstalledSkillsFromDisk() {
|
|
2445
|
+
return await scanInstalledSkillsFromDir(getSkillsInstallDir());
|
|
2446
|
+
}
|
|
2442
2447
|
async function collectInstalledSkillsMap(appServer) {
|
|
2443
2448
|
const installedMap = await scanInstalledSkillsFromDisk();
|
|
2444
2449
|
try {
|
|
@@ -2700,13 +2705,14 @@ async function writeRemoteSkillsManifest(token, repoOwner, repoName, skills) {
|
|
|
2700
2705
|
function toGitHubTokenRemote(repoOwner, repoName, token) {
|
|
2701
2706
|
return `https://x-access-token:${encodeURIComponent(token)}@github.com/${repoOwner}/${repoName}.git`;
|
|
2702
2707
|
}
|
|
2703
|
-
async function ensureSkillsWorkingTreeRepo(repoUrl, branch) {
|
|
2704
|
-
const localDir = getSkillsInstallDir();
|
|
2708
|
+
async function ensureSkillsWorkingTreeRepo(repoUrl, branch, options = {}) {
|
|
2709
|
+
const localDir = options.localDir ?? getSkillsInstallDir();
|
|
2705
2710
|
await mkdir3(localDir, { recursive: true });
|
|
2706
2711
|
const gitDir = join4(localDir, ".git");
|
|
2707
2712
|
let hasGitDir = false;
|
|
2708
2713
|
try {
|
|
2709
|
-
|
|
2714
|
+
const gitDirStat = await lstat(gitDir);
|
|
2715
|
+
hasGitDir = gitDirStat.isDirectory() || gitDirStat.isFile();
|
|
2710
2716
|
} catch {
|
|
2711
2717
|
hasGitDir = false;
|
|
2712
2718
|
}
|
|
@@ -2726,6 +2732,14 @@ async function ensureSkillsWorkingTreeRepo(repoUrl, branch) {
|
|
|
2726
2732
|
await runCommand2("git", ["remote", "set-url", "origin", repoUrl], { cwd: localDir });
|
|
2727
2733
|
}
|
|
2728
2734
|
await runGitFetchWithRefLockRetry(localDir);
|
|
2735
|
+
if (options.overwriteLocalFiles) {
|
|
2736
|
+
await runCommand2("git", ["reset", "--hard"], { cwd: localDir });
|
|
2737
|
+
await runCommand2("git", ["clean", "-fd"], { cwd: localDir });
|
|
2738
|
+
await runCommand2("git", ["checkout", "-B", branch, `origin/${branch}`], { cwd: localDir });
|
|
2739
|
+
await runCommand2("git", ["reset", "--hard", `origin/${branch}`], { cwd: localDir });
|
|
2740
|
+
await runCommand2("git", ["clean", "-fd"], { cwd: localDir });
|
|
2741
|
+
return localDir;
|
|
2742
|
+
}
|
|
2729
2743
|
try {
|
|
2730
2744
|
await runCommand2("git", ["merge", "--allow-unrelated-histories", "--no-edit", `origin/${branch}`], { cwd: localDir });
|
|
2731
2745
|
} catch {
|
|
@@ -2734,6 +2748,17 @@ async function ensureSkillsWorkingTreeRepo(repoUrl, branch) {
|
|
|
2734
2748
|
}
|
|
2735
2749
|
await runCommand2("git", ["remote", "set-url", "origin", repoUrl], { cwd: localDir });
|
|
2736
2750
|
await runGitFetchWithRefLockRetry(localDir);
|
|
2751
|
+
if (options.overwriteLocalFiles) {
|
|
2752
|
+
try {
|
|
2753
|
+
await runCommand2("git", ["reset", "--hard"], { cwd: localDir });
|
|
2754
|
+
} catch {
|
|
2755
|
+
}
|
|
2756
|
+
await runCommand2("git", ["clean", "-fd"], { cwd: localDir });
|
|
2757
|
+
await runCommand2("git", ["checkout", "-B", branch, `origin/${branch}`], { cwd: localDir });
|
|
2758
|
+
await runCommand2("git", ["reset", "--hard", `origin/${branch}`], { cwd: localDir });
|
|
2759
|
+
await runCommand2("git", ["clean", "-fd"], { cwd: localDir });
|
|
2760
|
+
return localDir;
|
|
2761
|
+
}
|
|
2737
2762
|
const hasLocalChangesBeforeSync = await hasLocalUncommittedChanges(localDir);
|
|
2738
2763
|
const localMtimesBeforeSync = hasLocalChangesBeforeSync ? await snapshotFileMtimes(localDir) : /* @__PURE__ */ new Map();
|
|
2739
2764
|
await resolveMergeConflictsByNewerCommit(localDir, branch, localMtimesBeforeSync);
|
|
@@ -2994,13 +3019,19 @@ async function syncInstalledSkillsFolderToRepo(token, repoOwner, repoName, _inst
|
|
|
2994
3019
|
}
|
|
2995
3020
|
async function pullInstalledSkillsFolderFromRepo(token, repoOwner, repoName) {
|
|
2996
3021
|
const remoteUrl = toGitHubTokenRemote(repoOwner, repoName, token);
|
|
2997
|
-
const
|
|
2998
|
-
|
|
3022
|
+
const isUpstream = isUpstreamSkillsRepo(repoOwner, repoName);
|
|
3023
|
+
const branch = isUpstream ? PUBLIC_UPSTREAM_BRANCH_ANDROID : PRIVATE_SYNC_BRANCH;
|
|
3024
|
+
return await ensureSkillsWorkingTreeRepo(remoteUrl, branch, {
|
|
3025
|
+
...isUpstream ? { localDir: getSharedSkillsInstallDir() } : {},
|
|
3026
|
+
overwriteLocalFiles: isUpstream
|
|
3027
|
+
});
|
|
2999
3028
|
}
|
|
3000
3029
|
async function bootstrapSkillsFromUpstreamIntoLocal() {
|
|
3001
3030
|
const repoUrl = `https://github.com/${SYNC_UPSTREAM_SKILLS_OWNER}/${SYNC_UPSTREAM_SKILLS_REPO}.git`;
|
|
3002
|
-
|
|
3003
|
-
|
|
3031
|
+
return await ensureSkillsWorkingTreeRepo(repoUrl, PUBLIC_UPSTREAM_BRANCH_ANDROID, {
|
|
3032
|
+
localDir: getSharedSkillsInstallDir(),
|
|
3033
|
+
overwriteLocalFiles: true
|
|
3034
|
+
});
|
|
3004
3035
|
}
|
|
3005
3036
|
async function collectLocalSyncedSkills(appServer) {
|
|
3006
3037
|
const state = await readSkillsSyncState();
|
|
@@ -3305,12 +3336,31 @@ async function handleSkillsRoutes(req, res, url, context) {
|
|
|
3305
3336
|
try {
|
|
3306
3337
|
const state = await readSkillsSyncState();
|
|
3307
3338
|
if (!state.githubToken || !state.repoOwner || !state.repoName) {
|
|
3308
|
-
await bootstrapSkillsFromUpstreamIntoLocal();
|
|
3339
|
+
const repoDir = await bootstrapSkillsFromUpstreamIntoLocal();
|
|
3340
|
+
const localSkills2 = await scanInstalledSkillsFromDir(repoDir);
|
|
3341
|
+
try {
|
|
3342
|
+
await appServer.rpc("skills/list", { forceReload: true });
|
|
3343
|
+
} catch {
|
|
3344
|
+
}
|
|
3345
|
+
setJson3(res, 200, { ok: true, data: { synced: localSkills2.size, source: "upstream" } });
|
|
3346
|
+
return true;
|
|
3347
|
+
}
|
|
3348
|
+
if (isUpstreamSkillsRepo(state.repoOwner, state.repoName)) {
|
|
3349
|
+
const repoDir = await pullInstalledSkillsFolderFromRepo(state.githubToken, state.repoOwner, state.repoName);
|
|
3350
|
+
const localSkills2 = await scanInstalledSkillsFromDir(repoDir);
|
|
3351
|
+
const pulledHead2 = await runCommandWithOutput("git", ["rev-parse", "HEAD"], { cwd: repoDir }).catch(() => "");
|
|
3352
|
+
await writeSkillsSyncState({
|
|
3353
|
+
...state,
|
|
3354
|
+
lastPullCommitSha: pulledHead2.trim(),
|
|
3355
|
+
lastSyncAttemptCount: 1,
|
|
3356
|
+
lastSyncError: "",
|
|
3357
|
+
lastSyncAtIso: (/* @__PURE__ */ new Date()).toISOString()
|
|
3358
|
+
});
|
|
3309
3359
|
try {
|
|
3310
3360
|
await appServer.rpc("skills/list", { forceReload: true });
|
|
3311
3361
|
} catch {
|
|
3312
3362
|
}
|
|
3313
|
-
setJson3(res, 200, { ok: true, data: { synced:
|
|
3363
|
+
setJson3(res, 200, { ok: true, data: { synced: localSkills2.size, source: "upstream" } });
|
|
3314
3364
|
return true;
|
|
3315
3365
|
}
|
|
3316
3366
|
const remote = await readRemoteSkillsManifest(state.githubToken, state.repoOwner, state.repoName);
|
|
@@ -4203,13 +4253,6 @@ var CUSTOM_PROVIDER_ID = "custom-endpoint";
|
|
|
4203
4253
|
var OPENCODE_ZEN_PROVIDER_ID = "opencode-zen";
|
|
4204
4254
|
var OPENCODE_ZEN_BASE_URL = "https://opencode.ai/zen/v1";
|
|
4205
4255
|
var OPENCODE_ZEN_DEFAULT_MODEL = "big-pickle";
|
|
4206
|
-
var OPENCODE_ZEN_FALLBACK_FREE_MODELS = [
|
|
4207
|
-
OPENCODE_ZEN_DEFAULT_MODEL,
|
|
4208
|
-
"minimax-m2.5-free",
|
|
4209
|
-
"hy3-preview-free",
|
|
4210
|
-
"nemotron-3-super-free",
|
|
4211
|
-
"trinity-large-preview-free"
|
|
4212
|
-
];
|
|
4213
4256
|
function createDefaultOpenCodeZenFreeModeState() {
|
|
4214
4257
|
return {
|
|
4215
4258
|
enabled: true,
|
|
@@ -4221,11 +4264,6 @@ function createDefaultOpenCodeZenFreeModeState() {
|
|
|
4221
4264
|
providerKeys: {}
|
|
4222
4265
|
};
|
|
4223
4266
|
}
|
|
4224
|
-
function getOpenCodeZenFreeModelIds(modelIds) {
|
|
4225
|
-
const uniqueIds = modelIds.map((id) => id.trim()).filter((id, index, ids) => id.length > 0 && ids.indexOf(id) === index);
|
|
4226
|
-
const freeIds = uniqueIds.filter((id) => id.endsWith("-free") || id === OPENCODE_ZEN_DEFAULT_MODEL);
|
|
4227
|
-
return freeIds.length > 0 ? freeIds : OPENCODE_ZEN_FALLBACK_FREE_MODELS;
|
|
4228
|
-
}
|
|
4229
4267
|
function shouldCreateDefaultFreeModeStateForMissingAuth(current, hasUsableCodexAuth) {
|
|
4230
4268
|
return current == null && !hasUsableCodexAuth;
|
|
4231
4269
|
}
|
|
@@ -4358,14 +4396,6 @@ function extractTextParts(value) {
|
|
|
4358
4396
|
if (!Array.isArray(value)) return "";
|
|
4359
4397
|
return value.map((part) => part && typeof part === "object" && typeof part.text === "string" ? part.text : "").filter((part) => part.length > 0).join("\n");
|
|
4360
4398
|
}
|
|
4361
|
-
function buildReasoningOutputItem(reasoningContent) {
|
|
4362
|
-
return {
|
|
4363
|
-
type: "reasoning",
|
|
4364
|
-
id: `rs_${Date.now()}`,
|
|
4365
|
-
summary: [{ type: "summary_text", text: reasoningContent }],
|
|
4366
|
-
content: []
|
|
4367
|
-
};
|
|
4368
|
-
}
|
|
4369
4399
|
function responsesInputToMessages(input, instructions) {
|
|
4370
4400
|
const messages = [];
|
|
4371
4401
|
let pendingReasoningContent = "";
|
|
@@ -4487,7 +4517,12 @@ function chatCompletionToResponsesFormat(chatResponse, model) {
|
|
|
4487
4517
|
});
|
|
4488
4518
|
}
|
|
4489
4519
|
if (message.reasoning_content) {
|
|
4490
|
-
output.push(
|
|
4520
|
+
output.push({
|
|
4521
|
+
type: "reasoning",
|
|
4522
|
+
id: `rs_${Date.now()}`,
|
|
4523
|
+
summary: [],
|
|
4524
|
+
content: [{ type: "reasoning_text", text: message.reasoning_content }]
|
|
4525
|
+
});
|
|
4491
4526
|
}
|
|
4492
4527
|
}
|
|
4493
4528
|
const usage = chatResponse.usage;
|
|
@@ -4553,7 +4588,12 @@ function forwardStreamingTextResponse(upstreamRes, res, model) {
|
|
|
4553
4588
|
const messageItem = { type: "message", role: "assistant", content: [{ type: "output_text", text: fullText }], status: "completed" };
|
|
4554
4589
|
const output = [messageItem];
|
|
4555
4590
|
if (fullReasoningText) {
|
|
4556
|
-
output.push(
|
|
4591
|
+
output.push({
|
|
4592
|
+
type: "reasoning",
|
|
4593
|
+
id: `rs_${Date.now()}`,
|
|
4594
|
+
summary: [],
|
|
4595
|
+
content: [{ type: "reasoning_text", text: fullReasoningText }]
|
|
4596
|
+
});
|
|
4557
4597
|
}
|
|
4558
4598
|
res.write(`data: {"type":"response.output_text.done","output_index":0,"content_index":0,"text":"${escapedFull}"}
|
|
4559
4599
|
|
|
@@ -5196,6 +5236,7 @@ function shellQuote(value) {
|
|
|
5196
5236
|
var COMPOSIO_CONNECTORS_PAGE_LIMIT_MAX = 1e3;
|
|
5197
5237
|
var PROVIDER_MODELS_FETCH_TIMEOUT_MS = 5e3;
|
|
5198
5238
|
var THREAD_RESPONSE_TURN_LIMIT = 10;
|
|
5239
|
+
var THREAD_TURN_PAGE_READ_CACHE_TTL_MS = 3e4;
|
|
5199
5240
|
var THREAD_METHODS_WITH_TURNS = /* @__PURE__ */ new Set(["thread/read", "thread/resume", "thread/fork", "thread/rollback"]);
|
|
5200
5241
|
var THREAD_SEARCH_FULL_TEXT_THREAD_LIMIT = 100;
|
|
5201
5242
|
var PROJECTLESS_THREAD_DIRECTORY_MAX_ATTEMPTS = 100;
|
|
@@ -5698,11 +5739,13 @@ function trimThreadTurnsInRpcResult(method, result) {
|
|
|
5698
5739
|
const thread = asRecord5(record?.thread);
|
|
5699
5740
|
const turns = Array.isArray(thread?.turns) ? thread.turns : null;
|
|
5700
5741
|
if (!record || !thread || !turns || turns.length <= THREAD_RESPONSE_TURN_LIMIT) return result;
|
|
5742
|
+
const startTurnIndex = Math.max(0, turns.length - THREAD_RESPONSE_TURN_LIMIT);
|
|
5701
5743
|
return {
|
|
5702
5744
|
...record,
|
|
5745
|
+
threadTurnStartIndex: startTurnIndex,
|
|
5703
5746
|
thread: {
|
|
5704
5747
|
...thread,
|
|
5705
|
-
turns: turns.slice(
|
|
5748
|
+
turns: turns.slice(startTurnIndex)
|
|
5706
5749
|
}
|
|
5707
5750
|
};
|
|
5708
5751
|
}
|
|
@@ -5770,6 +5813,56 @@ async function createProjectlessThreadDirectory(prompt) {
|
|
|
5770
5813
|
}
|
|
5771
5814
|
throw new Error("Unable to create a unique new chat folder");
|
|
5772
5815
|
}
|
|
5816
|
+
function normalizeGithubCloneUrl(rawUrl) {
|
|
5817
|
+
const trimmedUrl = rawUrl.trim();
|
|
5818
|
+
if (!trimmedUrl) throw new Error("Missing GitHub repository URL");
|
|
5819
|
+
const sshMatch = trimmedUrl.match(/^git@github\.com:([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+?)(?:\.git)?$/u);
|
|
5820
|
+
if (sshMatch) {
|
|
5821
|
+
const repoName2 = sshMatch[2];
|
|
5822
|
+
return { url: `git@github.com:${sshMatch[1]}/${repoName2}.git`, repoName: repoName2 };
|
|
5823
|
+
}
|
|
5824
|
+
let parsed;
|
|
5825
|
+
try {
|
|
5826
|
+
parsed = new URL(trimmedUrl);
|
|
5827
|
+
} catch {
|
|
5828
|
+
throw new Error("Enter a valid GitHub repository URL");
|
|
5829
|
+
}
|
|
5830
|
+
if (parsed.hostname.toLowerCase() !== "github.com") {
|
|
5831
|
+
throw new Error("Only github.com repository URLs are supported");
|
|
5832
|
+
}
|
|
5833
|
+
const segments = parsed.pathname.split("/").filter(Boolean);
|
|
5834
|
+
if (segments.length < 2) {
|
|
5835
|
+
throw new Error("Enter a GitHub repository URL with owner and repository name");
|
|
5836
|
+
}
|
|
5837
|
+
const owner = segments[0];
|
|
5838
|
+
const repoName = segments[1].replace(/\.git$/iu, "");
|
|
5839
|
+
if (!/^[A-Za-z0-9_.-]+$/u.test(owner) || !/^[A-Za-z0-9_.-]+$/u.test(repoName)) {
|
|
5840
|
+
throw new Error("GitHub repository owner or name contains unsupported characters");
|
|
5841
|
+
}
|
|
5842
|
+
return { url: `https://github.com/${owner}/${repoName}.git`, repoName };
|
|
5843
|
+
}
|
|
5844
|
+
async function cloneGithubRepositoryIntoBase(rawUrl, rawBasePath) {
|
|
5845
|
+
const basePath = rawBasePath.trim();
|
|
5846
|
+
if (!basePath) throw new Error("Missing clone destination folder");
|
|
5847
|
+
const normalizedBasePath = isAbsolute2(basePath) ? basePath : resolve2(basePath);
|
|
5848
|
+
await ensureRealDirectory(normalizedBasePath, "Clone destination folder");
|
|
5849
|
+
const { url, repoName } = normalizeGithubCloneUrl(rawUrl);
|
|
5850
|
+
const targetPath = join6(normalizedBasePath, repoName);
|
|
5851
|
+
try {
|
|
5852
|
+
await stat4(targetPath);
|
|
5853
|
+
throw new Error(`Destination already exists: ${targetPath}`);
|
|
5854
|
+
} catch (error) {
|
|
5855
|
+
if (error?.code !== "ENOENT") throw error;
|
|
5856
|
+
}
|
|
5857
|
+
try {
|
|
5858
|
+
await runCommand3("git", ["clone", url, targetPath], { cwd: normalizedBasePath, timeoutMs: 5 * 6e4 });
|
|
5859
|
+
} catch (error) {
|
|
5860
|
+
await rm4(targetPath, { recursive: true, force: true }).catch(() => void 0);
|
|
5861
|
+
throw error;
|
|
5862
|
+
}
|
|
5863
|
+
await persistWorkspaceRoot(targetPath, "");
|
|
5864
|
+
return targetPath;
|
|
5865
|
+
}
|
|
5773
5866
|
function normalizeHeaderValue(value) {
|
|
5774
5867
|
if (typeof value === "string") {
|
|
5775
5868
|
const trimmed = value.trim();
|
|
@@ -7179,14 +7272,33 @@ async function runCommand3(command, args, options = {}) {
|
|
|
7179
7272
|
});
|
|
7180
7273
|
let stdout = "";
|
|
7181
7274
|
let stderr = "";
|
|
7275
|
+
let timedOut = false;
|
|
7276
|
+
let closed = false;
|
|
7277
|
+
const timeout = typeof options.timeoutMs === "number" && Number.isFinite(options.timeoutMs) && options.timeoutMs > 0 ? setTimeout(() => {
|
|
7278
|
+
timedOut = true;
|
|
7279
|
+
proc.kill("SIGTERM");
|
|
7280
|
+
setTimeout(() => {
|
|
7281
|
+
if (!closed) proc.kill("SIGKILL");
|
|
7282
|
+
}, 5e3).unref();
|
|
7283
|
+
}, options.timeoutMs) : null;
|
|
7284
|
+
timeout?.unref();
|
|
7182
7285
|
proc.stdout.on("data", (chunk) => {
|
|
7183
7286
|
stdout += chunk.toString();
|
|
7184
7287
|
});
|
|
7185
7288
|
proc.stderr.on("data", (chunk) => {
|
|
7186
7289
|
stderr += chunk.toString();
|
|
7187
7290
|
});
|
|
7188
|
-
proc.on("error",
|
|
7291
|
+
proc.on("error", (error) => {
|
|
7292
|
+
if (timeout) clearTimeout(timeout);
|
|
7293
|
+
reject(error);
|
|
7294
|
+
});
|
|
7189
7295
|
proc.on("close", (code) => {
|
|
7296
|
+
closed = true;
|
|
7297
|
+
if (timeout) clearTimeout(timeout);
|
|
7298
|
+
if (timedOut) {
|
|
7299
|
+
reject(new Error(`Command timed out after ${options.timeoutMs}ms (${command} ${args.join(" ")})`));
|
|
7300
|
+
return;
|
|
7301
|
+
}
|
|
7190
7302
|
if (code === 0) {
|
|
7191
7303
|
resolve4();
|
|
7192
7304
|
return;
|
|
@@ -8463,6 +8575,8 @@ var AppServerProcess = class {
|
|
|
8463
8575
|
this.appServerArgs = buildAppServerArgs();
|
|
8464
8576
|
this.streamEventsByThreadId = /* @__PURE__ */ new Map();
|
|
8465
8577
|
this.lastThreadReadSnapshotByThreadId = /* @__PURE__ */ new Map();
|
|
8578
|
+
this.threadTurnPageReadCacheByThreadId = /* @__PURE__ */ new Map();
|
|
8579
|
+
this.threadTurnPageReadPromiseByThreadId = /* @__PURE__ */ new Map();
|
|
8466
8580
|
this.capturedItemsByThreadId = /* @__PURE__ */ new Map();
|
|
8467
8581
|
this.liveStateCache = /* @__PURE__ */ new Map();
|
|
8468
8582
|
this.chatgptAuthRefreshPromise = null;
|
|
@@ -8575,7 +8689,10 @@ var AppServerProcess = class {
|
|
|
8575
8689
|
this.recordStreamEvent(notification);
|
|
8576
8690
|
this.captureItemFromNotification(notification);
|
|
8577
8691
|
const nThreadId = this.extractThreadIdFromParams(notification.params);
|
|
8578
|
-
if (nThreadId)
|
|
8692
|
+
if (nThreadId) {
|
|
8693
|
+
this.invalidateLiveStateCache(nThreadId);
|
|
8694
|
+
this.threadTurnPageReadCacheByThreadId.delete(nThreadId);
|
|
8695
|
+
}
|
|
8579
8696
|
for (const listener of this.notificationListeners) {
|
|
8580
8697
|
listener(notification);
|
|
8581
8698
|
}
|
|
@@ -8619,10 +8736,33 @@ var AppServerProcess = class {
|
|
|
8619
8736
|
}
|
|
8620
8737
|
storeThreadReadSnapshot(threadId, snapshot) {
|
|
8621
8738
|
this.lastThreadReadSnapshotByThreadId.set(threadId, snapshot);
|
|
8739
|
+
this.threadTurnPageReadCacheByThreadId.delete(threadId);
|
|
8622
8740
|
}
|
|
8623
8741
|
getLastThreadReadSnapshot(threadId) {
|
|
8624
8742
|
return this.lastThreadReadSnapshotByThreadId.get(threadId) ?? null;
|
|
8625
8743
|
}
|
|
8744
|
+
async readThreadForTurnPage(threadId) {
|
|
8745
|
+
const now = Date.now();
|
|
8746
|
+
const cached = this.threadTurnPageReadCacheByThreadId.get(threadId);
|
|
8747
|
+
if (cached && cached.expiresAt > now) return cached.result;
|
|
8748
|
+
if (cached) this.threadTurnPageReadCacheByThreadId.delete(threadId);
|
|
8749
|
+
const pending = this.threadTurnPageReadPromiseByThreadId.get(threadId);
|
|
8750
|
+
if (pending) return pending;
|
|
8751
|
+
const promise = this.rpc("thread/read", {
|
|
8752
|
+
threadId,
|
|
8753
|
+
includeTurns: true
|
|
8754
|
+
}).then((result) => {
|
|
8755
|
+
this.threadTurnPageReadCacheByThreadId.set(threadId, {
|
|
8756
|
+
result,
|
|
8757
|
+
expiresAt: Date.now() + THREAD_TURN_PAGE_READ_CACHE_TTL_MS
|
|
8758
|
+
});
|
|
8759
|
+
return result;
|
|
8760
|
+
}).finally(() => {
|
|
8761
|
+
this.threadTurnPageReadPromiseByThreadId.delete(threadId);
|
|
8762
|
+
});
|
|
8763
|
+
this.threadTurnPageReadPromiseByThreadId.set(threadId, promise);
|
|
8764
|
+
return promise;
|
|
8765
|
+
}
|
|
8626
8766
|
cacheLiveState(threadId, data, turnCount, sessionSize) {
|
|
8627
8767
|
this.liveStateCache.set(threadId, { data, turnCount, sessionSize });
|
|
8628
8768
|
}
|
|
@@ -9291,11 +9431,10 @@ async function buildThreadSearchIndex(appServer) {
|
|
|
9291
9431
|
const docsById = new Map(docs.map((doc) => [doc.id, doc]));
|
|
9292
9432
|
return { docsById };
|
|
9293
9433
|
}
|
|
9294
|
-
function createCodexBridgeMiddleware(
|
|
9434
|
+
function createCodexBridgeMiddleware() {
|
|
9295
9435
|
const { appServer, terminalManager, methodCatalog, telegramBridge, backendQueueProcessor } = getSharedBridgeState();
|
|
9296
9436
|
let threadSearchIndex = null;
|
|
9297
9437
|
let threadSearchIndexPromise = null;
|
|
9298
|
-
let backgroundServicesStarted = false;
|
|
9299
9438
|
async function getThreadSearchIndex() {
|
|
9300
9439
|
if (threadSearchIndex) return threadSearchIndex;
|
|
9301
9440
|
if (!threadSearchIndexPromise) {
|
|
@@ -9308,21 +9447,14 @@ function createCodexBridgeMiddleware(options = {}) {
|
|
|
9308
9447
|
}
|
|
9309
9448
|
return threadSearchIndexPromise;
|
|
9310
9449
|
}
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
|
|
9314
|
-
|
|
9315
|
-
|
|
9316
|
-
|
|
9317
|
-
|
|
9318
|
-
|
|
9319
|
-
telegramBridge.start();
|
|
9320
|
-
}).catch(() => {
|
|
9321
|
-
});
|
|
9322
|
-
}
|
|
9323
|
-
if (options.startBackgroundServices !== false) {
|
|
9324
|
-
startBackgroundServices();
|
|
9325
|
-
}
|
|
9450
|
+
void initializeSkillsSyncOnStartup(appServer);
|
|
9451
|
+
void readTelegramBridgeConfig().then((config) => {
|
|
9452
|
+
if (!config.botToken) return;
|
|
9453
|
+
telegramBridge.configureToken(config.botToken);
|
|
9454
|
+
telegramBridge.configureAllowedUserIds(config.allowedUserIds);
|
|
9455
|
+
telegramBridge.start();
|
|
9456
|
+
}).catch(() => {
|
|
9457
|
+
});
|
|
9326
9458
|
const middleware = async (req, res, next) => {
|
|
9327
9459
|
const requestStartNs = process.hrtime.bigint();
|
|
9328
9460
|
const rawUrl = req.url ?? "";
|
|
@@ -9413,11 +9545,7 @@ function createCodexBridgeMiddleware(options = {}) {
|
|
|
9413
9545
|
}
|
|
9414
9546
|
if (url.pathname.startsWith("/codex-api/free-mode")) {
|
|
9415
9547
|
let readFreeModeState2 = function() {
|
|
9416
|
-
|
|
9417
|
-
if (state.provider === "opencode-zen" && !state.model?.trim()) {
|
|
9418
|
-
return { ...state, model: OPENCODE_ZEN_DEFAULT_MODEL };
|
|
9419
|
-
}
|
|
9420
|
-
return state;
|
|
9548
|
+
return ensureDefaultFreeModeStateForMissingAuthSync(statePath) ?? { enabled: false, apiKey: null, model: FREE_MODE_DEFAULT_MODEL };
|
|
9421
9549
|
};
|
|
9422
9550
|
var readFreeModeState = readFreeModeState2;
|
|
9423
9551
|
const statePath = join6(getCodexHomeDir3(), FREE_MODE_STATE_FILE);
|
|
@@ -9569,7 +9697,7 @@ function createCodexBridgeMiddleware(options = {}) {
|
|
|
9569
9697
|
if (resolvedKey) {
|
|
9570
9698
|
prevKeys[providerType] = resolvedKey;
|
|
9571
9699
|
}
|
|
9572
|
-
const resolvedModel = providerType === "openrouter" ? current.model || FREE_MODE_DEFAULT_MODEL : providerType === "custom" ? await fetchCustomEndpointDefaultModel(baseUrl, resolvedKey) :
|
|
9700
|
+
const resolvedModel = providerType === "openrouter" ? current.model || FREE_MODE_DEFAULT_MODEL : providerType === "custom" ? await fetchCustomEndpointDefaultModel(baseUrl, resolvedKey) : "";
|
|
9573
9701
|
const state = {
|
|
9574
9702
|
enabled: true,
|
|
9575
9703
|
apiKey: resolvedKey,
|
|
@@ -9729,6 +9857,61 @@ function createCodexBridgeMiddleware(options = {}) {
|
|
|
9729
9857
|
setJson4(res, 200, { result });
|
|
9730
9858
|
return;
|
|
9731
9859
|
}
|
|
9860
|
+
if (req.method === "GET" && url.pathname === "/codex-api/thread-turn-page") {
|
|
9861
|
+
try {
|
|
9862
|
+
const threadId = url.searchParams.get("threadId")?.trim() ?? "";
|
|
9863
|
+
const beforeTurnId = url.searchParams.get("beforeTurnId")?.trim() ?? "";
|
|
9864
|
+
const limitRaw = url.searchParams.get("limit")?.trim() ?? String(THREAD_RESPONSE_TURN_LIMIT);
|
|
9865
|
+
const limit = Math.max(1, Math.min(50, Number.parseInt(limitRaw, 10) || THREAD_RESPONSE_TURN_LIMIT));
|
|
9866
|
+
if (!threadId) {
|
|
9867
|
+
setJson4(res, 400, { error: "Missing threadId" });
|
|
9868
|
+
return;
|
|
9869
|
+
}
|
|
9870
|
+
const threadReadResult = await appServer.readThreadForTurnPage(threadId);
|
|
9871
|
+
const record = asRecord5(threadReadResult);
|
|
9872
|
+
const thread = asRecord5(record?.thread);
|
|
9873
|
+
if (!record || !thread) {
|
|
9874
|
+
setJson4(res, 502, { error: "thread/read returned an invalid thread response" });
|
|
9875
|
+
return;
|
|
9876
|
+
}
|
|
9877
|
+
const turns = Array.isArray(thread.turns) ? thread.turns : [];
|
|
9878
|
+
const beforeIndex = beforeTurnId ? turns.findIndex((turn) => asRecord5(turn)?.id === beforeTurnId) : turns.length;
|
|
9879
|
+
if (beforeTurnId && beforeIndex < 0) {
|
|
9880
|
+
setJson4(res, 200, {
|
|
9881
|
+
result: {
|
|
9882
|
+
...record,
|
|
9883
|
+
thread: {
|
|
9884
|
+
...thread,
|
|
9885
|
+
turns: []
|
|
9886
|
+
}
|
|
9887
|
+
},
|
|
9888
|
+
startTurnIndex: 0,
|
|
9889
|
+
hasMoreOlder: false
|
|
9890
|
+
});
|
|
9891
|
+
return;
|
|
9892
|
+
}
|
|
9893
|
+
const endIndex = beforeIndex;
|
|
9894
|
+
const startIndex = Math.max(0, endIndex - limit);
|
|
9895
|
+
const pageTurns = turns.slice(startIndex, endIndex);
|
|
9896
|
+
const pagedResult = {
|
|
9897
|
+
...record,
|
|
9898
|
+
thread: {
|
|
9899
|
+
...thread,
|
|
9900
|
+
turns: pageTurns
|
|
9901
|
+
}
|
|
9902
|
+
};
|
|
9903
|
+
const sanitized = await sanitizeThreadTurnsInlinePayloads("thread/read", pagedResult);
|
|
9904
|
+
const result = await mergeSessionSkillInputsIntoThreadResult(sanitized);
|
|
9905
|
+
setJson4(res, 200, {
|
|
9906
|
+
result,
|
|
9907
|
+
startTurnIndex: startIndex,
|
|
9908
|
+
hasMoreOlder: startIndex > 0
|
|
9909
|
+
});
|
|
9910
|
+
} catch (error) {
|
|
9911
|
+
setJson4(res, 500, { error: getErrorMessage5(error, "Failed to load earlier thread messages") });
|
|
9912
|
+
}
|
|
9913
|
+
return;
|
|
9914
|
+
}
|
|
9732
9915
|
if (req.method === "GET" && url.pathname === "/codex-api/thread-file-change-fallback") {
|
|
9733
9916
|
const threadId = url.searchParams.get("threadId")?.trim() ?? "";
|
|
9734
9917
|
if (!threadId) {
|
|
@@ -10018,28 +10201,21 @@ function createCodexBridgeMiddleware(options = {}) {
|
|
|
10018
10201
|
try {
|
|
10019
10202
|
const modelsUrl = "https://opencode.ai/zen/v1/models";
|
|
10020
10203
|
const headers = {};
|
|
10021
|
-
|
|
10022
|
-
if (hasZenApiKey) {
|
|
10204
|
+
if (fmState.apiKey && fmState.apiKey !== "dummy") {
|
|
10023
10205
|
headers["Authorization"] = `Bearer ${fmState.apiKey}`;
|
|
10024
10206
|
}
|
|
10025
10207
|
const resp = await fetch(modelsUrl, { headers, signal: AbortSignal.timeout(8e3) });
|
|
10026
10208
|
if (resp.ok) {
|
|
10027
10209
|
const json = await resp.json();
|
|
10028
10210
|
const allIds = (json.data ?? []).map((m) => m.id).filter(Boolean);
|
|
10029
|
-
|
|
10030
|
-
|
|
10031
|
-
|
|
10032
|
-
source: "opencode-zen"
|
|
10033
|
-
});
|
|
10211
|
+
const freeIds = allIds.filter((id) => id.endsWith("-free") || id === "big-pickle");
|
|
10212
|
+
const paidIds = allIds.filter((id) => !id.endsWith("-free") && id !== "big-pickle");
|
|
10213
|
+
setJson4(res, 200, { data: [...freeIds, ...paidIds], exclusive: true, source: "opencode-zen" });
|
|
10034
10214
|
return;
|
|
10035
10215
|
}
|
|
10036
10216
|
} catch {
|
|
10037
10217
|
}
|
|
10038
|
-
setJson4(res, 200, {
|
|
10039
|
-
data: getOpenCodeZenFreeModelIds([]),
|
|
10040
|
-
exclusive: true,
|
|
10041
|
-
source: "opencode-zen"
|
|
10042
|
-
});
|
|
10218
|
+
setJson4(res, 200, { data: ["big-pickle", "minimax-m2.5-free", "nemotron-3-super-free", "trinity-large-preview-free"], exclusive: true, source: "opencode-zen" });
|
|
10043
10219
|
return;
|
|
10044
10220
|
}
|
|
10045
10221
|
if (fmState.provider === "custom" && fmState.customBaseUrl) {
|
|
@@ -10342,7 +10518,7 @@ function createCodexBridgeMiddleware(options = {}) {
|
|
|
10342
10518
|
if (currentBranch && !branchActivityByName.has(currentBranch)) {
|
|
10343
10519
|
branchActivityByName.set(currentBranch, { timestamp: Number.MAX_SAFE_INTEGER, isRemote: false });
|
|
10344
10520
|
}
|
|
10345
|
-
const
|
|
10521
|
+
const options = Array.from(branchActivityByName.entries()).map(([value, metadata]) => ({
|
|
10346
10522
|
value,
|
|
10347
10523
|
label: value,
|
|
10348
10524
|
isCurrent: value === currentBranch,
|
|
@@ -10356,7 +10532,7 @@ function createCodexBridgeMiddleware(options = {}) {
|
|
|
10356
10532
|
setJson4(res, 200, {
|
|
10357
10533
|
data: {
|
|
10358
10534
|
...state,
|
|
10359
|
-
options
|
|
10535
|
+
options
|
|
10360
10536
|
}
|
|
10361
10537
|
});
|
|
10362
10538
|
} catch (error) {
|
|
@@ -10602,6 +10778,18 @@ function createCodexBridgeMiddleware(options = {}) {
|
|
|
10602
10778
|
setJson4(res, 200, { data: { path: normalizedPath } });
|
|
10603
10779
|
return;
|
|
10604
10780
|
}
|
|
10781
|
+
if (req.method === "POST" && url.pathname === "/codex-api/github-clone") {
|
|
10782
|
+
const payload = asRecord5(await readJsonBody2(req));
|
|
10783
|
+
const repoUrl = typeof payload?.url === "string" ? payload.url.trim() : "";
|
|
10784
|
+
const basePath = typeof payload?.basePath === "string" ? payload.basePath.trim() : "";
|
|
10785
|
+
try {
|
|
10786
|
+
const clonedPath = await cloneGithubRepositoryIntoBase(repoUrl, basePath);
|
|
10787
|
+
setJson4(res, 200, { data: { path: clonedPath } });
|
|
10788
|
+
} catch (error) {
|
|
10789
|
+
setJson4(res, 400, { error: error instanceof Error ? error.message : "Failed to clone GitHub repository" });
|
|
10790
|
+
}
|
|
10791
|
+
return;
|
|
10792
|
+
}
|
|
10605
10793
|
if (req.method === "POST" && url.pathname === "/codex-api/projectless-thread-cwd") {
|
|
10606
10794
|
const payload = asRecord5(await readJsonBody2(req));
|
|
10607
10795
|
const prompt = typeof payload?.prompt === "string" ? payload.prompt : null;
|
|
@@ -10913,7 +11101,6 @@ data: ${JSON.stringify({ ok: true })}
|
|
|
10913
11101
|
backendQueueProcessor.dispose();
|
|
10914
11102
|
appServer.dispose();
|
|
10915
11103
|
};
|
|
10916
|
-
middleware.startBackgroundServices = startBackgroundServices;
|
|
10917
11104
|
middleware.subscribeNotifications = (listener) => {
|
|
10918
11105
|
const unsubscribeAppServer = appServer.onNotification((notification) => {
|
|
10919
11106
|
listener({
|
|
@@ -11615,9 +11802,7 @@ function readWildcardPathParam(value) {
|
|
|
11615
11802
|
}
|
|
11616
11803
|
function createServer(options = {}) {
|
|
11617
11804
|
const app = express();
|
|
11618
|
-
const bridge = createCodexBridgeMiddleware(
|
|
11619
|
-
startBackgroundServices: options.deferBridgeBackgroundServices !== true
|
|
11620
|
-
});
|
|
11805
|
+
const bridge = createCodexBridgeMiddleware();
|
|
11621
11806
|
const authSession = options.password ? createAuthSession(options.password) : null;
|
|
11622
11807
|
if (authSession) {
|
|
11623
11808
|
app.use(authSession.middleware);
|
|
@@ -11763,7 +11948,6 @@ function createServer(options = {}) {
|
|
|
11763
11948
|
return {
|
|
11764
11949
|
app,
|
|
11765
11950
|
dispose: () => bridge.dispose(),
|
|
11766
|
-
startBridgeBackgroundServices: () => bridge.startBackgroundServices(),
|
|
11767
11951
|
attachWebSocket: (server) => {
|
|
11768
11952
|
const wss = new WebSocketServer({ noServer: true });
|
|
11769
11953
|
server.on("upgrade", (req, socket, head) => {
|
|
@@ -12243,15 +12427,10 @@ async function startServer(options) {
|
|
|
12243
12427
|
const passwordResolution = resolvePassword(options.password);
|
|
12244
12428
|
const password = passwordResolution.password;
|
|
12245
12429
|
const generatedPasswordPath = password && passwordResolution.generated ? await persistGeneratedPassword(password) : null;
|
|
12246
|
-
const { app, dispose, attachWebSocket
|
|
12247
|
-
password,
|
|
12248
|
-
deferBridgeBackgroundServices: true
|
|
12249
|
-
});
|
|
12430
|
+
const { app, dispose, attachWebSocket } = createServer({ password });
|
|
12250
12431
|
const server = createServer2(app);
|
|
12251
12432
|
attachWebSocket(server);
|
|
12252
12433
|
const port = await listenWithFallback(server, requestedPort);
|
|
12253
|
-
process.env.CODEXUI_SERVER_PORT = String(port);
|
|
12254
|
-
startBridgeBackgroundServices();
|
|
12255
12434
|
let tunnelChild = null;
|
|
12256
12435
|
let tunnelUrl = null;
|
|
12257
12436
|
if (options.tunnel) {
|