codex-copilot-dx 0.6.5 → 0.6.7
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 +12 -3
- package/bin/cli.mjs +17 -3
- package/package.json +1 -1
- package/scripts/benchmark.mjs +58 -1
- package/src/adapter.mjs +1 -0
- package/src/cli-auth.mjs +101 -14
- package/src/cli-models.mjs +74 -12
- package/src/cli-options.mjs +43 -16
- package/src/cli-table.mjs +114 -0
- package/src/copilot-client.mjs +3 -1
- package/src/doctor.mjs +9 -8
- package/src/http-transport.mjs +114 -13
- package/src/image-optimization.mjs +33 -11
- package/src/legacy-command-warning.mjs +3 -2
- package/src/models.mjs +14 -10
- package/src/observability.mjs +18 -1
- package/src/pm-studio-setup.mjs +28 -4
- package/src/pm-studio-status.mjs +177 -25
- package/src/profile-routing.mjs +7 -16
- package/src/profile-runtime.mjs +12 -3
- package/src/response-history.mjs +12 -2
- package/src/responses-byte-budget.mjs +53 -26
- package/src/responses-chat-payload.mjs +35 -16
- package/src/responses-content.mjs +36 -4
- package/src/responses-handler.mjs +47 -27
- package/src/responses-image-limit.mjs +16 -2
- package/src/responses-image-pressure.mjs +10 -4
- package/src/responses-request.mjs +28 -10
- package/src/usage.mjs +104 -9
package/src/pm-studio-setup.mjs
CHANGED
|
@@ -396,6 +396,19 @@ function backupName(recipe) {
|
|
|
396
396
|
return `${recipe.id}-${recipe.sourceAsarSha256.slice(0, 12)}`;
|
|
397
397
|
}
|
|
398
398
|
|
|
399
|
+
export function pmStudioBackupRoot(home = os.homedir()) {
|
|
400
|
+
return path.join(home, ".local", "share", "codex-copilot-dx", "pm-studio-backups");
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
export function pmStudioPatchManifestPath({
|
|
404
|
+
home = os.homedir(),
|
|
405
|
+
backupRoot = pmStudioBackupRoot(home),
|
|
406
|
+
recipe,
|
|
407
|
+
} = {}) {
|
|
408
|
+
if (!recipe) throw new TypeError("PM Studio patch recipe is required");
|
|
409
|
+
return path.join(backupRoot, backupName(recipe), "manifest.json");
|
|
410
|
+
}
|
|
411
|
+
|
|
399
412
|
function backupManifest(recipe, createdAt) {
|
|
400
413
|
return {
|
|
401
414
|
schema_version: 1,
|
|
@@ -512,13 +525,24 @@ function writePatchedBinaryRecord({ backup, inspection, operations }) {
|
|
|
512
525
|
backup.manifest = manifest;
|
|
513
526
|
}
|
|
514
527
|
|
|
515
|
-
function assertPatchedBinaryRecord({ inspection, manifestPath }) {
|
|
528
|
+
export function assertPatchedBinaryRecord({ inspection, manifestPath, recipe }) {
|
|
516
529
|
let manifest;
|
|
517
530
|
try {
|
|
518
531
|
manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
519
532
|
} catch {
|
|
520
533
|
throw setupError("PM_STUDIO_PATCH_RECORD_INVALID", "PM Studio patched-binary record is missing or invalid");
|
|
521
534
|
}
|
|
535
|
+
if (recipe && (manifest?.schema_version !== 1
|
|
536
|
+
|| manifest?.kind !== "ccdx-pm-studio-backup"
|
|
537
|
+
|| manifest?.recipe_id !== recipe.id
|
|
538
|
+
|| manifest?.app?.bundle_identifier !== recipe.bundleIdentifier
|
|
539
|
+
|| manifest?.app?.version !== recipe.version
|
|
540
|
+
|| manifest?.app?.build !== recipe.build
|
|
541
|
+
|| manifest?.patched?.asar_sha256 !== recipe.patchedAsarSha256
|
|
542
|
+
|| manifest?.patched?.asar_header_sha256 !== recipe.patchedHeaderSha256
|
|
543
|
+
|| !integrityMatches(manifest?.patched?.electron_asar_integrity, "SHA256", recipe.patchedHeaderSha256))) {
|
|
544
|
+
throw setupError("PM_STUDIO_PATCH_RECORD_INVALID", "PM Studio patched-binary record does not match the installed patch recipe");
|
|
545
|
+
}
|
|
522
546
|
const binaries = manifest?.patched?.binaries;
|
|
523
547
|
if (!binaries
|
|
524
548
|
|| binaries.main_executable_sha256 !== inspection.executableIntegrity.executableSha256
|
|
@@ -593,7 +617,7 @@ export async function runPmStudioSetup({
|
|
|
593
617
|
commandName = "ccdx",
|
|
594
618
|
appPath = DEFAULT_PM_STUDIO_APP_PATH,
|
|
595
619
|
home = os.homedir(),
|
|
596
|
-
backupRoot =
|
|
620
|
+
backupRoot = pmStudioBackupRoot(home),
|
|
597
621
|
recipes = PM_STUDIO_RECIPES,
|
|
598
622
|
operations: operationOverrides = {},
|
|
599
623
|
logger = (line) => console.log(line),
|
|
@@ -629,7 +653,7 @@ export async function runPmStudioSetup({
|
|
|
629
653
|
throw setupError("PM_STUDIO_BACKUP_INVALID", "Patched PM Studio has no matching verified backup; no files were changed");
|
|
630
654
|
}
|
|
631
655
|
const backup = validateBackup({ backupDir: finalBackupDir, recipe, operations });
|
|
632
|
-
assertPatchedBinaryRecord({ inspection: source, manifestPath: backup.manifestPath });
|
|
656
|
+
assertPatchedBinaryRecord({ inspection: source, manifestPath: backup.manifestPath, recipe });
|
|
633
657
|
emit(`[OK] PM Studio ${recipe.version} build ${recipe.build} is already patched; no backup or signing was repeated.`);
|
|
634
658
|
emit(`[OK] Verified backup: ${backup.backupAppPath}`);
|
|
635
659
|
emit(`[OK] Backup manifest: ${backup.manifestPath}`);
|
|
@@ -700,7 +724,7 @@ export async function runPmStudioSetup({
|
|
|
700
724
|
installed.issues);
|
|
701
725
|
}
|
|
702
726
|
try {
|
|
703
|
-
assertPatchedBinaryRecord({ inspection: installed, manifestPath: backup.manifestPath });
|
|
727
|
+
assertPatchedBinaryRecord({ inspection: installed, manifestPath: backup.manifestPath, recipe });
|
|
704
728
|
} catch (error) {
|
|
705
729
|
throw setupError("PM_STUDIO_INSTALL_VERIFY_FAILED",
|
|
706
730
|
`Installed PM Studio differs from the verified staging record; do not launch it. ${recoveryMessage(backup)}`,
|
package/src/pm-studio-status.mjs
CHANGED
|
@@ -7,16 +7,33 @@ import {
|
|
|
7
7
|
} from "./auth-profile.mjs";
|
|
8
8
|
import { PM_STUDIO_RECIPES } from "./pm-studio-asar.mjs";
|
|
9
9
|
import {
|
|
10
|
+
assertPatchedBinaryRecord,
|
|
10
11
|
createPmStudioSetupOperations,
|
|
11
12
|
DEFAULT_PM_STUDIO_APP_PATH,
|
|
12
13
|
inspectPmStudioApp,
|
|
13
14
|
PM_STUDIO_CLAUDE_AUTH_COMMAND,
|
|
15
|
+
pmStudioBackupRoot,
|
|
16
|
+
pmStudioPatchManifestPath,
|
|
14
17
|
} from "./pm-studio-setup.mjs";
|
|
18
|
+
import { readAdapterStatus } from "./cli-status.mjs";
|
|
15
19
|
import { checkRunningAdapter } from "./running-adapter.mjs";
|
|
16
20
|
import { status } from "./status.mjs";
|
|
21
|
+
import {
|
|
22
|
+
cliOutputFormat,
|
|
23
|
+
cliOutputWidth,
|
|
24
|
+
formatResponsiveCliTable,
|
|
25
|
+
terminalCell,
|
|
26
|
+
} from "./cli-table.mjs";
|
|
17
27
|
|
|
18
28
|
const PM_RELAY_HOST = "127.0.0.1";
|
|
19
29
|
const PM_RELAY_PORT = 2026;
|
|
30
|
+
const PM_RELAY_CAPABILITY = "pm_studio_relay_v1";
|
|
31
|
+
const PM_RELAY_ROUTE_NAMES = Object.freeze([
|
|
32
|
+
"pm_models",
|
|
33
|
+
"pm_chat_completions",
|
|
34
|
+
"pm_responses",
|
|
35
|
+
"pm_embeddings",
|
|
36
|
+
]);
|
|
20
37
|
|
|
21
38
|
function selectRecipe(metadata, recipes) {
|
|
22
39
|
return recipes.find((recipe) => recipe.version === String(metadata.version)
|
|
@@ -42,15 +59,52 @@ function inspectClaudeProfile(home, readCredentials) {
|
|
|
42
59
|
}
|
|
43
60
|
}
|
|
44
61
|
|
|
62
|
+
function inspectAdapterRuntime(adapter, runtimeStatus) {
|
|
63
|
+
const data = runtimeStatus?.data;
|
|
64
|
+
const health = adapter?.data;
|
|
65
|
+
const issues = [];
|
|
66
|
+
if (!data || typeof data !== "object") {
|
|
67
|
+
return { ok: false, baseUrl: runtimeStatus?.baseUrl || adapter?.baseUrl, data: null, issues: ["runtime status payload is missing"] };
|
|
68
|
+
}
|
|
69
|
+
if (data.version !== health?.version
|
|
70
|
+
|| data.protocol_version !== health?.protocol_version
|
|
71
|
+
|| data.instance_id !== health?.instance_id
|
|
72
|
+
|| data.pid !== health?.pid) {
|
|
73
|
+
issues.push("health and runtime status do not describe the same adapter instance");
|
|
74
|
+
}
|
|
75
|
+
const capabilities = new Set(Array.isArray(data.capabilities) ? data.capabilities : []);
|
|
76
|
+
if (!capabilities.has(PM_RELAY_CAPABILITY)) issues.push("PM Studio relay capability is missing");
|
|
77
|
+
if (data.profiles?.claude?.mode !== "isolated") issues.push("Claude profile mode is not isolated");
|
|
78
|
+
if (data.profiles?.claude?.profile_current !== true) {
|
|
79
|
+
issues.push("running Claude credentials do not match the current isolated profile");
|
|
80
|
+
}
|
|
81
|
+
if (data.routing?.responses !== "codex") issues.push("/v1/responses is not routed to Codex");
|
|
82
|
+
if (data.routing?.messages !== "claude") issues.push("/v1/messages is not routed to Claude");
|
|
83
|
+
const routes = data.requests?.by_route;
|
|
84
|
+
for (const routeName of PM_RELAY_ROUTE_NAMES) {
|
|
85
|
+
if (!routes || typeof routes !== "object" || !Object.hasOwn(routes, routeName)) {
|
|
86
|
+
issues.push(`runtime status does not expose ${routeName}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
...runtimeStatus,
|
|
91
|
+
ok: issues.length === 0,
|
|
92
|
+
issues,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
45
96
|
export async function inspectPmStudioStatus({
|
|
46
97
|
appPath = DEFAULT_PM_STUDIO_APP_PATH,
|
|
47
98
|
home = os.homedir(),
|
|
99
|
+
backupRoot = pmStudioBackupRoot(home),
|
|
48
100
|
recipes = PM_STUDIO_RECIPES,
|
|
49
101
|
operationOverrides = {},
|
|
50
102
|
existsSync = fs.existsSync,
|
|
51
103
|
inspectApp = inspectPmStudioApp,
|
|
104
|
+
verifyPatchRecord = assertPatchedBinaryRecord,
|
|
52
105
|
readClaudeCredentials = readAuthProfileCredentials,
|
|
53
106
|
checkRunningAdapterFn = checkRunningAdapter,
|
|
107
|
+
readAdapterStatusFn = readAdapterStatus,
|
|
54
108
|
} = {}) {
|
|
55
109
|
const operations = createPmStudioSetupOperations(operationOverrides);
|
|
56
110
|
let app;
|
|
@@ -69,9 +123,24 @@ export async function inspectPmStudioStatus({
|
|
|
69
123
|
bundleIdentifier: String(metadata.bundleIdentifier),
|
|
70
124
|
};
|
|
71
125
|
const recipe = selectRecipe(normalized, recipes);
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
126
|
+
if (recipe) {
|
|
127
|
+
app = { ...inspectApp({ appPath, recipe, operations }), recipe: recipe.id };
|
|
128
|
+
if (app.state === "patched") {
|
|
129
|
+
const manifestPath = pmStudioPatchManifestPath({ home, backupRoot, recipe });
|
|
130
|
+
try {
|
|
131
|
+
verifyPatchRecord({ inspection: app, manifestPath, recipe });
|
|
132
|
+
app.patchRecord = { valid: true, manifestPath, reason: "" };
|
|
133
|
+
} catch (error) {
|
|
134
|
+
const reason = safeMessage(error);
|
|
135
|
+
app.patchRecipeMatched = true;
|
|
136
|
+
app.patchRecord = { valid: false, manifestPath, reason };
|
|
137
|
+
app.state = "drift";
|
|
138
|
+
app.issues = [...(Array.isArray(app.issues) ? app.issues : []), reason];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
app = { state: "unsupported", metadata: normalized, issues: [], recipe: "" };
|
|
143
|
+
}
|
|
75
144
|
} catch (error) {
|
|
76
145
|
app = { state: "error", metadata: null, issues: [safeMessage(error)], recipe: "" };
|
|
77
146
|
}
|
|
@@ -84,42 +153,125 @@ export async function inspectPmStudioStatus({
|
|
|
84
153
|
} catch (error) {
|
|
85
154
|
adapter = { ok: false, error };
|
|
86
155
|
}
|
|
87
|
-
|
|
88
|
-
|
|
156
|
+
let runtime = { ok: false, baseUrl: adapter?.baseUrl, data: null, issues: ["adapter health check failed"] };
|
|
157
|
+
if (adapter?.ok === true) {
|
|
158
|
+
try {
|
|
159
|
+
runtime = inspectAdapterRuntime(adapter, await readAdapterStatusFn({
|
|
160
|
+
host: PM_RELAY_HOST,
|
|
161
|
+
port: PM_RELAY_PORT,
|
|
162
|
+
}));
|
|
163
|
+
} catch (error) {
|
|
164
|
+
runtime = { ok: false, baseUrl: adapter.baseUrl, data: null, issues: [safeMessage(error)] };
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const operational = app.state === "patched"
|
|
168
|
+
&& app.patchRecord?.valid === true
|
|
169
|
+
&& claude.valid
|
|
170
|
+
&& adapter?.ok === true
|
|
171
|
+
&& runtime.ok === true;
|
|
172
|
+
return { ok: operational, appPath, app, claude, adapter, runtime };
|
|
89
173
|
}
|
|
90
174
|
|
|
91
|
-
function
|
|
175
|
+
function appStatusItem(result, commandName) {
|
|
92
176
|
const { app } = result;
|
|
93
177
|
const version = app.metadata ? `${app.metadata.version} build ${app.metadata.build}` : "";
|
|
94
|
-
if (app.state === "patched"
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (app.state === "
|
|
98
|
-
|
|
99
|
-
|
|
178
|
+
if (app.state === "patched" && app.patchRecord?.valid === true) {
|
|
179
|
+
return { kind: "ok", component: "App patch", detail: `${version}; patched and verified`, message: `PM Studio ${version} is patched and verified against its installed patch record` };
|
|
180
|
+
}
|
|
181
|
+
if (app.state === "patched") {
|
|
182
|
+
const reason = app.patchRecord?.reason || "installed patch record is missing";
|
|
183
|
+
return { kind: "err", component: "App patch", detail: `${version}; patch record not verified`, message: `PM Studio ${version} matches the patch recipe, but its installed patch record is not verified: ${reason}` };
|
|
184
|
+
}
|
|
185
|
+
if (app.state === "clean") return { kind: "warn", component: "App patch", detail: `${version}; supported, not patched`, message: `PM Studio ${version} is supported but not patched; run ${commandName} pms setup` };
|
|
186
|
+
if (app.state === "unsupported") return { kind: "err", component: "App patch", detail: `${version}; unsupported version`, message: `PM Studio ${version} has no exact patch recipe; no files will be changed` };
|
|
187
|
+
if (app.state === "drift" && app.patchRecipeMatched) {
|
|
188
|
+
return { kind: "err", component: "App patch", detail: `${version}; patch record drift`, message: `PM Studio ${version} matches the patch recipe, but its installed patch record is not verified: ${app.issues.join("; ")}` };
|
|
189
|
+
}
|
|
190
|
+
if (app.state === "drift") return { kind: "err", component: "App patch", detail: `${version}; integrity drift`, message: `PM Studio ${version} does not match the clean or patched recipe: ${app.issues.join("; ")}` };
|
|
191
|
+
if (app.state === "not_installed") return { kind: "warn", component: "App patch", detail: "not installed", message: `PM Studio is not installed at ${result.appPath}` };
|
|
192
|
+
return { kind: "err", component: "App patch", detail: "inspection failed", message: `PM Studio could not be inspected: ${app.issues.join("; ")}` };
|
|
100
193
|
}
|
|
101
194
|
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
`${commandName} pms status`,
|
|
105
|
-
appStatusLine(result, commandName),
|
|
106
|
-
];
|
|
195
|
+
function pmStudioStatusItems(result, commandName) {
|
|
196
|
+
const items = [appStatusItem(result, commandName)];
|
|
107
197
|
if (result.claude.valid) {
|
|
108
198
|
const account = result.claude.login ? ` for ${result.claude.login}` : "";
|
|
109
|
-
|
|
199
|
+
const detail = result.claude.login ? `${result.claude.login}; isolated profile valid` : "isolated profile valid";
|
|
200
|
+
items.push({ kind: "ok", component: "Claude profile", detail, message: `Isolated Claude profile is valid${account}` });
|
|
110
201
|
} else {
|
|
111
202
|
const reason = result.claude.configured && result.claude.reason ? ` (${result.claude.reason})` : "";
|
|
112
|
-
|
|
203
|
+
items.push({ kind: "err", component: "Claude profile", detail: "not ready", message: `Isolated Claude profile is not ready${reason}; run ${PM_STUDIO_CLAUDE_AUTH_COMMAND}` });
|
|
113
204
|
}
|
|
114
|
-
if (result.adapter?.ok) {
|
|
115
|
-
|
|
205
|
+
if (result.adapter?.ok && result.runtime?.ok) {
|
|
206
|
+
items.push({ kind: "ok", component: "PM relay", detail: `verified at ${result.adapter.baseUrl}`, message: `PM relay and isolated Claude routing are verified at ${result.adapter.baseUrl}` });
|
|
207
|
+
} else if (result.adapter?.ok) {
|
|
208
|
+
const reason = result.runtime?.issues?.join("; ") || "runtime status is unavailable";
|
|
209
|
+
items.push({ kind: "err", component: "PM relay", detail: "running but routing not ready", message: `The adapter is running at ${result.adapter.baseUrl}, but PM relay routing is not ready: ${reason}; stop it and run ${commandName} start` });
|
|
116
210
|
} else if (result.adapter?.incompatible) {
|
|
117
|
-
|
|
211
|
+
items.push({ kind: "err", component: "PM relay", detail: `incompatible at ${result.adapter.baseUrl}`, message: `The adapter at ${result.adapter.baseUrl} is incompatible; stop it and run ${commandName} start` });
|
|
212
|
+
} else {
|
|
213
|
+
items.push({ kind: "warn", component: "PM relay", detail: `not running at ${PM_RELAY_HOST}:${PM_RELAY_PORT}`, message: `PM relay is not running at http://${PM_RELAY_HOST}:${PM_RELAY_PORT}; run ${commandName} start` });
|
|
214
|
+
}
|
|
215
|
+
if (result.runtime?.ok) {
|
|
216
|
+
items.push({ kind: "info", component: "Routing", detail: "GPT -> PM bearer; Claude -> isolated profile", message: "Routing: PM GPT uses the PM Studio bearer; eligible Claude chat uses the isolated Claude profile" });
|
|
118
217
|
} else {
|
|
119
|
-
|
|
218
|
+
items.push({ kind: "info", component: "Routing", detail: "expected: GPT -> PM bearer; Claude -> isolated profile", message: "Expected routing: PM GPT uses the PM Studio bearer; eligible Claude chat uses the isolated Claude profile" });
|
|
219
|
+
}
|
|
220
|
+
return items;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function formatPmStudioStatusPlain(result, commandName, { sanitize = false } = {}) {
|
|
224
|
+
const lines = [
|
|
225
|
+
`${commandName} pms status`,
|
|
226
|
+
...pmStudioStatusItems(result, commandName).map((item) => status(item.kind, item.message)),
|
|
227
|
+
];
|
|
228
|
+
return (sanitize ? lines.map((line) => terminalCell(line, { fallback: "" })) : lines).join("\n");
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function tableState(kind) {
|
|
232
|
+
return `[${String(kind).toUpperCase()}]`;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function formatPmStudioStatusTable(result, { commandName, output }) {
|
|
236
|
+
const items = pmStudioStatusItems(result, commandName);
|
|
237
|
+
const rows = items.map((item) => ({
|
|
238
|
+
component: item.component,
|
|
239
|
+
state: tableState(item.kind),
|
|
240
|
+
detail: item.detail,
|
|
241
|
+
}));
|
|
242
|
+
const table = formatResponsiveCliTable({
|
|
243
|
+
columns: [
|
|
244
|
+
{ key: "component", label: "COMPONENT" },
|
|
245
|
+
{ key: "state", label: "STATE" },
|
|
246
|
+
{ key: "detail", label: "DETAIL" },
|
|
247
|
+
],
|
|
248
|
+
compactColumns: [
|
|
249
|
+
{ key: "component", label: "COMPONENT" },
|
|
250
|
+
{ key: "state", label: "STATE" },
|
|
251
|
+
],
|
|
252
|
+
rows,
|
|
253
|
+
width: cliOutputWidth(output),
|
|
254
|
+
});
|
|
255
|
+
const lines = [`${commandName} pms status`, table.output];
|
|
256
|
+
const details = table.compact ? items : items.filter((item) => item.kind === "warn" || item.kind === "err");
|
|
257
|
+
if (details.length) {
|
|
258
|
+
lines.push("", "Details:", ...details.map((item) => terminalCell(status(item.kind, item.message), { fallback: "" })));
|
|
259
|
+
}
|
|
260
|
+
return { output: lines.join("\n"), overflow: table.overflow };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export function formatPmStudioStatus(result, {
|
|
264
|
+
commandName = "ccdx",
|
|
265
|
+
format = "plain",
|
|
266
|
+
output = process.stdout,
|
|
267
|
+
} = {}) {
|
|
268
|
+
if (cliOutputFormat(format, output) === "plain") {
|
|
269
|
+
return formatPmStudioStatusPlain(result, commandName);
|
|
120
270
|
}
|
|
121
|
-
|
|
122
|
-
return
|
|
271
|
+
const table = formatPmStudioStatusTable(result, { commandName, output });
|
|
272
|
+
return format === "auto" && table.overflow
|
|
273
|
+
? formatPmStudioStatusPlain(result, commandName, { sanitize: true })
|
|
274
|
+
: table.output;
|
|
123
275
|
}
|
|
124
276
|
|
|
125
277
|
export async function runPmStudioStatus(options = {}) {
|
package/src/profile-routing.mjs
CHANGED
|
@@ -1,22 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isClaudeCopilotCatalogEntry,
|
|
3
|
+
isClaudeCopilotModel,
|
|
4
|
+
} from "./models.mjs";
|
|
5
|
+
|
|
1
6
|
function catalogData(catalog) {
|
|
2
7
|
if (Array.isArray(catalog)) return catalog;
|
|
3
8
|
return Array.isArray(catalog?.data) ? catalog.data : [];
|
|
4
9
|
}
|
|
5
10
|
|
|
6
|
-
function isClaudeCatalogEntry(model) {
|
|
7
|
-
const id = String(model?.id || "").trim();
|
|
8
|
-
const vendor = String(model?.vendor || "").trim().toLowerCase();
|
|
9
|
-
return Boolean(id) && (vendor === "anthropic" || id.toLowerCase().startsWith("claude-"));
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function isAllowedClaudeModel(model) {
|
|
13
|
-
return isClaudeCatalogEntry(model)
|
|
14
|
-
&& String(model?.vendor || "").trim().toLowerCase() === "anthropic"
|
|
15
|
-
&& model?.model_picker_enabled !== false
|
|
16
|
-
&& Array.isArray(model?.supported_endpoints)
|
|
17
|
-
&& model.supported_endpoints.includes("/chat/completions");
|
|
18
|
-
}
|
|
19
|
-
|
|
20
11
|
export function profileRouting({ claudeMode = "inherited", claudeConfigured = false } = {}) {
|
|
21
12
|
const isolated = claudeMode === "isolated" || claudeConfigured === true;
|
|
22
13
|
return Object.freeze({ responses: "codex", messages: isolated ? "claude" : "codex" });
|
|
@@ -44,9 +35,9 @@ export function createPmStudioModelRouter({ getCatalog, isClaudeEnabled } = {})
|
|
|
44
35
|
const known = new Set();
|
|
45
36
|
for (const model of catalogData(catalog)) {
|
|
46
37
|
const id = String(model?.id || "").trim();
|
|
47
|
-
if (!id || !
|
|
38
|
+
if (!id || !isClaudeCopilotCatalogEntry(model)) continue;
|
|
48
39
|
known.add(id);
|
|
49
|
-
if (enabled &&
|
|
40
|
+
if (enabled && isClaudeCopilotModel(model) && !allowed.has(id)) allowed.set(id, model);
|
|
50
41
|
}
|
|
51
42
|
cachedCatalog = catalog;
|
|
52
43
|
cachedEnabled = enabled;
|
package/src/profile-runtime.mjs
CHANGED
|
@@ -46,6 +46,15 @@ export function createProfileRuntime({
|
|
|
46
46
|
};
|
|
47
47
|
const credentials = readCredentials();
|
|
48
48
|
const claudeProfile = safeProfileStatus(credentials);
|
|
49
|
+
const claudeCredentialFingerprint = credentials.valid
|
|
50
|
+
? githubTokenFingerprint(credentials.token)
|
|
51
|
+
: "";
|
|
52
|
+
const isClaudeProfileCurrent = () => {
|
|
53
|
+
if (!claudeCredentialFingerprint) return false;
|
|
54
|
+
const current = readCredentials();
|
|
55
|
+
return current.valid === true
|
|
56
|
+
&& githubTokenFingerprint(current.token) === claudeCredentialFingerprint;
|
|
57
|
+
};
|
|
49
58
|
let codexCredentialFingerprint = "";
|
|
50
59
|
try {
|
|
51
60
|
const codexCredentials = readAuthProfileCredentials(AUTH_PROFILE_CODEX, { home });
|
|
@@ -65,6 +74,7 @@ export function createProfileRuntime({
|
|
|
65
74
|
claudeProfile,
|
|
66
75
|
codexCredentialFingerprint,
|
|
67
76
|
claudeCredentialFingerprint: "",
|
|
77
|
+
isClaudeProfileCurrent,
|
|
68
78
|
});
|
|
69
79
|
}
|
|
70
80
|
|
|
@@ -86,8 +96,7 @@ export function createProfileRuntime({
|
|
|
86
96
|
claudeMode: "isolated",
|
|
87
97
|
claudeProfile,
|
|
88
98
|
codexCredentialFingerprint,
|
|
89
|
-
claudeCredentialFingerprint
|
|
90
|
-
|
|
91
|
-
: "",
|
|
99
|
+
claudeCredentialFingerprint,
|
|
100
|
+
isClaudeProfileCurrent,
|
|
92
101
|
});
|
|
93
102
|
}
|
package/src/response-history.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { httpError } from "./http-transport.mjs";
|
|
2
|
+
import { clearResponsesToolOutputPartsCache } from "./responses-content.mjs";
|
|
2
3
|
import { loadRuntimeConfig, parsePositiveInteger, RUNTIME_DEFAULTS } from "./runtime-config.mjs";
|
|
3
4
|
|
|
4
5
|
const DEFAULT_MAX_BYTES = RUNTIME_DEFAULTS.responseHistoryMaxBytes;
|
|
@@ -201,17 +202,26 @@ export function responseHistoryMaterializedBytes(responseId) {
|
|
|
201
202
|
return chain.reduce((bytes, entry) => bytes + entry.bytes, 0);
|
|
202
203
|
}
|
|
203
204
|
|
|
204
|
-
export function materializeResponseHistory(responseId) {
|
|
205
|
+
export function materializeResponseHistory(responseId, { assertActive } = {}) {
|
|
206
|
+
assertActive?.();
|
|
205
207
|
const { chain, rootId } = responseHistoryChain(responseId);
|
|
206
208
|
const items = [];
|
|
207
|
-
for (
|
|
209
|
+
for (let index = 0; index < chain.length; index += 1) {
|
|
210
|
+
if ((index & 63) === 0) assertActive?.();
|
|
211
|
+
const entry = chain[chain.length - index - 1];
|
|
212
|
+
items.push(...entry.inputItems, ...entry.outputItems);
|
|
213
|
+
}
|
|
214
|
+
assertActive?.();
|
|
208
215
|
const materialized = cloneJson(items);
|
|
216
|
+
assertActive?.();
|
|
209
217
|
touchTree(rootId);
|
|
210
218
|
return materialized;
|
|
211
219
|
}
|
|
212
220
|
|
|
213
221
|
export function rememberResponseHistoryNode({ id, parentId, inputItems, outputItems, takeOwnership = false }) {
|
|
214
222
|
if (!id || !Array.isArray(inputItems)) return;
|
|
223
|
+
clearResponsesToolOutputPartsCache(inputItems);
|
|
224
|
+
clearResponsesToolOutputPartsCache(outputItems);
|
|
215
225
|
const bytes = jsonByteLength([inputItems, outputItems]);
|
|
216
226
|
if (bytes > maxBytes) {
|
|
217
227
|
if (histories.has(id)) removeSubtree(id);
|