@bitkyc08/opencodex 2.75.0 → 2.76.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/README.md +6 -1
- package/bin/ocx.mjs +12 -1
- package/gui/dist/assets/App-CddorHdA.js +51 -0
- package/gui/dist/assets/App-DMudtISW.css +1 -0
- package/gui/dist/assets/{Tray-CGAD2AGk.js → Tray-DjNwwrWS.js} +1 -1
- package/gui/dist/assets/index-Btze1I3T.css +1 -0
- package/gui/dist/assets/index-CvnY7E6i.js +86 -0
- package/gui/dist/assets/quota-summary-B_AWpHfP.js +1 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/opengateway.svg +12 -0
- package/package.json +1 -1
- package/src/adapters/anthropic/account-metadata.ts +17 -0
- package/src/adapters/anthropic/client-identity.ts +69 -0
- package/src/adapters/anthropic/passthrough.ts +23 -4
- package/src/adapters/anthropic.ts +18 -1
- package/src/adapters/command-code-project-context.ts +7 -11
- package/src/adapters/devin/cloud-direct/tool-schema.ts +12 -48
- package/src/adapters/kiro-retry.ts +23 -8
- package/src/adapters/openai-chat/messages.ts +1 -1
- package/src/adapters/registry.ts +8 -1
- package/src/adapters/zed.ts +377 -0
- package/src/bridge/sse.ts +4 -2
- package/src/chatgpt/app-server-shim/app-server-rewrite.ts +45 -0
- package/src/chatgpt/app-server-shim/bundle-trust.ts +77 -0
- package/src/chatgpt/app-server-shim/filter.ts +157 -0
- package/src/chatgpt/app-server-shim/gate-rewrite.ts +99 -0
- package/src/chatgpt/app-server-shim/launcher.ts +91 -0
- package/src/claude/desktop-3p-startup.ts +62 -0
- package/src/claude/desktop-3p.ts +5 -0
- package/src/claude/first-party-settings.ts +14 -0
- package/src/claude/intercept/cli-catalog.ts +150 -0
- package/src/claude/intercept/cli-picker.ts +82 -0
- package/src/claude/intercept/listener.ts +21 -6
- package/src/claude/intercept/picker-bootstrap.ts +23 -5
- package/src/claude/intercept/picker-models.ts +57 -13
- package/src/claude/intercept/runtime.ts +30 -2
- package/src/claude/intercept/settings.ts +3 -0
- package/src/cli/agent.ts +172 -0
- package/src/cli/alias.ts +2 -1
- package/src/cli/capabilities-command.ts +3 -2
- package/src/cli/capabilities.ts +87 -0
- package/src/cli/chatgpt-command.ts +140 -0
- package/src/cli/combo.ts +144 -19
- package/src/cli/config-command.ts +3 -1
- package/src/cli/dispatch.ts +10 -1
- package/src/cli/help.ts +1 -0
- package/src/cli/index.ts +5 -24
- package/src/cli/init.ts +16 -2
- package/src/cli/integrations.ts +13 -0
- package/src/cli/internal-command.ts +12 -5
- package/src/cli/observe.ts +6 -1
- package/src/cli/registry.ts +6 -0
- package/src/cli/route-policy.ts +3 -1
- package/src/cli/runtime-api.ts +5 -2
- package/src/cli/status-oauth.ts +5 -0
- package/src/cli/status.ts +8 -1
- package/src/cli/stop-approval.ts +55 -16
- package/src/cli/stop-report.ts +5 -0
- package/src/clients/lazycodex.ts +48 -0
- package/src/clients/omo-role-models.ts +111 -0
- package/src/codex/account-credit-use.ts +99 -0
- package/src/codex/account-lifecycle.ts +2 -0
- package/src/codex/agent-role-models.ts +171 -0
- package/src/codex/auth-api/account-list.ts +10 -3
- package/src/codex/auth-api/routes.ts +34 -0
- package/src/codex/auth-context.ts +52 -11
- package/src/codex/catalog/discovered-natives.ts +11 -1
- package/src/codex/catalog/gather-capture.ts +6 -1
- package/src/codex/catalog/model-hints.ts +9 -1
- package/src/codex/catalog/provider-models.ts +60 -0
- package/src/codex/catalog/routed-gather.ts +30 -5
- package/src/codex/convergence-types.ts +2 -1
- package/src/codex/main-account-external-usage.ts +62 -0
- package/src/codex/main-account-hard-lock.ts +24 -13
- package/src/codex/native-profile-processes.ts +53 -16
- package/src/codex/prompt-layers/toml-edit.ts +1 -1
- package/src/codex/quota-types.ts +3 -7
- package/src/codex/quota.ts +19 -4
- package/src/codex/role-auto-assign.ts +159 -0
- package/src/codex/role-sizing-limits.ts +2 -0
- package/src/codex/role-sizing.ts +216 -0
- package/src/codex/routing/selection.ts +36 -0
- package/src/codex/routing/thread-affinity.ts +2 -0
- package/src/codex/routing.ts +4 -4
- package/src/codex/subagent-model-fallback.ts +82 -26
- package/src/codex/subagent-selectable-models.ts +32 -0
- package/src/combos/index.ts +18 -0
- package/src/combos/jev-decision-contract.ts +20 -0
- package/src/combos/jev-dispatch.ts +18 -0
- package/src/combos/jev-model-backend.ts +127 -0
- package/src/combos/jev.ts +249 -33
- package/src/combos/types.ts +125 -2
- package/src/config/diagnostics.ts +18 -0
- package/src/config/macos-system-proxy.ts +72 -15
- package/src/config/owner-registry.ts +13 -4
- package/src/config/process-state.ts +17 -8
- package/src/config/proxy-env.ts +35 -5
- package/src/config/schema/config-schema.ts +30 -1
- package/src/config/schema/leaf-validators.ts +4 -0
- package/src/config/serving-runtimes.ts +95 -1
- package/src/generated/compatibility-version.json +336 -156
- package/src/lib/errors.ts +7 -7
- package/src/lib/jsonc.ts +13 -0
- package/src/lib/local-chat-completion.ts +144 -0
- package/src/lib/provider-outbound.ts +87 -1
- package/src/lib/provider-runtime-fetch.ts +23 -0
- package/src/lib/provider-tls-profile.ts +221 -0
- package/src/lib/request-execution-budget.ts +16 -6
- package/src/lib/spend-reservation-ledger.ts +54 -20
- package/src/lib/sse-decoder.ts +20 -17
- package/src/lib/synced-state-location.ts +96 -0
- package/src/lib/winsw.ts +14 -5
- package/src/oauth/anthropic-account-refusal.ts +53 -4
- package/src/oauth/anthropic-model-quota.ts +83 -0
- package/src/oauth/anthropic-rate-limit-policy.ts +37 -0
- package/src/oauth/anthropic-routing.ts +112 -58
- package/src/oauth/health.ts +39 -3
- package/src/oauth/index.ts +14 -0
- package/src/oauth/kiro-account-load.ts +10 -0
- package/src/oauth/zed.ts +134 -0
- package/src/providers/model-discovery.ts +19 -8
- package/src/providers/model-rename-fields.ts +1 -0
- package/src/providers/provider-id-rewrite.ts +9 -2
- package/src/providers/quota/account-cache.ts +29 -7
- package/src/providers/quota/anthropic-cooldown-recovery.ts +18 -0
- package/src/providers/quota/anthropic-family-headers.ts +25 -0
- package/src/providers/quota/vendor-probes-oauth.ts +5 -1
- package/src/providers/quota-types.ts +4 -0
- package/src/providers/registry/entries-core.ts +18 -0
- package/src/providers/registry/entries-extended.ts +55 -0
- package/src/providers/registry/types.ts +3 -0
- package/src/providers/xai-fast-model.ts +12 -4
- package/src/providers/zed.ts +598 -0
- package/src/responses/state/snapshot-select.ts +22 -9
- package/src/responses/state.ts +2 -3
- package/src/router.ts +9 -0
- package/src/server/anthropic-rate-limit-headers.ts +7 -0
- package/src/server/auth-cors.ts +7 -0
- package/src/server/chat-completions.ts +9 -4
- package/src/server/claude-messages.ts +59 -21
- package/src/server/index/claude-intercept-lifecycle.ts +71 -25
- package/src/server/index/optional-listeners.ts +4 -0
- package/src/server/index/spend-ledger-lifecycle.ts +4 -0
- package/src/server/management/agent-settings-routes.ts +74 -37
- package/src/server/management/claude-desktop-picker-routes.ts +7 -0
- package/src/server/management/claude-intercept-routes.ts +35 -0
- package/src/server/management/codex-agent-role-routes.ts +122 -0
- package/src/server/management/codex-role-auto-assign.ts +208 -0
- package/src/server/management/combo-routes.ts +62 -5
- package/src/server/management/config-routes.ts +35 -3
- package/src/server/management/context.ts +4 -1
- package/src/server/management/decision-discovery.ts +74 -0
- package/src/server/management/decision-model-validation.ts +80 -0
- package/src/server/management/decision-routes.ts +160 -0
- package/src/server/management/native-integration-routes.ts +16 -0
- package/src/server/management/provider-routes.ts +30 -34
- package/src/server/management/route-registry.ts +12 -0
- package/src/server/management/sibling-guard.ts +1 -0
- package/src/server/management/zed-provider-probe.ts +38 -0
- package/src/server/management-api.ts +12 -1
- package/src/server/messages-native-oauth.ts +7 -2
- package/src/server/messages-native.ts +7 -3
- package/src/server/relay.ts +3 -1
- package/src/server/request-log.ts +2 -0
- package/src/server/responses/adapter-continuation.ts +3 -3
- package/src/server/responses/adapter-dispatch.ts +18 -3
- package/src/server/responses/compact.ts +14 -1
- package/src/server/responses/core-auth.ts +2 -0
- package/src/server/responses/core-combo.ts +67 -11
- package/src/server/responses/core-normalize.ts +21 -1
- package/src/server/responses/core-options.ts +11 -1
- package/src/server/responses/core.ts +5 -3
- package/src/server/responses/fetch-helpers.ts +6 -3
- package/src/server/responses/jev-model-invoke.ts +190 -0
- package/src/server/responses/passthrough-delivery.ts +16 -0
- package/src/server/responses/policy-fallback.ts +55 -3
- package/src/server/responses/policy-request-scope.ts +80 -0
- package/src/server/responses/request-prepare.ts +70 -18
- package/src/server/responses/request-transport.ts +54 -23
- package/src/server/responses/run-turn-execution.ts +1 -0
- package/src/server/responses/sidecar-execution.ts +9 -3
- package/src/server/responses-hosted-image-display.ts +225 -0
- package/src/service/managing-cli.ts +34 -4
- package/src/service/systemd-env.ts +75 -0
- package/src/service/windows-guarded-manager.ts +60 -19
- package/src/service/windows-scheduler.ts +22 -3
- package/src/service/windows-taskxml.ts +32 -10
- package/src/service-manager-probe.ts +72 -19
- package/src/types/config.ts +31 -1
- package/src/types/provider.ts +2 -0
- package/src/types/request.ts +2 -0
- package/src/update/index.ts +39 -6
- package/src/update/job.ts +63 -63
- package/src/update/npm-cache-preflight.d.mts +33 -1
- package/src/update/npm-cache-preflight.mjs +131 -18
- package/src/update/ownership-transaction.ts +19 -1
- package/src/update/restart-ownership.ts +80 -6
- package/src/update/transactional-install.d.mts +2 -0
- package/src/update/transactional-install.mjs +6 -1
- package/src/usage/jev-stats.ts +28 -1
- package/src/usage/log.ts +1 -1
- package/src/vision/anthropic-describe.ts +5 -3
- package/src/vision/index.ts +1 -0
- package/src/vision/plan.ts +2 -1
- package/src/vision/routed-describe.ts +29 -121
- package/src/web-search/alpha-search.ts +3 -3
- package/src/web-search/anthropic-executor.ts +6 -4
- package/src/web-search/index.ts +1 -1
- package/src/web-search/loop.ts +3 -3
- package/src/web-search/passthrough-bridge.ts +2 -1
- package/src/web-search/run-turn-loop.ts +1 -1
- package/src/web-search/sidecar-providers.ts +2 -1
- package/gui/dist/assets/App-ia4U0ufY.css +0 -1
- package/gui/dist/assets/App-xm0zGNOe.js +0 -51
- package/gui/dist/assets/index-DBT3Q31Y.css +0 -1
- package/gui/dist/assets/index-D_RGAT6Q.js +0 -86
- package/gui/dist/assets/quota-summary-CUFq48t-.js +0 -1
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
import { lstatSync, readdirSync, realpathSync } from "node:fs";
|
|
1
|
+
import { lstatSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
2
2
|
import { spawnSync } from "node:child_process";
|
|
3
|
-
import {
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { dirname, isAbsolute, resolve } from "node:path";
|
|
4
5
|
import { fileURLToPath } from "node:url";
|
|
5
6
|
import { npmInvocation } from "./npm-invocation.mjs";
|
|
6
7
|
|
|
7
8
|
const WORKER_ARG = "--ocx-npm-cache-preflight-worker";
|
|
9
|
+
const ROOT_ONLY_ARG = "--root-only";
|
|
8
10
|
const PROTOCOL_VERSION = 1;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
// Windows starts npm through cmd.exe and is routinely slowed by on-access scanning, so the
|
|
12
|
+
// same `npm config get cache` that takes well under a second on POSIX can take several there.
|
|
13
|
+
const WORKER_TIMEOUT_MS = process.platform === "win32" ? 25_000 : 10_000;
|
|
14
|
+
const NPM_CONFIG_TIMEOUT_MS = process.platform === "win32" ? 15_000 : 5_000;
|
|
11
15
|
const INSPECTION_TIMEOUT_MS = 7_500;
|
|
12
16
|
const MAX_ENTRIES = 100_000;
|
|
13
17
|
const MAX_DEPTH = 64;
|
|
@@ -17,6 +21,8 @@ const RESULT_REASONS = new Set([
|
|
|
17
21
|
"cache_entry_foreign_owner",
|
|
18
22
|
"cache_entry_inaccessible",
|
|
19
23
|
"cache_path_malformed",
|
|
24
|
+
"cache_root_dangling_link",
|
|
25
|
+
"cache_root_not_directory",
|
|
20
26
|
"inspection_incomplete",
|
|
21
27
|
"npm_config_failed",
|
|
22
28
|
"npm_unavailable",
|
|
@@ -29,12 +35,58 @@ function inaccessibleByMode(stat) {
|
|
|
29
35
|
return (ownerBits & 0o400) === 0;
|
|
30
36
|
}
|
|
31
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Check that npm can create or use its cache ROOT (#6288). This is the part of the inspection
|
|
40
|
+
* that is meaningful on every platform: npm's first action is `mkdir -p <cache>`, and it fails
|
|
41
|
+
* with ENOTDIR when the root — or the nearest existing ancestor npm would create it under — is
|
|
42
|
+
* a file, or a link/junction whose target is gone. A Windows directory junction relocated to
|
|
43
|
+
* another volume is ordinary configuration; only an unresolvable one is a problem.
|
|
44
|
+
*/
|
|
45
|
+
export function inspectNpmCacheRoot(cachePath, options = {}) {
|
|
46
|
+
const lstat = options.lstatFn ?? lstatSync;
|
|
47
|
+
const stat = options.statFn ?? statSync;
|
|
48
|
+
let current = resolve(cachePath);
|
|
49
|
+
let entry;
|
|
50
|
+
for (;;) {
|
|
51
|
+
try {
|
|
52
|
+
entry = lstat(current);
|
|
53
|
+
break;
|
|
54
|
+
} catch (error) {
|
|
55
|
+
// A file somewhere above the root: POSIX reports ENOTDIR here, which is exactly the
|
|
56
|
+
// ENOTDIR npm's own mkdir would hit. Windows reports ENOENT and the walk reaches the file.
|
|
57
|
+
if (error?.code === "ENOTDIR") return { ok: false, reason: "cache_root_not_directory" };
|
|
58
|
+
if (error?.code !== "ENOENT") return { ok: false, reason: "cache_entry_inaccessible" };
|
|
59
|
+
const parent = dirname(current);
|
|
60
|
+
// Nothing on the path exists at all (a missing drive root); npm cannot mkdir there either.
|
|
61
|
+
if (parent === current) return { ok: false, reason: "cache_root_not_directory" };
|
|
62
|
+
current = parent;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
let target = entry;
|
|
66
|
+
if (entry.isSymbolicLink()) {
|
|
67
|
+
try {
|
|
68
|
+
target = stat(current);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
return {
|
|
71
|
+
ok: false,
|
|
72
|
+
reason: error?.code === "ENOENT" || error?.code === "ENOTDIR" || error?.code === "ELOOP"
|
|
73
|
+
? "cache_root_dangling_link"
|
|
74
|
+
: "cache_entry_inaccessible",
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (!target.isDirectory()) return { ok: false, reason: "cache_root_not_directory" };
|
|
79
|
+
return { ok: true, reason: "cache_accessible" };
|
|
80
|
+
}
|
|
81
|
+
|
|
32
82
|
/**
|
|
33
83
|
* Inspect an existing Unix npm cache without following symlinks. The limits are
|
|
34
84
|
* deliberately part of the result contract: an incomplete inspection cannot prove
|
|
35
85
|
* that replacing the live package will succeed.
|
|
36
86
|
*/
|
|
37
87
|
export function inspectNpmCacheDirectory(cachePath, options = {}) {
|
|
88
|
+
const root = inspectNpmCacheRoot(cachePath, options);
|
|
89
|
+
if (!root.ok) return root;
|
|
38
90
|
const expectedUid = options.expectedUid ?? process.getuid?.();
|
|
39
91
|
const deadline = (options.nowMs ?? Date.now)() + (options.timeoutMs ?? INSPECTION_TIMEOUT_MS);
|
|
40
92
|
const maxEntries = options.maxEntries ?? MAX_ENTRIES;
|
|
@@ -118,22 +170,59 @@ export function inspectNpmCacheDirectory(cachePath, options = {}) {
|
|
|
118
170
|
return { ok: true, reason: "cache_accessible" };
|
|
119
171
|
}
|
|
120
172
|
|
|
121
|
-
|
|
122
|
-
|
|
173
|
+
// cmd.exe re-parses npm.cmd's %* after our quoting, so on Windows a pinned path must not carry
|
|
174
|
+
// characters that quoting cannot neutralize there. Such a path is refused, never escaped.
|
|
175
|
+
const WINDOWS_CMD_METACHARACTERS = /["%!^&|<>]/;
|
|
176
|
+
|
|
177
|
+
function validCachePath(value, platform = process.platform) {
|
|
178
|
+
return typeof value === "string" && value.length > 0 && value.length <= 4096
|
|
179
|
+
&& !value.includes("\0") && !/[\r\n]/.test(value) && isAbsolute(value)
|
|
180
|
+
&& !(platform === "win32" && WINDOWS_CMD_METACHARACTERS.test(value));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Resolve the cache npm itself uses, with the operator's full npmrc chain. The transactional
|
|
185
|
+
* stage pins this path with `--cache`: its `--prefix <stage>` moves npm's globalconfig to
|
|
186
|
+
* `<stage>/etc/npmrc`, so without the pin a `cache=` from the global npmrc is silently dropped
|
|
187
|
+
* and staging falls back to npm's default root — not the root this preflight checked (#6288).
|
|
188
|
+
*/
|
|
189
|
+
export function resolveNpmCachePath(options = {}) {
|
|
190
|
+
const env = options.env ?? process.env;
|
|
191
|
+
const platform = options.platform ?? process.platform;
|
|
192
|
+
// Global mode, from the home directory: the staged install is `npm install -g`, which never
|
|
193
|
+
// reads a project .npmrc, so a repository the user happens to run `ocx update` in must not be
|
|
194
|
+
// able to choose the cache that install is pinned to.
|
|
195
|
+
const invocation = (options.invocationFn ?? npmInvocation)(["config", "get", "cache", "--global"], platform, env);
|
|
123
196
|
if (!invocation) return { ok: false, reason: "npm_unavailable" };
|
|
124
|
-
const npm = spawnSync(invocation.file, invocation.args, {
|
|
197
|
+
const npm = (options.spawnSyncFn ?? spawnSync)(invocation.file, invocation.args, {
|
|
125
198
|
encoding: "utf8",
|
|
126
|
-
timeout: NPM_CONFIG_TIMEOUT_MS,
|
|
199
|
+
timeout: options.timeoutMs ?? NPM_CONFIG_TIMEOUT_MS,
|
|
127
200
|
windowsHide: true,
|
|
201
|
+
cwd: options.cwd ?? homedir(),
|
|
128
202
|
...invocation.options,
|
|
203
|
+
env: invocation.options?.env ?? env,
|
|
129
204
|
});
|
|
130
|
-
if (npm
|
|
131
|
-
|
|
205
|
+
if (npm?.status !== 0) return { ok: false, reason: "npm_config_failed" };
|
|
132
206
|
const output = typeof npm.stdout === "string" ? npm.stdout.trim() : "";
|
|
133
|
-
if (!output
|
|
134
|
-
|
|
207
|
+
if (!validCachePath(output, platform)) return { ok: false, reason: "cache_path_malformed" };
|
|
208
|
+
return { ok: true, path: output };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function workerResult(argv) {
|
|
212
|
+
const rootOnly = argv.includes(ROOT_ONLY_ARG);
|
|
213
|
+
const supplied = argv.find(arg => arg !== ROOT_ONLY_ARG);
|
|
214
|
+
let cachePath;
|
|
215
|
+
if (supplied !== undefined) {
|
|
216
|
+
if (!validCachePath(supplied)) return { ok: false, reason: "cache_path_malformed" };
|
|
217
|
+
cachePath = supplied;
|
|
218
|
+
} else {
|
|
219
|
+
const resolved = resolveNpmCachePath();
|
|
220
|
+
if (!resolved.ok) return resolved;
|
|
221
|
+
cachePath = resolved.path;
|
|
135
222
|
}
|
|
136
|
-
|
|
223
|
+
// Windows has no uid and no Unix owner bits, so the deep ownership/mode walk proves nothing
|
|
224
|
+
// there; the root check is the part of the contract that holds on every platform.
|
|
225
|
+
return rootOnly ? inspectNpmCacheRoot(cachePath) : inspectNpmCacheDirectory(cachePath);
|
|
137
226
|
}
|
|
138
227
|
|
|
139
228
|
// Reasons that legitimately accompany `ok: true`. The parser below cross-checks the flag against
|
|
@@ -161,15 +250,33 @@ function parseWorkerOutput(stdout) {
|
|
|
161
250
|
}
|
|
162
251
|
}
|
|
163
252
|
|
|
164
|
-
/**
|
|
253
|
+
/**
|
|
254
|
+
* Run the bounded cache inspection in an isolated, synchronously-timeboxed worker. Pass
|
|
255
|
+
* `cachePath` (from {@link resolveNpmCachePath}) to inspect exactly the root a later stage pins;
|
|
256
|
+
* without it the worker resolves npm's cache itself.
|
|
257
|
+
*/
|
|
165
258
|
export function runNpmCachePreflight(options = {}) {
|
|
166
|
-
|
|
259
|
+
const result = runPreflightWorker(options);
|
|
260
|
+
// Before #6288 Windows skipped this gate entirely. Only the root shapes that make npm's own
|
|
261
|
+
// mkdir fail with ENOTDIR may now refuse an update there; a slow or unavailable npm, a worker
|
|
262
|
+
// timeout, or any other inconclusive result keeps the previous behavior instead of blocking.
|
|
263
|
+
if ((options.platform ?? process.platform) === "win32" && !result.ok
|
|
264
|
+
&& !WINDOWS_BLOCKING_REASONS.has(result.reason)) {
|
|
167
265
|
return { ok: true, reason: "windows_skip" };
|
|
168
266
|
}
|
|
267
|
+
return result;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const WINDOWS_BLOCKING_REASONS = new Set(["cache_root_dangling_link", "cache_root_not_directory"]);
|
|
271
|
+
|
|
272
|
+
function runPreflightWorker(options) {
|
|
273
|
+
const workerArgs = [fileURLToPath(import.meta.url), WORKER_ARG];
|
|
274
|
+
if ((options.platform ?? process.platform) === "win32") workerArgs.push(ROOT_ONLY_ARG);
|
|
275
|
+
if (options.cachePath !== undefined) workerArgs.push(options.cachePath);
|
|
169
276
|
const spawn = options.spawnSyncFn ?? spawnSync;
|
|
170
277
|
const result = spawn(
|
|
171
278
|
options.execPath ?? process.execPath,
|
|
172
|
-
|
|
279
|
+
workerArgs,
|
|
173
280
|
{
|
|
174
281
|
encoding: "utf8",
|
|
175
282
|
timeout: options.timeoutMs ?? WORKER_TIMEOUT_MS,
|
|
@@ -182,8 +289,14 @@ export function runNpmCachePreflight(options = {}) {
|
|
|
182
289
|
return parseWorkerOutput(result.stdout) ?? { ok: false, reason: "worker_output_malformed" };
|
|
183
290
|
}
|
|
184
291
|
|
|
185
|
-
/** Fixed operator guidance; worker/npm output
|
|
292
|
+
/** Fixed operator guidance; worker/npm output and the cache path are intentionally never interpolated. */
|
|
186
293
|
export function npmCachePreflightFailureMessage(reason) {
|
|
294
|
+
if (reason === "cache_root_dangling_link") {
|
|
295
|
+
return `npm cache access pre-flight failed (${reason}); npm's cache folder (see 'npm config get cache') is a link or junction whose target is missing — recreate the target folder or remove the link, then retry`;
|
|
296
|
+
}
|
|
297
|
+
if (reason === "cache_root_not_directory") {
|
|
298
|
+
return `npm cache access pre-flight failed (${reason}); npm's cache folder (see 'npm config get cache') or a folder above it is a file or an unavailable drive, so npm would fail with ENOTDIR on mkdir — fix that path or set a different cache, then retry`;
|
|
299
|
+
}
|
|
187
300
|
return `npm cache access pre-flight failed (${reason}); fix cache ownership and permissions, then retry`;
|
|
188
301
|
}
|
|
189
302
|
|
|
@@ -193,7 +306,7 @@ const isWorker = process.argv[1]
|
|
|
193
306
|
if (isWorker) {
|
|
194
307
|
let result;
|
|
195
308
|
try {
|
|
196
|
-
result = workerResult();
|
|
309
|
+
result = workerResult(process.argv.slice(3));
|
|
197
310
|
} catch {
|
|
198
311
|
result = { ok: false, reason: "cache_entry_inaccessible" };
|
|
199
312
|
}
|
|
@@ -9,19 +9,37 @@ type Environment = Record<string, string | undefined>;
|
|
|
9
9
|
export interface UpdateOwnershipTransaction {
|
|
10
10
|
controlEnvironment(environment?: Environment): Environment;
|
|
11
11
|
unprivilegedEnvironment(environment?: Environment): Environment;
|
|
12
|
+
/**
|
|
13
|
+
* Free the lease before a service-manager-mediated start that cannot join it —
|
|
14
|
+
* the manager's `ocx start` child runs with the stored registration environment,
|
|
15
|
+
* not a delegated token, so held through the repair's serving wait the lease
|
|
16
|
+
* keeps that proxy from starting (#5760). Idempotent; controlEnvironment keeps
|
|
17
|
+
* injecting the token afterwards, which a released lease self-corrects (the
|
|
18
|
+
* delegated join fails and the child acquires normally).
|
|
19
|
+
*/
|
|
20
|
+
release(): void;
|
|
21
|
+
/**
|
|
22
|
+
* Take the lease back before re-reading ownership and mutating directly: the
|
|
23
|
+
* released refresh window could have admitted a claim that must be vetoed, not
|
|
24
|
+
* killed unleased. No-op while held; a still-claimed lease throws (bounded),
|
|
25
|
+
* which callers must treat as fail-closed.
|
|
26
|
+
*/
|
|
27
|
+
reacquire(): void;
|
|
12
28
|
}
|
|
13
29
|
|
|
14
30
|
/** One lease spans awaited stop, replacement and recovery. Never publish its token globally. */
|
|
15
31
|
export async function withUpdateOwnershipLease<T>(
|
|
16
32
|
paths: string[], run: (transaction: UpdateOwnershipTransaction) => Promise<T>,
|
|
17
33
|
): Promise<T> {
|
|
18
|
-
|
|
34
|
+
let lease = acquireOwnershipMutationLease(paths);
|
|
19
35
|
let released = false;
|
|
20
36
|
const release = () => { if (!released) { released = true; lease.release(); } };
|
|
21
37
|
try {
|
|
22
38
|
return await run({
|
|
23
39
|
controlEnvironment: (env = process.env) => ownershipMutationLeaseChildEnvironment(env, lease.token),
|
|
24
40
|
unprivilegedEnvironment: (env = process.env) => unprivilegedOwnershipMutationEnvironment(env),
|
|
41
|
+
release: () => release(),
|
|
42
|
+
reacquire: () => { if (released) { lease = acquireOwnershipMutationLease(paths); released = false; } },
|
|
25
43
|
});
|
|
26
44
|
} finally { release(); }
|
|
27
45
|
}
|
|
@@ -36,19 +36,93 @@ export function updateRestartVeto(
|
|
|
36
36
|
return plan.notice ?? "The background runtime is owned elsewhere; it was left running.";
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* The lease boundary inside a running restart, handed to `restart`.
|
|
41
|
+
*
|
|
42
|
+
* `ocx service repair` re-activates the OS service manager — Task Scheduler, launchd
|
|
43
|
+
* or systemd — and that `ocx start` child is not a descendant of this process: it runs
|
|
44
|
+
* with the stored registration environment, carries no delegated token, and can never
|
|
45
|
+
* join the lease held here. Held through the repair's serving wait, the lease keeps
|
|
46
|
+
* that child from ever starting (#5760; the npm lane releases at this boundary). The
|
|
47
|
+
* veto is the decision the lease must cover — the reclaim and kills it authorizes —
|
|
48
|
+
* so the fallthrough takes the lease back before re-running it: anything between the
|
|
49
|
+
* service refresh and the direct start would otherwise mutate the port unleased.
|
|
50
|
+
*/
|
|
51
|
+
export interface UpdateRestartLeaseControl {
|
|
52
|
+
/** Free the lease before a service-manager-mediated start that cannot join it. Idempotent. */
|
|
53
|
+
releaseForServiceManager(): void;
|
|
54
|
+
/**
|
|
55
|
+
* Take the lease back before the fallthrough mutates the port, then re-run the veto
|
|
56
|
+
* under it. A lease that stays claimed (a live claimant mid-mutation) fails closed:
|
|
57
|
+
* the returned notice stops the restart before any kill or start, and `failed` marks
|
|
58
|
+
* the job failed — the refresh just before this did not produce a serving proxy, so
|
|
59
|
+
* nothing is known to be running. An ownership veto is not a failure.
|
|
60
|
+
*/
|
|
61
|
+
reacquireForDirectStart(): UpdateRestartStop | null;
|
|
62
|
+
/** Re-run the recorded-owner veto; a non-null notice must stop the restart there. */
|
|
63
|
+
vetoAgain(): string | null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Why the direct-start fallthrough stopped; `failed` means no proxy is known to be serving. */
|
|
67
|
+
export interface UpdateRestartStop {
|
|
68
|
+
readonly notice: string;
|
|
69
|
+
readonly failed: boolean;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Long enough to outlast one service-wrapper respawn cycle (5 s) of the managed child the
|
|
74
|
+
* refresh just started, which briefly holds the lease while it binds and publishes.
|
|
75
|
+
*/
|
|
76
|
+
const REACQUIRE_WAIT_MS = 10_000;
|
|
77
|
+
|
|
39
78
|
export async function runUpdateRestartWithOwnershipLease<T>(
|
|
40
79
|
resolve: (() => ServiceOwnershipResolution) | undefined,
|
|
41
|
-
restart: () => Promise<T>,
|
|
80
|
+
restart: (lease: UpdateRestartLeaseControl) => Promise<T>,
|
|
42
81
|
): Promise<{ readonly kind: "veto"; readonly notice: string } | { readonly kind: "ran"; readonly value: T }> {
|
|
43
|
-
|
|
82
|
+
let lease = acquireOwnershipMutationLease(serviceStatePaths());
|
|
44
83
|
const previous = process.env[OWNERSHIP_MUTATION_LEASE_TOKEN_ENV];
|
|
45
84
|
process.env[OWNERSHIP_MUTATION_LEASE_TOKEN_ENV] = lease.token;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return veto ? { kind: "veto", notice: veto } : { kind: "ran", value: await restart() };
|
|
49
|
-
} finally {
|
|
85
|
+
let heldNow = true;
|
|
86
|
+
const restoreEnv = () => {
|
|
50
87
|
if (previous === undefined) delete process.env[OWNERSHIP_MUTATION_LEASE_TOKEN_ENV];
|
|
51
88
|
else process.env[OWNERSHIP_MUTATION_LEASE_TOKEN_ENV] = previous;
|
|
89
|
+
};
|
|
90
|
+
const release = () => {
|
|
91
|
+
if (!heldNow) return;
|
|
92
|
+
heldNow = false;
|
|
93
|
+
restoreEnv();
|
|
52
94
|
lease.release();
|
|
95
|
+
};
|
|
96
|
+
const reacquireForDirectStart = (): UpdateRestartStop | null => {
|
|
97
|
+
if (!heldNow) {
|
|
98
|
+
try {
|
|
99
|
+
lease = acquireOwnershipMutationLease(serviceStatePaths(), { waitMs: REACQUIRE_WAIT_MS });
|
|
100
|
+
} catch {
|
|
101
|
+
return {
|
|
102
|
+
failed: true,
|
|
103
|
+
notice: "Update installed, but another process kept the runtime ownership lease claimed, so no proxy was started. "
|
|
104
|
+
+ "Run 'ocx service status', then 'ocx service repair' or 'ocx start'.",
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
heldNow = true;
|
|
108
|
+
process.env[OWNERSHIP_MUTATION_LEASE_TOKEN_ENV] = lease.token;
|
|
109
|
+
}
|
|
110
|
+
const veto = updateRestartVeto(resolve);
|
|
111
|
+
return veto ? { notice: veto, failed: false } : null;
|
|
112
|
+
};
|
|
113
|
+
try {
|
|
114
|
+
const veto = updateRestartVeto(resolve);
|
|
115
|
+
return veto
|
|
116
|
+
? { kind: "veto", notice: veto }
|
|
117
|
+
: {
|
|
118
|
+
kind: "ran",
|
|
119
|
+
value: await restart({
|
|
120
|
+
releaseForServiceManager: release,
|
|
121
|
+
reacquireForDirectStart,
|
|
122
|
+
vetoAgain: () => updateRestartVeto(resolve),
|
|
123
|
+
}),
|
|
124
|
+
};
|
|
125
|
+
} finally {
|
|
126
|
+
release();
|
|
53
127
|
}
|
|
54
128
|
}
|
|
@@ -31,6 +31,8 @@ export function transactionalNpmUpdate(args: {
|
|
|
31
31
|
targetVersion?: string;
|
|
32
32
|
tag: string;
|
|
33
33
|
runNpm: (args: string[]) => { status: number | null };
|
|
34
|
+
/** npm cache root the pre-flight resolved and checked; pinned with `--cache` for staging. */
|
|
35
|
+
cachePath?: string;
|
|
34
36
|
log?: (line: string) => void;
|
|
35
37
|
deps?: UpdateFsDeps;
|
|
36
38
|
}): {
|
|
@@ -390,6 +390,7 @@ export function transactionalNpmUpdate({
|
|
|
390
390
|
targetVersion,
|
|
391
391
|
tag,
|
|
392
392
|
runNpm,
|
|
393
|
+
cachePath,
|
|
393
394
|
log = () => {},
|
|
394
395
|
deps = {},
|
|
395
396
|
}) {
|
|
@@ -432,8 +433,12 @@ export function transactionalNpmUpdate({
|
|
|
432
433
|
// @oven/bun-* executable into bun/bin, so a successful npm exit without this narrow
|
|
433
434
|
// approval leaves the staged tree intentionally incomplete. Allow only the package
|
|
434
435
|
// whose executable the manifest verifies below; never broaden this to all scripts.
|
|
436
|
+
// --prefix <stage> also moves npm's globalconfig to <stage>/etc/npmrc, so a `cache=` from the
|
|
437
|
+
// operator's global npmrc would be dropped and npm would fall back to its default root. Pin
|
|
438
|
+
// the cache the pre-flight resolved and checked, so staging uses that exact root (#6288).
|
|
439
|
+
const cacheArgs = typeof cachePath === "string" && cachePath.length > 0 ? ["--cache", cachePath] : [];
|
|
435
440
|
const install = runNpm([
|
|
436
|
-
"install", "-g", "--prefix", stageRoot,
|
|
441
|
+
"install", "-g", "--prefix", stageRoot, ...cacheArgs,
|
|
437
442
|
"--allow-scripts=bun", "--no-audit", "--no-fund", spec,
|
|
438
443
|
]);
|
|
439
444
|
if (install.status !== 0) {
|
package/src/usage/jev-stats.ts
CHANGED
|
@@ -18,6 +18,7 @@ export const JEV_DECISION_GATES = [
|
|
|
18
18
|
export type JevDecisionGate = (typeof JEV_DECISION_GATES)[number];
|
|
19
19
|
|
|
20
20
|
const JEV_GATE_SET = new Set<string>(JEV_DECISION_GATES);
|
|
21
|
+
const JEV_STATS_BACKENDS = ["typesafe", "systemone", "model", "unknown"] as const;
|
|
21
22
|
const JEV_EFFORTS = new Set<OcxComboDefaultEffort>([
|
|
22
23
|
"low", "medium", "high", "xhigh", "max", "ultra",
|
|
23
24
|
]);
|
|
@@ -30,6 +31,7 @@ const CONTROL_CHARS = /[\u0000-\u001f\u007f]/u;
|
|
|
30
31
|
export interface PersistedJevDecisionV1 {
|
|
31
32
|
version: 1;
|
|
32
33
|
comboId: string;
|
|
34
|
+
backend?: "typesafe" | "systemone" | "model";
|
|
33
35
|
selected: {
|
|
34
36
|
provider: string;
|
|
35
37
|
model: string;
|
|
@@ -103,6 +105,12 @@ export interface JevStatsResponse {
|
|
|
103
105
|
averageChosenProbability: number | null;
|
|
104
106
|
};
|
|
105
107
|
gates: Array<{ gate: JevDecisionGate; decisions: number }>;
|
|
108
|
+
backends: Array<{
|
|
109
|
+
backend: "typesafe" | "systemone" | "model" | "unknown";
|
|
110
|
+
decisions: number;
|
|
111
|
+
applied: number;
|
|
112
|
+
averageLatencyMs: number | null;
|
|
113
|
+
}>;
|
|
106
114
|
models: JevStatsModelRow[];
|
|
107
115
|
snapshotWindowStart: number | null;
|
|
108
116
|
snapshotWindowEnd: number | null;
|
|
@@ -174,9 +182,13 @@ export function normalizePersistedJevDecision(value: unknown): PersistedJevDecis
|
|
|
174
182
|
const confidence = probability(value.confidence);
|
|
175
183
|
const chosenProbability = probability(value.chosenProbability);
|
|
176
184
|
const usage = normalizedDecisionUsage(value.usage);
|
|
185
|
+
const backend = value.backend === "typesafe" || value.backend === "systemone" || value.backend === "model"
|
|
186
|
+
? value.backend
|
|
187
|
+
: undefined;
|
|
177
188
|
return {
|
|
178
189
|
version: 1,
|
|
179
190
|
comboId,
|
|
191
|
+
...(backend !== undefined ? { backend } : {}),
|
|
180
192
|
selected: {
|
|
181
193
|
provider,
|
|
182
194
|
model,
|
|
@@ -269,6 +281,9 @@ class StreamingJevStatsAccumulator implements JevStatsAccumulator {
|
|
|
269
281
|
private readonly models = new Map<string, MutableModelRow>();
|
|
270
282
|
private overflowModel: MutableModelRow | null = null;
|
|
271
283
|
private readonly gates = new Map<JevDecisionGate, number>();
|
|
284
|
+
private readonly backends = new Map<
|
|
285
|
+
JevStatsResponse["backends"][number]["backend"], JevStatsResponse["backends"][number]
|
|
286
|
+
>();
|
|
272
287
|
private snapshotWindowStart: number | null = null;
|
|
273
288
|
private snapshotWindowEnd: number | null = null;
|
|
274
289
|
private decisions = 0;
|
|
@@ -300,7 +315,7 @@ class StreamingJevStatsAccumulator implements JevStatsAccumulator {
|
|
|
300
315
|
}
|
|
301
316
|
|
|
302
317
|
get estimatedBytes(): number {
|
|
303
|
-
let bytes = 512;
|
|
318
|
+
let bytes = 512 + this.backends.size * 96;
|
|
304
319
|
for (const row of this.models.values()) {
|
|
305
320
|
bytes += 256 + (row.provider.length + row.model.length) * 2 + row.effortCounts.size * 32;
|
|
306
321
|
}
|
|
@@ -340,6 +355,13 @@ class StreamingJevStatsAccumulator implements JevStatsAccumulator {
|
|
|
340
355
|
const decision = normalizePersistedJevDecision(entry.jevDecision);
|
|
341
356
|
if (!decision || (this.comboId !== null && decision.comboId !== this.comboId)) return;
|
|
342
357
|
|
|
358
|
+
const backend = decision.backend ?? "unknown";
|
|
359
|
+
const backendRow = this.backends.get(backend)
|
|
360
|
+
?? { backend, decisions: 0, applied: 0, averageLatencyMs: null };
|
|
361
|
+
backendRow.averageLatencyMs = nextMean(backendRow.averageLatencyMs, backendRow.decisions, decision.latencyMs);
|
|
362
|
+
backendRow.decisions = saturatingAdd(backendRow.decisions, 1);
|
|
363
|
+
if (decision.gate === "apply") backendRow.applied = saturatingAdd(backendRow.applied, 1);
|
|
364
|
+
this.backends.set(backend, backendRow);
|
|
343
365
|
this.averageLatencyMs = nextMean(this.averageLatencyMs, this.decisions, decision.latencyMs);
|
|
344
366
|
this.decisions = saturatingAdd(this.decisions, 1);
|
|
345
367
|
if (decision.gate === "apply") this.appliedDecisions = saturatingAdd(this.appliedDecisions, 1);
|
|
@@ -419,6 +441,7 @@ class StreamingJevStatsAccumulator implements JevStatsAccumulator {
|
|
|
419
441
|
for (const [key, row] of this.models) cloned.models.set(key, cloneModelRow(row));
|
|
420
442
|
cloned.overflowModel = this.overflowModel ? cloneModelRow(this.overflowModel) : null;
|
|
421
443
|
for (const [gate, count] of this.gates) cloned.gates.set(gate, count);
|
|
444
|
+
for (const [backend, row] of this.backends) cloned.backends.set(backend, { ...row });
|
|
422
445
|
cloned.snapshotWindowStart = this.snapshotWindowStart;
|
|
423
446
|
cloned.snapshotWindowEnd = this.snapshotWindowEnd;
|
|
424
447
|
cloned.decisions = this.decisions;
|
|
@@ -487,6 +510,10 @@ class StreamingJevStatsAccumulator implements JevStatsAccumulator {
|
|
|
487
510
|
const count = this.gates.get(gate) ?? 0;
|
|
488
511
|
return count > 0 ? [{ gate, decisions: count }] : [];
|
|
489
512
|
}),
|
|
513
|
+
backends: JEV_STATS_BACKENDS.flatMap(backend => {
|
|
514
|
+
const row = this.backends.get(backend);
|
|
515
|
+
return row && row.decisions > 0 ? [{ ...row }] : [];
|
|
516
|
+
}),
|
|
490
517
|
models: rows,
|
|
491
518
|
snapshotWindowStart: this.snapshotWindowStart,
|
|
492
519
|
snapshotWindowEnd: this.snapshotWindowEnd,
|
package/src/usage/log.ts
CHANGED
|
@@ -506,7 +506,7 @@ const KNOWN_AFFINITY_MOVES = new Set<NonNullable<PersistedUsageEntry["affinity"]
|
|
|
506
506
|
]);
|
|
507
507
|
const KNOWN_AFFINITY_REASONS = new Set<NonNullable<PersistedUsageEntry["affinityReason"]>>([
|
|
508
508
|
"healthy", "quota_headroom", "quota_refusal", "transient", "transient_hold_expired",
|
|
509
|
-
"unusable", "paused", "plan_excluded", "cooldown", "quota_avoided", "generation",
|
|
509
|
+
"unusable", "paused", "plan_excluded", "credits_off", "cooldown", "quota_avoided", "generation",
|
|
510
510
|
"expired", "model_lane",
|
|
511
511
|
]);
|
|
512
512
|
const KNOWN_CONVERSATION_STATE_SCRUBS = new Set<NonNullable<PersistedUsageEntry["conversationStateScrub"]>>([
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import type { OcxProviderConfig } from "../types";
|
|
1
|
+
import type { OcxConfig, OcxProviderConfig } from "../types";
|
|
2
2
|
import { sidecarThinkingOff } from "../adapters/anthropic-model-contract";
|
|
3
3
|
import { CLAUDE_CODE_HEADERS, claudeCodeSessionId } from "../adapters/client-fingerprint";
|
|
4
4
|
import { signalWithTimeout, cancelBodyOnAbort } from "../lib/abort";
|
|
5
5
|
import { sidecarEnter } from "../lib/sidecar-tracker";
|
|
6
6
|
import { applyUpstreamRecoveryInit, fetchWithResetRetry } from "../lib/upstream-retry";
|
|
7
|
-
import {
|
|
7
|
+
import { publicOAuthAuthenticationErrorMessage } from "../oauth";
|
|
8
|
+
import { getAnthropicSidecarAccessToken } from "../oauth/anthropic-routing";
|
|
8
9
|
import { ANTHROPIC_OAUTH_BETA, CLAUDE_CODE_SYSTEM_INSTRUCTION } from "../oauth/anthropic";
|
|
9
10
|
import type { DescribeOutcome, VisionSettings } from "./describe";
|
|
10
11
|
|
|
@@ -152,13 +153,14 @@ export async function describeImageAnthropic(
|
|
|
152
153
|
provider: OcxProviderConfig,
|
|
153
154
|
settings: VisionSettings,
|
|
154
155
|
abortSignal?: AbortSignal,
|
|
156
|
+
config?: OcxConfig,
|
|
155
157
|
): Promise<DescribeOutcome> {
|
|
156
158
|
const image = buildImageBlock(imageUrl);
|
|
157
159
|
if (!image.block) return { text: "", error: image.error ?? "invalid image" };
|
|
158
160
|
|
|
159
161
|
let token: string;
|
|
160
162
|
try {
|
|
161
|
-
token = await
|
|
163
|
+
token = await getAnthropicSidecarAccessToken(providerName, settings.model, config);
|
|
162
164
|
} catch (error) {
|
|
163
165
|
return { text: "", error: `anthropic vision sidecar auth failed: ${publicOAuthAuthenticationErrorMessage(error)}` };
|
|
164
166
|
}
|
package/src/vision/index.ts
CHANGED
package/src/vision/plan.ts
CHANGED
|
@@ -43,6 +43,7 @@ export function resolveVisionTimeoutMs(value: unknown): number {
|
|
|
43
43
|
export interface AnthropicVisionProvider {
|
|
44
44
|
providerName: string;
|
|
45
45
|
provider: OcxProviderConfig;
|
|
46
|
+
config: OcxConfig;
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
/**
|
|
@@ -52,7 +53,7 @@ export interface AnthropicVisionProvider {
|
|
|
52
53
|
export function findAnthropicVisionProvider(config: OcxConfig): AnthropicVisionProvider | undefined {
|
|
53
54
|
const auth = resolveSidecarAuth(config);
|
|
54
55
|
if (!auth.isAnthropicAuth || !auth.anthropicProviderName || !auth.anthropicProvider) return undefined;
|
|
55
|
-
return { providerName: auth.anthropicProviderName, provider: auth.anthropicProvider };
|
|
56
|
+
return { providerName: auth.anthropicProviderName, provider: auth.anthropicProvider, config };
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
export function resolveVisionBackend(
|