@phi-code-admin/phi-code 0.90.0 → 0.91.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/CHANGELOG.md +27 -0
- package/dist/core/provider-display-names.d.ts.map +1 -1
- package/dist/core/provider-display-names.js +3 -0
- package/dist/core/provider-display-names.js.map +1 -1
- package/extensions/phi/keys.ts +36 -6
- package/extensions/phi/providers/provider-bootstrap.ts +98 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.91.0] - 2026-07-11
|
|
4
|
+
|
|
5
|
+
### Added — Alibaba Cloud Coding Plan, first-class and one-command
|
|
6
|
+
|
|
7
|
+
The Alibaba Coding Plan support (endpoints, live catalog, ping, wizard) already
|
|
8
|
+
existed but was only reachable through the interactive `/setup` wizard — a user
|
|
9
|
+
could not simply add the provider. Verified against the official docs
|
|
10
|
+
(https://www.alibabacloud.com/help/en/model-studio/coding-plan) and a live API
|
|
11
|
+
round-trip (catalog listing + a real `qwen3.7-plus` completion).
|
|
12
|
+
|
|
13
|
+
- **`/keys set` now bootstraps known providers.** `providers/provider-bootstrap.ts`
|
|
14
|
+
(pure, 11 tests) builds a complete first-time models.json entry — baseUrl, api
|
|
15
|
+
protocol, bundled model list — from a single API key, with each provider's own
|
|
16
|
+
key-format validation applied up front (`sk-sp-` prefix for Alibaba). One
|
|
17
|
+
command configures the provider end to end:
|
|
18
|
+
`/keys set alibaba-codingplan sk-sp-…` — then the session-start refresh
|
|
19
|
+
replaces the bundled list with the live catalog. Bootstrappable ids:
|
|
20
|
+
`alibaba-codingplan`, `alibaba-codingplan-anthropic`, `opencode-go`,
|
|
21
|
+
`opencode-go-anthropic`. Unknown providers keep the previous /setup guidance.
|
|
22
|
+
- **`/keys test` now actually works.** `registerProviderPing` existed but nothing
|
|
23
|
+
ever called it; keys.ts now registers the built-in pings (Alibaba, OpenCode Go)
|
|
24
|
+
at load, so `/keys test alibaba-codingplan` validates the key against the real
|
|
25
|
+
`/v1/models` endpoint.
|
|
26
|
+
- **Provider display names.** `alibaba-codingplan`, `alibaba-codingplan-anthropic`
|
|
27
|
+
and `opencode-go-anthropic` now render with proper names in the /model picker
|
|
28
|
+
(they fell back to raw ids).
|
|
29
|
+
|
|
3
30
|
## [0.90.0] - 2026-07-11
|
|
4
31
|
|
|
5
32
|
### Added — the execution sandbox (the guaranteed oracle)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider-display-names.d.ts","sourceRoot":"","sources":["../../src/core/provider-display-names.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,+BAA+B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"provider-display-names.d.ts","sourceRoot":"","sources":["../../src/core/provider-display-names.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,+BAA+B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAkClE,CAAC","sourcesContent":["export const BUILT_IN_PROVIDER_DISPLAY_NAMES: Record<string, string> = {\n\t\"alibaba-codingplan\": \"Alibaba Coding Plan\",\n\t\"alibaba-codingplan-anthropic\": \"Alibaba Coding Plan (Anthropic-compat)\",\n\tanthropic: \"Anthropic\",\n\t\"amazon-bedrock\": \"Amazon Bedrock\",\n\t\"azure-openai-responses\": \"Azure OpenAI Responses\",\n\tcerebras: \"Cerebras\",\n\t\"cloudflare-ai-gateway\": \"Cloudflare AI Gateway\",\n\t\"cloudflare-workers-ai\": \"Cloudflare Workers AI\",\n\tdeepseek: \"DeepSeek\",\n\tfireworks: \"Fireworks\",\n\tgoogle: \"Google Gemini\",\n\t\"google-vertex\": \"Google Vertex AI\",\n\tgroq: \"Groq\",\n\thuggingface: \"Hugging Face\",\n\t\"kimi-coding\": \"Kimi For Coding\",\n\tmistral: \"Mistral\",\n\tminimax: \"MiniMax\",\n\t\"minimax-cn\": \"MiniMax (China)\",\n\tmoonshotai: \"Moonshot AI\",\n\t\"moonshotai-cn\": \"Moonshot AI (China)\",\n\topencode: \"OpenCode Zen\",\n\t\"opencode-go\": \"OpenCode Go\",\n\t\"opencode-go-anthropic\": \"OpenCode Go (Anthropic-compat)\",\n\topenai: \"OpenAI\",\n\topenrouter: \"OpenRouter\",\n\ttogether: \"Together AI\",\n\t\"vercel-ai-gateway\": \"Vercel AI Gateway\",\n\txai: \"xAI\",\n\tzai: \"ZAI\",\n\txiaomi: \"Xiaomi MiMo\",\n\t\"xiaomi-token-plan-cn\": \"Xiaomi MiMo Token Plan (China)\",\n\t\"xiaomi-token-plan-ams\": \"Xiaomi MiMo Token Plan (Amsterdam)\",\n\t\"xiaomi-token-plan-sgp\": \"Xiaomi MiMo Token Plan (Singapore)\",\n};\n"]}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export const BUILT_IN_PROVIDER_DISPLAY_NAMES = {
|
|
2
|
+
"alibaba-codingplan": "Alibaba Coding Plan",
|
|
3
|
+
"alibaba-codingplan-anthropic": "Alibaba Coding Plan (Anthropic-compat)",
|
|
2
4
|
anthropic: "Anthropic",
|
|
3
5
|
"amazon-bedrock": "Amazon Bedrock",
|
|
4
6
|
"azure-openai-responses": "Azure OpenAI Responses",
|
|
@@ -19,6 +21,7 @@ export const BUILT_IN_PROVIDER_DISPLAY_NAMES = {
|
|
|
19
21
|
"moonshotai-cn": "Moonshot AI (China)",
|
|
20
22
|
opencode: "OpenCode Zen",
|
|
21
23
|
"opencode-go": "OpenCode Go",
|
|
24
|
+
"opencode-go-anthropic": "OpenCode Go (Anthropic-compat)",
|
|
22
25
|
openai: "OpenAI",
|
|
23
26
|
openrouter: "OpenRouter",
|
|
24
27
|
together: "Together AI",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider-display-names.js","sourceRoot":"","sources":["../../src/core/provider-display-names.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,+BAA+B,GAA2B;IACtE,SAAS,EAAE,WAAW;IACtB,gBAAgB,EAAE,gBAAgB;IAClC,wBAAwB,EAAE,wBAAwB;IAClD,QAAQ,EAAE,UAAU;IACpB,uBAAuB,EAAE,uBAAuB;IAChD,uBAAuB,EAAE,uBAAuB;IAChD,QAAQ,EAAE,UAAU;IACpB,SAAS,EAAE,WAAW;IACtB,MAAM,EAAE,eAAe;IACvB,eAAe,EAAE,kBAAkB;IACnC,IAAI,EAAE,MAAM;IACZ,WAAW,EAAE,cAAc;IAC3B,aAAa,EAAE,iBAAiB;IAChC,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;IAClB,YAAY,EAAE,iBAAiB;IAC/B,UAAU,EAAE,aAAa;IACzB,eAAe,EAAE,qBAAqB;IACtC,QAAQ,EAAE,cAAc;IACxB,aAAa,EAAE,aAAa;IAC5B,MAAM,EAAE,QAAQ;IAChB,UAAU,EAAE,YAAY;IACxB,QAAQ,EAAE,aAAa;IACvB,mBAAmB,EAAE,mBAAmB;IACxC,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,MAAM,EAAE,aAAa;IACrB,sBAAsB,EAAE,gCAAgC;IACxD,uBAAuB,EAAE,oCAAoC;IAC7D,uBAAuB,EAAE,oCAAoC;CAC7D,CAAC","sourcesContent":["export const BUILT_IN_PROVIDER_DISPLAY_NAMES: Record<string, string> = {\n\tanthropic: \"Anthropic\",\n\t\"amazon-bedrock\": \"Amazon Bedrock\",\n\t\"azure-openai-responses\": \"Azure OpenAI Responses\",\n\tcerebras: \"Cerebras\",\n\t\"cloudflare-ai-gateway\": \"Cloudflare AI Gateway\",\n\t\"cloudflare-workers-ai\": \"Cloudflare Workers AI\",\n\tdeepseek: \"DeepSeek\",\n\tfireworks: \"Fireworks\",\n\tgoogle: \"Google Gemini\",\n\t\"google-vertex\": \"Google Vertex AI\",\n\tgroq: \"Groq\",\n\thuggingface: \"Hugging Face\",\n\t\"kimi-coding\": \"Kimi For Coding\",\n\tmistral: \"Mistral\",\n\tminimax: \"MiniMax\",\n\t\"minimax-cn\": \"MiniMax (China)\",\n\tmoonshotai: \"Moonshot AI\",\n\t\"moonshotai-cn\": \"Moonshot AI (China)\",\n\topencode: \"OpenCode Zen\",\n\t\"opencode-go\": \"OpenCode Go\",\n\topenai: \"OpenAI\",\n\topenrouter: \"OpenRouter\",\n\ttogether: \"Together AI\",\n\t\"vercel-ai-gateway\": \"Vercel AI Gateway\",\n\txai: \"xAI\",\n\tzai: \"ZAI\",\n\txiaomi: \"Xiaomi MiMo\",\n\t\"xiaomi-token-plan-cn\": \"Xiaomi MiMo Token Plan (China)\",\n\t\"xiaomi-token-plan-ams\": \"Xiaomi MiMo Token Plan (Amsterdam)\",\n\t\"xiaomi-token-plan-sgp\": \"Xiaomi MiMo Token Plan (Singapore)\",\n};\n"]}
|
|
1
|
+
{"version":3,"file":"provider-display-names.js","sourceRoot":"","sources":["../../src/core/provider-display-names.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,+BAA+B,GAA2B;IACtE,oBAAoB,EAAE,qBAAqB;IAC3C,8BAA8B,EAAE,wCAAwC;IACxE,SAAS,EAAE,WAAW;IACtB,gBAAgB,EAAE,gBAAgB;IAClC,wBAAwB,EAAE,wBAAwB;IAClD,QAAQ,EAAE,UAAU;IACpB,uBAAuB,EAAE,uBAAuB;IAChD,uBAAuB,EAAE,uBAAuB;IAChD,QAAQ,EAAE,UAAU;IACpB,SAAS,EAAE,WAAW;IACtB,MAAM,EAAE,eAAe;IACvB,eAAe,EAAE,kBAAkB;IACnC,IAAI,EAAE,MAAM;IACZ,WAAW,EAAE,cAAc;IAC3B,aAAa,EAAE,iBAAiB;IAChC,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;IAClB,YAAY,EAAE,iBAAiB;IAC/B,UAAU,EAAE,aAAa;IACzB,eAAe,EAAE,qBAAqB;IACtC,QAAQ,EAAE,cAAc;IACxB,aAAa,EAAE,aAAa;IAC5B,uBAAuB,EAAE,gCAAgC;IACzD,MAAM,EAAE,QAAQ;IAChB,UAAU,EAAE,YAAY;IACxB,QAAQ,EAAE,aAAa;IACvB,mBAAmB,EAAE,mBAAmB;IACxC,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,MAAM,EAAE,aAAa;IACrB,sBAAsB,EAAE,gCAAgC;IACxD,uBAAuB,EAAE,oCAAoC;IAC7D,uBAAuB,EAAE,oCAAoC;CAC7D,CAAC","sourcesContent":["export const BUILT_IN_PROVIDER_DISPLAY_NAMES: Record<string, string> = {\n\t\"alibaba-codingplan\": \"Alibaba Coding Plan\",\n\t\"alibaba-codingplan-anthropic\": \"Alibaba Coding Plan (Anthropic-compat)\",\n\tanthropic: \"Anthropic\",\n\t\"amazon-bedrock\": \"Amazon Bedrock\",\n\t\"azure-openai-responses\": \"Azure OpenAI Responses\",\n\tcerebras: \"Cerebras\",\n\t\"cloudflare-ai-gateway\": \"Cloudflare AI Gateway\",\n\t\"cloudflare-workers-ai\": \"Cloudflare Workers AI\",\n\tdeepseek: \"DeepSeek\",\n\tfireworks: \"Fireworks\",\n\tgoogle: \"Google Gemini\",\n\t\"google-vertex\": \"Google Vertex AI\",\n\tgroq: \"Groq\",\n\thuggingface: \"Hugging Face\",\n\t\"kimi-coding\": \"Kimi For Coding\",\n\tmistral: \"Mistral\",\n\tminimax: \"MiniMax\",\n\t\"minimax-cn\": \"MiniMax (China)\",\n\tmoonshotai: \"Moonshot AI\",\n\t\"moonshotai-cn\": \"Moonshot AI (China)\",\n\topencode: \"OpenCode Zen\",\n\t\"opencode-go\": \"OpenCode Go\",\n\t\"opencode-go-anthropic\": \"OpenCode Go (Anthropic-compat)\",\n\topenai: \"OpenAI\",\n\topenrouter: \"OpenRouter\",\n\ttogether: \"Together AI\",\n\t\"vercel-ai-gateway\": \"Vercel AI Gateway\",\n\txai: \"xAI\",\n\tzai: \"ZAI\",\n\txiaomi: \"Xiaomi MiMo\",\n\t\"xiaomi-token-plan-cn\": \"Xiaomi MiMo Token Plan (China)\",\n\t\"xiaomi-token-plan-ams\": \"Xiaomi MiMo Token Plan (Amsterdam)\",\n\t\"xiaomi-token-plan-sgp\": \"Xiaomi MiMo Token Plan (Singapore)\",\n};\n"]}
|
package/extensions/phi/keys.ts
CHANGED
|
@@ -21,6 +21,9 @@
|
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
23
|
import { ApiKeyStore, type ExtensionAPI, getApiKeyStore, getConfigWatcher } from "phi-code";
|
|
24
|
+
import { pingAlibaba } from "./providers/alibaba.js";
|
|
25
|
+
import { pingOpenCodeGo } from "./providers/opencode-go.js";
|
|
26
|
+
import { bootstrapProviderConfig, isBootstrappableProvider } from "./providers/provider-bootstrap.js";
|
|
24
27
|
|
|
25
28
|
type ProviderPingFn = (key: string, timeoutMs?: number) => Promise<{ ok: boolean; error?: string }>;
|
|
26
29
|
|
|
@@ -38,6 +41,13 @@ export default function keysExtension(pi: ExtensionAPI) {
|
|
|
38
41
|
const store = getApiKeyStore();
|
|
39
42
|
const watcher = getConfigWatcher();
|
|
40
43
|
|
|
44
|
+
// Built-in pings so `/keys test <id>` works out of the box for the providers
|
|
45
|
+
// phi ships builders for (previously nothing ever registered a ping).
|
|
46
|
+
registerProviderPing("alibaba-codingplan", pingAlibaba);
|
|
47
|
+
registerProviderPing("alibaba-codingplan-anthropic", pingAlibaba);
|
|
48
|
+
registerProviderPing("opencode-go", pingOpenCodeGo);
|
|
49
|
+
registerProviderPing("opencode-go-anthropic", pingOpenCodeGo);
|
|
50
|
+
|
|
41
51
|
pi.registerCommand("keys", {
|
|
42
52
|
description: "Manage API keys (list / set / remove / test / reload)",
|
|
43
53
|
handler: async (args, ctx) => {
|
|
@@ -75,15 +85,35 @@ export default function keysExtension(pi: ExtensionAPI) {
|
|
|
75
85
|
return;
|
|
76
86
|
}
|
|
77
87
|
// A bare `set` only updates the key; it cannot supply baseUrl/api/models.
|
|
78
|
-
// For a never-configured id
|
|
79
|
-
//
|
|
88
|
+
// For a never-configured id: bootstrap the full entry when phi ships a
|
|
89
|
+
// builder for it (Alibaba Coding Plan, OpenCode Go); otherwise keep the
|
|
90
|
+
// old behaviour and point at /setup.
|
|
80
91
|
const existing = store.getProvider(id);
|
|
81
92
|
if (!existing?.baseUrl) {
|
|
93
|
+
if (!isBootstrappableProvider(id)) {
|
|
94
|
+
ctx.ui.notify(
|
|
95
|
+
`\`${id}\` is not a configured provider (no baseUrl on file). ` +
|
|
96
|
+
`Run \`/setup\` to add it with an endpoint and models first; ` +
|
|
97
|
+
`\`/keys set\` only updates the key of an already-configured provider.`,
|
|
98
|
+
"warning",
|
|
99
|
+
);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const boot = bootstrapProviderConfig(id, key);
|
|
103
|
+
if (!boot.ok) {
|
|
104
|
+
ctx.ui.notify(`Cannot configure \`${id}\`: ${boot.error}`, "warning");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
watcher.muteForWrite("models_json_changed");
|
|
108
|
+
store.setKey(id, key, {
|
|
109
|
+
baseUrl: boot.config.baseUrl,
|
|
110
|
+
api: boot.config.api,
|
|
111
|
+
models: boot.config.models,
|
|
112
|
+
});
|
|
82
113
|
ctx.ui.notify(
|
|
83
|
-
|
|
84
|
-
`
|
|
85
|
-
|
|
86
|
-
"warning",
|
|
114
|
+
`Configured \`${id}\` (\`${ApiKeyStore.maskKey(key)}\`) — ${boot.note}\n` +
|
|
115
|
+
`Stored in ${store.configPath}. Use \`/keys test ${id}\` to validate, then pick a model with \`/model\`.`,
|
|
116
|
+
"info",
|
|
87
117
|
);
|
|
88
118
|
return;
|
|
89
119
|
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider bootstrap — first-time, non-interactive provider configuration.
|
|
3
|
+
*
|
|
4
|
+
* `/keys set <id> <key>` used to refuse a provider that had no models.json
|
|
5
|
+
* entry yet ("run /setup first"), which made adding a known provider (e.g. the
|
|
6
|
+
* Alibaba Coding Plan) impossible outside the interactive wizard. This module
|
|
7
|
+
* closes that gap: for providers phi ships a config builder for, it produces a
|
|
8
|
+
* complete models.json entry (baseUrl + api + model list) from a single API
|
|
9
|
+
* key, with the provider's own key-format validation applied first.
|
|
10
|
+
*
|
|
11
|
+
* Pure (no fs, no network) so every branch is unit-tested; keys.ts is the only
|
|
12
|
+
* call site.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { ALIBABA_PROVIDERS, buildAlibabaProviderConfig, validateAlibabaApiKey } from "./alibaba.js";
|
|
16
|
+
import {
|
|
17
|
+
buildOpenCodeGoAnthropicProviderConfig,
|
|
18
|
+
buildOpenCodeGoProviderConfig,
|
|
19
|
+
OPENCODE_GO_FALLBACK_MODELS,
|
|
20
|
+
type OpenCodeGoModel,
|
|
21
|
+
validateOpenCodeGoApiKey,
|
|
22
|
+
} from "./opencode-go.js";
|
|
23
|
+
|
|
24
|
+
export interface BootstrapModel {
|
|
25
|
+
id: string;
|
|
26
|
+
name: string;
|
|
27
|
+
reasoning: boolean;
|
|
28
|
+
input: readonly ("text" | "image")[];
|
|
29
|
+
contextWindow: number;
|
|
30
|
+
maxTokens: number;
|
|
31
|
+
compat?: Record<string, unknown>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface BootstrapConfig {
|
|
35
|
+
baseUrl: string;
|
|
36
|
+
api: string;
|
|
37
|
+
apiKey: string;
|
|
38
|
+
models: BootstrapModel[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type BootstrapResult = { ok: true; config: BootstrapConfig; note: string } | { ok: false; error: string };
|
|
42
|
+
|
|
43
|
+
/** Provider ids `/keys set` can fully configure from just an API key. */
|
|
44
|
+
export const BOOTSTRAPPABLE_PROVIDERS = [
|
|
45
|
+
"alibaba-codingplan",
|
|
46
|
+
"alibaba-codingplan-anthropic",
|
|
47
|
+
"opencode-go",
|
|
48
|
+
"opencode-go-anthropic",
|
|
49
|
+
] as const;
|
|
50
|
+
|
|
51
|
+
export function isBootstrappableProvider(id: string): boolean {
|
|
52
|
+
return (BOOTSTRAPPABLE_PROVIDERS as readonly string[]).includes(id);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Build a complete first-time provider config from an API key. Validates the
|
|
57
|
+
* key FORMAT (prefix/length) per provider; liveness is `/keys test`'s job.
|
|
58
|
+
* The bundled model list seeds the entry — the session-start refresh and
|
|
59
|
+
* `/models refresh` replace it with the live catalog afterwards.
|
|
60
|
+
*/
|
|
61
|
+
export function bootstrapProviderConfig(id: string, apiKey: string): BootstrapResult {
|
|
62
|
+
const key = apiKey.trim();
|
|
63
|
+
|
|
64
|
+
switch (id) {
|
|
65
|
+
case "alibaba-codingplan":
|
|
66
|
+
case "alibaba-codingplan-anthropic": {
|
|
67
|
+
const invalid = validateAlibabaApiKey(key);
|
|
68
|
+
if (invalid) return { ok: false, error: invalid };
|
|
69
|
+
const variant = id === "alibaba-codingplan-anthropic" ? "anthropic" : "openai";
|
|
70
|
+
const config: BootstrapConfig = buildAlibabaProviderConfig(variant, key);
|
|
71
|
+
return {
|
|
72
|
+
ok: true,
|
|
73
|
+
config,
|
|
74
|
+
note: `${ALIBABA_PROVIDERS[variant].displayName} — ${config.models.length} bundled model(s); a live refresh follows on next session start (or run /models refresh ${id}).`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
case "opencode-go":
|
|
78
|
+
case "opencode-go-anthropic": {
|
|
79
|
+
const invalid = validateOpenCodeGoApiKey(key);
|
|
80
|
+
if (invalid) return { ok: false, error: invalid };
|
|
81
|
+
const models: OpenCodeGoModel[] = [...OPENCODE_GO_FALLBACK_MODELS];
|
|
82
|
+
const config: BootstrapConfig =
|
|
83
|
+
id === "opencode-go-anthropic"
|
|
84
|
+
? buildOpenCodeGoAnthropicProviderConfig(key, models)
|
|
85
|
+
: buildOpenCodeGoProviderConfig(key, models);
|
|
86
|
+
return {
|
|
87
|
+
ok: true,
|
|
88
|
+
config,
|
|
89
|
+
note: `OpenCode Go — ${config.models.length} bundled model(s); a live refresh follows on next session start (or run /models refresh ${id}).`,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
default:
|
|
93
|
+
return {
|
|
94
|
+
ok: false,
|
|
95
|
+
error: `\`${id}\` has no bootstrap builder. Run \`/setup\` to configure it interactively, or add it to ~/.phi/agent/models.json. Bootstrappable ids: ${BOOTSTRAPPABLE_PROVIDERS.join(", ")}.`,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|