fix: sync-models will now prompt for API key when there is none, and it won't terminate when there is no opencode.json
This commit is contained in:
parent
ac29d26e12
commit
78a4f42bc0
@ -99,6 +99,11 @@ without writing. Each row shows the model id and name plus a second line with it
|
|||||||
(`ctx 1M (1000000)`); the catalog carries no pricing fields, so no cost is shown. Needs an
|
(`ctx 1M (1000000)`); the catalog carries no pricing fields, so no cost is shown. Needs an
|
||||||
interactive terminal.
|
interactive terminal.
|
||||||
|
|
||||||
|
Before anything else the script requires a CommandCode API key (from
|
||||||
|
`provider.commandcode.options.apiKey` or `COMMANDCODE_API_KEY`). If neither is present — including
|
||||||
|
when `opencode.json` does not exist yet — it exits and points you at the CommandCode Console
|
||||||
|
(<https://commandcode.ai/studio/provider>) to generate one. It never writes without a key.
|
||||||
|
|
||||||
Then restart opencode and confirm:
|
Then restart opencode and confirm:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
|
|||||||
@ -7,15 +7,16 @@
|
|||||||
// the result into the global opencode config. Deselecting a model removes it from the config.
|
// the result into the global opencode config. Deselecting a model removes it from the config.
|
||||||
//
|
//
|
||||||
// Run: node scripts/sync-models.mjs (build first: npm run build)
|
// Run: node scripts/sync-models.mjs (build first: npm run build)
|
||||||
import { readFileSync, writeFileSync, renameSync } from "node:fs";
|
import { mkdirSync, readFileSync, writeFileSync, renameSync } from "node:fs";
|
||||||
import { homedir } from "node:os";
|
import { homedir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { dirname, join } from "node:path";
|
||||||
import readline from "node:readline";
|
import readline from "node:readline";
|
||||||
import { createCommandCode } from "../dist/index.js";
|
import { createCommandCode } from "../dist/index.js";
|
||||||
import { redact } from "../dist/redact.js";
|
import { redact } from "../dist/redact.js";
|
||||||
import { DEFAULT_BASE_URL, DEFAULT_CC_VERSION, DEFAULT_MAX_TOKENS, MODELS_PATH } from "../dist/constants.js";
|
import { DEFAULT_BASE_URL, DEFAULT_CC_VERSION, DEFAULT_MAX_TOKENS, MODELS_PATH } from "../dist/constants.js";
|
||||||
|
|
||||||
const CONFIG_PATH = join(homedir(), ".config", "opencode", "opencode.json");
|
const CONFIG_PATH = join(homedir(), ".config", "opencode", "opencode.json");
|
||||||
|
const CONSOLE_URL = "https://commandcode.ai/studio/provider";
|
||||||
const CONCURRENCY = 6;
|
const CONCURRENCY = 6;
|
||||||
|
|
||||||
// 64x64 solid red PNG, used only to provoke a vision-accepting vs vision-rejecting response.
|
// 64x64 solid red PNG, used only to provoke a vision-accepting vs vision-rejecting response.
|
||||||
@ -36,8 +37,15 @@ const NO_IMAGE =
|
|||||||
const VISION_HEURISTIC = /claude|gpt-5|gemini|grok|qwen.*vl|vision|(?:^|[^a-z])vl(?:[^a-z]|$)|omni|multimodal/i;
|
const VISION_HEURISTIC = /claude|gpt-5|gemini|grok|qwen.*vl|vision|(?:^|[^a-z])vl(?:[^a-z]|$)|omni|multimodal/i;
|
||||||
|
|
||||||
function readConfig(path) {
|
function readConfig(path) {
|
||||||
|
try {
|
||||||
const text = readFileSync(path, "utf8");
|
const text = readFileSync(path, "utf8");
|
||||||
return { text, data: JSON.parse(text) };
|
return { text, data: JSON.parse(text), exists: true };
|
||||||
|
} catch (error) {
|
||||||
|
// A fresh install has no opencode.json yet; treat it as empty so the console
|
||||||
|
// guidance / env-key path can still run. Bad JSON or unreadable files still throw.
|
||||||
|
if (error.code === "ENOENT") return { text: "", data: {}, exists: false };
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function detectIndent(text) {
|
function detectIndent(text) {
|
||||||
@ -360,23 +368,36 @@ function pickModels(catalog, existingIds) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
||||||
console.error("Interactive picker needs a TTY; nothing was written.");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let config;
|
let config;
|
||||||
let text;
|
let text;
|
||||||
|
let exists;
|
||||||
try {
|
try {
|
||||||
({ text, data: config } = readConfig(CONFIG_PATH));
|
({ text, data: config, exists } = readConfig(CONFIG_PATH));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Cannot read ${CONFIG_PATH}: ${error instanceof Error ? error.message : String(error)}`);
|
console.error(`Cannot read ${CONFIG_PATH}: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mandatory first step: without a CommandCode key there is nothing to fetch or
|
||||||
|
// probe, so point a fresh user at the Console before doing anything else.
|
||||||
const key = resolveKey(config, process.env);
|
const key = resolveKey(config, process.env);
|
||||||
if (!key) {
|
if (!key) {
|
||||||
console.error("No API key: set COMMANDCODE_API_KEY or provider.commandcode.options.apiKey");
|
const where = exists
|
||||||
|
? `No CommandCode API key in ${CONFIG_PATH}.`
|
||||||
|
: `No opencode.json found at ${CONFIG_PATH}.`;
|
||||||
|
console.error(
|
||||||
|
`${where}\n` +
|
||||||
|
`Generate an API key in the CommandCode Console: ${CONSOLE_URL}\n` +
|
||||||
|
(exists
|
||||||
|
? `Add it as provider.commandcode.options.apiKey, or export COMMANDCODE_API_KEY.`
|
||||||
|
: `Create that file with provider.commandcode.options.apiKey, or export COMMANDCODE_API_KEY.`) +
|
||||||
|
`\nNothing was written.`,
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
||||||
|
console.error("Interactive picker needs a TTY; nothing was written.");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,7 +468,9 @@ async function main() {
|
|||||||
config.provider.commandcode.models = merged;
|
config.provider.commandcode.models = merged;
|
||||||
|
|
||||||
const indent = detectIndent(text);
|
const indent = detectIndent(text);
|
||||||
const trailing = text.endsWith("\n") ? "\n" : "";
|
const trailing = text === "" || text.endsWith("\n") ? "\n" : "";
|
||||||
|
// The config directory may not exist yet on a fresh install; create it before the atomic write.
|
||||||
|
mkdirSync(dirname(CONFIG_PATH), { recursive: true });
|
||||||
writeAtomic(CONFIG_PATH, `${JSON.stringify(config, null, indent)}${trailing}`);
|
writeAtomic(CONFIG_PATH, `${JSON.stringify(config, null, indent)}${trailing}`);
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user