fix: count claude-code as configured when the CLI is installed
This commit is contained in:
@@ -43,6 +43,7 @@ import type {
|
|||||||
} from "../core/contracts/models.ts";
|
} from "../core/contracts/models.ts";
|
||||||
import type { EffectiveModelSpec, EffectiveSlot } from "../core/settings-resolution.ts";
|
import type { EffectiveModelSpec, EffectiveSlot } from "../core/settings-resolution.ts";
|
||||||
import { AUTH_PROVIDER_KEYS } from "../core/auth-keys.ts";
|
import { AUTH_PROVIDER_KEYS } from "../core/auth-keys.ts";
|
||||||
|
import { getClaudeBinary } from "../agent/claude-code/binary.ts";
|
||||||
import { CLAUDE_CODE_PROVIDER } from "../agent/claude-code/constants.ts";
|
import { CLAUDE_CODE_PROVIDER } from "../agent/claude-code/constants.ts";
|
||||||
import { BACKGROUND_MODEL_ROLES, MODEL_SLOTS, type BackgroundModelRole, type ModelSlotName, type ModelsSettings } from "../core/settings.ts";
|
import { BACKGROUND_MODEL_ROLES, MODEL_SLOTS, type BackgroundModelRole, type ModelSlotName, type ModelsSettings } from "../core/settings.ts";
|
||||||
|
|
||||||
@@ -135,15 +136,19 @@ export function catalogProviders(): CatalogProvider[] {
|
|||||||
id: p.id,
|
id: p.id,
|
||||||
envVar: p.envVar,
|
envVar: p.envVar,
|
||||||
keyPresent: keyValue(p.envVar) !== undefined,
|
keyPresent: keyValue(p.envVar) !== undefined,
|
||||||
})).concat([{ id: "chatgpt", envVar: "", keyPresent: chatGPTConnected() }]).sort((a, b) => a.id.localeCompare(b.id));
|
}))
|
||||||
|
.concat([
|
||||||
|
{ id: "chatgpt", envVar: "", keyPresent: chatGPTConnected() },
|
||||||
|
{ id: CLAUDE_CODE_PROVIDER, envVar: "", keyPresent: getClaudeBinary() !== null },
|
||||||
|
])
|
||||||
|
.sort((a, b) => a.id.localeCompare(b.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The providers whose key is present — the picker federates exactly
|
/** The providers whose key is present — the picker federates exactly
|
||||||
* these (a keyless provider's models are unreachable by design; the
|
* these (a keyless provider's models are unreachable by design; the
|
||||||
* Keys page is the discovery surface, same discipline as search), plus
|
* Keys page is the discovery surface, same discipline as search). */
|
||||||
* claude-code, which runs on the local CLI's subscription and takes no key. */
|
|
||||||
function keyedProviderIds(): Set<string> {
|
function keyedProviderIds(): Set<string> {
|
||||||
return new Set([...catalogProviders().filter((p) => p.keyPresent).map((p) => p.id), CLAUDE_CODE_PROVIDER]);
|
return new Set(catalogProviders().filter((p) => p.keyPresent).map((p) => p.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Project one catalog model to the client DTO. */
|
/** Project one catalog model to the client DTO. */
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import * as fs from "node:fs";
|
||||||
|
import * as os from "node:os";
|
||||||
|
import * as path from "node:path";
|
||||||
import { describe, it } from "node:test";
|
import { describe, it } from "node:test";
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import {
|
import {
|
||||||
@@ -329,17 +332,61 @@ describe("pi thinking truth (pinned against pi-ai upgrades)", () => {
|
|||||||
assert.throws(() => piEngineFor("prober"), /no configured model for role "prober"/);
|
assert.throws(() => piEngineFor("prober"), /no configured model for role "prober"/);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
/** Runs fn with PATH and HOME pointed at a temp dir, so the CLI lookup only
|
||||||
|
* finds a claude binary when installed is true. */
|
||||||
|
function withClaudeInstalled(installed: boolean, fn: () => void): void {
|
||||||
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "claude-catalog-"));
|
||||||
|
const saved = { PATH: process.env.PATH, HOME: process.env.HOME, npm_config_prefix: process.env.npm_config_prefix };
|
||||||
|
|
||||||
|
if (installed) fs.writeFileSync(path.join(dir, "claude"), "#!/bin/sh\n", { mode: 0o755 });
|
||||||
|
process.env.PATH = dir;
|
||||||
|
process.env.HOME = dir;
|
||||||
|
delete process.env.npm_config_prefix;
|
||||||
|
|
||||||
|
try {
|
||||||
|
fn();
|
||||||
|
} finally {
|
||||||
|
for (const [name, value] of Object.entries(saved)) {
|
||||||
|
if (value === undefined) delete process.env[name];
|
||||||
|
else process.env[name] = value;
|
||||||
|
}
|
||||||
|
fs.rmSync(dir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
describe("the claude-code provider (subscription CLI, no key)", () => {
|
describe("the claude-code provider (subscription CLI, no key)", () => {
|
||||||
it("modelCatalog() lists claude-code opus/sonnet/haiku with no keys saved", () => {
|
it("modelCatalog() lists claude-code opus/sonnet/haiku when the CLI is installed", () => {
|
||||||
|
withClaudeInstalled(true, () => {
|
||||||
const ids = modelCatalog()
|
const ids = modelCatalog()
|
||||||
.models.filter((m) => m.provider === "claude-code")
|
.models.filter((m) => m.provider === "claude-code")
|
||||||
.map((m) => m.id);
|
.map((m) => m.id);
|
||||||
assert.deepEqual(ids, ["haiku", "opus", "sonnet"]);
|
assert.deepEqual(ids, ["haiku", "opus", "sonnet"]);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("catalogContextLimits() has claude-code:opus", () => {
|
it("catalogProviders() marks claude-code present when the CLI is installed", () => {
|
||||||
|
withClaudeInstalled(true, () => {
|
||||||
|
const row = catalogProviders().find((p) => p.id === "claude-code");
|
||||||
|
assert.deepEqual(row, { id: "claude-code", envVar: "", keyPresent: true });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("catalogProviders() marks claude-code absent and modelCatalog() hides it when the CLI is missing", () => {
|
||||||
|
withClaudeInstalled(false, () => {
|
||||||
|
const row = catalogProviders().find((p) => p.id === "claude-code");
|
||||||
|
assert.equal(row?.keyPresent, false);
|
||||||
|
assert.equal(
|
||||||
|
modelCatalog().models.some((m) => m.provider === "claude-code"),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("catalogContextLimits() has claude-code:opus when the CLI is installed", () => {
|
||||||
|
withClaudeInstalled(true, () => {
|
||||||
assert.ok((catalogContextLimits().get("claude-code:opus") ?? 0) > 0);
|
assert.ok((catalogContextLimits().get("claude-code:opus") ?? 0) > 0);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('piEngineForSpec({ provider: "claude-code", id: "sonnet" }) resolves', () => {
|
it('piEngineForSpec({ provider: "claude-code", id: "sonnet" }) resolves', () => {
|
||||||
const engine = piEngineForSpec({ provider: "claude-code", id: "sonnet" });
|
const engine = piEngineForSpec({ provider: "claude-code", id: "sonnet" });
|
||||||
|
|||||||
Reference in New Issue
Block a user