@thegitai/cli 1.0.0-beta.2 → 1.0.0-beta.20
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 +37 -2
- package/dist/bin/ai.js +148 -75
- package/dist/parsers/NOTICE +18 -0
- package/dist/src/agent-mode.js +5 -0
- package/dist/src/api/auth.js +6 -4
- package/dist/src/api/browser-login.js +7 -41
- package/dist/src/api/chat.js +77 -20
- package/dist/src/api/http.js +81 -4
- package/dist/src/api/models.js +26 -18
- package/dist/src/artifact-policy.js +12 -0
- package/dist/src/background-jobs.js +410 -0
- package/dist/src/cli-args.js +60 -0
- package/dist/src/client-environment.js +129 -0
- package/dist/src/colors.js +50 -0
- package/dist/src/core/clipboard.js +75 -0
- package/dist/src/core/image-path-extractor.js +144 -0
- package/dist/src/edit-journal.js +39 -6
- package/dist/src/executor.js +48 -12
- package/dist/src/help-text.js +24 -5
- package/dist/src/markdown-renderer.js +1 -1
- package/dist/src/patcher.js +17 -2
- package/dist/src/scanner.js +58 -17
- package/dist/src/scratch-dir.js +57 -0
- package/dist/src/secret-preview.js +0 -10
- package/dist/src/session-safety.js +64 -31
- package/dist/src/session-store.js +0 -1
- package/dist/src/todo-list.js +106 -0
- package/dist/src/tool-executor.js +164 -18
- package/dist/src/tools/delete-file.js +1 -1
- package/dist/src/tools/index.js +8 -0
- package/dist/src/tools/patch-file.js +16 -2
- package/dist/src/tools/path-suggest.js +139 -0
- package/dist/src/tools/read-document.js +15 -4
- package/dist/src/tools/read-file.js +23 -7
- package/dist/src/tools/replace-document-text.js +234 -0
- package/dist/src/tools/restore-checkpoint.js +1 -1
- package/dist/src/tools/run-command.js +83 -16
- package/dist/src/tools/run-node-script.js +3 -1
- package/dist/src/tools/shell-job-kill.js +48 -0
- package/dist/src/tools/shell-job-output.js +51 -0
- package/dist/src/tools/str-replace.js +16 -2
- package/dist/src/tools/undo-edit.js +7 -5
- package/dist/src/tools/update-todos.js +27 -0
- package/dist/src/tools/write-file.js +14 -1
- package/dist/src/tree-sitter-runtime.js +8 -1
- package/dist/src/ui/repl.js +315 -24
- package/dist/src/ui/tui/bridge.js +2 -6
- package/dist/src/ui/tui/build-frame.js +224 -25
- package/dist/src/ui/tui/shell-input.js +42 -5
- package/dist/src/version.js +29 -0
- package/dist/vendor/web-tree-sitter/LICENSE +21 -0
- package/dist/vendor/web-tree-sitter/NOTICE +13 -0
- package/dist/vendor/web-tree-sitter/web-tree-sitter.cjs +4063 -0
- package/dist/vendor/web-tree-sitter/web-tree-sitter.wasm +0 -0
- package/package.json +14 -15
package/dist/src/api/chat.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { drainBackgroundJobNotifications } from '../background-jobs.js';
|
|
1
2
|
import { createPromptCheckpoint, sanitizeSessionSafetyForServer, } from '../session-safety.js';
|
|
2
3
|
import { applySessionSnapshot, snapshotFromSession, } from '../session-store.js';
|
|
3
4
|
import { executeLocalToolCall } from '../tool-executor.js';
|
|
4
|
-
import { normalizeServerUrl, readErrorResponse, } from './http.js';
|
|
5
|
+
import { createTraceContext, normalizeServerUrl, readErrorResponse, } from './http.js';
|
|
6
|
+
import { collectClientEnvironment } from '../client-environment.js';
|
|
7
|
+
import { autoAttachImages } from '../core/image-path-extractor.js';
|
|
5
8
|
export class TurnCancelledError extends Error {
|
|
6
9
|
name = 'TurnCancelledError';
|
|
7
10
|
constructor(message = 'Turn cancelled.') {
|
|
@@ -12,10 +15,12 @@ export class ChatTurnFailedError extends Error {
|
|
|
12
15
|
name = 'ChatTurnFailedError';
|
|
13
16
|
category;
|
|
14
17
|
retryable;
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
traceId;
|
|
19
|
+
constructor(message, category = 'unknown_error', retryable = false, traceId = '') {
|
|
20
|
+
super(traceId ? `${message}\nTrace ID: ${traceId}` : message);
|
|
17
21
|
this.category = category;
|
|
18
22
|
this.retryable = retryable;
|
|
23
|
+
this.traceId = traceId;
|
|
19
24
|
}
|
|
20
25
|
}
|
|
21
26
|
export function isTurnCancelledError(error) {
|
|
@@ -60,6 +65,11 @@ function snapshotForServer(session) {
|
|
|
60
65
|
snapshot.clientState.safety = sanitizeSessionSafetyForServer(snapshot.clientState.safety);
|
|
61
66
|
return snapshot;
|
|
62
67
|
}
|
|
68
|
+
function imageAttachmentsForServer(attachments) {
|
|
69
|
+
return (attachments ?? []).map(({ filePath, ...attachment }) => attachment.source === 'file' && filePath
|
|
70
|
+
? { ...attachment, filePath }
|
|
71
|
+
: attachment);
|
|
72
|
+
}
|
|
63
73
|
function userHistoryText(entry) {
|
|
64
74
|
return (entry.parts ?? [])
|
|
65
75
|
.map((part) => (typeof part?.text === 'string' ? part.text : ''))
|
|
@@ -139,17 +149,48 @@ function publicStatusMessage(data) {
|
|
|
139
149
|
return `Running ${toolName} locally...`;
|
|
140
150
|
return null;
|
|
141
151
|
}
|
|
142
|
-
|
|
152
|
+
function normalizeShellJobToolCall(call) {
|
|
153
|
+
if (call.name !== 'shell_job_output' && call.name !== 'shell_job_kill') {
|
|
154
|
+
return call;
|
|
155
|
+
}
|
|
156
|
+
const args = call.args && typeof call.args === 'object' && !Array.isArray(call.args)
|
|
157
|
+
? { ...call.args }
|
|
158
|
+
: {};
|
|
159
|
+
let changed = false;
|
|
160
|
+
if (args.job_id === undefined) {
|
|
161
|
+
const alias = args.jobId ?? args.id;
|
|
162
|
+
if (alias !== undefined) {
|
|
163
|
+
args.job_id = alias;
|
|
164
|
+
delete args.jobId;
|
|
165
|
+
delete args.id;
|
|
166
|
+
changed = true;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (call.name === 'shell_job_output' && args.wait_ms === undefined) {
|
|
170
|
+
const alias = args.waitMs ?? args.wait ?? args.wait_millis;
|
|
171
|
+
if (alias !== undefined) {
|
|
172
|
+
args.wait_ms = alias;
|
|
173
|
+
delete args.waitMs;
|
|
174
|
+
delete args.wait;
|
|
175
|
+
delete args.wait_millis;
|
|
176
|
+
changed = true;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return changed ? { ...call, args } : call;
|
|
180
|
+
}
|
|
181
|
+
async function postToolResult({ config, turnId, event, result, session, fetchImpl, traceId, }) {
|
|
143
182
|
const payload = {
|
|
144
183
|
toolCallId: event.call.id,
|
|
145
184
|
result,
|
|
146
185
|
toolState: toolStateFromSession(session),
|
|
147
186
|
};
|
|
187
|
+
const trace = createTraceContext(traceId);
|
|
148
188
|
const response = await fetchImpl(`${normalizeServerUrl(config.serverUrl)}/v1/chat/turn/${encodeURIComponent(turnId)}/tool-result`, {
|
|
149
189
|
method: 'POST',
|
|
150
190
|
headers: {
|
|
151
191
|
authorization: `Bearer ${config.token}`,
|
|
152
192
|
'content-type': 'application/json',
|
|
193
|
+
...trace.headers,
|
|
153
194
|
},
|
|
154
195
|
body: JSON.stringify(payload),
|
|
155
196
|
});
|
|
@@ -157,10 +198,10 @@ async function postToolResult({ config, turnId, event, result, session, fetchImp
|
|
|
157
198
|
return;
|
|
158
199
|
}
|
|
159
200
|
if (!response.ok) {
|
|
160
|
-
throw await readErrorResponse(response);
|
|
201
|
+
throw await readErrorResponse(response, trace.traceId);
|
|
161
202
|
}
|
|
162
203
|
}
|
|
163
|
-
async function executeAndPostToolResult({ config, projectIndex, session, event, input, fetchImpl, signal, }) {
|
|
204
|
+
async function executeAndPostToolResult({ config, projectIndex, session, event, input, fetchImpl, signal, traceId, }) {
|
|
164
205
|
const turnId = String(event?.turnId ?? '').trim();
|
|
165
206
|
if (!turnId || !event?.call?.id || !event.call.name) {
|
|
166
207
|
throw new Error('Server emitted an invalid tool-call event.');
|
|
@@ -177,8 +218,9 @@ async function executeAndPostToolResult({ config, projectIndex, session, event,
|
|
|
177
218
|
}
|
|
178
219
|
}
|
|
179
220
|
try {
|
|
180
|
-
const
|
|
181
|
-
|
|
221
|
+
const call = normalizeShellJobToolCall(event.call);
|
|
222
|
+
const rawResult = await executeLocalToolCall({ projectIndex }, session, call);
|
|
223
|
+
preserveCancelledTurnToolResult(session, input, { ...event, call }, rawResult);
|
|
182
224
|
if (signal?.aborted) {
|
|
183
225
|
throw new TurnCancelledError();
|
|
184
226
|
}
|
|
@@ -189,13 +231,14 @@ async function executeAndPostToolResult({ config, projectIndex, session, event,
|
|
|
189
231
|
result: rawResult,
|
|
190
232
|
session,
|
|
191
233
|
fetchImpl,
|
|
234
|
+
traceId,
|
|
192
235
|
});
|
|
193
236
|
}
|
|
194
237
|
finally {
|
|
195
238
|
session.turnState.id = previousTurnId;
|
|
196
239
|
}
|
|
197
240
|
}
|
|
198
|
-
async function consumeTurnStream({ response, config, projectIndex, session, input, fetchImpl, signal, }) {
|
|
241
|
+
async function consumeTurnStream({ response, config, projectIndex, session, input, fetchImpl, signal, traceId, }) {
|
|
199
242
|
if (!response.body) {
|
|
200
243
|
throw new Error('Server returned an empty chat stream.');
|
|
201
244
|
}
|
|
@@ -224,6 +267,7 @@ async function consumeTurnStream({ response, config, projectIndex, session, inpu
|
|
|
224
267
|
input,
|
|
225
268
|
fetchImpl,
|
|
226
269
|
signal,
|
|
270
|
+
traceId,
|
|
227
271
|
});
|
|
228
272
|
return;
|
|
229
273
|
}
|
|
@@ -243,7 +287,7 @@ async function consumeTurnStream({ response, config, projectIndex, session, inpu
|
|
|
243
287
|
if (event.event === 'cancelled') {
|
|
244
288
|
throw new TurnCancelledError(message);
|
|
245
289
|
}
|
|
246
|
-
throw new ChatTurnFailedError(message, typeof event.data?.category === 'string' ? event.data.category : 'unknown_error', Boolean(event.data?.retryable));
|
|
290
|
+
throw new ChatTurnFailedError(message, typeof event.data?.category === 'string' ? event.data.category : 'unknown_error', Boolean(event.data?.retryable), typeof event.data?.traceId === 'string' ? event.data.traceId : traceId);
|
|
247
291
|
}
|
|
248
292
|
}
|
|
249
293
|
while (true) {
|
|
@@ -278,17 +322,31 @@ async function consumeTurnStream({ response, config, projectIndex, session, inpu
|
|
|
278
322
|
return finalResult.current;
|
|
279
323
|
}
|
|
280
324
|
export async function sendServerUserMessage({ config, projectIndex, session, input, imageAttachments = [], fetchImpl = globalThis.fetch, signal, }) {
|
|
325
|
+
const autoAttach = autoAttachImages(input, session.rootDir, imageAttachments);
|
|
326
|
+
const requestImageAttachments = autoAttach.attachments.length > 0
|
|
327
|
+
? [...imageAttachments, ...autoAttach.attachments]
|
|
328
|
+
: imageAttachments;
|
|
329
|
+
const requestInputBase = autoAttach.attachments.length > 0 ? autoAttach.sanitizedInput : input;
|
|
330
|
+
const backgroundJobUpdate = drainBackgroundJobNotifications({
|
|
331
|
+
sessionId: session.sessionId,
|
|
332
|
+
});
|
|
333
|
+
for (const err of autoAttach.errors) {
|
|
334
|
+
session.onStatus(`Image: ${err}`);
|
|
335
|
+
}
|
|
281
336
|
const request = {
|
|
282
337
|
modelId: session.modelId,
|
|
283
338
|
session: snapshotForServer(session),
|
|
284
|
-
input,
|
|
285
|
-
|
|
339
|
+
input: requestInputBase,
|
|
340
|
+
backgroundJobUpdate: backgroundJobUpdate || undefined,
|
|
341
|
+
clientEnvironment: collectClientEnvironment({ env: session.env }),
|
|
342
|
+
imageAttachments: imageAttachmentsForServer(requestImageAttachments),
|
|
286
343
|
maxToolSteps: session.maxToolSteps,
|
|
287
344
|
autoYes: session.autoYes,
|
|
288
345
|
agentMode: session.agentMode,
|
|
289
346
|
};
|
|
347
|
+
const trace = createTraceContext();
|
|
290
348
|
const preTurnHistoryLength = session.history.length;
|
|
291
|
-
const preserveOnAbort = () => preserveCancelledTurnInput(session,
|
|
349
|
+
const preserveOnAbort = () => preserveCancelledTurnInput(session, requestInputBase);
|
|
292
350
|
if (signal?.aborted) {
|
|
293
351
|
preserveOnAbort();
|
|
294
352
|
}
|
|
@@ -302,21 +360,23 @@ export async function sendServerUserMessage({ config, projectIndex, session, inp
|
|
|
302
360
|
accept: 'text/event-stream',
|
|
303
361
|
authorization: `Bearer ${config.token}`,
|
|
304
362
|
'content-type': 'application/json',
|
|
363
|
+
...trace.headers,
|
|
305
364
|
},
|
|
306
365
|
body: JSON.stringify(request),
|
|
307
366
|
signal,
|
|
308
367
|
});
|
|
309
368
|
if (!response.ok) {
|
|
310
|
-
throw await readErrorResponse(response);
|
|
369
|
+
throw await readErrorResponse(response, trace.traceId);
|
|
311
370
|
}
|
|
312
371
|
const result = await consumeTurnStream({
|
|
313
372
|
response,
|
|
314
373
|
config,
|
|
315
374
|
projectIndex,
|
|
316
375
|
session,
|
|
317
|
-
input,
|
|
376
|
+
input: requestInputBase,
|
|
318
377
|
fetchImpl,
|
|
319
378
|
signal,
|
|
379
|
+
traceId: trace.traceId,
|
|
320
380
|
});
|
|
321
381
|
applySessionSnapshot(session, result.snapshot, { preserveAgentMode: true });
|
|
322
382
|
return {
|
|
@@ -328,16 +388,13 @@ export async function sendServerUserMessage({ config, projectIndex, session, inp
|
|
|
328
388
|
}
|
|
329
389
|
catch (error) {
|
|
330
390
|
if (isTurnCancelledError(error)) {
|
|
331
|
-
preserveCancelledTurnInput(session,
|
|
391
|
+
preserveCancelledTurnInput(session, requestInputBase);
|
|
332
392
|
throw error instanceof TurnCancelledError
|
|
333
393
|
? error
|
|
334
394
|
: new TurnCancelledError();
|
|
335
395
|
}
|
|
336
|
-
// Non-cancel failures (e.g. upstream connection errors) must not leave
|
|
337
|
-
// speculative cancelled-turn entries in history — otherwise the next
|
|
338
|
-
// request replays a malformed transcript to the server.
|
|
339
396
|
session.history.length = preTurnHistoryLength;
|
|
340
|
-
preserveFailedTurnInput(session,
|
|
397
|
+
preserveFailedTurnInput(session, requestInputBase, error instanceof ChatTurnFailedError ? error.category : 'unknown_error');
|
|
341
398
|
throw error;
|
|
342
399
|
}
|
|
343
400
|
finally {
|
package/dist/src/api/http.js
CHANGED
|
@@ -1,4 +1,78 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
1
2
|
const DEFAULT_SERVER_URL = 'https://thegit.ai';
|
|
3
|
+
export const TRACE_ID_HEADER = 'x-thegitai-trace-id';
|
|
4
|
+
export const CLIENT_HEADER = 'x-thegitai-client';
|
|
5
|
+
export const CLIENT_PLATFORM_HEADER = 'x-thegitai-client-platform';
|
|
6
|
+
export class ServerApiError extends Error {
|
|
7
|
+
status;
|
|
8
|
+
traceId;
|
|
9
|
+
constructor(message, status, traceId) {
|
|
10
|
+
super(traceId ? `${message}\nTrace ID: ${traceId}` : message);
|
|
11
|
+
this.name = 'ServerApiError';
|
|
12
|
+
this.status = status;
|
|
13
|
+
this.traceId = traceId;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function createTraceId() {
|
|
17
|
+
return `tr_${randomUUID().replace(/-/g, '')}`;
|
|
18
|
+
}
|
|
19
|
+
export function createTraceContext(traceId = createTraceId()) {
|
|
20
|
+
return {
|
|
21
|
+
traceId,
|
|
22
|
+
headers: {
|
|
23
|
+
[TRACE_ID_HEADER]: traceId,
|
|
24
|
+
[CLIENT_HEADER]: 'cli',
|
|
25
|
+
[CLIENT_PLATFORM_HEADER]: `${process.platform}/${process.arch}`,
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export const REQUEST_TIMEOUT_MS = 8000;
|
|
30
|
+
const TRANSIENT_NETWORK_CODES = new Set([
|
|
31
|
+
'ECONNRESET',
|
|
32
|
+
'ECONNREFUSED',
|
|
33
|
+
'ETIMEDOUT',
|
|
34
|
+
'EAI_AGAIN',
|
|
35
|
+
'ENOTFOUND',
|
|
36
|
+
'ENETUNREACH',
|
|
37
|
+
'EHOSTUNREACH',
|
|
38
|
+
'EPIPE',
|
|
39
|
+
'UND_ERR_CONNECT_TIMEOUT',
|
|
40
|
+
'UND_ERR_SOCKET',
|
|
41
|
+
'UND_ERR_HEADERS_TIMEOUT',
|
|
42
|
+
'UND_ERR_BODY_TIMEOUT',
|
|
43
|
+
]);
|
|
44
|
+
export function isTransientNetworkError(error) {
|
|
45
|
+
if (error instanceof ServerApiError)
|
|
46
|
+
return false;
|
|
47
|
+
if (!error || typeof error !== 'object')
|
|
48
|
+
return false;
|
|
49
|
+
const err = error;
|
|
50
|
+
if (err.name === 'AbortError' || err.name === 'TimeoutError')
|
|
51
|
+
return true;
|
|
52
|
+
const code = err.code ?? err.cause?.code;
|
|
53
|
+
if (code) {
|
|
54
|
+
return TRANSIENT_NETWORK_CODES.has(code);
|
|
55
|
+
}
|
|
56
|
+
if (error instanceof TypeError && /fetch failed/i.test(err.message ?? '')) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
export async function retryTransient(run, { retries = 2, baseDelayMs = 400 } = {}) {
|
|
62
|
+
let attempt = 0;
|
|
63
|
+
for (;;) {
|
|
64
|
+
try {
|
|
65
|
+
return await run();
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (attempt >= retries || !isTransientNetworkError(error))
|
|
69
|
+
throw error;
|
|
70
|
+
const delayMs = baseDelayMs * 2 ** attempt;
|
|
71
|
+
attempt += 1;
|
|
72
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
2
76
|
export function normalizeServerUrl(serverUrl) {
|
|
3
77
|
const normalized = String(serverUrl || DEFAULT_SERVER_URL)
|
|
4
78
|
.trim()
|
|
@@ -22,23 +96,26 @@ export async function readJsonResponse(response) {
|
|
|
22
96
|
export function failureMessage(data, status) {
|
|
23
97
|
return String(data?.error?.message ?? data?.message ?? `Request failed with ${status}`);
|
|
24
98
|
}
|
|
25
|
-
export async function readErrorResponse(response) {
|
|
99
|
+
export async function readErrorResponse(response, traceId = response.headers.get(TRACE_ID_HEADER) ?? '') {
|
|
26
100
|
const data = await readJsonResponse(response);
|
|
27
|
-
return new
|
|
101
|
+
return new ServerApiError(failureMessage(data, response.status), response.status, traceId);
|
|
28
102
|
}
|
|
29
|
-
export async function authorizedJson({ config, path, method = 'GET', body = null, headers = {}, fetchImpl = globalThis.fetch, }) {
|
|
103
|
+
export async function authorizedJson({ config, path, method = 'GET', body = null, headers = {}, fetchImpl = globalThis.fetch, timeoutMs = REQUEST_TIMEOUT_MS, }) {
|
|
104
|
+
const trace = createTraceContext();
|
|
30
105
|
const response = await fetchImpl(`${normalizeServerUrl(config.serverUrl)}${path}`, {
|
|
31
106
|
method,
|
|
32
107
|
headers: {
|
|
33
108
|
authorization: `Bearer ${config.token}`,
|
|
109
|
+
...trace.headers,
|
|
34
110
|
...headers,
|
|
35
111
|
...(body === null ? {} : { 'content-type': 'application/json' }),
|
|
36
112
|
},
|
|
37
113
|
body: body === null ? undefined : JSON.stringify(body),
|
|
114
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
38
115
|
});
|
|
39
116
|
const data = await readJsonResponse(response);
|
|
40
117
|
if (!response.ok) {
|
|
41
|
-
throw new
|
|
118
|
+
throw new ServerApiError(failureMessage(data, response.status), response.status, trace.traceId);
|
|
42
119
|
}
|
|
43
120
|
return data;
|
|
44
121
|
}
|
package/dist/src/api/models.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { getClientStateDir } from '../client-state.js';
|
|
4
|
-
import { failureMessage, normalizeServerUrl, readJsonResponse, } from './http.js';
|
|
4
|
+
import { REQUEST_TIMEOUT_MS, ServerApiError, createTraceContext, failureMessage, normalizeServerUrl, readJsonResponse, retryTransient, } from './http.js';
|
|
5
5
|
function sanitizeModelInfo(raw) {
|
|
6
6
|
if (!raw || typeof raw !== 'object') {
|
|
7
7
|
return null;
|
|
@@ -45,6 +45,11 @@ export function readCachedServerModels(env = process.env) {
|
|
|
45
45
|
return null;
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
|
+
export function selectCacheForServer(cached, serverUrl) {
|
|
49
|
+
if (!cached)
|
|
50
|
+
return null;
|
|
51
|
+
return cached.serverUrl === normalizeServerUrl(serverUrl) ? cached : null;
|
|
52
|
+
}
|
|
48
53
|
export function writeCachedServerModels(cache, env = process.env) {
|
|
49
54
|
const filePath = getModelsCachePath(env);
|
|
50
55
|
mkdirSync(path.dirname(filePath), { recursive: true, mode: 0o700 });
|
|
@@ -54,24 +59,27 @@ export function writeCachedServerModels(cache, env = process.env) {
|
|
|
54
59
|
});
|
|
55
60
|
}
|
|
56
61
|
export async function fetchServerModels({ config, fetchImpl = globalThis.fetch, }) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
return retryTransient(async () => {
|
|
63
|
+
const trace = createTraceContext();
|
|
64
|
+
const response = await fetchImpl(`${normalizeServerUrl(config.serverUrl)}/v1/models`, {
|
|
65
|
+
headers: {
|
|
66
|
+
authorization: `Bearer ${config.token}`,
|
|
67
|
+
...trace.headers,
|
|
68
|
+
},
|
|
69
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
70
|
+
});
|
|
71
|
+
const data = (await readJsonResponse(response));
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
throw new ServerApiError(failureMessage(data, response.status), response.status, trace.traceId);
|
|
74
|
+
}
|
|
75
|
+
const models = Array.isArray(data?.models)
|
|
76
|
+
? data.models.map(sanitizeModelInfo).filter(Boolean)
|
|
77
|
+
: [];
|
|
78
|
+
if (models.length === 0) {
|
|
79
|
+
throw new Error('Server returned an invalid model list.');
|
|
80
|
+
}
|
|
81
|
+
return { models };
|
|
61
82
|
});
|
|
62
|
-
const data = (await readJsonResponse(response));
|
|
63
|
-
if (!response.ok) {
|
|
64
|
-
throw new Error(failureMessage(data, response.status));
|
|
65
|
-
}
|
|
66
|
-
const models = Array.isArray(data?.models)
|
|
67
|
-
? data.models.map(sanitizeModelInfo).filter(Boolean)
|
|
68
|
-
: [];
|
|
69
|
-
if (models.length === 0) {
|
|
70
|
-
throw new Error('Server returned an invalid model list.');
|
|
71
|
-
}
|
|
72
|
-
return {
|
|
73
|
-
models,
|
|
74
|
-
};
|
|
75
83
|
}
|
|
76
84
|
export function selectServerModel({ requestedModelId, cached, serverModels, }) {
|
|
77
85
|
const supportedIds = new Set(serverModels.models.map((model) => model.id));
|
|
@@ -37,6 +37,15 @@ export const BINARY_ARTIFACT_EXTENSIONS = createArtifactNameSet([
|
|
|
37
37
|
'.bz2',
|
|
38
38
|
'.7z',
|
|
39
39
|
'.pdf',
|
|
40
|
+
'.doc',
|
|
41
|
+
'.docx',
|
|
42
|
+
'.docm',
|
|
43
|
+
'.dot',
|
|
44
|
+
'.dotx',
|
|
45
|
+
'.dotm',
|
|
46
|
+
'.xls',
|
|
47
|
+
'.xlsx',
|
|
48
|
+
'.xlsm',
|
|
40
49
|
'.exe',
|
|
41
50
|
'.dll',
|
|
42
51
|
'.so',
|
|
@@ -145,6 +154,8 @@ const SENSITIVE_BASENAME_PATTERNS = [
|
|
|
145
154
|
/^\.?pypirc$/i,
|
|
146
155
|
/^credentials(?:\..*)?$/i,
|
|
147
156
|
/^secrets?(?:\..*)?$/i,
|
|
157
|
+
/^service[-_]?account(?:\..*)?\.json$/i,
|
|
158
|
+
/^.*credentials.*\.json$/i,
|
|
148
159
|
];
|
|
149
160
|
const SENSITIVE_PATH_PATTERNS = [
|
|
150
161
|
/(^|[/\\])\.aws[/\\]credentials$/i,
|
|
@@ -152,6 +163,7 @@ const SENSITIVE_PATH_PATTERNS = [
|
|
|
152
163
|
/(^|[/\\])credentials?([._-]|$)/i,
|
|
153
164
|
/(^|[/\\])secrets?([._-]|$)/i,
|
|
154
165
|
/(^|[/\\])private[-_]?key([._-]|$)/i,
|
|
166
|
+
/(^|[/\\])service[-_]?account/i,
|
|
155
167
|
/\.(?:pem|key|p12|pfx)$/i,
|
|
156
168
|
];
|
|
157
169
|
export function normalizeArtifactPath(relPath) {
|