codex-plus-patcher 0.2.0 → 0.3.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 +15 -2
- package/package.json +2 -2
- package/src/cli.js +53 -0
- package/src/core/asar.js +29 -0
- package/src/core/patch-engine.js +12 -1
- package/src/patches/26.616.41845-4198.js +31 -670
- package/src/patches/26.616.51431-4212.js +31 -742
- package/src/patches/26.616.71553-4265.js +31 -0
- package/src/patches/index.js +2 -1
- package/src/patches/lib/common-patches.js +606 -0
- package/src/patches/lib/host-hooks.js +24 -0
- package/src/patches/lib/make-patch-set.js +14 -0
- package/src/patches/lib/replace.js +17 -0
- package/src/runtime/assets.js +27 -0
- package/src/runtime/plugins/aboutMetadata.js +93 -0
- package/src/runtime/plugins/diagnosticErrors.js +43 -0
- package/src/runtime/plugins/nestedRepositories.js +448 -0
- package/src/runtime/plugins/projectColors.js +153 -0
- package/src/runtime/plugins/sidebarNameBlur.js +28 -0
- package/src/runtime/plugins/userBubbleColors.js +134 -0
- package/src/runtime/runtime.js +363 -0
- package/src/runtime/worker.js +228 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
const fs = require("node:fs");
|
|
2
|
+
|
|
3
|
+
function errorObject(error) {
|
|
4
|
+
return {
|
|
5
|
+
name: error?.name ?? null,
|
|
6
|
+
code: error?.code ?? null,
|
|
7
|
+
message: error?.message ?? String(error),
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function trace(event, data) {
|
|
12
|
+
try {
|
|
13
|
+
fs.appendFileSync(
|
|
14
|
+
"/tmp/codex-plus-trace.log",
|
|
15
|
+
`${JSON.stringify({ ts: new Date().toISOString(), event, data: data ?? null })}\n`,
|
|
16
|
+
);
|
|
17
|
+
} catch {}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function traceRequest(params) {
|
|
21
|
+
trace(params?.event ?? "trace", params?.data ?? null);
|
|
22
|
+
return { ok: true };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function unquoteTomlValue(value) {
|
|
26
|
+
const trimmed = String(value || "").trim();
|
|
27
|
+
if (trimmed.length >= 2 && trimmed.startsWith('"') && trimmed.endsWith('"')) return trimmed.slice(1, -1);
|
|
28
|
+
if (trimmed.length >= 2 && trimmed.startsWith("'") && trimmed.endsWith("'")) return trimmed.slice(1, -1);
|
|
29
|
+
return trimmed;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parsePlusToml(text) {
|
|
33
|
+
const repositories = [];
|
|
34
|
+
const ignoredLines = [];
|
|
35
|
+
let current = null;
|
|
36
|
+
let tableCount = 0;
|
|
37
|
+
|
|
38
|
+
for (const [index, rawLine] of String(text || "").split(/\r?\n/).entries()) {
|
|
39
|
+
const line = rawLine.trim();
|
|
40
|
+
if (!line || line.startsWith("#")) continue;
|
|
41
|
+
if (line === "[[repositories]]") {
|
|
42
|
+
if (current?.path) repositories.push(current);
|
|
43
|
+
current = {};
|
|
44
|
+
tableCount += 1;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const match = line.match(/^([A-Za-z0-9_-]+)\s*=\s*(.+)$/);
|
|
48
|
+
if (match && current) {
|
|
49
|
+
const [, key, rawValue] = match;
|
|
50
|
+
if (key === "path" || key === "label") current[key] = unquoteTomlValue(rawValue);
|
|
51
|
+
else ignoredLines.push({ line: index + 1, text: rawLine });
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
ignoredLines.push({ line: index + 1, text: rawLine });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (current?.path) repositories.push(current);
|
|
58
|
+
return { repositories, tableCount, ignoredLines };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function readPlusToml(projectRoot, platform) {
|
|
62
|
+
const path = await platform.platformPath();
|
|
63
|
+
const tomlPath = path.join(projectRoot, ".codex", "plus.toml");
|
|
64
|
+
const debug = { path: tomlPath, attempted: true, readOk: false, bytes: 0, preview: null, error: null };
|
|
65
|
+
trace("plus-toml:read-start", { path: tomlPath });
|
|
66
|
+
try {
|
|
67
|
+
const text = await new Response(await platform.readFile(tomlPath)).text();
|
|
68
|
+
debug.readOk = true;
|
|
69
|
+
debug.bytes = text.length;
|
|
70
|
+
debug.preview = text.slice(0, 300);
|
|
71
|
+
trace("plus-toml:read-ok", { path: tomlPath, bytes: debug.bytes, preview: debug.preview });
|
|
72
|
+
return { text, debug };
|
|
73
|
+
} catch (error) {
|
|
74
|
+
debug.error = errorObject(error);
|
|
75
|
+
trace("plus-toml:read-error", { path: tomlPath, error: debug.error });
|
|
76
|
+
return { text: null, debug };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function normalizeSlash(value) {
|
|
81
|
+
return String(value || "").replaceAll("\\", "/");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function repositoryTargets(gitManager, params, platform, signal, getSubmodulePaths) {
|
|
85
|
+
trace("repository-targets:start", { cwd: params?.cwd, hostId: params?.hostId });
|
|
86
|
+
const warnings = [];
|
|
87
|
+
const metadata = await gitManager.getStableMetadata(params.cwd, platform);
|
|
88
|
+
if (metadata == null) {
|
|
89
|
+
const result = {
|
|
90
|
+
main: null,
|
|
91
|
+
repositories: [],
|
|
92
|
+
warnings: [{ type: "main-not-git", path: params.cwd, message: "Current directory is not inside a git repository." }],
|
|
93
|
+
debug: { requestCwd: params.cwd, projectRoot: null },
|
|
94
|
+
};
|
|
95
|
+
trace("repository-targets:main-not-git", result);
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const main = {
|
|
100
|
+
id: `main:${metadata.root}`,
|
|
101
|
+
kind: "main",
|
|
102
|
+
path: ".",
|
|
103
|
+
label: "Main",
|
|
104
|
+
cwd: metadata.root,
|
|
105
|
+
root: metadata.root,
|
|
106
|
+
commonDir: metadata.commonDir,
|
|
107
|
+
valid: true,
|
|
108
|
+
};
|
|
109
|
+
const path = await platform.platformPath();
|
|
110
|
+
const submoduleCandidates = (await getSubmodulePaths(metadata.root, signal)).map((entry) => ({
|
|
111
|
+
kind: "submodule",
|
|
112
|
+
path: entry,
|
|
113
|
+
label: entry.split("/").filter(Boolean).pop() || entry,
|
|
114
|
+
}));
|
|
115
|
+
const plusToml = await readPlusToml(metadata.root, platform);
|
|
116
|
+
const parsed = parsePlusToml(plusToml.text);
|
|
117
|
+
const configuredCandidates = parsed.repositories.map((entry) => ({ kind: "configured", ...entry }));
|
|
118
|
+
const debug = {
|
|
119
|
+
requestCwd: params.cwd,
|
|
120
|
+
projectRoot: metadata.root,
|
|
121
|
+
plusToml: {
|
|
122
|
+
...plusToml.debug,
|
|
123
|
+
parsedRepositories: parsed.repositories.length,
|
|
124
|
+
tableCount: parsed.tableCount,
|
|
125
|
+
ignoredLines: parsed.ignoredLines.slice(0, 12),
|
|
126
|
+
},
|
|
127
|
+
submoduleCandidates: submoduleCandidates.map((entry) => ({ path: entry.path, label: entry.label })),
|
|
128
|
+
configuredCandidates: configuredCandidates.map((entry) => ({ path: entry.path, label: entry.label ?? null })),
|
|
129
|
+
accepted: [],
|
|
130
|
+
skipped: [],
|
|
131
|
+
};
|
|
132
|
+
const seen = new Set();
|
|
133
|
+
const repositories = [];
|
|
134
|
+
|
|
135
|
+
function skip(entry) {
|
|
136
|
+
warnings.push(entry);
|
|
137
|
+
debug.skipped.push(entry);
|
|
138
|
+
trace("repository-targets:skip", entry);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function acceptCandidate(candidate) {
|
|
142
|
+
const rawPath = candidate.path.trim();
|
|
143
|
+
if (!rawPath) {
|
|
144
|
+
skip({ kind: candidate.kind, type: "empty-path", path: rawPath, message: "Skipped empty repository path." });
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (path.isAbsolute(rawPath) || rawPath === ".." || rawPath.startsWith("../") || rawPath.startsWith("..\\")) {
|
|
148
|
+
skip({ kind: candidate.kind, type: "out-of-root", path: rawPath, message: "Skipped repository outside project root." });
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const resolved = path.normalize(path.join(metadata.root, rawPath));
|
|
152
|
+
const relative = path.relative(metadata.root, resolved);
|
|
153
|
+
if (!relative || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
|
|
154
|
+
skip({
|
|
155
|
+
kind: candidate.kind,
|
|
156
|
+
type: "out-of-root",
|
|
157
|
+
path: rawPath,
|
|
158
|
+
resolved,
|
|
159
|
+
relative,
|
|
160
|
+
message: "Skipped repository outside project root.",
|
|
161
|
+
});
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const normalized = normalizeSlash(relative);
|
|
165
|
+
if (seen.has(normalized)) {
|
|
166
|
+
skip({ kind: candidate.kind, type: "duplicate", path: normalized, message: "Skipped duplicate repository path." });
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
seen.add(normalized);
|
|
170
|
+
|
|
171
|
+
let repoMetadata;
|
|
172
|
+
try {
|
|
173
|
+
repoMetadata = await gitManager.getStableMetadata(resolved, platform);
|
|
174
|
+
} catch (error) {
|
|
175
|
+
skip({
|
|
176
|
+
kind: candidate.kind,
|
|
177
|
+
type: "metadata-error",
|
|
178
|
+
path: normalized,
|
|
179
|
+
resolved,
|
|
180
|
+
error: errorObject(error),
|
|
181
|
+
message: "Failed to inspect repository metadata.",
|
|
182
|
+
});
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (repoMetadata == null) {
|
|
186
|
+
if (candidate.kind === "configured") {
|
|
187
|
+
skip({ kind: candidate.kind, type: "non-git", path: normalized, resolved, message: "Configured repository is not a git repository." });
|
|
188
|
+
}
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const repo = {
|
|
193
|
+
id: `${candidate.kind}:${normalized}`,
|
|
194
|
+
kind: candidate.kind,
|
|
195
|
+
path: normalized,
|
|
196
|
+
label: candidate.label ?? normalized,
|
|
197
|
+
cwd: resolved,
|
|
198
|
+
root: repoMetadata.root,
|
|
199
|
+
commonDir: repoMetadata.commonDir,
|
|
200
|
+
valid: true,
|
|
201
|
+
};
|
|
202
|
+
repositories.push(repo);
|
|
203
|
+
debug.accepted.push({ kind: candidate.kind, path: normalized, cwd: resolved, root: repoMetadata.root });
|
|
204
|
+
trace("repository-targets:accept", { kind: candidate.kind, path: normalized, cwd: resolved, root: repoMetadata.root });
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
for (const candidate of submoduleCandidates) await acceptCandidate(candidate);
|
|
208
|
+
for (const candidate of configuredCandidates) await acceptCandidate(candidate);
|
|
209
|
+
|
|
210
|
+
const result = { main, repositories, warnings, debug };
|
|
211
|
+
trace("repository-targets:done", {
|
|
212
|
+
repositoryCount: repositories.length,
|
|
213
|
+
warningCount: warnings.length,
|
|
214
|
+
accepted: debug.accepted,
|
|
215
|
+
skipped: debug.skipped,
|
|
216
|
+
});
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function isReadOnlyBranchRequest(requestKind, source) {
|
|
221
|
+
return source === "codex_plus_review" && (requestKind === "recent-branches" || requestKind === "search-branches");
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
module.exports = {
|
|
225
|
+
isReadOnlyBranchRequest,
|
|
226
|
+
repositoryTargets,
|
|
227
|
+
traceRequest,
|
|
228
|
+
};
|