codex-overleaf-link 1.1.1
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/LICENSE +21 -0
- package/README.md +457 -0
- package/bin/codex-overleaf-link.mjs +223 -0
- package/extension/src/shared/agentTranscript.js +1175 -0
- package/extension/src/shared/auditRecords.js +568 -0
- package/extension/src/shared/compatibility.js +372 -0
- package/extension/src/shared/compileAdapter.js +176 -0
- package/extension/src/shared/governanceRules.js +252 -0
- package/extension/src/shared/i18n.js +565 -0
- package/extension/src/shared/models.js +106 -0
- package/extension/src/shared/otText.js +505 -0
- package/extension/src/shared/projectFiles.js +180 -0
- package/extension/src/shared/reviewing.js +99 -0
- package/extension/src/shared/sensitiveScan.js +116 -0
- package/extension/src/shared/sessionState.js +1084 -0
- package/extension/src/shared/staleGuard.js +150 -0
- package/extension/src/shared/storageDb.js +986 -0
- package/extension/src/shared/storageKeys.js +29 -0
- package/extension/src/shared/storageMigration.js +168 -0
- package/extension/src/shared/summary.js +248 -0
- package/extension/src/shared/undoOperations.js +369 -0
- package/native-host/src/codexArgs.js +43 -0
- package/native-host/src/codexHome.js +538 -0
- package/native-host/src/codexModels.js +247 -0
- package/native-host/src/codexPrompt.js +192 -0
- package/native-host/src/codexPromptAssembly.js +411 -0
- package/native-host/src/codexSessionRunner.js +1247 -0
- package/native-host/src/commandApproval.js +914 -0
- package/native-host/src/debugLog.js +78 -0
- package/native-host/src/diffEngine.js +247 -0
- package/native-host/src/index.js +132 -0
- package/native-host/src/launcher.js +81 -0
- package/native-host/src/localSkills.js +476 -0
- package/native-host/src/manifest.js +226 -0
- package/native-host/src/mirrorSensitiveScan.js +119 -0
- package/native-host/src/mirrorWorkspace.js +1019 -0
- package/native-host/src/nativeDoctor.js +826 -0
- package/native-host/src/nativeEnvironment.js +315 -0
- package/native-host/src/nativeHostPlatform.js +112 -0
- package/native-host/src/nativeMessaging.js +60 -0
- package/native-host/src/nativeQuotas.js +294 -0
- package/native-host/src/nativeResponseBudget.js +194 -0
- package/native-host/src/runtimeInstaller.js +357 -0
- package/native-host/src/taskRunner.js +3 -0
- package/native-host/src/taskRunnerRuntime.js +1083 -0
- package/native-host/src/textPatch.js +287 -0
- package/package.json +40 -0
- package/scripts/codex-json-agent.mjs +269 -0
- package/scripts/install-native-host.mjs +255 -0
- package/scripts/npm-package-files-v1.1.1.txt +52 -0
- package/scripts/uninstall-native-host.mjs +298 -0
- package/scripts/verify-npm-package.mjs +296 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const {
|
|
6
|
+
getCodexOverleafHome,
|
|
7
|
+
getNativeHostPlatform
|
|
8
|
+
} = require('./nativeHostPlatform');
|
|
9
|
+
|
|
10
|
+
const MAX_FIELD_LENGTH = 8000;
|
|
11
|
+
|
|
12
|
+
function getDebugLogDir(options = {}) {
|
|
13
|
+
return getCodexOverleafHome(options);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function getDebugLogPath(options = {}) {
|
|
17
|
+
const platformPath = getNativeHostPlatform(options) === 'win32' ? path.win32 : path.posix;
|
|
18
|
+
return platformPath.join(getDebugLogDir(options), 'native-host.log');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function logDebug(event, detail = {}, options = {}) {
|
|
22
|
+
try {
|
|
23
|
+
const logDir = getDebugLogDir(options);
|
|
24
|
+
const logPath = getDebugLogPath(options);
|
|
25
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
26
|
+
fs.appendFileSync(logPath, `${JSON.stringify({
|
|
27
|
+
time: new Date().toISOString(),
|
|
28
|
+
event,
|
|
29
|
+
detail: sanitize(detail)
|
|
30
|
+
})}\n`, 'utf8');
|
|
31
|
+
} catch {
|
|
32
|
+
// Debug logging must never break the native messaging host.
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function truncateText(value, limit = MAX_FIELD_LENGTH) {
|
|
37
|
+
const text = String(value || '');
|
|
38
|
+
if (text.length <= limit) {
|
|
39
|
+
return text;
|
|
40
|
+
}
|
|
41
|
+
return `${text.slice(0, limit)}\n...[truncated ${text.length - limit} chars]`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function sanitize(value) {
|
|
45
|
+
if (typeof value === 'string') {
|
|
46
|
+
return truncateText(value);
|
|
47
|
+
}
|
|
48
|
+
if (!value || typeof value !== 'object') {
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
if (Array.isArray(value)) {
|
|
52
|
+
return value.slice(0, 20).map(sanitize);
|
|
53
|
+
}
|
|
54
|
+
const result = {};
|
|
55
|
+
for (const [key, item] of Object.entries(value)) {
|
|
56
|
+
if (key === 'content') {
|
|
57
|
+
result[key] = `[${String(item || '').length} chars]`;
|
|
58
|
+
} else if (key === 'files' && Array.isArray(item)) {
|
|
59
|
+
result[key] = item.map(file => ({
|
|
60
|
+
path: file?.path,
|
|
61
|
+
contentLength: String(file?.content || '').length
|
|
62
|
+
}));
|
|
63
|
+
} else {
|
|
64
|
+
result[key] = sanitize(item);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
module.exports = {
|
|
71
|
+
getDebugLogDir,
|
|
72
|
+
getDebugLogPath,
|
|
73
|
+
get LOG_PATH() {
|
|
74
|
+
return getDebugLogPath();
|
|
75
|
+
},
|
|
76
|
+
logDebug,
|
|
77
|
+
truncateText
|
|
78
|
+
};
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function computeLineDiff(oldText, newText, contextLines = 3) {
|
|
4
|
+
const oldLines = splitLines(oldText);
|
|
5
|
+
const newLines = splitLines(newText);
|
|
6
|
+
|
|
7
|
+
if (oldLines.join('\n') === newLines.join('\n')) {
|
|
8
|
+
return [];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const MAX_LINES = 5000;
|
|
12
|
+
const MAX_DIFF_PRODUCT = 4000000;
|
|
13
|
+
|
|
14
|
+
if (oldLines.length > MAX_LINES || newLines.length > MAX_LINES || oldLines.length * newLines.length > MAX_DIFF_PRODUCT) {
|
|
15
|
+
return buildTruncatedResult(oldLines.length, newLines.length);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const edits = myersDiff(oldLines, newLines);
|
|
19
|
+
if (edits === null) {
|
|
20
|
+
return buildTruncatedResult(oldLines.length, newLines.length);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return buildHunks(oldLines, newLines, edits, contextLines);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function buildTruncatedResult(oldCount, newCount) {
|
|
27
|
+
return [{
|
|
28
|
+
startA: 1,
|
|
29
|
+
startB: 1,
|
|
30
|
+
truncated: true,
|
|
31
|
+
lines: [{ type: 'context', text: `文件改动较大(${oldCount} → ${newCount} 行),请在 Overleaf 中查看完整差异。` }]
|
|
32
|
+
}];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function splitLines(text) {
|
|
36
|
+
const lines = String(text || '').split('\n');
|
|
37
|
+
if (lines.length > 0 && lines[lines.length - 1] === '') {
|
|
38
|
+
lines.pop();
|
|
39
|
+
}
|
|
40
|
+
return lines;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function myersDiff(oldLines, newLines) {
|
|
44
|
+
const n = oldLines.length;
|
|
45
|
+
const m = newLines.length;
|
|
46
|
+
const max = n + m;
|
|
47
|
+
|
|
48
|
+
if (max === 0) {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const vSize = 2 * max + 1;
|
|
53
|
+
let v = new Array(vSize).fill(0);
|
|
54
|
+
const trace = [];
|
|
55
|
+
|
|
56
|
+
const MAX_EDIT_DISTANCE = 1500;
|
|
57
|
+
|
|
58
|
+
for (let d = 0; d <= max; d++) {
|
|
59
|
+
if (d > MAX_EDIT_DISTANCE) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
trace.push([...v]);
|
|
63
|
+
const newV = [...v];
|
|
64
|
+
for (let k = -d; k <= d; k += 2) {
|
|
65
|
+
const kIndex = k + max;
|
|
66
|
+
let x;
|
|
67
|
+
if (k === -d || (k !== d && v[kIndex - 1] < v[kIndex + 1])) {
|
|
68
|
+
x = v[kIndex + 1];
|
|
69
|
+
} else {
|
|
70
|
+
x = v[kIndex - 1] + 1;
|
|
71
|
+
}
|
|
72
|
+
let y = x - k;
|
|
73
|
+
|
|
74
|
+
while (x < n && y < m && oldLines[x] === newLines[y]) {
|
|
75
|
+
x++;
|
|
76
|
+
y++;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
newV[kIndex] = x;
|
|
80
|
+
|
|
81
|
+
if (x >= n && y >= m) {
|
|
82
|
+
trace[d] = newV;
|
|
83
|
+
return backtrack(trace, oldLines, newLines, d, max);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
v = newV;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return buildFallbackEdits(oldLines, newLines);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function backtrack(trace, oldLines, newLines, finalD, max) {
|
|
93
|
+
const edits = [];
|
|
94
|
+
let x = oldLines.length;
|
|
95
|
+
let y = newLines.length;
|
|
96
|
+
|
|
97
|
+
for (let d = finalD; d >= 0; d--) {
|
|
98
|
+
const k = x - y;
|
|
99
|
+
|
|
100
|
+
let prevK, prevX, prevY;
|
|
101
|
+
if (d === 0) {
|
|
102
|
+
prevX = 0;
|
|
103
|
+
prevY = 0;
|
|
104
|
+
} else {
|
|
105
|
+
const prevVState = trace[d];
|
|
106
|
+
if (k === -d || (k !== d && prevVState[k - 1 + max] < prevVState[k + 1 + max])) {
|
|
107
|
+
prevK = k + 1;
|
|
108
|
+
} else {
|
|
109
|
+
prevK = k - 1;
|
|
110
|
+
}
|
|
111
|
+
prevX = prevVState[prevK + max];
|
|
112
|
+
prevY = prevX - prevK;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Snake: diagonal moves after the edit
|
|
116
|
+
const midX = d > 0 ? (prevK < k ? prevX + 1 : prevX) : 0;
|
|
117
|
+
const midY = d > 0 ? (prevK > k ? prevY + 1 : prevY) : 0;
|
|
118
|
+
|
|
119
|
+
while (x > midX && y > midY) {
|
|
120
|
+
x--;
|
|
121
|
+
y--;
|
|
122
|
+
edits.unshift({ type: 'equal', oldIndex: x, newIndex: y });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// The edit move itself
|
|
126
|
+
if (d > 0) {
|
|
127
|
+
if (prevK < k) {
|
|
128
|
+
// Moved right: deletion
|
|
129
|
+
x--;
|
|
130
|
+
edits.unshift({ type: 'remove', oldIndex: x });
|
|
131
|
+
} else {
|
|
132
|
+
// Moved down: insertion
|
|
133
|
+
y--;
|
|
134
|
+
edits.unshift({ type: 'add', newIndex: y });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Initial diagonal at d=0
|
|
140
|
+
while (x > 0 && y > 0) {
|
|
141
|
+
x--;
|
|
142
|
+
y--;
|
|
143
|
+
edits.unshift({ type: 'equal', oldIndex: x, newIndex: y });
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return edits;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function buildFallbackEdits(oldLines, newLines) {
|
|
150
|
+
const edits = [];
|
|
151
|
+
for (let i = 0; i < oldLines.length; i++) {
|
|
152
|
+
edits.push({ type: 'remove', oldIndex: i });
|
|
153
|
+
}
|
|
154
|
+
for (let i = 0; i < newLines.length; i++) {
|
|
155
|
+
edits.push({ type: 'add', newIndex: i });
|
|
156
|
+
}
|
|
157
|
+
return edits;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function buildHunks(oldLines, newLines, edits, contextLines) {
|
|
161
|
+
const changes = edits.filter(e => e.type !== 'equal');
|
|
162
|
+
if (!changes.length) {
|
|
163
|
+
return [];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const changeIndices = [];
|
|
167
|
+
for (let i = 0; i < edits.length; i++) {
|
|
168
|
+
if (edits[i].type !== 'equal') {
|
|
169
|
+
changeIndices.push(i);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const groups = [];
|
|
174
|
+
let currentGroup = [changeIndices[0]];
|
|
175
|
+
|
|
176
|
+
for (let i = 1; i < changeIndices.length; i++) {
|
|
177
|
+
const gap = changeIndices[i] - changeIndices[i - 1] - 1;
|
|
178
|
+
if (gap <= contextLines * 2) {
|
|
179
|
+
currentGroup.push(changeIndices[i]);
|
|
180
|
+
} else {
|
|
181
|
+
groups.push(currentGroup);
|
|
182
|
+
currentGroup = [changeIndices[i]];
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
groups.push(currentGroup);
|
|
186
|
+
|
|
187
|
+
const hunks = [];
|
|
188
|
+
for (const group of groups) {
|
|
189
|
+
const firstIdx = Math.max(0, group[0] - contextLines);
|
|
190
|
+
const lastIdx = Math.min(edits.length - 1, group[group.length - 1] + contextLines);
|
|
191
|
+
const hunkEdits = edits.slice(firstIdx, lastIdx + 1);
|
|
192
|
+
|
|
193
|
+
const lines = [];
|
|
194
|
+
let startA = -1;
|
|
195
|
+
let startB = -1;
|
|
196
|
+
|
|
197
|
+
for (const edit of hunkEdits) {
|
|
198
|
+
if (edit.type === 'equal') {
|
|
199
|
+
if (startA === -1) {
|
|
200
|
+
startA = edit.oldIndex;
|
|
201
|
+
startB = edit.newIndex;
|
|
202
|
+
}
|
|
203
|
+
lines.push({ type: 'context', text: oldLines[edit.oldIndex] });
|
|
204
|
+
} else if (edit.type === 'remove') {
|
|
205
|
+
if (startA === -1) {
|
|
206
|
+
startA = edit.oldIndex;
|
|
207
|
+
startB = findNewIndex(edits, firstIdx, edit.oldIndex);
|
|
208
|
+
}
|
|
209
|
+
lines.push({ type: 'remove', text: oldLines[edit.oldIndex] });
|
|
210
|
+
} else if (edit.type === 'add') {
|
|
211
|
+
if (startA === -1) {
|
|
212
|
+
startA = findOldIndex(edits, firstIdx, edit.newIndex);
|
|
213
|
+
startB = edit.newIndex;
|
|
214
|
+
}
|
|
215
|
+
lines.push({ type: 'add', text: newLines[edit.newIndex] });
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
hunks.push({
|
|
220
|
+
startA: startA >= 0 ? startA + 1 : 1,
|
|
221
|
+
startB: startB >= 0 ? startB + 1 : 1,
|
|
222
|
+
lines
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return hunks;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function findNewIndex(edits, startFrom, oldIndex) {
|
|
230
|
+
for (let i = startFrom; i < edits.length; i++) {
|
|
231
|
+
if (edits[i].newIndex !== undefined) {
|
|
232
|
+
return edits[i].newIndex;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return 0;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function findOldIndex(edits, startFrom, newIndex) {
|
|
239
|
+
for (let i = startFrom; i < edits.length; i++) {
|
|
240
|
+
if (edits[i].oldIndex !== undefined) {
|
|
241
|
+
return edits[i].oldIndex;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return 0;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
module.exports = { computeLineDiff };
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const { decodeFrames, encodeMessage } = require('./nativeMessaging');
|
|
5
|
+
const { logDebug } = require('./debugLog');
|
|
6
|
+
const { handleRequest } = require('./taskRunner');
|
|
7
|
+
const { buildNativeRuntimeEnv, summarizeNativeEnvironment } = require('./nativeEnvironment');
|
|
8
|
+
|
|
9
|
+
let buffered = Buffer.alloc(0);
|
|
10
|
+
const runtimeEnv = buildNativeRuntimeEnv(process.env);
|
|
11
|
+
Object.assign(process.env, runtimeEnv);
|
|
12
|
+
logDebug('environment.ready', summarizeNativeEnvironment(runtimeEnv));
|
|
13
|
+
|
|
14
|
+
process.stdin.on('data', chunk => {
|
|
15
|
+
logDebug('stdin.data', { bytes: chunk.length, bufferedBytes: buffered.length });
|
|
16
|
+
buffered = Buffer.concat([buffered, chunk]);
|
|
17
|
+
|
|
18
|
+
let decoded;
|
|
19
|
+
try {
|
|
20
|
+
decoded = decodeFrames(buffered);
|
|
21
|
+
} catch (error) {
|
|
22
|
+
writeResponse({
|
|
23
|
+
ok: false,
|
|
24
|
+
error: {
|
|
25
|
+
code: 'invalid_native_message',
|
|
26
|
+
message: error.message
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
buffered = Buffer.alloc(0);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
buffered = decoded.remainder;
|
|
34
|
+
for (const message of decoded.messages) {
|
|
35
|
+
handleDecodedMessage(message);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
async function handleDecodedMessage(message) {
|
|
40
|
+
try {
|
|
41
|
+
logDebug('request.received', summarizeRequest(message));
|
|
42
|
+
const response = await handleRequest(message, runtimeEnv, event => {
|
|
43
|
+
writeResponse({
|
|
44
|
+
id: message?.id,
|
|
45
|
+
ok: true,
|
|
46
|
+
event
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
logDebug('response.ready', summarizeResponse(response));
|
|
50
|
+
writeResponse(response);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
const response = {
|
|
53
|
+
id: message?.id,
|
|
54
|
+
ok: false,
|
|
55
|
+
error: {
|
|
56
|
+
code: 'internal_error',
|
|
57
|
+
message: error.message
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
logDebug('response.internal_error', summarizeResponse(response));
|
|
61
|
+
writeResponse(response);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
process.stdin.on('error', error => {
|
|
66
|
+
logDebug('stdin.error', { message: error.message });
|
|
67
|
+
console.error(`stdin error: ${error.message}`);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
process.on('uncaughtException', error => {
|
|
71
|
+
logDebug('process.uncaught_exception', {
|
|
72
|
+
message: error.message,
|
|
73
|
+
stack: error.stack
|
|
74
|
+
});
|
|
75
|
+
process.exit(1);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
process.on('unhandledRejection', reason => {
|
|
79
|
+
logDebug('process.unhandled_rejection', {
|
|
80
|
+
message: reason?.message || String(reason),
|
|
81
|
+
stack: reason?.stack
|
|
82
|
+
});
|
|
83
|
+
process.exit(1);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
function writeResponse(response) {
|
|
87
|
+
const frame = encodeMessage(response);
|
|
88
|
+
logDebug('stdout.write', { bytes: frame.length, ok: response?.ok, code: response?.error?.code });
|
|
89
|
+
process.stdout.write(frame);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function summarizeRequest(message) {
|
|
93
|
+
const params = message?.params || {};
|
|
94
|
+
return {
|
|
95
|
+
id: message?.id,
|
|
96
|
+
method: message?.method,
|
|
97
|
+
mode: params.mode,
|
|
98
|
+
model: params.model,
|
|
99
|
+
reasoningEffort: params.reasoningEffort,
|
|
100
|
+
taskLength: String(params.task || '').length,
|
|
101
|
+
reviewingOk: params.reviewing?.ok,
|
|
102
|
+
checkpointOk: params.checkpoint?.ok,
|
|
103
|
+
activePath: params.project?.activePath,
|
|
104
|
+
fileCount: Array.isArray(params.project?.files) ? params.project.files.length : 0,
|
|
105
|
+
fileSummary: summarizeProjectFiles(params.project?.files)
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function summarizeProjectFiles(files) {
|
|
110
|
+
return (Array.isArray(files) ? files : []).slice(0, 50).map(file => ({
|
|
111
|
+
path: file?.path,
|
|
112
|
+
kind: file?.kind || (file?.contentBase64 ? 'binary' : 'text'),
|
|
113
|
+
size: Number(file?.size || file?.byteLength || 0) || stringByteLength(file?.content)
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function stringByteLength(value) {
|
|
118
|
+
return typeof value === 'string' ? Buffer.byteLength(value, 'utf8') : 0;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function summarizeResponse(response) {
|
|
122
|
+
return {
|
|
123
|
+
id: response?.id,
|
|
124
|
+
ok: response?.ok,
|
|
125
|
+
code: response?.error?.code,
|
|
126
|
+
message: response?.error?.message,
|
|
127
|
+
status: response?.result?.status,
|
|
128
|
+
operationCount: Array.isArray(response?.result?.operations) ? response.result.operations.length : 0,
|
|
129
|
+
hasDeletePlan: Boolean(response?.result?.deletePlan),
|
|
130
|
+
hasPlanId: Boolean(response?.result?.planId)
|
|
131
|
+
};
|
|
132
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const DEFAULT_POSIX_PATH_SEGMENTS = [
|
|
4
|
+
'/Library/TeX/texbin',
|
|
5
|
+
'/opt/homebrew/bin',
|
|
6
|
+
'/usr/local/bin',
|
|
7
|
+
'/usr/bin',
|
|
8
|
+
'/bin',
|
|
9
|
+
'/usr/sbin',
|
|
10
|
+
'/sbin'
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
function buildLauncher({ platform = process.platform, nodePath, bridgeEntryPath, agentPath, defaultPathSegments }) {
|
|
14
|
+
if (platform === 'win32') {
|
|
15
|
+
return buildWindowsLauncherScript({ nodePath, bridgeEntryPath, agentPath });
|
|
16
|
+
}
|
|
17
|
+
if (platform === 'darwin' || platform === 'linux') {
|
|
18
|
+
return buildLauncherScript({ nodePath, bridgeEntryPath, agentPath, defaultPathSegments });
|
|
19
|
+
}
|
|
20
|
+
throw new Error(`Unsupported launcher platform: ${platform}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function buildLauncherScript({ nodePath, bridgeEntryPath, agentPath, defaultPathSegments = DEFAULT_POSIX_PATH_SEGMENTS }) {
|
|
24
|
+
const launcherPath = defaultPathSegments.join(':');
|
|
25
|
+
return [
|
|
26
|
+
'#!/bin/sh',
|
|
27
|
+
'set -eu',
|
|
28
|
+
'HOME_DIR="${HOME:-/tmp}"',
|
|
29
|
+
'LOG_DIR="$HOME_DIR/.codex-overleaf"',
|
|
30
|
+
'mkdir -p "$LOG_DIR" 2>/dev/null || true',
|
|
31
|
+
'{',
|
|
32
|
+
' printf \'%s launcher.start pid=%s ppid=%s home=%s path=%s\\n\' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$$" "${PPID:-}" "$HOME_DIR" "${PATH:-}"',
|
|
33
|
+
' printf \'%s launcher.node=%s\\n\' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "' + escapeShellSingleQuoted(nodePath) + '"',
|
|
34
|
+
'} >> "$LOG_DIR/native-host-launcher.log" 2>/dev/null || true',
|
|
35
|
+
`export PATH="${escapeDoubleQuoted(launcherPath)}:\${PATH:-}"`,
|
|
36
|
+
`export CODEX_OVERLEAF_AGENT_FILE=${shellSingleQuoted(nodePath)}`,
|
|
37
|
+
`export CODEX_OVERLEAF_AGENT_ARGS_JSON=${shellSingleQuoted(JSON.stringify([agentPath]))}`,
|
|
38
|
+
`exec "${escapeDoubleQuoted(nodePath)}" "${escapeDoubleQuoted(bridgeEntryPath)}"`,
|
|
39
|
+
''
|
|
40
|
+
].join('\n');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function buildWindowsLauncherScript({ nodePath, bridgeEntryPath, agentPath }) {
|
|
44
|
+
return [
|
|
45
|
+
'@echo off',
|
|
46
|
+
'setlocal',
|
|
47
|
+
`set "CODEX_OVERLEAF_AGENT_FILE=${escapeBatchSetValue(nodePath)}"`,
|
|
48
|
+
`set CODEX_OVERLEAF_AGENT_ARGS_JSON=${escapeBatchRawSetValue(JSON.stringify([agentPath]))}`,
|
|
49
|
+
`"${escapeBatchCommandArg(nodePath)}" "${escapeBatchCommandArg(bridgeEntryPath)}"`,
|
|
50
|
+
''
|
|
51
|
+
].join('\r\n');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function escapeDoubleQuoted(value) {
|
|
55
|
+
return String(value).replace(/(["\\$`])/g, '\\$1');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function escapeShellSingleQuoted(value) {
|
|
59
|
+
return String(value).replace(/'/g, "'\\''");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function shellSingleQuoted(value) {
|
|
63
|
+
return `'${escapeShellSingleQuoted(value)}'`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function escapeBatchSetValue(value) {
|
|
67
|
+
return String(value).replace(/%/g, '%%').replace(/"/g, '""');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function escapeBatchCommandArg(value) {
|
|
71
|
+
return String(value).replace(/%/g, '%%').replace(/"/g, '""');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function escapeBatchRawSetValue(value) {
|
|
75
|
+
return String(value).replace(/%/g, '%%').replace(/[\r\n]/g, '');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = {
|
|
79
|
+
buildLauncher,
|
|
80
|
+
buildLauncherScript
|
|
81
|
+
};
|