topchester-ai 0.53.0 → 0.55.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/dist/bin.mjs +1 -1
- package/dist/{cli-CqFhOvlt.mjs → cli-AJknxj59.mjs} +407 -28
- package/dist/cli-AJknxj59.mjs.map +1 -0
- package/dist/cli.mjs +1 -1
- package/package.json +1 -1
- package/dist/cli-CqFhOvlt.mjs.map +0 -1
package/dist/bin.mjs
CHANGED
|
@@ -127,13 +127,13 @@ function extractAccountIdFromClaims(claims) {
|
|
|
127
127
|
const flatClaim = getStringProperty(claims, "chatgpt_account_id");
|
|
128
128
|
if (flatClaim) return flatClaim;
|
|
129
129
|
const authClaims = claims["https://api.openai.com/auth"];
|
|
130
|
-
if (isPlainObject$
|
|
130
|
+
if (isPlainObject$3(authClaims)) {
|
|
131
131
|
const namespacedClaim = getStringProperty(authClaims, "chatgpt_account_id");
|
|
132
132
|
if (namespacedClaim) return namespacedClaim;
|
|
133
133
|
}
|
|
134
134
|
const organizations = claims.organizations;
|
|
135
135
|
if (Array.isArray(organizations)) {
|
|
136
|
-
const firstOrganization = organizations.find(isPlainObject$
|
|
136
|
+
const firstOrganization = organizations.find(isPlainObject$3);
|
|
137
137
|
const organizationId = firstOrganization ? getStringProperty(firstOrganization, "id") : void 0;
|
|
138
138
|
if (organizationId) return organizationId;
|
|
139
139
|
}
|
|
@@ -143,7 +143,7 @@ function parseJwtClaims(token) {
|
|
|
143
143
|
if (!parts || parts.length !== 3 || !parts[1]) return;
|
|
144
144
|
try {
|
|
145
145
|
const parsed = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
|
|
146
|
-
return isPlainObject$
|
|
146
|
+
return isPlainObject$3(parsed) ? parsed : void 0;
|
|
147
147
|
} catch {
|
|
148
148
|
return;
|
|
149
149
|
}
|
|
@@ -190,7 +190,7 @@ async function readJsonObject(response, label) {
|
|
|
190
190
|
} catch (error) {
|
|
191
191
|
throw new CodexAuthError("invalid_response", `Invalid Codex ${label}: ${formatErrorMessage$4(error)}.`);
|
|
192
192
|
}
|
|
193
|
-
if (!isPlainObject$
|
|
193
|
+
if (!isPlainObject$3(parsed)) throw new CodexAuthError("invalid_response", `Invalid Codex ${label}: expected an object.`);
|
|
194
194
|
return parsed;
|
|
195
195
|
}
|
|
196
196
|
function isPendingDeviceAuthResponse(response) {
|
|
@@ -214,7 +214,7 @@ function optionalPositiveNumber(value) {
|
|
|
214
214
|
const parsed = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
|
|
215
215
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
|
|
216
216
|
}
|
|
217
|
-
function isPlainObject$
|
|
217
|
+
function isPlainObject$3(value) {
|
|
218
218
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
219
219
|
}
|
|
220
220
|
function formatErrorMessage$4(error) {
|
|
@@ -249,7 +249,7 @@ async function readAuthStore(options = {}) {
|
|
|
249
249
|
try {
|
|
250
250
|
source = await readFile(path, "utf8");
|
|
251
251
|
} catch (error) {
|
|
252
|
-
if (isNodeError$
|
|
252
|
+
if (isNodeError$5(error) && error.code === "ENOENT") return createEmptyAuthStore();
|
|
253
253
|
throw error;
|
|
254
254
|
}
|
|
255
255
|
let parsed;
|
|
@@ -297,7 +297,7 @@ async function getAuthStoreStatus(options = {}) {
|
|
|
297
297
|
try {
|
|
298
298
|
await stat(path);
|
|
299
299
|
} catch (error) {
|
|
300
|
-
if (isNodeError$
|
|
300
|
+
if (isNodeError$5(error) && error.code === "ENOENT") return {
|
|
301
301
|
path,
|
|
302
302
|
exists: false,
|
|
303
303
|
providers: []
|
|
@@ -325,13 +325,13 @@ async function getAuthStoreStatus(options = {}) {
|
|
|
325
325
|
}
|
|
326
326
|
}
|
|
327
327
|
function parseAuthStore(path, value) {
|
|
328
|
-
if (!isPlainObject$
|
|
328
|
+
if (!isPlainObject$2(value)) throw new AuthStoreError(path, "<root>: expected an object");
|
|
329
329
|
if (value.version !== 1) throw new AuthStoreError(path, "version: expected 1");
|
|
330
|
-
if (!isPlainObject$
|
|
330
|
+
if (!isPlainObject$2(value.providers)) throw new AuthStoreError(path, "providers: expected an object");
|
|
331
331
|
const providers = {};
|
|
332
332
|
for (const [providerId, providerRecord] of Object.entries(value.providers)) {
|
|
333
333
|
if (!providerId) throw new AuthStoreError(path, "providers: provider id must not be empty");
|
|
334
|
-
if (!isPlainObject$
|
|
334
|
+
if (!isPlainObject$2(providerRecord)) throw new AuthStoreError(path, `providers.${providerId}: expected an object`);
|
|
335
335
|
providers[providerId] = parseProviderRecord(path, providerId, providerRecord);
|
|
336
336
|
}
|
|
337
337
|
return {
|
|
@@ -388,14 +388,14 @@ function redactProviderStatus(id, record, now) {
|
|
|
388
388
|
}
|
|
389
389
|
async function setModeIfSupported(path, mode) {
|
|
390
390
|
await chmod(path, mode).catch((error) => {
|
|
391
|
-
if (isNodeError$
|
|
391
|
+
if (isNodeError$5(error) && (error.code === "ENOSYS" || error.code === "EPERM" || error.code === "EINVAL")) return;
|
|
392
392
|
throw error;
|
|
393
393
|
});
|
|
394
394
|
}
|
|
395
|
-
function isPlainObject$
|
|
395
|
+
function isPlainObject$2(value) {
|
|
396
396
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
397
397
|
}
|
|
398
|
-
function isNodeError$
|
|
398
|
+
function isNodeError$5(error) {
|
|
399
399
|
return error instanceof Error && "code" in error;
|
|
400
400
|
}
|
|
401
401
|
function formatErrorMessage$3(error) {
|
|
@@ -1299,7 +1299,7 @@ async function statExistingFile(resolvedPath, originalPath) {
|
|
|
1299
1299
|
try {
|
|
1300
1300
|
fileStat = await stat(resolvedPath);
|
|
1301
1301
|
} catch (error) {
|
|
1302
|
-
if (isNodeError$
|
|
1302
|
+
if (isNodeError$4(error) && error.code === "ENOENT") throw new Error(`edit_file can only edit existing files: ${originalPath}`);
|
|
1303
1303
|
throw error;
|
|
1304
1304
|
}
|
|
1305
1305
|
if (!fileStat.isFile()) throw new Error(`edit_file can only edit regular files: ${originalPath}`);
|
|
@@ -1341,7 +1341,7 @@ function summarizeDiff(diff) {
|
|
|
1341
1341
|
}
|
|
1342
1342
|
return `+${added}/-${removed}`;
|
|
1343
1343
|
}
|
|
1344
|
-
function isNodeError$
|
|
1344
|
+
function isNodeError$4(error) {
|
|
1345
1345
|
return error instanceof Error && "code" in error;
|
|
1346
1346
|
}
|
|
1347
1347
|
function normalizeEdits(edits) {
|
|
@@ -4859,7 +4859,7 @@ async function statTarget(path) {
|
|
|
4859
4859
|
try {
|
|
4860
4860
|
return await stat(path);
|
|
4861
4861
|
} catch (error) {
|
|
4862
|
-
if (isNodeError$
|
|
4862
|
+
if (isNodeError$3(error) && error.code === "ENOENT") return;
|
|
4863
4863
|
throw error;
|
|
4864
4864
|
}
|
|
4865
4865
|
}
|
|
@@ -4869,7 +4869,7 @@ async function ensureParentDirectory(workspaceRoot, path, relativePath, createPa
|
|
|
4869
4869
|
if (!(await stat(parent)).isDirectory()) throw new Error(`write_file parent path is not a directory: ${dirname(relativePath)}`);
|
|
4870
4870
|
return [];
|
|
4871
4871
|
} catch (error) {
|
|
4872
|
-
if (!isNodeError$
|
|
4872
|
+
if (!isNodeError$3(error) || error.code !== "ENOENT") throw error;
|
|
4873
4873
|
}
|
|
4874
4874
|
if (!createParentDirs) throw new Error(`write_file parent directory does not exist: ${dirname(relativePath)}`);
|
|
4875
4875
|
const createdParentDirs = await collectMissingParentDirs(workspaceRoot, parent);
|
|
@@ -4883,7 +4883,7 @@ async function collectMissingParentDirs(workspaceRoot, parent) {
|
|
|
4883
4883
|
if (!(await stat(current)).isDirectory()) throw new Error(`write_file parent path is not a directory: ${relative(workspaceRoot, current)}`);
|
|
4884
4884
|
break;
|
|
4885
4885
|
} catch (error) {
|
|
4886
|
-
if (!isNodeError$
|
|
4886
|
+
if (!isNodeError$3(error) || error.code !== "ENOENT") throw error;
|
|
4887
4887
|
missing.push(relative(workspaceRoot, current));
|
|
4888
4888
|
current = dirname(current);
|
|
4889
4889
|
}
|
|
@@ -4924,7 +4924,7 @@ function countLogicalLines$1(content) {
|
|
|
4924
4924
|
const withoutTrailingLineEnding = content.replace(/\r?\n$/u, "");
|
|
4925
4925
|
return withoutTrailingLineEnding.length === 0 ? 1 : withoutTrailingLineEnding.split(/\r?\n/u).length;
|
|
4926
4926
|
}
|
|
4927
|
-
function isNodeError$
|
|
4927
|
+
function isNodeError$3(error) {
|
|
4928
4928
|
return error instanceof Error && "code" in error;
|
|
4929
4929
|
}
|
|
4930
4930
|
//#endregion
|
|
@@ -5335,6 +5335,7 @@ function createCodexProviderFetch(options = {}) {
|
|
|
5335
5335
|
const providerId = options.providerId ?? "codex";
|
|
5336
5336
|
const upstreamFetch = options.fetch ?? fetch;
|
|
5337
5337
|
return (async (input, init) => {
|
|
5338
|
+
const request = await rewriteCodexRequest(input, init);
|
|
5338
5339
|
const auth = await resolveCodexAuth({
|
|
5339
5340
|
...options,
|
|
5340
5341
|
providerId,
|
|
@@ -5344,10 +5345,13 @@ function createCodexProviderFetch(options = {}) {
|
|
|
5344
5345
|
headers.delete("authorization");
|
|
5345
5346
|
headers.set("Authorization", `Bearer ${auth.accessToken}`);
|
|
5346
5347
|
if (auth.accountId) headers.set("ChatGPT-Account-Id", auth.accountId);
|
|
5347
|
-
|
|
5348
|
-
...init,
|
|
5348
|
+
const response = await upstreamFetch(request.input, {
|
|
5349
|
+
...request.init,
|
|
5349
5350
|
headers
|
|
5350
5351
|
});
|
|
5352
|
+
if (request.responseMode === "chat-json") return codexResponsesSseToChatJson(response);
|
|
5353
|
+
if (request.responseMode === "chat-sse") return codexResponsesSseToChatSse(response);
|
|
5354
|
+
return response;
|
|
5351
5355
|
});
|
|
5352
5356
|
}
|
|
5353
5357
|
function rewriteCodexRequestUrl(input) {
|
|
@@ -5360,6 +5364,193 @@ function rewriteCodexRequestUrl(input) {
|
|
|
5360
5364
|
}
|
|
5361
5365
|
return input;
|
|
5362
5366
|
}
|
|
5367
|
+
async function rewriteCodexRequest(input, init) {
|
|
5368
|
+
const rewrittenUrl = rewriteCodexRequestUrl(input);
|
|
5369
|
+
const body = await readJsonBody(init?.body);
|
|
5370
|
+
if (!isChatCompletionsBody(body)) return {
|
|
5371
|
+
input: rewrittenUrl,
|
|
5372
|
+
...init ? { init } : {}
|
|
5373
|
+
};
|
|
5374
|
+
const stream = body.stream === true;
|
|
5375
|
+
const codexBody = chatCompletionsBodyToCodexResponsesBody(body);
|
|
5376
|
+
const headers = new Headers(init?.headers);
|
|
5377
|
+
headers.set("Content-Type", "application/json");
|
|
5378
|
+
return {
|
|
5379
|
+
input: rewrittenUrl,
|
|
5380
|
+
init: {
|
|
5381
|
+
...init,
|
|
5382
|
+
headers,
|
|
5383
|
+
body: JSON.stringify(codexBody)
|
|
5384
|
+
},
|
|
5385
|
+
responseMode: stream ? "chat-sse" : "chat-json"
|
|
5386
|
+
};
|
|
5387
|
+
}
|
|
5388
|
+
function chatCompletionsBodyToCodexResponsesBody(body) {
|
|
5389
|
+
const instructions = body.messages.filter((message) => message.role === "system" || message.role === "developer").map((message) => messageContentToText(message.content)).filter((text) => text.trim().length > 0).join("\n\n");
|
|
5390
|
+
const input = body.messages.filter((message) => message.role !== "system" && message.role !== "developer").map(chatMessageToResponseInputItem).filter((item) => item !== void 0);
|
|
5391
|
+
return {
|
|
5392
|
+
model: body.model,
|
|
5393
|
+
instructions: instructions || "You are a helpful assistant.",
|
|
5394
|
+
input,
|
|
5395
|
+
store: false,
|
|
5396
|
+
stream: true,
|
|
5397
|
+
...typeof body.temperature === "number" ? { temperature: body.temperature } : {},
|
|
5398
|
+
...typeof body.top_p === "number" ? { top_p: body.top_p } : {},
|
|
5399
|
+
...typeof body.max_tokens === "number" ? { max_output_tokens: body.max_tokens } : {}
|
|
5400
|
+
};
|
|
5401
|
+
}
|
|
5402
|
+
function chatMessageToResponseInputItem(message) {
|
|
5403
|
+
const text = messageContentToText(message.content);
|
|
5404
|
+
if (!text.trim()) return;
|
|
5405
|
+
return {
|
|
5406
|
+
type: "message",
|
|
5407
|
+
role: message.role === "assistant" ? "assistant" : "user",
|
|
5408
|
+
content: [{
|
|
5409
|
+
type: message.role === "assistant" ? "output_text" : "input_text",
|
|
5410
|
+
text
|
|
5411
|
+
}]
|
|
5412
|
+
};
|
|
5413
|
+
}
|
|
5414
|
+
async function codexResponsesSseToChatJson(response) {
|
|
5415
|
+
if (!response.ok) return response;
|
|
5416
|
+
const parsed = parseCodexResponsesSse(await response.text());
|
|
5417
|
+
const headers = new Headers(response.headers);
|
|
5418
|
+
headers.set("Content-Type", "application/json");
|
|
5419
|
+
return new Response(JSON.stringify({
|
|
5420
|
+
id: parsed.id ?? "codex-response",
|
|
5421
|
+
object: "chat.completion",
|
|
5422
|
+
created: parsed.created ?? Math.floor(Date.now() / 1e3),
|
|
5423
|
+
model: parsed.model ?? "codex",
|
|
5424
|
+
choices: [{
|
|
5425
|
+
index: 0,
|
|
5426
|
+
finish_reason: "stop",
|
|
5427
|
+
message: {
|
|
5428
|
+
role: "assistant",
|
|
5429
|
+
content: parsed.text
|
|
5430
|
+
}
|
|
5431
|
+
}],
|
|
5432
|
+
...parsed.usage ? { usage: parsed.usage } : {}
|
|
5433
|
+
}), {
|
|
5434
|
+
status: response.status,
|
|
5435
|
+
statusText: response.statusText,
|
|
5436
|
+
headers
|
|
5437
|
+
});
|
|
5438
|
+
}
|
|
5439
|
+
async function codexResponsesSseToChatSse(response) {
|
|
5440
|
+
if (!response.ok) return response;
|
|
5441
|
+
const parsed = parseCodexResponsesSse(await response.text());
|
|
5442
|
+
const encoder = new TextEncoder();
|
|
5443
|
+
const id = parsed.id ?? "codex-response";
|
|
5444
|
+
const created = parsed.created ?? Math.floor(Date.now() / 1e3);
|
|
5445
|
+
const model = parsed.model ?? "codex";
|
|
5446
|
+
const chunks = [
|
|
5447
|
+
{
|
|
5448
|
+
id,
|
|
5449
|
+
object: "chat.completion.chunk",
|
|
5450
|
+
created,
|
|
5451
|
+
model,
|
|
5452
|
+
choices: [{
|
|
5453
|
+
index: 0,
|
|
5454
|
+
delta: { role: "assistant" },
|
|
5455
|
+
finish_reason: null
|
|
5456
|
+
}]
|
|
5457
|
+
},
|
|
5458
|
+
...(parsed.text.match(/[\s\S]{1,2048}/gu) ?? []).map((delta) => ({
|
|
5459
|
+
id,
|
|
5460
|
+
object: "chat.completion.chunk",
|
|
5461
|
+
created,
|
|
5462
|
+
model,
|
|
5463
|
+
choices: [{
|
|
5464
|
+
index: 0,
|
|
5465
|
+
delta: { content: delta },
|
|
5466
|
+
finish_reason: null
|
|
5467
|
+
}]
|
|
5468
|
+
})),
|
|
5469
|
+
{
|
|
5470
|
+
id,
|
|
5471
|
+
object: "chat.completion.chunk",
|
|
5472
|
+
created,
|
|
5473
|
+
model,
|
|
5474
|
+
choices: [{
|
|
5475
|
+
index: 0,
|
|
5476
|
+
delta: {},
|
|
5477
|
+
finish_reason: "stop"
|
|
5478
|
+
}],
|
|
5479
|
+
...parsed.usage ? { usage: parsed.usage } : {}
|
|
5480
|
+
}
|
|
5481
|
+
];
|
|
5482
|
+
const stream = new ReadableStream({ start(controller) {
|
|
5483
|
+
for (const chunk of chunks) controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}\n\n`));
|
|
5484
|
+
controller.enqueue(encoder.encode("data: [DONE]\n\n"));
|
|
5485
|
+
controller.close();
|
|
5486
|
+
} });
|
|
5487
|
+
const headers = new Headers(response.headers);
|
|
5488
|
+
headers.set("Content-Type", "text/event-stream");
|
|
5489
|
+
return new Response(stream, {
|
|
5490
|
+
status: response.status,
|
|
5491
|
+
statusText: response.statusText,
|
|
5492
|
+
headers
|
|
5493
|
+
});
|
|
5494
|
+
}
|
|
5495
|
+
function parseCodexResponsesSse(source) {
|
|
5496
|
+
let text = "";
|
|
5497
|
+
let response;
|
|
5498
|
+
for (const event of source.split(/\n\n/u)) {
|
|
5499
|
+
const dataLines = event.split(/\r?\n/u).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart());
|
|
5500
|
+
if (dataLines.length === 0) continue;
|
|
5501
|
+
const data = dataLines.join("\n");
|
|
5502
|
+
if (data === "[DONE]") continue;
|
|
5503
|
+
const parsed = safeJsonParse(data);
|
|
5504
|
+
if (!isPlainObject$1(parsed)) continue;
|
|
5505
|
+
if (parsed.type === "response.output_text.delta" && typeof parsed.delta === "string") text += parsed.delta;
|
|
5506
|
+
if (parsed.type === "response.completed" && isPlainObject$1(parsed.response)) response = parsed.response;
|
|
5507
|
+
}
|
|
5508
|
+
return {
|
|
5509
|
+
...typeof response?.id === "string" ? { id: response.id } : {},
|
|
5510
|
+
...typeof response?.created_at === "number" ? { created: response.created_at } : {},
|
|
5511
|
+
...typeof response?.model === "string" ? { model: response.model } : {},
|
|
5512
|
+
text,
|
|
5513
|
+
...normalizeCodexUsage(response?.usage)
|
|
5514
|
+
};
|
|
5515
|
+
}
|
|
5516
|
+
function normalizeCodexUsage(usage) {
|
|
5517
|
+
if (!isPlainObject$1(usage)) return {};
|
|
5518
|
+
const promptTokens = typeof usage.input_tokens === "number" ? usage.input_tokens : void 0;
|
|
5519
|
+
const completionTokens = typeof usage.output_tokens === "number" ? usage.output_tokens : void 0;
|
|
5520
|
+
const totalTokens = typeof usage.total_tokens === "number" ? usage.total_tokens : void 0;
|
|
5521
|
+
return { usage: {
|
|
5522
|
+
...promptTokens === void 0 ? {} : { prompt_tokens: promptTokens },
|
|
5523
|
+
...completionTokens === void 0 ? {} : { completion_tokens: completionTokens },
|
|
5524
|
+
...totalTokens === void 0 ? {} : { total_tokens: totalTokens }
|
|
5525
|
+
} };
|
|
5526
|
+
}
|
|
5527
|
+
async function readJsonBody(body) {
|
|
5528
|
+
if (typeof body !== "string") return;
|
|
5529
|
+
return safeJsonParse(body);
|
|
5530
|
+
}
|
|
5531
|
+
function safeJsonParse(source) {
|
|
5532
|
+
try {
|
|
5533
|
+
return JSON.parse(source);
|
|
5534
|
+
} catch {
|
|
5535
|
+
return;
|
|
5536
|
+
}
|
|
5537
|
+
}
|
|
5538
|
+
function isChatCompletionsBody(value) {
|
|
5539
|
+
return isPlainObject$1(value) && typeof value.model === "string" && Array.isArray(value.messages) && value.messages.every((message) => isPlainObject$1(message) && typeof message.role === "string");
|
|
5540
|
+
}
|
|
5541
|
+
function messageContentToText(content) {
|
|
5542
|
+
if (typeof content === "string") return content;
|
|
5543
|
+
if (!Array.isArray(content)) return "";
|
|
5544
|
+
return content.map((part) => {
|
|
5545
|
+
if (!isPlainObject$1(part)) return "";
|
|
5546
|
+
if (typeof part.text === "string") return part.text;
|
|
5547
|
+
if (typeof part.content === "string") return part.content;
|
|
5548
|
+
return "";
|
|
5549
|
+
}).filter(Boolean).join("\n");
|
|
5550
|
+
}
|
|
5551
|
+
function isPlainObject$1(value) {
|
|
5552
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5553
|
+
}
|
|
5363
5554
|
async function resolveCodexAuth(options = {}) {
|
|
5364
5555
|
const providerId = options.providerId ?? "codex";
|
|
5365
5556
|
const record = (await readAuthStore({ path: options.authStorePath })).providers[providerId];
|
|
@@ -6197,7 +6388,8 @@ const openRouterProviderDefaults = {
|
|
|
6197
6388
|
};
|
|
6198
6389
|
const codexProviderDefaults = {
|
|
6199
6390
|
type: "openai-compatible",
|
|
6200
|
-
baseURL: CODEX_BACKEND_BASE_URL
|
|
6391
|
+
baseURL: CODEX_BACKEND_BASE_URL,
|
|
6392
|
+
toolProtocol: "text-json"
|
|
6201
6393
|
};
|
|
6202
6394
|
const codexStarterModelChoices = [
|
|
6203
6395
|
"codex/gpt-5.5",
|
|
@@ -7973,11 +8165,11 @@ async function directoryExists(path) {
|
|
|
7973
8165
|
if (!(await stat(path)).isDirectory()) throw new Error(`${path} exists but is not a folder`);
|
|
7974
8166
|
return true;
|
|
7975
8167
|
} catch (error) {
|
|
7976
|
-
if (isNodeError$
|
|
8168
|
+
if (isNodeError$2(error) && error.code === "ENOENT") return false;
|
|
7977
8169
|
throw error;
|
|
7978
8170
|
}
|
|
7979
8171
|
}
|
|
7980
|
-
function isNodeError$
|
|
8172
|
+
function isNodeError$2(error) {
|
|
7981
8173
|
return error instanceof Error && "code" in error;
|
|
7982
8174
|
}
|
|
7983
8175
|
//#endregion
|
|
@@ -8016,7 +8208,7 @@ async function removeIfPresent(path) {
|
|
|
8016
8208
|
});
|
|
8017
8209
|
return true;
|
|
8018
8210
|
} catch (error) {
|
|
8019
|
-
if (isNodeError(error) && error.code === "ENOENT") return false;
|
|
8211
|
+
if (isNodeError$1(error) && error.code === "ENOENT") return false;
|
|
8020
8212
|
throw error;
|
|
8021
8213
|
}
|
|
8022
8214
|
}
|
|
@@ -8029,7 +8221,7 @@ function assertSafeResetPath(workspaceRoot, path) {
|
|
|
8029
8221
|
if (target === workspace) throw new Error(`Refusing to reset KB because the configured KB path is the workspace root: ${target}`);
|
|
8030
8222
|
if (dirname(target) === target) throw new Error(`Refusing to reset KB because the configured KB path is a filesystem root: ${target}`);
|
|
8031
8223
|
}
|
|
8032
|
-
function isNodeError(error) {
|
|
8224
|
+
function isNodeError$1(error) {
|
|
8033
8225
|
return error instanceof Error && "code" in error;
|
|
8034
8226
|
}
|
|
8035
8227
|
//#endregion
|
|
@@ -11844,10 +12036,16 @@ const agentMetadataSchema = z.object({
|
|
|
11844
12036
|
capabilities: z.array(z.string().min(1)).optional(),
|
|
11845
12037
|
model_support: agentModelSupportSchema.optional()
|
|
11846
12038
|
}).strict();
|
|
11847
|
-
z.object({
|
|
12039
|
+
const agentsMetadata = z.object({
|
|
11848
12040
|
version: z.literal(1),
|
|
11849
12041
|
agents: z.record(z.string().min(1), agentMetadataSchema)
|
|
11850
12042
|
}).strict().parse(agents_default);
|
|
12043
|
+
function listAgentMetadata() {
|
|
12044
|
+
return Object.entries(agentsMetadata.agents).map(([id, metadata]) => ({
|
|
12045
|
+
id,
|
|
12046
|
+
metadata
|
|
12047
|
+
})).sort((left, right) => left.id.localeCompare(right.id));
|
|
12048
|
+
}
|
|
11851
12049
|
//#endregion
|
|
11852
12050
|
//#region src/agent/prompts.ts
|
|
11853
12051
|
function getChatSystemPrompt(options = {}) {
|
|
@@ -15317,6 +15515,135 @@ function formatInfoError(error) {
|
|
|
15317
15515
|
return error instanceof Error ? error.message : String(error);
|
|
15318
15516
|
}
|
|
15319
15517
|
//#endregion
|
|
15518
|
+
//#region src/cli/integrations.ts
|
|
15519
|
+
const TOPCHESTER_CODEX_BLOCK_START = "# >>> topchester integration: codex";
|
|
15520
|
+
const TOPCHESTER_CODEX_BLOCK_END = "# <<< topchester integration: codex";
|
|
15521
|
+
function listIntegrationStatuses() {
|
|
15522
|
+
return listAgentMetadata().filter(({ id }) => id !== "topchester").map(({ id, metadata }) => getIntegrationStatus(id, metadata.display_name));
|
|
15523
|
+
}
|
|
15524
|
+
function getIntegrationStatus(agent, displayName) {
|
|
15525
|
+
const normalizedAgent = normalizeAgentId(agent);
|
|
15526
|
+
const metadata = listAgentMetadata().find(({ id }) => id === normalizedAgent)?.metadata;
|
|
15527
|
+
const name = displayName ?? metadata?.display_name ?? normalizedAgent;
|
|
15528
|
+
if (normalizedAgent !== "codex") return {
|
|
15529
|
+
agent: normalizedAgent,
|
|
15530
|
+
displayName: name,
|
|
15531
|
+
supported: false,
|
|
15532
|
+
installed: false,
|
|
15533
|
+
detail: "not supported yet"
|
|
15534
|
+
};
|
|
15535
|
+
return {
|
|
15536
|
+
agent: normalizedAgent,
|
|
15537
|
+
displayName: name,
|
|
15538
|
+
supported: true,
|
|
15539
|
+
installed: false,
|
|
15540
|
+
configPath: getCodexConfigPath(),
|
|
15541
|
+
detail: "not installed"
|
|
15542
|
+
};
|
|
15543
|
+
}
|
|
15544
|
+
async function getIntegrationStatusAsync(agent) {
|
|
15545
|
+
const status = getIntegrationStatus(agent);
|
|
15546
|
+
if (status.agent !== "codex" || !status.configPath) return status;
|
|
15547
|
+
const contents = await readTextIfExists(status.configPath);
|
|
15548
|
+
const installed = contents.includes(TOPCHESTER_CODEX_BLOCK_START) && contents.includes(TOPCHESTER_CODEX_BLOCK_END);
|
|
15549
|
+
return {
|
|
15550
|
+
...status,
|
|
15551
|
+
installed,
|
|
15552
|
+
detail: installed ? "installed" : "not installed"
|
|
15553
|
+
};
|
|
15554
|
+
}
|
|
15555
|
+
async function listIntegrationStatusesAsync(agent) {
|
|
15556
|
+
if (agent) return [await getIntegrationStatusAsync(agent)];
|
|
15557
|
+
return await Promise.all(listIntegrationStatuses().map((status) => getIntegrationStatusAsync(status.agent)));
|
|
15558
|
+
}
|
|
15559
|
+
async function installIntegration(agent, action = "installed") {
|
|
15560
|
+
const status = await getIntegrationStatusAsync(agent);
|
|
15561
|
+
if (!status.supported || status.agent !== "codex" || !status.configPath) throw new Error(`Unsupported integration: ${agent}`);
|
|
15562
|
+
const next = appendMarkedBlock(removeMarkedBlock(await readTextIfExists(status.configPath)), formatCodexIntegrationBlock());
|
|
15563
|
+
await mkdir(dirname(status.configPath), { recursive: true });
|
|
15564
|
+
await writeFile(status.configPath, next);
|
|
15565
|
+
return {
|
|
15566
|
+
action,
|
|
15567
|
+
status: await getIntegrationStatusAsync(status.agent)
|
|
15568
|
+
};
|
|
15569
|
+
}
|
|
15570
|
+
async function removeIntegration(agent) {
|
|
15571
|
+
const status = await getIntegrationStatusAsync(agent);
|
|
15572
|
+
if (!status.supported || status.agent !== "codex" || !status.configPath) throw new Error(`Unsupported integration: ${agent}`);
|
|
15573
|
+
const before = await readTextIfExists(status.configPath);
|
|
15574
|
+
await mkdir(dirname(status.configPath), { recursive: true });
|
|
15575
|
+
await writeFile(status.configPath, ensureTrailingNewline(removeMarkedBlock(before)));
|
|
15576
|
+
return {
|
|
15577
|
+
action: "removed",
|
|
15578
|
+
status: await getIntegrationStatusAsync(status.agent)
|
|
15579
|
+
};
|
|
15580
|
+
}
|
|
15581
|
+
function formatIntegrationStatuses(statuses) {
|
|
15582
|
+
const lines = ["Integrations"];
|
|
15583
|
+
for (const status of statuses) {
|
|
15584
|
+
const state = status.supported ? status.installed ? "installed" : "not installed" : "unsupported";
|
|
15585
|
+
lines.push(`${status.agent}: ${state}`);
|
|
15586
|
+
if (status.configPath) lines.push(` config: ${status.configPath}`);
|
|
15587
|
+
if (!status.supported) lines.push(` detail: ${status.detail}`);
|
|
15588
|
+
}
|
|
15589
|
+
return lines;
|
|
15590
|
+
}
|
|
15591
|
+
function formatIntegrationAction(action, status) {
|
|
15592
|
+
return [
|
|
15593
|
+
action === "installed" ? "Integration installed" : action === "repaired" ? "Integration repaired" : "Integration removed",
|
|
15594
|
+
`agent: ${status.agent}`,
|
|
15595
|
+
`state: ${status.installed ? "installed" : "not installed"}`,
|
|
15596
|
+
...status.configPath ? [`config: ${status.configPath}`] : []
|
|
15597
|
+
];
|
|
15598
|
+
}
|
|
15599
|
+
async function executeHookStop(agent) {
|
|
15600
|
+
if (normalizeAgentId(agent ?? "") !== "codex") throw new Error(agent ? `Unsupported hook agent: ${agent}` : "Usage: topchester hook stop <agent>");
|
|
15601
|
+
}
|
|
15602
|
+
function getCodexConfigPath() {
|
|
15603
|
+
return join(process.env.CODEX_HOME?.trim() || join(process.env.HOME || homedir(), ".codex"), "config.toml");
|
|
15604
|
+
}
|
|
15605
|
+
function formatCodexIntegrationBlock() {
|
|
15606
|
+
return [
|
|
15607
|
+
TOPCHESTER_CODEX_BLOCK_START,
|
|
15608
|
+
"[[Stop]]",
|
|
15609
|
+
"",
|
|
15610
|
+
"[[Stop.hooks]]",
|
|
15611
|
+
"type = \"command\"",
|
|
15612
|
+
"command = \"topchester hook stop codex\"",
|
|
15613
|
+
"timeout = 10",
|
|
15614
|
+
"statusMessage = \"Notifying Topchester\"",
|
|
15615
|
+
TOPCHESTER_CODEX_BLOCK_END
|
|
15616
|
+
].join("\n");
|
|
15617
|
+
}
|
|
15618
|
+
function appendMarkedBlock(contents, block) {
|
|
15619
|
+
const base = contents.trimEnd();
|
|
15620
|
+
return `${base ? `${base}\n\n` : ""}${block}\n`;
|
|
15621
|
+
}
|
|
15622
|
+
function removeMarkedBlock(contents) {
|
|
15623
|
+
const start = contents.indexOf(TOPCHESTER_CODEX_BLOCK_START);
|
|
15624
|
+
if (start === -1) return contents;
|
|
15625
|
+
const end = contents.indexOf(TOPCHESTER_CODEX_BLOCK_END, start);
|
|
15626
|
+
if (end === -1) return contents;
|
|
15627
|
+
return `${contents.slice(0, start)}${contents.slice(end + 35)}`.trimEnd();
|
|
15628
|
+
}
|
|
15629
|
+
async function readTextIfExists(path) {
|
|
15630
|
+
try {
|
|
15631
|
+
return await readFile(path, "utf8");
|
|
15632
|
+
} catch (error) {
|
|
15633
|
+
if (isNodeError(error) && error.code === "ENOENT") return "";
|
|
15634
|
+
throw error;
|
|
15635
|
+
}
|
|
15636
|
+
}
|
|
15637
|
+
function ensureTrailingNewline(value) {
|
|
15638
|
+
return value ? `${value.trimEnd()}\n` : "";
|
|
15639
|
+
}
|
|
15640
|
+
function normalizeAgentId(value) {
|
|
15641
|
+
return value.trim().toLowerCase();
|
|
15642
|
+
}
|
|
15643
|
+
function isNodeError(error) {
|
|
15644
|
+
return error instanceof Error && "code" in error;
|
|
15645
|
+
}
|
|
15646
|
+
//#endregion
|
|
15320
15647
|
//#region src/cli.ts
|
|
15321
15648
|
async function runTopchesterCli(argv = process.argv, options = {}) {
|
|
15322
15649
|
const program = createTopchesterProgram();
|
|
@@ -15407,6 +15734,58 @@ function createTopchesterProgram() {
|
|
|
15407
15734
|
program.command("search").description("search compiled L1 knowledge entries").argument("<query...>", "search query").option("--limit <count>", "maximum number of matches", parsePositiveInteger).option("--json", "write full JSON search result to stdout").action(async (queryParts, options) => {
|
|
15408
15735
|
await executeKbSearchCommand(program, queryParts, options);
|
|
15409
15736
|
});
|
|
15737
|
+
const integrationsCommand = program.command("integrations").description("manage Topchester integrations with other agents");
|
|
15738
|
+
integrationsCommand.command("list").description("list supported integrations").action(async () => {
|
|
15739
|
+
try {
|
|
15740
|
+
console.log(formatIntegrationStatuses(await listIntegrationStatusesAsync()).join("\n"));
|
|
15741
|
+
} catch (error) {
|
|
15742
|
+
console.error(formatStartupError(error));
|
|
15743
|
+
process.exitCode = 1;
|
|
15744
|
+
}
|
|
15745
|
+
});
|
|
15746
|
+
integrationsCommand.command("status").description("check whether integrations are installed").argument("[agent]", "agent id").action(async (agent) => {
|
|
15747
|
+
try {
|
|
15748
|
+
console.log(formatIntegrationStatuses(await listIntegrationStatusesAsync(agent)).join("\n"));
|
|
15749
|
+
} catch (error) {
|
|
15750
|
+
console.error(formatStartupError(error));
|
|
15751
|
+
process.exitCode = 1;
|
|
15752
|
+
}
|
|
15753
|
+
});
|
|
15754
|
+
integrationsCommand.command("install").description("install Topchester lifecycle hooks for another agent").argument("<agent>", "agent id").action(async (agent) => {
|
|
15755
|
+
try {
|
|
15756
|
+
const result = await installIntegration(agent);
|
|
15757
|
+
console.log(formatIntegrationAction(result.action, result.status).join("\n"));
|
|
15758
|
+
} catch (error) {
|
|
15759
|
+
console.error(formatStartupError(error));
|
|
15760
|
+
process.exitCode = 1;
|
|
15761
|
+
}
|
|
15762
|
+
});
|
|
15763
|
+
integrationsCommand.command("repair").description("repair a Topchester integration").argument("<agent>", "agent id").action(async (agent) => {
|
|
15764
|
+
try {
|
|
15765
|
+
const result = await installIntegration(agent, "repaired");
|
|
15766
|
+
console.log(formatIntegrationAction(result.action, result.status).join("\n"));
|
|
15767
|
+
} catch (error) {
|
|
15768
|
+
console.error(formatStartupError(error));
|
|
15769
|
+
process.exitCode = 1;
|
|
15770
|
+
}
|
|
15771
|
+
});
|
|
15772
|
+
integrationsCommand.command("remove").description("remove a Topchester integration").argument("<agent>", "agent id").action(async (agent) => {
|
|
15773
|
+
try {
|
|
15774
|
+
const result = await removeIntegration(agent);
|
|
15775
|
+
console.log(formatIntegrationAction(result.action, result.status).join("\n"));
|
|
15776
|
+
} catch (error) {
|
|
15777
|
+
console.error(formatStartupError(error));
|
|
15778
|
+
process.exitCode = 1;
|
|
15779
|
+
}
|
|
15780
|
+
});
|
|
15781
|
+
program.command("hook").description("low-level lifecycle hook endpoints").command("stop").description("receive a Stop hook from another agent").argument("<agent>", "agent id").action(async (agent) => {
|
|
15782
|
+
try {
|
|
15783
|
+
await executeHookStop(agent);
|
|
15784
|
+
} catch (error) {
|
|
15785
|
+
console.error(formatStartupError(error));
|
|
15786
|
+
process.exitCode = 1;
|
|
15787
|
+
}
|
|
15788
|
+
});
|
|
15410
15789
|
const kbCommand = program.command("kb").description("knowledge base commands");
|
|
15411
15790
|
kbCommand.command("init").description("initialize a project knowledge base").action(async () => {
|
|
15412
15791
|
const context = createContextFromOptions(program);
|
|
@@ -15650,4 +16029,4 @@ function formatDryRunSyncStatus(status) {
|
|
|
15650
16029
|
//#endregion
|
|
15651
16030
|
export { runTopchesterCli as t };
|
|
15652
16031
|
|
|
15653
|
-
//# sourceMappingURL=cli-
|
|
16032
|
+
//# sourceMappingURL=cli-AJknxj59.mjs.map
|