@bitkyc08/opencodex 2.7.42 → 2.7.43
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 +8 -1
- package/bin/ocx.mjs +13 -14
- package/gui/dist/assets/index-Czw-jpTU.css +1 -0
- package/gui/dist/assets/index-cmds12BG.js +67 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/AGENTS.md +28 -0
- package/src/adapters/anthropic.ts +15 -6
- package/src/adapters/cursor/native-exec-shell.ts +18 -6
- package/src/adapters/cursor/protobuf-events.ts +24 -2
- package/src/adapters/cursor/protobuf-request.ts +1 -2
- package/src/adapters/cursor/tool-definitions.ts +68 -29
- package/src/adapters/google-wire-compiler.ts +4 -0
- package/src/adapters/google.ts +128 -2
- package/src/adapters/identity.ts +12 -2
- package/src/adapters/kiro.ts +49 -6
- package/src/adapters/mimo-free.ts +2 -0
- package/src/adapters/openai-responses.ts +246 -59
- package/src/claude/agents-inject.ts +5 -0
- package/src/claude/inbound.ts +26 -9
- package/src/cli/account-auth.ts +1 -1
- package/src/cli/agent-driven.ts +37 -0
- package/src/cli/claude.ts +3 -3
- package/src/cli/doctor.ts +23 -18
- package/src/cli/help.ts +42 -6
- package/src/cli/index.ts +88 -19
- package/src/cli/interactive-confirm.ts +129 -0
- package/src/cli/opencode.ts +701 -0
- package/src/cli/provider-runtime.ts +3 -0
- package/src/cli/provider.ts +31 -10
- package/src/cli/star-prompt.ts +55 -16
- package/src/cli/status.ts +47 -13
- package/src/codex/account-id.ts +34 -0
- package/src/codex/account-lifecycle.ts +4 -1
- package/src/codex/account-namespace-match.ts +63 -0
- package/src/codex/account-namespaces.ts +149 -0
- package/src/codex/account-pause.ts +20 -0
- package/src/codex/account-usability.ts +6 -1
- package/src/codex/app-server-processes.ts +511 -0
- package/src/codex/auth-api.ts +293 -34
- package/src/codex/auth-collision.ts +2 -1
- package/src/codex/auth-context.ts +60 -17
- package/src/codex/catalog/parsing.ts +16 -1
- package/src/codex/catalog/provider-fetch.ts +48 -12
- package/src/codex/catalog/sync.ts +18 -3
- package/src/codex/catalog.ts +1 -1
- package/src/codex/history-provider.ts +145 -1
- package/src/codex/inject.ts +114 -14
- package/src/codex/main-account.ts +2 -8
- package/src/codex/pool-rotation.ts +186 -0
- package/src/codex/quota.ts +15 -0
- package/src/codex/routing.ts +695 -106
- package/src/codex/shim.ts +4 -1
- package/src/codex/subagent-defaults.ts +550 -0
- package/src/codex/subagent-model-fallback.ts +2 -0
- package/src/codex/sync.ts +3 -0
- package/src/config.ts +566 -25
- package/src/images/artifacts.ts +516 -0
- package/src/images/fulfill-video.ts +163 -0
- package/src/images/fulfill.ts +111 -0
- package/src/images/index.ts +4 -0
- package/src/images/loop.ts +789 -0
- package/src/images/plan.ts +133 -0
- package/src/images/synthetic-tool.ts +133 -0
- package/src/images/types.ts +41 -0
- package/src/images/xai-client.ts +141 -0
- package/src/images/xai-video-client.ts +163 -0
- package/src/lib/admin-secrets.ts +25 -0
- package/src/lib/config-ownership.ts +327 -0
- package/src/lib/crash-guard.ts +2 -0
- package/src/lib/destination-policy.ts +122 -4
- package/src/lib/pinned-http.ts +151 -0
- package/src/lib/process-control.ts +2 -2
- package/src/lib/provider-outbound.ts +164 -0
- package/src/lib/provider-url.ts +14 -0
- package/src/lib/proxy-env.ts +18 -0
- package/src/lib/windows-elevation.ts +81 -3
- package/src/lib/windows-secret-acl.ts +189 -12
- package/src/lib/winsw.ts +2 -0
- package/src/oauth/anthropic-routing.ts +570 -0
- package/src/oauth/health.ts +6 -0
- package/src/oauth/index.ts +283 -72
- package/src/oauth/key-providers.ts +17 -6
- package/src/oauth/kimi.ts +2 -0
- package/src/oauth/kiro-credentials.ts +322 -10
- package/src/oauth/kiro.ts +423 -43
- package/src/oauth/login-cli.ts +32 -5
- package/src/oauth/store.ts +54 -4
- package/src/oauth/types.ts +11 -0
- package/src/providers/alibaba-region-migration.ts +16 -3
- package/src/providers/antigravity-models.ts +3 -0
- package/src/providers/api-keys.ts +13 -6
- package/src/providers/derive.ts +6 -0
- package/src/providers/key-failover.ts +24 -4
- package/src/providers/quota.ts +233 -29
- package/src/providers/registry.ts +11 -3
- package/src/responses/parser.ts +11 -0
- package/src/responses/state.ts +22 -8
- package/src/responses/tool-groups.ts +19 -0
- package/src/router.ts +14 -4
- package/src/server/auth-cors.ts +110 -22
- package/src/server/claude-messages.ts +8 -1
- package/src/server/gui-static.ts +30 -6
- package/src/server/images.ts +303 -9
- package/src/server/index.ts +77 -9
- package/src/server/lifecycle.ts +25 -1
- package/src/server/management/agent-settings-routes.ts +28 -4
- package/src/server/management/combo-routes.ts +7 -0
- package/src/server/management/config-routes.ts +5 -2
- package/src/server/management/logs-usage-routes.ts +167 -3
- package/src/server/management/oauth-account-routes.ts +163 -17
- package/src/server/management/provider-routes.ts +35 -7
- package/src/server/management/system-restart.ts +172 -0
- package/src/server/management/system-routes.ts +33 -10
- package/src/server/management-api.ts +2 -2
- package/src/server/management-auth.ts +216 -0
- package/src/server/proxy-liveness.ts +14 -3
- package/src/server/responses/compact.ts +21 -13
- package/src/server/responses/core.ts +583 -152
- package/src/server/responses-image-gen-repair.ts +118 -0
- package/src/server/responses-item-id-repair.ts +10 -85
- package/src/server/sse-payload-rewrite.ts +116 -0
- package/src/server/system-env.ts +28 -10
- package/src/service.ts +49 -2
- package/src/storage/cleanup-job.ts +57 -0
- package/src/storage/cleanup.ts +1504 -28
- package/src/storage/policy-job.ts +366 -0
- package/src/storage/policy-scheduler.ts +40 -0
- package/src/storage/policy-worker.ts +53 -0
- package/src/storage/policy.ts +522 -0
- package/src/storage/restore-job.ts +242 -0
- package/src/storage/restore-worker.ts +52 -0
- package/src/storage/storage-mutation-coordinator.ts +109 -0
- package/src/tray/windows.ts +2 -0
- package/src/types.ts +96 -1
- package/src/update/index.ts +36 -18
- package/src/update/job.ts +103 -12
- package/src/update/npm-invocation.d.mts +23 -0
- package/src/update/npm-invocation.mjs +94 -0
- package/src/usage/debug.ts +2 -0
- package/src/usage/log.ts +4 -0
- package/gui/dist/assets/index-Bl_VBGoI.js +0 -65
- package/gui/dist/assets/index-DfVGuN88.css +0 -1
package/src/adapters/kiro.ts
CHANGED
|
@@ -52,6 +52,24 @@ const AMZ_TARGET = "AmazonCodeWhispererStreamingService.GenerateAssistantRespons
|
|
|
52
52
|
const SDK_VERSION = "1.0.27";
|
|
53
53
|
const NODE_VERSION = "22.21.1";
|
|
54
54
|
const KIRO_IDE_VERSION = "1.0.0";
|
|
55
|
+
type KiroWireClient = "ide" | "cli";
|
|
56
|
+
|
|
57
|
+
function kiroCliPlatform(): "linux" | "macos" | "windows" {
|
|
58
|
+
return process.platform === "win32" ? "windows" : process.platform === "darwin" ? "macos" : "linux";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function kiroCliUserAgent(includeAppVersion: boolean): string {
|
|
62
|
+
return [
|
|
63
|
+
"aws-sdk-rust/1.3.15",
|
|
64
|
+
"ua/2.1",
|
|
65
|
+
"api/codewhispererstreaming/0.1.17975",
|
|
66
|
+
`os/${kiroCliPlatform()}`,
|
|
67
|
+
"lang/rust/1.92.0",
|
|
68
|
+
...(includeAppVersion ? ["md/appVersion-2.14.2"] : []),
|
|
69
|
+
"m/F",
|
|
70
|
+
"app/AmazonQ-For-CLI",
|
|
71
|
+
].join(" ");
|
|
72
|
+
}
|
|
55
73
|
|
|
56
74
|
// Payload construction (conversationState)
|
|
57
75
|
interface KiroToolUse {
|
|
@@ -68,7 +86,10 @@ interface KiroUserInputMessage {
|
|
|
68
86
|
content: string;
|
|
69
87
|
modelId?: string;
|
|
70
88
|
origin?: string;
|
|
71
|
-
userInputMessageContext?: {
|
|
89
|
+
userInputMessageContext?: {
|
|
90
|
+
tools?: unknown[];
|
|
91
|
+
toolResults?: KiroToolResult[];
|
|
92
|
+
};
|
|
72
93
|
images?: KiroImage[];
|
|
73
94
|
}
|
|
74
95
|
interface KiroHistoryEntry {
|
|
@@ -385,6 +406,7 @@ export function buildKiroPayload(
|
|
|
385
406
|
parsed: OcxParsedRequest,
|
|
386
407
|
profileArn: string | undefined,
|
|
387
408
|
forcedCompletionMode?: KiroCompletionMode,
|
|
409
|
+
wireClient: KiroWireClient = "ide",
|
|
388
410
|
): {
|
|
389
411
|
payload: Record<string, unknown>;
|
|
390
412
|
nameMap: Map<string, string>;
|
|
@@ -509,7 +531,7 @@ export function buildKiroPayload(
|
|
|
509
531
|
userInputMessage: {
|
|
510
532
|
content: turn.content,
|
|
511
533
|
modelId,
|
|
512
|
-
origin: "AI_EDITOR",
|
|
534
|
+
origin: wireClient === "cli" ? "KIRO_CLI" : "AI_EDITOR",
|
|
513
535
|
...(turn.images.length > 0 ? { images: turn.images } : {}),
|
|
514
536
|
...(turn.toolResults.length > 0 ? { userInputMessageContext: { toolResults: turn.toolResults } } : {}),
|
|
515
537
|
},
|
|
@@ -539,6 +561,10 @@ export function buildKiroPayload(
|
|
|
539
561
|
const payload: Record<string, unknown> = {
|
|
540
562
|
conversationState: {
|
|
541
563
|
chatTriggerType: "MANUAL",
|
|
564
|
+
...(wireClient === "cli" ? {
|
|
565
|
+
agentContinuationId: crypto.randomUUID(),
|
|
566
|
+
agentTaskType: "vibe",
|
|
567
|
+
} : {}),
|
|
542
568
|
conversationId,
|
|
543
569
|
currentMessage: { userInputMessage: currentUim },
|
|
544
570
|
...(history.length > 0 ? { history } : {}),
|
|
@@ -1445,10 +1471,26 @@ export function createKiroAdapter(provider: OcxProviderConfig): ProviderAdapter
|
|
|
1445
1471
|
if (typeof provider.apiKey !== "string" || provider.apiKey.trim() === "") {
|
|
1446
1472
|
throw new Error("kiro token missing — run ocx login kiro");
|
|
1447
1473
|
}
|
|
1448
|
-
const region = resolveKiroApiRegion();
|
|
1449
|
-
const
|
|
1474
|
+
const region = resolveKiroApiRegion(parsed._kiroAuthContext);
|
|
1475
|
+
const resolvedProfileArn = resolveKiroProfileArn(parsed._kiroAuthContext);
|
|
1476
|
+
const isApiKey = provider.apiKey.trim().startsWith("ksk_");
|
|
1477
|
+
const profileArn = isApiKey ? undefined : resolvedProfileArn;
|
|
1478
|
+
// Builder ID and Kiro API keys have no profile ARN and are accepted only on Kiro's CLI
|
|
1479
|
+
// request path. Enterprise profiles retain the existing IDE-shaped request.
|
|
1480
|
+
const wireClient: KiroWireClient = isApiKey || !profileArn ? "cli" : "ide";
|
|
1450
1481
|
const fp = fingerprint().slice(0, 64);
|
|
1451
|
-
const headers: Record<string, string> = {
|
|
1482
|
+
const headers: Record<string, string> = wireClient === "cli" ? {
|
|
1483
|
+
authorization: `Bearer ${provider.apiKey}`,
|
|
1484
|
+
"content-type": "application/x-amz-json-1.0",
|
|
1485
|
+
accept: "*/*",
|
|
1486
|
+
"x-amz-target": AMZ_TARGET,
|
|
1487
|
+
"user-agent": kiroCliUserAgent(true),
|
|
1488
|
+
"x-amz-user-agent": kiroCliUserAgent(false),
|
|
1489
|
+
"x-amzn-codewhisperer-optout": "true",
|
|
1490
|
+
"amz-sdk-request": "attempt=1; max=3",
|
|
1491
|
+
"amz-sdk-invocation-id": invocationId(),
|
|
1492
|
+
...(isApiKey ? { tokentype: "API_KEY" } : {}),
|
|
1493
|
+
} : {
|
|
1452
1494
|
authorization: `Bearer ${provider.apiKey}`,
|
|
1453
1495
|
"content-type": "application/x-amz-json-1.0",
|
|
1454
1496
|
accept: "application/vnd.amazon.eventstream",
|
|
@@ -1460,7 +1502,7 @@ export function createKiroAdapter(provider: OcxProviderConfig): ProviderAdapter
|
|
|
1460
1502
|
"amz-sdk-invocation-id": invocationId(),
|
|
1461
1503
|
};
|
|
1462
1504
|
if (profileArn) headers["x-amzn-kiro-profile-arn"] = profileArn;
|
|
1463
|
-
const built = buildKiroPayload(parsed, profileArn, forcedCompletionMode);
|
|
1505
|
+
const built = buildKiroPayload(parsed, profileArn, forcedCompletionMode, wireClient);
|
|
1464
1506
|
await normalizeKiroImages(built.payload);
|
|
1465
1507
|
const contextInputEstimate = estimateKiroPayloadInputTokens(built.payload, parsed.modelId);
|
|
1466
1508
|
const body = JSON.stringify(built.payload);
|
|
@@ -1472,6 +1514,7 @@ export function createKiroAdapter(provider: OcxProviderConfig): ProviderAdapter
|
|
|
1472
1514
|
messageCount: kiroPayloadMessages(parsed).length,
|
|
1473
1515
|
toolCount: parsed.context.tools?.length ?? 0,
|
|
1474
1516
|
hasProfileArn: Boolean(profileArn),
|
|
1517
|
+
wireClient,
|
|
1475
1518
|
hasPreviousResponseId: Boolean(parsed.previousResponseId),
|
|
1476
1519
|
});
|
|
1477
1520
|
return {
|
|
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { getConfigDir } from "../config";
|
|
5
|
+
import { recordOwnedConfigPath } from "../lib/config-ownership";
|
|
5
6
|
import type { OcxProviderConfig, OcxParsedRequest } from "../types";
|
|
6
7
|
import { createOpenAIChatAdapter } from "./openai-chat";
|
|
7
8
|
import type { ProviderAdapter, AdapterRequest } from "./base";
|
|
@@ -59,6 +60,7 @@ export function getMimoClientId(): string {
|
|
|
59
60
|
} catch { /* fall through to regenerate */ }
|
|
60
61
|
const fresh = randomUUID();
|
|
61
62
|
try {
|
|
63
|
+
recordOwnedConfigPath(dir, file);
|
|
62
64
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
63
65
|
writeFileSync(file, `${fresh}\n`, "utf8");
|
|
64
66
|
} catch { /* persist best-effort; still usable for this process */ }
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import type { IncomingMeta, ProviderAdapter } from "./base";
|
|
3
|
-
import type
|
|
3
|
+
import { namespacedToolName, type AdapterEvent, type OcxParsedRequest, type OcxProviderConfig, type OcxUsage } from "../types";
|
|
4
4
|
import { catalogModelSupportsReasoningSummaries } from "../codex/catalog";
|
|
5
5
|
import { COMPACT_PROMPT, decodeCompactionSummary, SUMMARY_PREFIX } from "../responses/compaction";
|
|
6
|
+
import { collectResponsesToolGroups } from "../responses/tool-groups";
|
|
6
7
|
import { decodeServerSentEvents } from "../lib/sse-decoder";
|
|
7
8
|
import { isCanonicalOpenAiForwardProvider } from "../providers/openai-tiers";
|
|
8
9
|
import { OCX_REASONING_PREFIX } from "../responses/reasoning-envelope";
|
|
@@ -500,72 +501,252 @@ function stripUnsupportedForwardParams(body: unknown): unknown {
|
|
|
500
501
|
return rest;
|
|
501
502
|
}
|
|
502
503
|
|
|
504
|
+
const IMAGE_GEN_NAMESPACE = "image_gen";
|
|
505
|
+
const HOSTED_IMAGE_GENERATION_TOOL = "image_generation";
|
|
506
|
+
const IMAGE_GEN_DOTTED_PREFIX = `${IMAGE_GEN_NAMESPACE}.`;
|
|
507
|
+
const IMAGE_GEN_WIRE_PREFIX = `${IMAGE_GEN_NAMESPACE}__`;
|
|
508
|
+
|
|
509
|
+
/** Remove a supported client prefix before constructing the canonical image-gen wire alias. */
|
|
510
|
+
function imageGenLocalName(name: string): string {
|
|
511
|
+
if (name.startsWith(IMAGE_GEN_DOTTED_PREFIX)) return name.slice(IMAGE_GEN_DOTTED_PREFIX.length);
|
|
512
|
+
if (name.startsWith(IMAGE_GEN_WIRE_PREFIX)) return name.slice(IMAGE_GEN_WIRE_PREFIX.length);
|
|
513
|
+
return name;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/** Build the flat public-Responses name used only on the upstream wire. */
|
|
517
|
+
function imageGenWireName(name: string): string {
|
|
518
|
+
return namespacedToolName(IMAGE_GEN_NAMESPACE, imageGenLocalName(name));
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/** Match client image-gen declarations across namespace, legacy dotted, and canonical wire forms. */
|
|
522
|
+
function isImageGenClientName(name: string): boolean {
|
|
523
|
+
return name === IMAGE_GEN_NAMESPACE
|
|
524
|
+
|| name.startsWith(IMAGE_GEN_DOTTED_PREFIX)
|
|
525
|
+
|| name.startsWith(IMAGE_GEN_WIRE_PREFIX);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/** Identify declarations that should activate image-gen request normalization. */
|
|
529
|
+
function declaresImageGenClientTool(tool: unknown): boolean {
|
|
530
|
+
if (!isPlainObject(tool) || typeof tool.name !== "string") return false;
|
|
531
|
+
if (tool.type === "namespace") return tool.name === IMAGE_GEN_NAMESPACE;
|
|
532
|
+
return isImageGenClientName(tool.name);
|
|
533
|
+
}
|
|
534
|
+
|
|
503
535
|
/**
|
|
504
|
-
*
|
|
505
|
-
*
|
|
506
|
-
*
|
|
507
|
-
*
|
|
508
|
-
* `
|
|
509
|
-
*
|
|
510
|
-
*
|
|
536
|
+
* Lower one complete Codex image-gen namespace to public Responses function tools.
|
|
537
|
+
*
|
|
538
|
+
* The public API reserves the `image_gen` namespace and restricts function names to a flat safe
|
|
539
|
+
* alphabet. `image_gen__<tool>` is therefore an upstream-only alias; client-facing responses are
|
|
540
|
+
* restored to explicit `{ namespace: "image_gen", name: "<tool>" }` calls by the server. Only a
|
|
541
|
+
* non-empty namespace containing named function tools is safe to lower. Malformed, empty, and
|
|
542
|
+
* future namespace shapes stay untouched instead of silently losing client capabilities.
|
|
511
543
|
*/
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
544
|
+
function flattenImageGenNamespace(tool: unknown): Record<string, unknown>[] | undefined {
|
|
545
|
+
if (
|
|
546
|
+
!isPlainObject(tool)
|
|
547
|
+
|| tool.type !== "namespace"
|
|
548
|
+
|| tool.name !== IMAGE_GEN_NAMESPACE
|
|
549
|
+
|| !Array.isArray(tool.tools)
|
|
550
|
+
|| tool.tools.length === 0
|
|
551
|
+
) return undefined;
|
|
552
|
+
|
|
553
|
+
for (const innerTool of tool.tools) {
|
|
554
|
+
if (
|
|
555
|
+
!isPlainObject(innerTool)
|
|
556
|
+
|| innerTool.type !== "function"
|
|
557
|
+
|| typeof innerTool.name !== "string"
|
|
558
|
+
|| innerTool.name.length === 0
|
|
559
|
+
) return undefined;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
return tool.tools.map(innerTool => {
|
|
563
|
+
const functionTool = innerTool as Record<string, unknown> & { name: string };
|
|
564
|
+
return {
|
|
565
|
+
...functionTool,
|
|
566
|
+
name: imageGenWireName(functionTool.name),
|
|
567
|
+
};
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/** Convert a legacy dotted function declaration while preserving all other function metadata. */
|
|
572
|
+
function normalizeFlatImageGenFunction(tool: unknown): unknown {
|
|
573
|
+
if (
|
|
574
|
+
!isPlainObject(tool)
|
|
575
|
+
|| tool.type !== "function"
|
|
576
|
+
|| typeof tool.name !== "string"
|
|
577
|
+
|| !tool.name.startsWith(IMAGE_GEN_DOTTED_PREFIX)
|
|
578
|
+
) return tool;
|
|
579
|
+
return { ...tool, name: imageGenWireName(tool.name) };
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/** Return the image-gen function name used for stable cross-container deduplication. */
|
|
583
|
+
function imageGenFunctionName(tool: unknown): string | undefined {
|
|
584
|
+
if (!isPlainObject(tool) || tool.type !== "function" || typeof tool.name !== "string") {
|
|
585
|
+
return undefined;
|
|
586
|
+
}
|
|
587
|
+
return isImageGenClientName(tool.name) ? tool.name : undefined;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/** True only when a declaration can yield a callable upstream-safe image-gen function alias. */
|
|
591
|
+
function declaresUsableImageGenAlias(tool: unknown): boolean {
|
|
592
|
+
if (flattenImageGenNamespace(tool)) return true;
|
|
593
|
+
if (!isPlainObject(tool) || tool.type !== "function" || typeof tool.name !== "string") {
|
|
594
|
+
return false;
|
|
595
|
+
}
|
|
596
|
+
if (tool.name.startsWith(IMAGE_GEN_DOTTED_PREFIX)) {
|
|
597
|
+
return tool.name.length > IMAGE_GEN_DOTTED_PREFIX.length;
|
|
598
|
+
}
|
|
599
|
+
return tool.name.startsWith(IMAGE_GEN_WIRE_PREFIX)
|
|
600
|
+
&& tool.name.length > IMAGE_GEN_WIRE_PREFIX.length;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/** Collect client tool-choice names and the exact upstream aliases declared for them. */
|
|
604
|
+
function imageGenToolChoiceAliases(toolGroups: unknown[][]): Map<string, string> {
|
|
605
|
+
const aliases = new Map<string, string>();
|
|
606
|
+
|
|
607
|
+
for (const group of toolGroups) {
|
|
608
|
+
for (const tool of group) {
|
|
609
|
+
const flattened = flattenImageGenNamespace(tool);
|
|
610
|
+
if (flattened) {
|
|
611
|
+
for (const candidate of flattened) {
|
|
612
|
+
const wireName = candidate.name as string;
|
|
613
|
+
aliases.set(`${IMAGE_GEN_DOTTED_PREFIX}${imageGenLocalName(wireName)}`, wireName);
|
|
614
|
+
aliases.set(wireName, wireName);
|
|
615
|
+
}
|
|
616
|
+
continue;
|
|
617
|
+
}
|
|
618
|
+
if (!isPlainObject(tool) || tool.type !== "function" || typeof tool.name !== "string") {
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
621
|
+
if (
|
|
622
|
+
tool.name.startsWith(IMAGE_GEN_DOTTED_PREFIX)
|
|
623
|
+
&& tool.name.length > IMAGE_GEN_DOTTED_PREFIX.length
|
|
624
|
+
) {
|
|
625
|
+
aliases.set(tool.name, imageGenWireName(tool.name));
|
|
626
|
+
} else if (
|
|
627
|
+
tool.name.startsWith(IMAGE_GEN_WIRE_PREFIX)
|
|
628
|
+
&& tool.name.length > IMAGE_GEN_WIRE_PREFIX.length
|
|
629
|
+
) {
|
|
630
|
+
aliases.set(tool.name, tool.name);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
return aliases;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/** Rewrite function selectors only when their corresponding declaration receives a wire alias. */
|
|
639
|
+
function normalizeImageGenToolChoice(
|
|
640
|
+
toolChoice: unknown,
|
|
641
|
+
aliases: ReadonlyMap<string, string>,
|
|
642
|
+
): unknown {
|
|
643
|
+
if (!isPlainObject(toolChoice)) return toolChoice;
|
|
644
|
+
|
|
645
|
+
if (toolChoice.type === "function" && typeof toolChoice.name === "string") {
|
|
646
|
+
const alias = aliases.get(toolChoice.name);
|
|
647
|
+
return alias && alias !== toolChoice.name ? { ...toolChoice, name: alias } : toolChoice;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
if (toolChoice.type !== "allowed_tools" || !Array.isArray(toolChoice.tools)) return toolChoice;
|
|
651
|
+
let changed = false;
|
|
652
|
+
const tools = toolChoice.tools.map(tool => {
|
|
653
|
+
if (!isPlainObject(tool) || tool.type !== "function" || typeof tool.name !== "string") {
|
|
654
|
+
return tool;
|
|
655
|
+
}
|
|
656
|
+
const alias = aliases.get(tool.name);
|
|
657
|
+
if (!alias || alias === tool.name) return tool;
|
|
658
|
+
changed = true;
|
|
659
|
+
return { ...tool, name: alias };
|
|
660
|
+
});
|
|
661
|
+
return changed ? { ...toolChoice, tools } : toolChoice;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/** Identify replayed image-gen calls that require upstream wire encoding. */
|
|
665
|
+
function declaresImageGenFunctionCall(item: unknown): boolean {
|
|
666
|
+
if (!isPlainObject(item) || item.type !== "function_call" || typeof item.name !== "string") {
|
|
667
|
+
return false;
|
|
668
|
+
}
|
|
669
|
+
return item.namespace === IMAGE_GEN_NAMESPACE || isImageGenClientName(item.name);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/** Encode native or legacy replay calls to the same flat name used by tool declarations. */
|
|
673
|
+
function normalizeImageGenFunctionCall(item: unknown): unknown {
|
|
674
|
+
if (!declaresImageGenFunctionCall(item) || !isPlainObject(item) || typeof item.name !== "string") {
|
|
675
|
+
return item;
|
|
676
|
+
}
|
|
677
|
+
if (item.namespace === IMAGE_GEN_NAMESPACE) {
|
|
678
|
+
const { namespace: _namespace, ...rest } = item;
|
|
679
|
+
return { ...rest, name: imageGenWireName(item.name) };
|
|
680
|
+
}
|
|
681
|
+
if (item.name.startsWith(IMAGE_GEN_DOTTED_PREFIX)) {
|
|
682
|
+
return { ...item, name: imageGenWireName(item.name) };
|
|
683
|
+
}
|
|
684
|
+
return item;
|
|
685
|
+
}
|
|
515
686
|
|
|
516
687
|
/**
|
|
517
|
-
*
|
|
518
|
-
* HOSTED_TOOL_NAME_CONFLICTS). Only applies on the API-key platform path: the ChatGPT backend
|
|
519
|
-
* ("forward" mode) accepts the pair, and stripping there would disable native imagegen. No-op
|
|
520
|
-
* (returns the original reference) when nothing matches.
|
|
688
|
+
* Normalize Codex's private image-gen tool declaration for API-key Responses providers.
|
|
521
689
|
*
|
|
522
|
-
*
|
|
523
|
-
* the
|
|
524
|
-
*
|
|
525
|
-
*
|
|
690
|
+
* A complete `image_gen` namespace is flattened to safe `image_gen__<tool>` aliases even when it is
|
|
691
|
+
* the only image tool in the request. Replayed client calls are encoded to the same alias, including
|
|
692
|
+
* legacy dotted calls from older compatibility attempts. When a usable alias replaces a client
|
|
693
|
+
* image-gen declaration, the duplicate hosted `image_generation` entry is removed. Duplicate aliases
|
|
694
|
+
* are resolved in stable container order: top-level tools first, then Responses Lite
|
|
695
|
+
* `additional_tools` entries.
|
|
696
|
+
*
|
|
697
|
+
* This function is called only on the API-key path. ChatGPT forward mode understands the private
|
|
698
|
+
* namespace and must keep it. Copy-on-write preserves the original request reference when no
|
|
699
|
+
* namespace is flattened, hosted tool removed, or duplicate function discarded.
|
|
526
700
|
*/
|
|
527
|
-
function
|
|
701
|
+
function normalizeImageGenClientTools(body: unknown): unknown {
|
|
528
702
|
if (!isPlainObject(body)) return body;
|
|
529
703
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
if (
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
704
|
+
const toolGroups = collectResponsesToolGroups(body);
|
|
705
|
+
const hasImageGenClientTool = toolGroups.some(group => group.some(declaresImageGenClientTool))
|
|
706
|
+
|| (Array.isArray(body.input) && body.input.some(declaresImageGenFunctionCall));
|
|
707
|
+
if (!hasImageGenClientTool) return body;
|
|
708
|
+
const hasUsableImageGenAlias = toolGroups.some(group => group.some(declaresUsableImageGenAlias));
|
|
709
|
+
const toolChoiceAliases = imageGenToolChoiceAliases(toolGroups);
|
|
710
|
+
|
|
711
|
+
const seenFunctionNames = new Set<string>();
|
|
712
|
+
const normalizeGroup = (tools: unknown[]): unknown[] => {
|
|
713
|
+
const normalized: unknown[] = [];
|
|
714
|
+
let groupChanged = false;
|
|
715
|
+
|
|
716
|
+
for (const tool of tools) {
|
|
717
|
+
if (
|
|
718
|
+
hasUsableImageGenAlias
|
|
719
|
+
&& isPlainObject(tool)
|
|
720
|
+
&& tool.type === HOSTED_IMAGE_GENERATION_TOOL
|
|
721
|
+
) {
|
|
722
|
+
groupChanged = true;
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
const flattened = flattenImageGenNamespace(tool);
|
|
727
|
+
const candidates = flattened ?? [tool];
|
|
728
|
+
if (flattened) groupChanged = true;
|
|
729
|
+
|
|
730
|
+
for (const candidate of candidates) {
|
|
731
|
+
const normalizedCandidate = normalizeFlatImageGenFunction(candidate);
|
|
732
|
+
if (normalizedCandidate !== candidate) groupChanged = true;
|
|
733
|
+
const functionName = imageGenFunctionName(normalizedCandidate);
|
|
734
|
+
if (functionName && seenFunctionNames.has(functionName)) {
|
|
735
|
+
groupChanged = true;
|
|
736
|
+
continue;
|
|
737
|
+
}
|
|
738
|
+
if (functionName) seenFunctionNames.add(functionName);
|
|
739
|
+
normalized.push(normalizedCandidate);
|
|
538
740
|
}
|
|
539
741
|
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
// Detect conflicts across containers because the client namespace and the hosted tool may be
|
|
544
|
-
// declared in different groups while still sharing one platform-validated namespace.
|
|
545
|
-
const conflicting = HOSTED_TOOL_NAME_CONFLICTS.filter(c => toolGroups.some(group =>
|
|
546
|
-
group.some(t => {
|
|
547
|
-
if (!isPlainObject(t) || typeof t.name !== "string") return false;
|
|
548
|
-
if (t.type === "namespace") return t.name === c.namePrefix;
|
|
549
|
-
return t.name === c.namePrefix || t.name.startsWith(`${c.namePrefix}.`);
|
|
550
|
-
}),
|
|
551
|
-
));
|
|
552
|
-
if (conflicting.length === 0) return body;
|
|
553
|
-
|
|
554
|
-
// Allocate a replacement only when a hosted tool is removed. Preserve malformed entries,
|
|
555
|
-
// unrelated tools, and future tool types so this compatibility rule remains narrowly scoped.
|
|
556
|
-
const stripGroup = (tools: unknown[]): unknown[] => {
|
|
557
|
-
const filtered = tools.filter(t => {
|
|
558
|
-
const type = isPlainObject(t) && typeof t.type === "string" ? t.type : undefined;
|
|
559
|
-
if (!type) return true;
|
|
560
|
-
return !conflicting.some(c => c.hostedType === type);
|
|
561
|
-
});
|
|
562
|
-
return filtered.length === tools.length ? tools : filtered;
|
|
742
|
+
|
|
743
|
+
return groupChanged ? normalized : tools;
|
|
563
744
|
};
|
|
564
745
|
|
|
565
746
|
let changed = false;
|
|
566
747
|
let tools = body.tools;
|
|
567
748
|
if (Array.isArray(body.tools)) {
|
|
568
|
-
tools =
|
|
749
|
+
tools = normalizeGroup(body.tools);
|
|
569
750
|
changed ||= tools !== body.tools;
|
|
570
751
|
}
|
|
571
752
|
|
|
@@ -573,13 +754,15 @@ function stripConflictingHostedTools(body: unknown): unknown {
|
|
|
573
754
|
if (Array.isArray(body.input)) {
|
|
574
755
|
let nestedChanged = false;
|
|
575
756
|
const mappedInput = body.input.map(item => {
|
|
576
|
-
if (
|
|
577
|
-
|
|
757
|
+
if (isPlainObject(item) && item.type === "additional_tools" && Array.isArray(item.tools)) {
|
|
758
|
+
const nestedTools = normalizeGroup(item.tools);
|
|
759
|
+
if (nestedTools === item.tools) return item;
|
|
760
|
+
nestedChanged = true;
|
|
761
|
+
return { ...item, tools: nestedTools };
|
|
578
762
|
}
|
|
579
|
-
const
|
|
580
|
-
if (
|
|
581
|
-
|
|
582
|
-
return { ...item, tools: nestedTools };
|
|
763
|
+
const normalizedCall = normalizeImageGenFunctionCall(item);
|
|
764
|
+
if (normalizedCall !== item) nestedChanged = true;
|
|
765
|
+
return normalizedCall;
|
|
583
766
|
});
|
|
584
767
|
if (nestedChanged) {
|
|
585
768
|
input = mappedInput;
|
|
@@ -587,11 +770,15 @@ function stripConflictingHostedTools(body: unknown): unknown {
|
|
|
587
770
|
}
|
|
588
771
|
}
|
|
589
772
|
|
|
773
|
+
const toolChoice = normalizeImageGenToolChoice(body.tool_choice, toolChoiceAliases);
|
|
774
|
+
changed ||= toolChoice !== body.tool_choice;
|
|
775
|
+
|
|
590
776
|
if (!changed) return body;
|
|
591
777
|
return {
|
|
592
778
|
...body,
|
|
593
779
|
...(Array.isArray(body.tools) ? { tools } : {}),
|
|
594
780
|
...(Array.isArray(body.input) ? { input } : {}),
|
|
781
|
+
...(Object.prototype.hasOwnProperty.call(body, "tool_choice") ? { tool_choice: toolChoice } : {}),
|
|
595
782
|
};
|
|
596
783
|
}
|
|
597
784
|
|
|
@@ -740,7 +927,7 @@ export function createResponsesPassthroughAdapter(provider: OcxProviderConfig):
|
|
|
740
927
|
outBody = repairOrphanedInputItems(outBody, unexpandedMiss);
|
|
741
928
|
outBody = stripUnsupportedForwardParams(outBody);
|
|
742
929
|
}
|
|
743
|
-
else outBody =
|
|
930
|
+
else outBody = normalizeImageGenClientTools(outBody);
|
|
744
931
|
if (forward || parsed._previousResponseInputExpanded === true) {
|
|
745
932
|
outBody = repairOversizedReplayCallIds(outBody);
|
|
746
933
|
}
|
|
@@ -27,6 +27,7 @@ export interface ClaudeAgentDef {
|
|
|
27
27
|
name: string;
|
|
28
28
|
model: string;
|
|
29
29
|
description: string;
|
|
30
|
+
effort?: NonNullable<OcxConfig["claudeCode"]>["subagentEffort"];
|
|
30
31
|
blockedSkills: readonly string[];
|
|
31
32
|
}
|
|
32
33
|
|
|
@@ -99,6 +100,7 @@ export function buildClaudeAgentDefs(config: OcxConfig, windows: Record<string,
|
|
|
99
100
|
name: `${OWNED_PREFIX}${unique}`,
|
|
100
101
|
model,
|
|
101
102
|
description,
|
|
103
|
+
effort: config.claudeCode?.subagentEffort,
|
|
102
104
|
blockedSkills: blockedSkillsFor(model),
|
|
103
105
|
});
|
|
104
106
|
};
|
|
@@ -124,6 +126,7 @@ export function buildClaudeAgentDefs(config: OcxConfig, windows: Record<string,
|
|
|
124
126
|
name: `${OWNED_PREFIX}self`,
|
|
125
127
|
model: marked,
|
|
126
128
|
description: `Self-clone: delegate to your default main model (${marked}), synced from the /model picker at launch. ${NO_MODEL_ARG}`,
|
|
129
|
+
effort: config.claudeCode?.subagentEffort,
|
|
127
130
|
blockedSkills: blockedSkillsFor(marked),
|
|
128
131
|
});
|
|
129
132
|
}
|
|
@@ -149,6 +152,7 @@ function renderAgentDef(def: ClaudeAgentDef): string {
|
|
|
149
152
|
`name: ${JSON.stringify(def.name)}`,
|
|
150
153
|
`description: ${JSON.stringify(def.description)}`,
|
|
151
154
|
`model: ${JSON.stringify(def.model)}`,
|
|
155
|
+
...(def.effort ? [`effort: ${JSON.stringify(def.effort)}`] : []),
|
|
152
156
|
"---",
|
|
153
157
|
"",
|
|
154
158
|
`<!-- ${GENERATED_MARKER} -->`,
|
|
@@ -157,6 +161,7 @@ function renderAgentDef(def: ClaudeAgentDef): string {
|
|
|
157
161
|
// BODY rides the subagent's system prompt verbatim. The proxy detects this
|
|
158
162
|
// directive and overrides the request model before routing/passthrough.
|
|
159
163
|
`<!-- ocx-route: ${def.model} -->`,
|
|
164
|
+
...(def.effort ? [`<!-- ocx-effort: ${def.effort} -->`] : []),
|
|
160
165
|
"",
|
|
161
166
|
`You are a delegated worker running on \`${def.model}\` through the local opencodex proxy.`,
|
|
162
167
|
`IDENTITY: your ACTUAL underlying model is \`${def.model}\` — the opencodex proxy routes this`,
|
package/src/claude/inbound.ts
CHANGED
|
@@ -147,23 +147,40 @@ export function effectiveBlockedSkillNames(cc?: Pick<OcxClaudeCodeConfig, "block
|
|
|
147
147
|
* FIRST directive wins; the scan is bounded to the system field.
|
|
148
148
|
*/
|
|
149
149
|
const OCX_ROUTE_RE = /<!--\s*ocx-route:\s*([^\s]+)\s*-->/;
|
|
150
|
+
const OCX_EFFORT_RE = /<!--\s*ocx-effort:\s*(low|medium|high|xhigh|max)\s*-->/;
|
|
150
151
|
|
|
151
|
-
|
|
152
|
+
function systemText(body: unknown): string | null {
|
|
152
153
|
if (!isRec(body)) return null;
|
|
153
154
|
const system = body.system;
|
|
154
|
-
|
|
155
|
-
if (
|
|
156
|
-
|
|
157
|
-
text
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
155
|
+
if (typeof system === "string") return system || null;
|
|
156
|
+
if (!Array.isArray(system)) return null;
|
|
157
|
+
const text = system
|
|
158
|
+
.filter((b): b is Rec => isRec(b) && b.type === "text" && typeof b.text === "string")
|
|
159
|
+
.map(b => b.text as string)
|
|
160
|
+
.join("\n");
|
|
161
|
+
return text || null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function extractOcxRouteDirective(body: unknown): string | null {
|
|
165
|
+
const text = systemText(body);
|
|
162
166
|
if (!text) return null;
|
|
163
167
|
const match = OCX_ROUTE_RE.exec(text);
|
|
164
168
|
return match ? match[1]! : null;
|
|
165
169
|
}
|
|
166
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Claude Code 2.1.220 collapses custom-agent frontmatter `effort: max` and
|
|
173
|
+
* `effort: xhigh` into the legacy `thinking.budget_tokens` shape. Preserve the
|
|
174
|
+
* exact generated-agent setting through the same trusted system-body channel as
|
|
175
|
+
* ocx-route so the inbound translator can restore `output_config.effort`.
|
|
176
|
+
*/
|
|
177
|
+
export function extractOcxEffortDirective(body: unknown): NonNullable<OcxClaudeCodeConfig["subagentEffort"]> | null {
|
|
178
|
+
const text = systemText(body);
|
|
179
|
+
if (!text) return null;
|
|
180
|
+
const match = OCX_EFFORT_RE.exec(text);
|
|
181
|
+
return match ? match[1] as NonNullable<OcxClaudeCodeConfig["subagentEffort"]> : null;
|
|
182
|
+
}
|
|
183
|
+
|
|
167
184
|
/** Injected-skill payloads below this size are never stubbed (not worth it). */
|
|
168
185
|
const SKILL_ELISION_MIN_CHARS = 10_000;
|
|
169
186
|
const SKILL_TEXT_MARKER = "Base directory for this skill: ";
|
package/src/cli/account-auth.ts
CHANGED
|
@@ -137,11 +137,11 @@ async function login(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
|
137
137
|
for (let attempt = 0; attempt < 100; attempt++) {
|
|
138
138
|
await Bun.sleep(2_000);
|
|
139
139
|
const state = await runtimeRequest<Record<string, unknown>>(`/api/oauth/status?provider=${encodeURIComponent(provider)}`, {}, deps);
|
|
140
|
+
if (state.error) throw new CliUsageError(String(state.error));
|
|
140
141
|
if (state.loggedIn === true) {
|
|
141
142
|
printData(state, wantsJson, [`Logged in to ${provider}.`]);
|
|
142
143
|
return;
|
|
143
144
|
}
|
|
144
|
-
if (state.error) throw new CliUsageError(String(state.error));
|
|
145
145
|
}
|
|
146
146
|
throw new CliUsageError("login timed out");
|
|
147
147
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects whether this process was started by a coding agent rather than typed
|
|
3
|
+
* by a person.
|
|
4
|
+
*
|
|
5
|
+
* Agent harnesses run `ocx` on the user's behalf and answer prompts from their
|
|
6
|
+
* own logic, which means a consent question would be decided by the agent
|
|
7
|
+
* instead of the account owner. Prompts that act on the user's identity check
|
|
8
|
+
* this and defer instead: they stay silent so the question reaches the human on
|
|
9
|
+
* a later hand-typed run.
|
|
10
|
+
*
|
|
11
|
+
* Detection is env-var based and deliberately conservative — a false positive
|
|
12
|
+
* only postpones a prompt, while a false negative would let an agent answer for
|
|
13
|
+
* the user.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** Env vars set by agent harnesses and CI runners inside the shell they spawn. */
|
|
17
|
+
const AGENT_ENV_VARS = [
|
|
18
|
+
"CLAUDECODE",
|
|
19
|
+
"CLAUDE_CODE_ENTRYPOINT",
|
|
20
|
+
"CODEX_THREAD_ID",
|
|
21
|
+
"CODEX_SHELL",
|
|
22
|
+
"CODEX_CI",
|
|
23
|
+
"CURSOR_TRACE_ID",
|
|
24
|
+
"CURSOR_SESSION_TOKEN",
|
|
25
|
+
"AIDER_CHAT",
|
|
26
|
+
"REPL_ID",
|
|
27
|
+
"CI",
|
|
28
|
+
"GITHUB_ACTIONS",
|
|
29
|
+
] as const;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* True when an agent or automated runner is driving this process. Reads the
|
|
33
|
+
* environment on every call so tests and long-lived processes see current state.
|
|
34
|
+
*/
|
|
35
|
+
export function isAgentDriven(env: NodeJS.ProcessEnv = process.env): boolean {
|
|
36
|
+
return AGENT_ENV_VARS.some(name => (env[name] ?? "").trim() !== "");
|
|
37
|
+
}
|
package/src/cli/claude.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { refreshGatewayModelCacheFromProxy } from "../claude/gateway-cache";
|
|
|
14
14
|
import { commandInvocation } from "../lib/win-exec";
|
|
15
15
|
import { findLiveProxy } from "../server/proxy-liveness";
|
|
16
16
|
import type { OcxConfig } from "../types";
|
|
17
|
+
import { configuredAdminToken } from "../lib/admin-secrets";
|
|
17
18
|
import { PROXY_MARKER, ownAdmissionTokens, defaultAuthDetectDeps, detectClaudeAuth, type AuthDetectDeps } from "../claude/auth-detect";
|
|
18
19
|
import { resolveClaudeAuthMode } from "../claude/auth-mode";
|
|
19
20
|
|
|
@@ -149,14 +150,13 @@ export function buildClaudeEnv(
|
|
|
149
150
|
|
|
150
151
|
/**
|
|
151
152
|
* Context-window map from the RUNNING proxy's management API (warm TTL cache; the
|
|
152
|
-
* daemon registers every selector form — audit R3#1). 3s bound + auth header
|
|
153
|
-
* (OPENCODEX_API_AUTH_TOKEN first, config key fallback — audit R4#1). Failure → {}
|
|
153
|
+
* daemon registers every selector form — audit R3#1). 3s bound + management auth header.
|
|
154
154
|
* (no [1m] marking, conservative).
|
|
155
155
|
*/
|
|
156
156
|
export async function fetchClaudeContextWindows(config: OcxConfig, port: number, timeoutMs = 3_000): Promise<Record<string, number>> {
|
|
157
157
|
try {
|
|
158
158
|
const headers = new Headers();
|
|
159
|
-
const token =
|
|
159
|
+
const token = configuredAdminToken();
|
|
160
160
|
if (token) headers.set("x-opencodex-api-key", token);
|
|
161
161
|
const res = await fetch(`http://127.0.0.1:${port}/api/claude-code`, {
|
|
162
162
|
headers,
|