mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 16:59:43 +02:00
fix(custom-model): split the two confirmation questions, and seed the API key the way claude reads it
Two findings from the review of 5fc391a4, both fixed here rather than sent back.
**The API-key trust seed never matched a real key.** `seedApiKeyTrustFile()` wrote the
key verbatim into `customApiKeyResponses.approved`, but Claude Code stores and compares
only the last 20 characters (`key.trim().slice(-20)`, applied on both the write and the
lookup). For any real key the seed missed, so claude stopped at the interactive
"Detected a custom API key in your environment" prompt, whose default is
"No (recommended)": the launch hangs, or silently refuses the key this feature just
injected and falls through to an OAuth login the isolated config dir does not have. It
survived review because a keyless llama.cpp/llama-swap endpoint uses DEFAULT_API_KEY
('local-dummy-key', 15 chars), where slice(-20) returns the whole string and the seed
matches by accident, and every test used a key shorter than that. Now truncated through
`truncateApiKeyForTrustFile()`, with a test using a 57-character key that also asserts
the full credential never reaches that second file.
**One `confirmed` flag answered two different questions.** The context-floor warning
("this model's window is below what this CLI needs") and the swap-conflict warning
("loading this unloads the model another session is using") shared it, and the context
check runs first, so a user clicking "launch anyway" past the context warning silently
consented to evicting someone else's model. They are about different people, so an
answer to one is not consent to the other. Both routes now read `confirmedContext` and
`confirmedSwap` independently; the legacy `confirmed` still means both, because it
shipped in this feature's HTTP-API-only cut and an existing caller must keep working.
The frontend answers each question with its own flag and accumulates them, on the
one-shot path, the restart path and the batch carry-forward alike.
Also from the same review: the swap-confirm dialog no longer renders " are currently
using ..." when multi-user scoping leaves the affected-session list empty (the swap is
blocked regardless of ownership; only the NAMES are scoped), and the per-endpoint
llama-swap log tails are closed in `WebServer.stop()` instead of only by the idle sweep
whose interval that same teardown disposes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+20
-6
@@ -1071,15 +1071,17 @@ export const QuickStartSchema = z.object({
|
||||
* model` does — never trusting raw env values from the client. One-shot, launch-time
|
||||
* equivalent of that route: no restart, so no visible relaunch (that route's restart-in-
|
||||
* place is still what an ALREADY-RUNNING session uses to switch later). Rejected for
|
||||
* remote/docker cases, same reasoning as `envOverrides` above. `confirmed` mirrors that
|
||||
* route's field: skips the llama-swap "this will unload it for another session" check on
|
||||
* a deliberate retry.
|
||||
* remote/docker cases, same reasoning as `envOverrides` above. The three confirmation
|
||||
* flags mirror that route's fields; see `SessionCustomModelSchema` for why there are
|
||||
* two specific ones rather than the single legacy `confirmed`.
|
||||
*/
|
||||
customModel: z
|
||||
.object({
|
||||
endpointId: z.string().regex(/^[a-zA-Z0-9_-]+$/, 'Invalid endpoint id'),
|
||||
modelId: z.string().min(1).max(200),
|
||||
confirmed: z.boolean().optional(),
|
||||
confirmedContext: z.boolean().optional(),
|
||||
confirmedSwap: z.boolean().optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
@@ -2000,10 +2002,22 @@ export const CustomModelSelectionSchema = z.union([
|
||||
z.object({
|
||||
endpointId: z.string().regex(/^[a-zA-Z0-9_-]+$/, 'Invalid endpoint id'),
|
||||
modelId: z.string().min(1).max(200),
|
||||
// Set once the caller has already shown the "this will unload <model> for session(s)
|
||||
// X" warning (see session-routes.ts's llama-swap conflict check) and the user chose to
|
||||
// proceed anyway — skips that check on this call instead of asking again.
|
||||
/**
|
||||
* Two DIFFERENT questions can block a launch, and answering one is not consent to
|
||||
* the other: `confirmedContext` answers "this model's context window is below the
|
||||
* floor for this CLI", which affects only the caller, while `confirmedSwap` answers
|
||||
* "loading this will unload the model another session is using", which affects
|
||||
* someone else. They were one flag until the context check (which runs first)
|
||||
* silently spent the swap answer too, so a user clicking "launch anyway" past a
|
||||
* too-small context evicted another session's model without ever being asked.
|
||||
*
|
||||
* `confirmed` is the original single flag and still means BOTH, because it shipped
|
||||
* in the HTTP-API-only cut of this feature and an existing caller must keep working.
|
||||
* New callers should send the specific one they actually asked about.
|
||||
*/
|
||||
confirmed: z.boolean().optional(),
|
||||
confirmedContext: z.boolean().optional(),
|
||||
confirmedSwap: z.boolean().optional(),
|
||||
}),
|
||||
z.object({ clear: z.literal(true) }),
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user