@bitkyc08/opencodex 2.52.0 → 2.53.0-preview.20260913
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/gui/dist/assets/index-BBOZWGB6.css +1 -0
- package/gui/dist/assets/index-D7ynYo2K.js +128 -0
- package/gui/dist/index.html +2 -2
- package/native/remote-workspace-helper/Cargo.lock +130 -0
- package/native/remote-workspace-helper/Cargo.toml +24 -0
- package/native/remote-workspace-helper/src/main.rs +49 -0
- package/native/remote-workspace-helper/src/protocol.rs +246 -0
- package/native/remote-workspace-helper/src/sandbox/macos.rs +19 -0
- package/native/remote-workspace-helper/src/sandbox/mod.rs +77 -0
- package/native/remote-workspace-helper/src/sandbox/windows.rs +15 -0
- package/package.json +6 -1
- package/src/adapters/anthropic-image-normalize.ts +30 -2
- package/src/adapters/anthropic.ts +1 -1
- package/src/adapters/base.ts +8 -2
- package/src/adapters/cursor/cursor-errors.ts +12 -0
- package/src/adapters/cursor/thread-continuity.ts +93 -0
- package/src/adapters/cursor.ts +104 -73
- package/src/adapters/devin/cloud-direct/chat.ts +312 -23
- package/src/adapters/devin/cloud-direct/metadata.ts +31 -2
- package/src/adapters/devin/live-models.ts +70 -3
- package/src/adapters/devin.ts +281 -21
- package/src/adapters/google-wire-compiler.ts +14 -6
- package/src/adapters/google.ts +22 -8
- package/src/adapters/kiro/adapter.ts +316 -0
- package/src/adapters/kiro/conversation.ts +136 -0
- package/src/adapters/kiro/payload.ts +432 -0
- package/src/adapters/kiro/reasoning.ts +56 -0
- package/src/adapters/kiro/stream.ts +1153 -0
- package/src/adapters/kiro/usage.ts +223 -0
- package/src/adapters/kiro/wire.ts +76 -0
- package/src/adapters/kiro.ts +8 -2319
- package/src/adapters/mimo-free.ts +1 -1
- package/src/adapters/openai-chat-images.ts +101 -0
- package/src/adapters/openai-chat.ts +201 -181
- package/src/adapters/openai-responses.ts +92 -224
- package/src/adapters/registry.ts +0 -7
- package/src/adapters/run-turn-queue.ts +13 -6
- package/src/bridge.ts +14 -15
- package/src/chat/inbound.ts +29 -4
- package/src/chat/outbound.ts +145 -107
- package/src/claude/desktop-profile.ts +4 -6
- package/src/cli/account-api.ts +14 -0
- package/src/cli/account-extended.ts +1 -1
- package/src/cli/account-history.ts +60 -0
- package/src/cli/account-main.ts +80 -0
- package/src/cli/account.ts +11 -3
- package/src/cli/capabilities.ts +113 -0
- package/src/cli/catalog.ts +109 -0
- package/src/cli/dispatch.ts +9 -0
- package/src/cli/help.ts +2 -0
- package/src/cli/index.ts +2 -2
- package/src/cli/observe.ts +28 -1
- package/src/cli/opencode.ts +42 -8
- package/src/cli/provider-runtime.ts +11 -1
- package/src/cli/provider.ts +22 -2
- package/src/cli/registry.ts +21 -0
- package/src/cli/remote-workspace.ts +154 -0
- package/src/cli/status.ts +39 -7
- package/src/cli/usage-report.ts +14 -2
- package/src/client/hub-client.ts +34 -0
- package/src/client/hub-state.ts +9 -1
- package/src/codex/account-store.ts +78 -0
- package/src/codex/auth-api.ts +81 -54
- package/src/codex/auth-context.ts +45 -16
- package/src/codex/catalog/effort.ts +1 -1
- package/src/codex/catalog/metadata.ts +3 -6
- package/src/codex/catalog/native-models.ts +4 -4
- package/src/codex/catalog/parsing.ts +2 -20
- package/src/codex/catalog/provider-fetch.ts +10 -1
- package/src/codex/catalog/remote.ts +233 -0
- package/src/codex/catalog/sync.ts +403 -35
- package/src/codex/convergence.ts +1 -1
- package/src/codex/history-manifest.ts +36 -0
- package/src/codex/history-provider.ts +32 -5
- package/src/codex/inject.ts +9 -0
- package/src/codex/main-account.ts +113 -0
- package/src/codex/main-device-reauth-api.ts +89 -0
- package/src/codex/main-device-reauth.ts +217 -0
- package/src/codex/native-residue.ts +9 -2
- package/src/codex/quota-auto-refresh.ts +3 -2
- package/src/codex/quota-capacity.ts +98 -0
- package/src/codex/quota-history.ts +160 -0
- package/src/codex/quota-types.ts +8 -0
- package/src/codex/quota.ts +118 -91
- package/src/codex/refresh.ts +2 -1
- package/src/codex/routing.ts +90 -17
- package/src/codex/sync.ts +33 -4
- package/src/combos/request.ts +19 -1
- package/src/config/multi-agent-surface.ts +61 -0
- package/src/config/provider-validation.ts +176 -0
- package/src/config.ts +213 -11
- package/src/generated/compatibility-version.json +436 -168
- package/src/images/loop.ts +119 -36
- package/src/lib/admission.ts +12 -6
- package/src/lib/redact.ts +7 -0
- package/src/lib/translator-budget.ts +4 -3
- package/src/lib/windows-atomic-replace.ts +1 -0
- package/src/lib/windows-elevation.ts +1 -1
- package/src/oauth/chatgpt-device.ts +62 -5
- package/src/oauth/devin/cli-import.ts +130 -0
- package/src/oauth/devin.ts +63 -8
- package/src/oauth/index.ts +29 -14
- package/src/oauth/kiro.ts +18 -6
- package/src/oauth/login-cli.ts +9 -1
- package/src/oauth/meta-muse-device.ts +464 -0
- package/src/oauth/meta-muse.ts +123 -32
- package/src/oauth/pool-kernel.ts +9 -0
- package/src/oauth/pool-settings-capability.ts +2 -2
- package/src/oauth/store.ts +57 -0
- package/src/oauth/types.ts +31 -0
- package/src/providers/derive.ts +13 -3
- package/src/providers/devin-cli-authmode-migration.ts +57 -35
- package/src/providers/devin-provider-merge-migration.ts +240 -0
- package/src/providers/muse-key-quota.ts +117 -0
- package/src/providers/muse-subscription-usage.ts +14 -2
- package/src/providers/openai-sidecar.ts +25 -3
- package/src/providers/opencode-zen-rate-limit.ts +58 -0
- package/src/providers/provider-id-rewrite.ts +20 -5
- package/src/providers/quota-types.ts +12 -0
- package/src/providers/quota.ts +143 -102
- package/src/providers/reasoning-metadata.ts +543 -0
- package/src/providers/registry.ts +80 -49
- package/src/reasoning-effort.ts +26 -2
- package/src/remote/hub-usage.ts +32 -0
- package/src/remote-control/index.ts +192 -41
- package/src/remote-control/workspace-activation.ts +9 -0
- package/src/remote-control/workspace-agent-connection.ts +366 -0
- package/src/remote-control/workspace-claude-runtime.ts +243 -0
- package/src/remote-control/workspace-codex-runtime.ts +531 -0
- package/src/remote-control/workspace-codex-sandbox.ts +115 -0
- package/src/remote-control/workspace-command-runner.ts +748 -0
- package/src/remote-control/workspace-coordinator.ts +231 -0
- package/src/remote-control/workspace-device.ts +585 -0
- package/src/remote-control/workspace-executable.ts +43 -0
- package/src/remote-control/workspace-executor.ts +397 -0
- package/src/remote-control/workspace-hub.ts +519 -0
- package/src/remote-control/workspace-pi-runtime.ts +382 -0
- package/src/remote-control/workspace-process.ts +129 -0
- package/src/remote-control/workspace-rpc.ts +304 -0
- package/src/remote-control/workspace-runtime.ts +60 -0
- package/src/remote-control/workspace-secret-store.ts +39 -0
- package/src/remote-control/workspace-sessions.ts +799 -0
- package/src/remote-control/workspace-tool-bridge.ts +192 -0
- package/src/responses/code-mode-helper-compat.ts +22 -3
- package/src/responses/hosted-tool-policy.ts +0 -1
- package/src/responses/muse-tool-name-alias.ts +379 -0
- package/src/responses/plaintext-v2-agent-messages.ts +902 -0
- package/src/router.ts +7 -0
- package/src/routing/compatibility/behavior.ts +0 -1
- package/src/server/audio-client.ts +64 -0
- package/src/server/audio-dictation.ts +91 -0
- package/src/server/audio-live.ts +185 -0
- package/src/server/audio-transcriptions.ts +183 -0
- package/src/server/audio-upstream.ts +153 -0
- package/src/server/auth-cors.ts +61 -2
- package/src/server/chat-completions.ts +1 -1
- package/src/server/chat-native-sse.ts +92 -48
- package/src/server/chat-native.ts +37 -15
- package/src/server/hub-usage.ts +57 -0
- package/src/server/images.ts +4 -0
- package/src/server/index.ts +722 -57
- package/src/server/lifecycle.ts +5 -6
- package/src/server/live-call-bindings.ts +60 -0
- package/src/server/live.ts +12 -1
- package/src/server/management/agent-settings-routes.ts +25 -4
- package/src/server/management/api-access.ts +37 -0
- package/src/server/management/api-key-usage.ts +7 -2
- package/src/server/management/config-routes.ts +1 -18
- package/src/server/management/context.ts +15 -0
- package/src/server/management/logs-usage-routes.ts +2 -0
- package/src/server/management/oauth-account-routes.ts +39 -12
- package/src/server/management/provider-routes.ts +125 -2
- package/src/server/management/remote-workspace-routes.ts +140 -0
- package/src/server/management/route-registry.ts +15 -0
- package/src/server/management/usage-aggregate-cache.ts +14 -15
- package/src/server/management/usage-summary-cache.ts +2 -0
- package/src/server/management-api.ts +23 -0
- package/src/server/ports.ts +17 -0
- package/src/server/relay-eager.ts +4 -1
- package/src/server/relay.ts +70 -10
- package/src/server/request-decompress.ts +6 -3
- package/src/server/responses/agent-task-recovery.ts +25 -32
- package/src/server/responses/codex-auth-error.ts +11 -0
- package/src/server/responses/codex-ws-exchange.ts +52 -3
- package/src/server/responses/codex-ws-wire.ts +55 -0
- package/src/server/responses/compact.ts +9 -1
- package/src/server/responses/core.ts +337 -73
- package/src/server/responses/encrypted-payload.ts +45 -2
- package/src/server/responses/ws-upstream.ts +4 -1
- package/src/server/responses-self-named-namespace-scrub.ts +1 -3
- package/src/server/responses-undeclared-tool-guard.ts +1 -1
- package/src/server/search.ts +3 -0
- package/src/server/sse-payload-rewrite.ts +136 -51
- package/src/server/ws-bridge.ts +35 -1
- package/src/service/cli.ts +372 -0
- package/src/service/diagnostics.ts +340 -0
- package/src/service/guards.ts +303 -0
- package/src/service/health.ts +222 -0
- package/src/service/launchd.ts +853 -0
- package/src/service/orchestration.ts +617 -0
- package/src/service/repair.ts +334 -0
- package/src/service/state.ts +363 -0
- package/src/service/systemd.ts +229 -0
- package/src/service/windows-ops.ts +690 -0
- package/src/service/windows-scheduler.ts +769 -0
- package/src/service/windows-taskxml.ts +613 -0
- package/src/service.ts +22 -5550
- package/src/storage/cleanup/db.ts +258 -0
- package/src/storage/cleanup/execute.ts +358 -0
- package/src/storage/cleanup/paths.ts +189 -0
- package/src/storage/cleanup/pending.ts +140 -0
- package/src/storage/cleanup/preview.ts +292 -0
- package/src/storage/cleanup/reconcile.ts +347 -0
- package/src/storage/cleanup/restore.ts +932 -0
- package/src/storage/cleanup/satellite.ts +474 -0
- package/src/storage/cleanup/staging.ts +129 -0
- package/src/storage/cleanup/types.ts +98 -0
- package/src/storage/cleanup.ts +49 -3127
- package/src/types/accounts.ts +2 -0
- package/src/types/config.ts +13 -12
- package/src/types/provider.ts +37 -0
- package/src/types/request.ts +2 -0
- package/src/types/tools.ts +17 -5
- package/src/types.ts +1 -0
- package/src/usage/expected-prices.ts +127 -0
- package/src/usage/log.ts +58 -1
- package/src/vision/eligibility.ts +13 -2
- package/src/web-search/loop.ts +56 -3
- package/gui/dist/assets/index-CWXut3rG.js +0 -115
- package/gui/dist/assets/index-EdoPnm9_.css +0 -1
- package/src/adapters/devin-cli/acp.ts +0 -204
- package/src/adapters/devin-cli/adapter.ts +0 -345
- package/src/adapters/devin-cli/binary.ts +0 -69
- package/src/adapters/devin-cli/models.ts +0 -57
- package/src/oauth/devin-cli.ts +0 -149
- package/src/server/responses-reasoning-summary-rewrite.ts +0 -178
package/src/oauth/meta-muse.ts
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
import { homedir } from "node:os";
|
|
26
26
|
import { join } from "node:path";
|
|
27
27
|
import { sanitizeApiKeyValue } from "../providers/api-keys";
|
|
28
|
+
import { MuseDeviceLoginError, loginMetaMuseDevice } from "./meta-muse-device";
|
|
28
29
|
import type { OAuthController, OAuthCredentials } from "./types";
|
|
29
30
|
|
|
30
31
|
const MUSE_POINTER_PATH = join(homedir(), ".config", "muse", "auth.json");
|
|
@@ -43,6 +44,7 @@ const KEYCHAIN_TIMEOUT_MS = 5_000;
|
|
|
43
44
|
*/
|
|
44
45
|
const CONSENT_WARNING = [
|
|
45
46
|
"Meta scopes the Muse Code credential to the Muse Code CLI.",
|
|
47
|
+
"A device login authenticates as Meta own Muse Code client, which is a stronger claim than reusing a key your CLI already minted, and that grant has not been exercised against Meta from OpenCodex.",
|
|
46
48
|
"Using it here is UNSUPPORTED: Meta does not authorize subscription coverage outside its own CLI,",
|
|
47
49
|
"how these calls settle is not observable from the API, and you should treat every call as billable.",
|
|
48
50
|
"The key you import or paste is copied into OpenCodex's auth store (~/.opencodex/auth.json, 0600).",
|
|
@@ -65,6 +67,16 @@ export interface MuseImportDeps {
|
|
|
65
67
|
readPointer?: () => Promise<string | null>;
|
|
66
68
|
readKeychain?: (signal?: AbortSignal) => Promise<string | null>;
|
|
67
69
|
fetchImpl?: typeof fetch;
|
|
70
|
+
/** Injected so login-order tests exercise the order without running a grant. */
|
|
71
|
+
loginDevice?: (ctrl: OAuthController) => Promise<OAuthCredentials>;
|
|
72
|
+
/** Forwarded into the device grant so no test can reach the network or a real timer. */
|
|
73
|
+
sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
74
|
+
now?: () => number;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface MuseLoginOptions {
|
|
78
|
+
/** `"off"` skips the Keychain import; add-account and reauth pass it. */
|
|
79
|
+
importLocal?: "fallback" | "off";
|
|
68
80
|
}
|
|
69
81
|
|
|
70
82
|
async function defaultReadPointer(): Promise<string | null> {
|
|
@@ -111,9 +123,6 @@ async function defaultReadKeychain(signal?: AbortSignal): Promise<string | null>
|
|
|
111
123
|
}
|
|
112
124
|
}
|
|
113
125
|
|
|
114
|
-
const INSTALL_HINT =
|
|
115
|
-
"Install it from https://dev.meta.ai/install.sh, run `muse login`, then retry.";
|
|
116
|
-
|
|
117
126
|
/**
|
|
118
127
|
* Where a user without the CLI gets a key by hand.
|
|
119
128
|
*
|
|
@@ -165,40 +174,62 @@ function normalizedEmail(value: unknown): string | undefined {
|
|
|
165
174
|
export async function loginMetaMuse(
|
|
166
175
|
ctrl: OAuthController = {},
|
|
167
176
|
deps: MuseImportDeps = {},
|
|
177
|
+
options: MuseLoginOptions = {},
|
|
168
178
|
): Promise<OAuthCredentials> {
|
|
169
179
|
// Before ANY read: the CLI has no other warning surface.
|
|
170
180
|
ctrl.onProgress?.(CONSENT_WARNING);
|
|
171
181
|
|
|
172
182
|
const platform = deps.platform ?? process.platform;
|
|
173
|
-
|
|
174
|
-
//
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
183
|
+
|
|
184
|
+
// Import first on darwin, and only for a plain login. A user who already ran `muse
|
|
185
|
+
// login` keeps the zero-interaction path, and a device grant ends in a mint against a
|
|
186
|
+
// rate-limited endpoint, so starting one while a working credential sits on disk spends
|
|
187
|
+
// a request to arrive at the same key. `forceLogin` maps to "off" upstream because
|
|
188
|
+
// reimporting is how an add-account silently re-adds the account the user already has.
|
|
189
|
+
if (platform === "darwin" && options.importLocal !== "off") {
|
|
190
|
+
const imported = await importFromKeychain(ctrl, deps);
|
|
191
|
+
if (imported) return imported;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// fetchImpl/sleep/now are FORWARDED. Without this the device module would carry its own
|
|
195
|
+
// deps object and any test reaching this path would call auth.meta.com for real.
|
|
196
|
+
const device = deps.loginDevice
|
|
197
|
+
?? (c => loginMetaMuseDevice(c, {
|
|
198
|
+
...(deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}),
|
|
199
|
+
...(deps.sleep ? { sleep: deps.sleep } : {}),
|
|
200
|
+
...(deps.now ? { now: deps.now } : {}),
|
|
201
|
+
}));
|
|
202
|
+
try {
|
|
203
|
+
ctrl.onProgress?.("Starting the Meta device login...");
|
|
204
|
+
return await device(ctrl);
|
|
205
|
+
} catch (error) {
|
|
206
|
+
// A cancelled login must never be answered with a paste prompt.
|
|
207
|
+
if (isCancellation(error, ctrl)) throw error;
|
|
208
|
+
const reason = deviceFailureReason(error);
|
|
187
209
|
const pasted = await manualKeyCredential(ctrl, reason);
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
+ `or the dashboard and paste yours from ${MANUAL_KEY_URL}, `
|
|
192
|
-
+ "or use the meta-model provider with your own key (META_MODEL_API_KEY).",
|
|
193
|
-
);
|
|
194
|
-
}
|
|
210
|
+
// No paste surface, or an empty paste. The refusal KEEPS the guidance the previous
|
|
211
|
+
// implementation gave; a device error must not replace it.
|
|
212
|
+
if (pasted === null) throw noPasteSurfaceError(platform, reason);
|
|
195
213
|
return await validatedMetaMuseCredential(pasted, ctrl, deps, undefined, "manual");
|
|
196
214
|
}
|
|
215
|
+
}
|
|
197
216
|
|
|
217
|
+
/**
|
|
218
|
+
* Adopt the credential the Muse Code CLI already holds, or report that there is none.
|
|
219
|
+
*
|
|
220
|
+
* Returns null for the two conditions that genuinely mean "nothing here to import", so the
|
|
221
|
+
* caller can offer a device grant instead of a dead end. Everything else still throws: a
|
|
222
|
+
* corrupt file, an unmeasured storage backend, a Keychain read that times out, and a
|
|
223
|
+
* Keychain entry that is unreadable or carries no usable key are all cases where a
|
|
224
|
+
* credential probably EXISTS, and silently starting a browser grant would create a second
|
|
225
|
+
* login to work around a permissions dialog.
|
|
226
|
+
*/
|
|
227
|
+
async function importFromKeychain(
|
|
228
|
+
ctrl: OAuthController,
|
|
229
|
+
deps: MuseImportDeps,
|
|
230
|
+
): Promise<OAuthCredentials | null> {
|
|
198
231
|
const pointerRaw = await (deps.readPointer ?? defaultReadPointer)();
|
|
199
|
-
if (pointerRaw === null)
|
|
200
|
-
throw new Error(`Muse Code CLI credential not found at ${MUSE_POINTER_PATH}. ${INSTALL_HINT}`);
|
|
201
|
-
}
|
|
232
|
+
if (pointerRaw === null) return null;
|
|
202
233
|
|
|
203
234
|
let pointer: MusePointer;
|
|
204
235
|
try {
|
|
@@ -208,9 +239,7 @@ export async function loginMetaMuse(
|
|
|
208
239
|
}
|
|
209
240
|
|
|
210
241
|
const meta = pointer.providers?.meta;
|
|
211
|
-
if (!meta || meta.mechanism !== "oauth")
|
|
212
|
-
throw new Error("The Muse Code credential file has no signed-in Meta account. Run `muse login`, then retry.");
|
|
213
|
-
}
|
|
242
|
+
if (!meta || meta.mechanism !== "oauth") return null;
|
|
214
243
|
// A different storage backend is a shape we have not measured; refuse rather than guess.
|
|
215
244
|
if (meta.storage !== "keychain") {
|
|
216
245
|
throw new Error(
|
|
@@ -218,10 +247,12 @@ export async function loginMetaMuse(
|
|
|
218
247
|
);
|
|
219
248
|
}
|
|
220
249
|
|
|
250
|
+
// ctrl.signal is handed to the reader, not dropped: the caller can cancel a Keychain
|
|
251
|
+
// prompt nobody is going to answer.
|
|
221
252
|
const secretRaw = await (deps.readKeychain ?? defaultReadKeychain)(ctrl.signal);
|
|
222
253
|
if (secretRaw === null) {
|
|
223
254
|
throw new Error(
|
|
224
|
-
"Could not read the Muse Code credential from the macOS Keychain within 5s. Approve the Keychain prompt, or run `muse login` again.",
|
|
255
|
+
"Could not read the Muse Code credential from the macOS Keychain within 5s. Approve the Keychain prompt, or run `muse login` again, or add the account again to sign in with a browser code instead.",
|
|
225
256
|
);
|
|
226
257
|
}
|
|
227
258
|
|
|
@@ -242,6 +273,57 @@ export async function loginMetaMuse(
|
|
|
242
273
|
);
|
|
243
274
|
}
|
|
244
275
|
|
|
276
|
+
/** A cancelled login is not a failed login, and must not be answered with a prompt. */
|
|
277
|
+
function isCancellation(error: unknown, ctrl: OAuthController): boolean {
|
|
278
|
+
if (ctrl.signal?.aborted) return true;
|
|
279
|
+
if (error instanceof MuseDeviceLoginError) return error.kind === "cancelled";
|
|
280
|
+
return error instanceof Error && error.name === "AbortError";
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** One line above the paste field saying why it appeared. Never includes a credential. */
|
|
284
|
+
function deviceFailureReason(error: unknown): string {
|
|
285
|
+
if (error instanceof MuseDeviceLoginError) {
|
|
286
|
+
switch (error.kind) {
|
|
287
|
+
case "subscription-inactive":
|
|
288
|
+
return "This Meta account has no active Muse Code subscription.";
|
|
289
|
+
case "entitlement-required":
|
|
290
|
+
return error.message;
|
|
291
|
+
case "mint-rate-limited":
|
|
292
|
+
return "Meta rate-limited the Muse Code key request.";
|
|
293
|
+
case "device-denied":
|
|
294
|
+
return "The browser approval was denied.";
|
|
295
|
+
case "device-expired":
|
|
296
|
+
return "The device code expired before it was approved.";
|
|
297
|
+
default:
|
|
298
|
+
return "The Meta device login did not complete.";
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return "The Meta device login did not complete.";
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* The refusal for a host that can neither finish a grant nor accept a paste.
|
|
306
|
+
*
|
|
307
|
+
* Composed, not substituted. A host in this position needs to be told where the key lives,
|
|
308
|
+
* which is what the previous implementation said and what three existing tests assert. The
|
|
309
|
+
* non-darwin clause is retained for the same reason: Meta ships no Windows CLI and the
|
|
310
|
+
* Linux credential shape has never been measured, and collapsing those into one message
|
|
311
|
+
* would tell a Linux user something false about their own machine.
|
|
312
|
+
*/
|
|
313
|
+
function noPasteSurfaceError(platform: string, reason: string): Error {
|
|
314
|
+
const platformClause = platform === "darwin"
|
|
315
|
+
? ""
|
|
316
|
+
: platform === "win32"
|
|
317
|
+
? " Meta ships no native Windows Muse Code CLI, so there is no credential to import."
|
|
318
|
+
: " The Muse Code CLI runs here, but where it stores its credential has not been measured, "
|
|
319
|
+
+ "so importing one is refused rather than guessed.";
|
|
320
|
+
return new Error(
|
|
321
|
+
`${reason}${platformClause} This client cannot prompt for a key, so run \`ocx login meta-muse\` from the CLI `
|
|
322
|
+
+ `or the dashboard and paste yours from ${MANUAL_KEY_URL}, `
|
|
323
|
+
+ "or use the meta-model provider with your own key (META_MODEL_API_KEY).",
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
245
327
|
/**
|
|
246
328
|
* The single gate every credential passes, imported or pasted.
|
|
247
329
|
*
|
|
@@ -332,6 +414,15 @@ export async function refreshMetaMuseToken(
|
|
|
332
414
|
access: apiKey,
|
|
333
415
|
refresh: apiKey,
|
|
334
416
|
expires: Number.MAX_SAFE_INTEGER,
|
|
335
|
-
|
|
417
|
+
// Preserve the provenance the slot already recorded. A device login is "oauth";
|
|
418
|
+
// relabelling it "local-cli" would misreport where the credential came from, the same
|
|
419
|
+
// failure this function comment already warns about for "manual".
|
|
420
|
+
source: credential?.source === "manual" || credential?.source === "oauth"
|
|
421
|
+
? credential.source
|
|
422
|
+
: "local-cli",
|
|
423
|
+
// The account token is not re-derivable: Meta rejects refresh_token grants on this
|
|
424
|
+
// client (001 A). Dropping it here would silently cost the on-demand quota probe with
|
|
425
|
+
// no way back except a full re-login.
|
|
426
|
+
...(credential?.muse ? { muse: credential.muse } : {}),
|
|
336
427
|
};
|
|
337
428
|
}
|
package/src/oauth/pool-kernel.ts
CHANGED
|
@@ -41,6 +41,15 @@ export function parseAccountPoolStrategy(raw: unknown): OcxAccountPoolRotationSt
|
|
|
41
41
|
return null;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/** Codex alone supports ordering by the next shared quota reset. */
|
|
45
|
+
export function parseCodexAccountPoolStrategy(raw: unknown): OcxAccountPoolRotationStrategy | "reset-first" | null {
|
|
46
|
+
return raw === "reset-first" ? raw : parseAccountPoolStrategy(raw);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function normalizeCodexAccountPoolStrategy(raw: unknown): OcxAccountPoolRotationStrategy | "reset-first" {
|
|
50
|
+
return parseCodexAccountPoolStrategy(raw) ?? DEFAULT_STRATEGY;
|
|
51
|
+
}
|
|
52
|
+
|
|
44
53
|
/** Strict parse for management APIs — returns null instead of defaulting. */
|
|
45
54
|
export function parseAccountPoolStickyLimit(raw: unknown): number | null {
|
|
46
55
|
if (typeof raw === "number" && Number.isInteger(raw) && raw >= MIN_STICKY_LIMIT && raw <= MAX_STICKY_LIMIT) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isGenericFailoverProvider } from "./generic-account-failover";
|
|
2
|
-
import { parseAccountPoolStickyLimit, parseAccountPoolStrategy } from "./pool-kernel";
|
|
2
|
+
import { parseAccountPoolStickyLimit, parseAccountPoolStrategy, parseCodexAccountPoolStrategy } from "./pool-kernel";
|
|
3
3
|
import type { OcxConfig, OcxProviderConfig } from "../types";
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -147,7 +147,7 @@ export function unifiedPoolSettingsDto(
|
|
|
147
147
|
// honest answer is "not a field here" rather than a fabricated true.
|
|
148
148
|
enabled: null,
|
|
149
149
|
enabledEffective: true,
|
|
150
|
-
strategy:
|
|
150
|
+
strategy: parseCodexAccountPoolStrategy(config.accountPoolStrategy) ?? "quota",
|
|
151
151
|
stickyLimit: parseGenericStickyLimit(config.accountPoolStickyLimit) ?? 1,
|
|
152
152
|
autoSwitchThreshold: parseGenericAutoSwitchThreshold(config.autoSwitchThreshold) ?? 80,
|
|
153
153
|
quotaWindow: null,
|
package/src/oauth/store.ts
CHANGED
|
@@ -490,6 +490,30 @@ function normalizeCredential(cred: unknown): OAuthCredentials | null {
|
|
|
490
490
|
};
|
|
491
491
|
}
|
|
492
492
|
}
|
|
493
|
+
if (candidate.muse && typeof candidate.muse === "object") {
|
|
494
|
+
const muse = candidate.muse;
|
|
495
|
+
const cleanMuse = (value: unknown, max: number): string | undefined => {
|
|
496
|
+
if (typeof value !== "string") return undefined;
|
|
497
|
+
const trimmed = value.trim();
|
|
498
|
+
return trimmed && trimmed.length <= max && !/[\x00-\x1f\x7f]/.test(trimmed) ? trimmed : undefined;
|
|
499
|
+
};
|
|
500
|
+
const oauthAccessToken = cleanMuse(muse.oauthAccessToken, 4096);
|
|
501
|
+
const userId = cleanMuse(muse.userId, 128);
|
|
502
|
+
const tierName = cleanMuse(muse.tierName, 128);
|
|
503
|
+
const mintedAt = typeof muse.mintedAt === "number" && Number.isFinite(muse.mintedAt)
|
|
504
|
+
? muse.mintedAt
|
|
505
|
+
: undefined;
|
|
506
|
+
// oauthAccessToken is the only load-bearing member: without it there is nothing to
|
|
507
|
+
// mint or probe with, and a row carrying only a tier label would be noise.
|
|
508
|
+
if (oauthAccessToken) {
|
|
509
|
+
normalized.muse = {
|
|
510
|
+
oauthAccessToken,
|
|
511
|
+
...(userId ? { userId } : {}),
|
|
512
|
+
...(tierName ? { tierName } : {}),
|
|
513
|
+
...(mintedAt !== undefined ? { mintedAt } : {}),
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
}
|
|
493
517
|
return normalized;
|
|
494
518
|
}
|
|
495
519
|
|
|
@@ -1024,6 +1048,39 @@ export async function replaceProviderAccountSet(
|
|
|
1024
1048
|
}, [provider, set]);
|
|
1025
1049
|
}
|
|
1026
1050
|
|
|
1051
|
+
export type ProviderCredentialRekeyOutcome = "moved" | "absent" | "conflict";
|
|
1052
|
+
|
|
1053
|
+
/**
|
|
1054
|
+
* Move a provider's whole account set to a different provider key.
|
|
1055
|
+
*
|
|
1056
|
+
* Used by provider-id merge migrations (e.g. devin-cli -> devin): the
|
|
1057
|
+
* credential itself stays valid under the canonical id, only the slot name is
|
|
1058
|
+
* stale. The move goes through mutateStore so it takes the same file lock and
|
|
1059
|
+
* revision bookkeeping as every other auth.json write.
|
|
1060
|
+
*
|
|
1061
|
+
* Both slots occupied is a REFUSAL, not a merge: two account sets may belong
|
|
1062
|
+
* to different humans, and picking a survivor is a user decision, so the
|
|
1063
|
+
* outcome is reported and both are left in place.
|
|
1064
|
+
*
|
|
1065
|
+
* Orphaned auth.refresh.<provider>.<hash>.json intent files are not moved.
|
|
1066
|
+
* They are keyed by provider name plus an account-id hash, so a file left
|
|
1067
|
+
* under the old id simply never matches a lookup again — harmless litter, and
|
|
1068
|
+
* rewriting them would have to guess at an intent's in-flight state anyway.
|
|
1069
|
+
*/
|
|
1070
|
+
export async function rekeyProviderCredentials(
|
|
1071
|
+
from: string,
|
|
1072
|
+
to: string,
|
|
1073
|
+
): Promise<ProviderCredentialRekeyOutcome> {
|
|
1074
|
+
return await mutateStore(store => {
|
|
1075
|
+
const source = store[from];
|
|
1076
|
+
if (!source) return "absent";
|
|
1077
|
+
if (store[to]) return "conflict";
|
|
1078
|
+
store[to] = source;
|
|
1079
|
+
delete store[from];
|
|
1080
|
+
return "moved";
|
|
1081
|
+
}, [from, to]);
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1027
1084
|
export async function markAccountNeedsReauth(
|
|
1028
1085
|
provider: string,
|
|
1029
1086
|
accountId: string,
|
package/src/oauth/types.ts
CHANGED
|
@@ -25,6 +25,35 @@ export interface KiroOAuthMetadata {
|
|
|
25
25
|
authType?: KiroCredentialAuthType;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Account-scoped Muse Code data that is NOT the request bearer.
|
|
30
|
+
*
|
|
31
|
+
* The Model API is authenticated by the `LLM|` key in `access`; this token authenticates
|
|
32
|
+
* the Meta ACCOUNT and exists only to mint that key and to read subscription usage
|
|
33
|
+
* (devlog/_plan/260912_muse_device_oauth/002 A). Keeping it out of `access` is what lets
|
|
34
|
+
* every request path stay unchanged.
|
|
35
|
+
*
|
|
36
|
+
* It must never be added to `OAuthAccountSummary` (src/oauth/index.ts:1803) or to
|
|
37
|
+
* `OAuthAccessSnapshot` (src/oauth/index.ts:85-100). Both are hand-built allowlists, and
|
|
38
|
+
* that construction — not a redactor — is what keeps a secret out of a response.
|
|
39
|
+
*/
|
|
40
|
+
export interface MuseOAuthMetadata {
|
|
41
|
+
/** Meta account access token from the device grant. Never sent to api.meta.ai/v1. */
|
|
42
|
+
oauthAccessToken: string;
|
|
43
|
+
/**
|
|
44
|
+
* The stable Meta account id. Kept HERE rather than in `accountId` on purpose (wp2
|
|
45
|
+
* audit fold W2): the store keys a slot on `accountId ?? email`, and this provider
|
|
46
|
+
* import path has always supplied email only. Promoting `user_id` to `accountId` would
|
|
47
|
+
* make a device login fail to match the row an imported login already created, giving
|
|
48
|
+
* one human two accounts.
|
|
49
|
+
*/
|
|
50
|
+
userId?: string;
|
|
51
|
+
/** Epoch ms of the mint that produced the stored key. */
|
|
52
|
+
mintedAt?: number;
|
|
53
|
+
/** Subscription tier label as Meta reported it. Display only. */
|
|
54
|
+
tierName?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
28
57
|
export type OAuthCredentials = {
|
|
29
58
|
refresh: string;
|
|
30
59
|
access: string;
|
|
@@ -42,6 +71,8 @@ export type OAuthCredentials = {
|
|
|
42
71
|
apiBaseUrl?: string;
|
|
43
72
|
/** Never returned by management APIs; persisted only inside the protected auth-store boundary. */
|
|
44
73
|
kiro?: KiroOAuthMetadata;
|
|
74
|
+
/** Never returned by management APIs; persisted only inside the protected auth-store boundary. */
|
|
75
|
+
muse?: MuseOAuthMetadata;
|
|
45
76
|
};
|
|
46
77
|
|
|
47
78
|
/** One logged-in account inside a provider's account set (multiauth). */
|
package/src/providers/derive.ts
CHANGED
|
@@ -44,6 +44,7 @@ export interface DerivedKeyLoginProvider {
|
|
|
44
44
|
autoToolChoiceOnlyModels?: string[];
|
|
45
45
|
preserveReasoningContentModels?: string[];
|
|
46
46
|
requiresReasoningPlaceholderModels?: string[];
|
|
47
|
+
showThinkingSummary?: boolean;
|
|
47
48
|
reasoningSplitModels?: string[];
|
|
48
49
|
reasoningDetailsModels?: string[];
|
|
49
50
|
thinkingToggleModels?: string[];
|
|
@@ -271,6 +272,7 @@ export function providerConfigSeed(entry: ProviderRegistryEntry): OcxProviderCon
|
|
|
271
272
|
...(entry.autoToolChoiceOnlyModels ? { autoToolChoiceOnlyModels: [...entry.autoToolChoiceOnlyModels] } : {}),
|
|
272
273
|
...(entry.preserveReasoningContentModels ? { preserveReasoningContentModels: [...entry.preserveReasoningContentModels] } : {}),
|
|
273
274
|
...(entry.requiresReasoningPlaceholderModels ? { requiresReasoningPlaceholderModels: [...entry.requiresReasoningPlaceholderModels] } : {}),
|
|
275
|
+
...(entry.showThinkingSummary !== undefined ? { showThinkingSummary: entry.showThinkingSummary } : {}),
|
|
274
276
|
...(entry.reasoningSplitModels ? { reasoningSplitModels: [...entry.reasoningSplitModels] } : {}),
|
|
275
277
|
...(entry.reasoningDetailsModels ? { reasoningDetailsModels: [...entry.reasoningDetailsModels] } : {}),
|
|
276
278
|
...(entry.thinkingToggleModels ? { thinkingToggleModels: [...entry.thinkingToggleModels] } : {}),
|
|
@@ -320,6 +322,7 @@ export function deriveKeyLoginMap(): Record<string, DerivedKeyLoginProvider> {
|
|
|
320
322
|
...(entry.autoToolChoiceOnlyModels ? { autoToolChoiceOnlyModels: [...entry.autoToolChoiceOnlyModels] } : {}),
|
|
321
323
|
...(entry.preserveReasoningContentModels ? { preserveReasoningContentModels: [...entry.preserveReasoningContentModels] } : {}),
|
|
322
324
|
...(entry.requiresReasoningPlaceholderModels ? { requiresReasoningPlaceholderModels: [...entry.requiresReasoningPlaceholderModels] } : {}),
|
|
325
|
+
...(entry.showThinkingSummary !== undefined ? { showThinkingSummary: entry.showThinkingSummary } : {}),
|
|
323
326
|
...(entry.reasoningSplitModels ? { reasoningSplitModels: [...entry.reasoningSplitModels] } : {}),
|
|
324
327
|
...(entry.reasoningDetailsModels ? { reasoningDetailsModels: [...entry.reasoningDetailsModels] } : {}),
|
|
325
328
|
...(entry.thinkingToggleModels ? { thinkingToggleModels: [...entry.thinkingToggleModels] } : {}),
|
|
@@ -441,13 +444,19 @@ function applyVerbosityDefaults(prov: OcxProviderConfig, entry: ProviderRegistry
|
|
|
441
444
|
* was skipped and the reasoning ladder was advertised without summary support — exactly the
|
|
442
445
|
* inconsistency that makes Codex drop the inbound reasoning object.
|
|
443
446
|
*
|
|
444
|
-
* Deliberately narrow:
|
|
447
|
+
* Deliberately narrow: reasoning-summary and effort metadata only, via
|
|
445
448
|
* `registryEntryForProviderDestination`, which matches fixed key destinations and refuses
|
|
446
449
|
* templated or overridable base URLs. A custom row keeps its own identity for everything else.
|
|
447
450
|
*/
|
|
448
|
-
function
|
|
451
|
+
function enrichReasoningMetadataByDestination(prov: OcxProviderConfig): void {
|
|
449
452
|
const destination = registryEntryForProviderDestination(prov);
|
|
450
453
|
applyReasoningSummaryDefaults(prov, destination?.modelSupportsReasoningSummaries);
|
|
454
|
+
if (destination?.modelReasoningEfforts) {
|
|
455
|
+
prov.modelReasoningEfforts = fillRecordOfArrays(destination.modelReasoningEfforts, prov.modelReasoningEfforts);
|
|
456
|
+
}
|
|
457
|
+
if (prov.reasoningEfforts === undefined && destination?.reasoningEfforts !== undefined) {
|
|
458
|
+
prov.reasoningEfforts = [...destination.reasoningEfforts];
|
|
459
|
+
}
|
|
451
460
|
}
|
|
452
461
|
|
|
453
462
|
/** Repair the exact low-only ClinePass ladder generated by older key-login presets. */
|
|
@@ -467,7 +476,7 @@ export function enrichProviderFromRegistry(name: string, prov: OcxProviderConfig
|
|
|
467
476
|
// `registryEntryForProviderDestination` answers the question that actually matters here —
|
|
468
477
|
// which vendor endpoint is this row talking to — and is already restricted to fixed key
|
|
469
478
|
// destinations, so a templated or overridable base URL cannot be claimed by it.
|
|
470
|
-
|
|
479
|
+
enrichReasoningMetadataByDestination(prov);
|
|
471
480
|
applyServiceTierModelDefaults(prov, serviceTierModelDefaultsFor(registryEntryForProviderDestination(prov), prov));
|
|
472
481
|
applyVerbosityDefaults(prov, registryEntryForProviderDestination(prov));
|
|
473
482
|
return;
|
|
@@ -574,6 +583,7 @@ export function enrichProviderFromRegistry(name: string, prov: OcxProviderConfig
|
|
|
574
583
|
if (!prov.thinkingToggleModels && seed.thinkingToggleModels) prov.thinkingToggleModels = [...seed.thinkingToggleModels];
|
|
575
584
|
if (!prov.thinkingBudgetModels && seed.thinkingBudgetModels) prov.thinkingBudgetModels = [...seed.thinkingBudgetModels];
|
|
576
585
|
if (prov.escapeBuiltinToolNames === undefined && seed.escapeBuiltinToolNames !== undefined) prov.escapeBuiltinToolNames = seed.escapeBuiltinToolNames;
|
|
586
|
+
if (prov.showThinkingSummary === undefined && seed.showThinkingSummary !== undefined) prov.showThinkingSummary = seed.showThinkingSummary;
|
|
577
587
|
if (prov.keyOptional === undefined && seed.keyOptional !== undefined) prov.keyOptional = seed.keyOptional;
|
|
578
588
|
if (prov.freeTier === undefined && seed.freeTier !== undefined) prov.freeTier = seed.freeTier;
|
|
579
589
|
if (prov.modelSuffixBracketStrip === undefined && seed.modelSuffixBracketStrip !== undefined) prov.modelSuffixBracketStrip = seed.modelSuffixBracketStrip;
|
|
@@ -1,23 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Repair
|
|
2
|
+
* Repair saved Devin rows written while `devin-cli` was a local ACP provider.
|
|
3
3
|
*
|
|
4
|
-
* `
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* the registry entry is not local. Without this, an existing install can no
|
|
9
|
-
* longer save provider changes from the dashboard.
|
|
4
|
+
* `devin-cli` used to be a local provider whose adapter spawned `devin acp`;
|
|
5
|
+
* it later became an account provider on the shared `devin` adapter, and the
|
|
6
|
+
* registry id itself has since merged into `devin` outright
|
|
7
|
+
* (devlog/_plan/260913_devin_provider_merge).
|
|
10
8
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* Only the adapter repair still lives here. The `authMode` half — rewriting
|
|
10
|
+
* `"local"` to `"oauth"` on the registry-id row — moved into
|
|
11
|
+
* devin-provider-merge-migration.ts, which normalizes the row as it moves the
|
|
12
|
+
* key: the `devin-cli` registry entry is gone, so the PROVIDER_REGISTRY lookup
|
|
13
|
+
* that gated the rewrite here could never fire again.
|
|
13
14
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
15
|
+
* The adapter repair must keep working for CUSTOM-NAMED rows. The ACP adapter
|
|
16
|
+
* was removed, so `"devin-cli"` is not a constructible adapter id and
|
|
17
|
+
* `createRegisteredAdapter` would throw `Unknown adapter: devin-cli`. The
|
|
18
|
+
* canonical `devin` row gets its adapter pinned from the registry by
|
|
19
|
+
* `routedProviderConfig`, but a row named e.g. `"devin-acp"` has nothing
|
|
20
|
+
* pinning it and would fail every request. So every row naming the retired
|
|
21
|
+
* adapter is rewritten to `devin`, whatever the row is called, and each
|
|
22
|
+
* rewrite is reported.
|
|
23
|
+
*
|
|
24
|
+
* A row carrying the retired ACP identity URL is repointed at the api-server
|
|
25
|
+
* in the same pass. That URL was never a destination — it existed only so
|
|
26
|
+
* provider validation would accept an `http(s)` scheme for a child process —
|
|
27
|
+
* so leaving it in place would turn a constructible adapter into a request
|
|
28
|
+
* that cannot resolve a host.
|
|
19
29
|
*/
|
|
20
|
-
import {
|
|
30
|
+
import { DEVIN_DEFAULT_API_SERVER } from "../oauth/devin/api-base";
|
|
21
31
|
import type { OcxConfig } from "../types";
|
|
22
32
|
|
|
23
33
|
export interface DevinCliAuthModeProjection {
|
|
@@ -26,32 +36,44 @@ export interface DevinCliAuthModeProjection {
|
|
|
26
36
|
warnings: string[];
|
|
27
37
|
}
|
|
28
38
|
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
/** The adapter id the removed ACP transport was registered under. */
|
|
40
|
+
const RETIRED_ACP_ADAPTER = "devin-cli";
|
|
41
|
+
/** Identity-only URL the ACP rows carried; never a destination. */
|
|
42
|
+
const RETIRED_ACP_IDENTITY_HOST = "cli.devin.ai";
|
|
43
|
+
|
|
44
|
+
/** Exported for the merge migration, which applies the same rule to the row it moves. */
|
|
45
|
+
export function isRetiredDevinAcpIdentityUrl(baseUrl: string | undefined): boolean {
|
|
46
|
+
if (typeof baseUrl !== "string") return false;
|
|
47
|
+
try {
|
|
48
|
+
return new URL(baseUrl).hostname.toLowerCase() === RETIRED_ACP_IDENTITY_HOST;
|
|
49
|
+
} catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
31
53
|
|
|
32
54
|
export function projectDevinCliAuthMode(config: OcxConfig): DevinCliAuthModeProjection {
|
|
33
55
|
const warnings: string[] = [];
|
|
34
|
-
|
|
35
|
-
if (!prov) return { config, changed: false, warnings };
|
|
36
|
-
|
|
37
|
-
const entry = PROVIDER_REGISTRY.find(row => row.id === "devin-cli");
|
|
38
|
-
if (!entry || entry.authKind !== "oauth") return { config, changed: false, warnings };
|
|
56
|
+
let changed = false;
|
|
39
57
|
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
|
|
58
|
+
// Every row, not only the registry id: a custom-named row had no registry pin,
|
|
59
|
+
// so after the ACP removal it is the one that cannot construct an adapter.
|
|
60
|
+
for (const [name, row] of Object.entries(config.providers ?? {})) {
|
|
61
|
+
if (!row || row.adapter !== RETIRED_ACP_ADAPTER) continue;
|
|
62
|
+
row.adapter = "devin";
|
|
63
|
+
changed = true;
|
|
64
|
+
let detail = "";
|
|
65
|
+
if (isRetiredDevinAcpIdentityUrl(row.baseUrl)) {
|
|
66
|
+
// The shared default, not a second copy of the host: a migration that
|
|
67
|
+
// hardcodes it would keep writing the old address after the default moves.
|
|
68
|
+
row.baseUrl = DEVIN_DEFAULT_API_SERVER;
|
|
69
|
+
detail = ` and repointed its baseUrl at ${DEVIN_DEFAULT_API_SERVER}`;
|
|
70
|
+
}
|
|
43
71
|
warnings.push(
|
|
44
|
-
|
|
45
|
-
+
|
|
72
|
+
`rewrote "${name}" adapter ${RETIRED_ACP_ADAPTER} -> devin${detail}: the local ACP transport `
|
|
73
|
+
+ "was removed, and Devin now streams over Cognition's api-server with the credential the "
|
|
74
|
+
+ "installed CLI already holds.",
|
|
46
75
|
);
|
|
47
76
|
}
|
|
48
77
|
|
|
49
|
-
|
|
50
|
-
prov.authMode = "oauth";
|
|
51
|
-
warnings.push(
|
|
52
|
-
'rewrote "devin-cli" authMode local -> oauth: the registry no longer classifies it as local, '
|
|
53
|
-
+ "and the management write boundary fails closed on the mismatch.",
|
|
54
|
-
);
|
|
55
|
-
return { config, changed: true, warnings };
|
|
78
|
+
return { config, changed, warnings };
|
|
56
79
|
}
|
|
57
|
-
|