opencode-resolve 0.1.13 → 0.1.17
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/README.ko.md +216 -850
- package/README.md +215 -849
- package/dist/agents.js +14 -14
- package/dist/config.d.ts +1 -0
- package/dist/config.js +10 -0
- package/dist/hooks/index.js +131 -40
- package/dist/index.js +11 -0
- package/dist/messages.d.ts +34 -0
- package/dist/messages.js +867 -0
- package/dist/state.d.ts +3 -0
- package/dist/state.js +2 -1
- package/dist/tools/index.d.ts +5 -0
- package/dist/tools/index.js +20 -1
- package/dist/types.d.ts +2 -0
- package/dist/utils.js +20 -3
- package/opencode-resolve.example.json +1 -0
- package/opencode-resolve.reference.jsonc +20 -3
- package/package.json +5 -2
- package/scripts/cli.mjs +75 -0
- package/scripts/install-local.mjs +33 -7
- package/scripts/postinstall.mjs +215 -22
package/scripts/postinstall.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawn } from "node:child_process"
|
|
2
2
|
import { constants } from "node:fs"
|
|
3
|
-
import { access, mkdir, readFile, writeFile } from "node:fs/promises"
|
|
3
|
+
import { access, mkdir, readFile, rm, writeFile } from "node:fs/promises"
|
|
4
4
|
import { homedir } from "node:os"
|
|
5
5
|
import { dirname, join, resolve } from "node:path"
|
|
6
6
|
import { createInterface } from "node:readline/promises"
|
|
@@ -9,14 +9,21 @@ import { fileURLToPath } from "node:url"
|
|
|
9
9
|
const packageName = "opencode-resolve"
|
|
10
10
|
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..")
|
|
11
11
|
const configDir = process.env.OPENCODE_CONFIG_HOME || join(homedir(), ".config", "opencode")
|
|
12
|
+
const cacheDir = process.env.OPENCODE_CACHE_HOME || join(homedir(), ".cache", "opencode")
|
|
12
13
|
const opencodeConfigPath = join(configDir, "opencode.json")
|
|
13
14
|
const resolveConfigPath = join(configDir, "resolve.json")
|
|
14
15
|
const exampleConfigPath = join(root, "opencode-resolve.example.json")
|
|
16
|
+
const selfPluginCachePath = join(cacheDir, "packages", `${packageName}@latest`)
|
|
17
|
+
const selfPluginCachedPackageJson = join(selfPluginCachePath, "node_modules", packageName, "package.json")
|
|
15
18
|
|
|
16
19
|
const ADDITIVE_DEFAULTS = {
|
|
17
20
|
autoApprove: true,
|
|
18
21
|
}
|
|
19
22
|
|
|
23
|
+
const DEFAULT_ENABLED_AGENTS = ["coder", "resolver", "explorer", "reviewer", "deep-reviewer", "planner"]
|
|
24
|
+
const GPT_ENABLED_AGENTS = ["coder", "resolver", "gpt", "explorer", "reviewer", "deep-reviewer", "planner"]
|
|
25
|
+
const GLM_ENABLED_AGENTS = ["coder", "resolver", "glm", "explorer", "reviewer", "planner"]
|
|
26
|
+
|
|
20
27
|
// ZAI local MCP server bootstrap for GLM users.
|
|
21
28
|
// Do not copy provider secrets from OpenCode's auth store into opencode.json.
|
|
22
29
|
// The MCP process should receive credentials from the user's runtime environment.
|
|
@@ -53,16 +60,21 @@ const OPENAI_MODEL_HINTS = [
|
|
|
53
60
|
|
|
54
61
|
const GLM_MODEL_HINTS = [
|
|
55
62
|
"zai-coding-plan/glm-5.1",
|
|
63
|
+
"zai-coding-plan/glm-4.5",
|
|
64
|
+
"zai-coding-plan/glm-4.5-air",
|
|
56
65
|
"zai-coding-plan/glm-5",
|
|
57
|
-
"zai-coding-plan/glm-4.7
|
|
58
|
-
"zai-coding-plan/glm-4.5-flash",
|
|
66
|
+
"zai-coding-plan/glm-4.7",
|
|
59
67
|
"zai/glm-5.1",
|
|
68
|
+
"zai/glm-4.5",
|
|
69
|
+
"zai/glm-4.5-air",
|
|
60
70
|
"zai/glm-5",
|
|
61
|
-
"zai/glm-4.7-flash",
|
|
62
71
|
"zai/glm-4.7",
|
|
72
|
+
"zai-coding-plan/glm-4.7-flashx",
|
|
73
|
+
"zai/glm-4.7-flashx",
|
|
74
|
+
"zai-coding-plan/glm-4.5-flash",
|
|
63
75
|
"zai/glm-4.5-flash",
|
|
64
|
-
"zai/glm-4.
|
|
65
|
-
"zai/glm-4.
|
|
76
|
+
"zai-coding-plan/glm-4.7-flash",
|
|
77
|
+
"zai/glm-4.7-flash",
|
|
66
78
|
]
|
|
67
79
|
|
|
68
80
|
if (process.env.OPENCODE_RESOLVE_SKIP_POSTINSTALL === "1") {
|
|
@@ -74,6 +86,7 @@ console.log(`[${packageName}] installing v${pluginVersion}`)
|
|
|
74
86
|
|
|
75
87
|
try {
|
|
76
88
|
await registerPlugin()
|
|
89
|
+
await refreshSelfPluginCache(pluginVersion)
|
|
77
90
|
await offerCompanionPlugins()
|
|
78
91
|
console.log(`[${packageName}] v${pluginVersion} install complete — restart OpenCode to load the plugin`)
|
|
79
92
|
} catch (error) {
|
|
@@ -121,6 +134,68 @@ async function registerPlugin() {
|
|
|
121
134
|
await handleExistingResolveConfig(probe, scriptedAnswers)
|
|
122
135
|
}
|
|
123
136
|
|
|
137
|
+
async function refreshSelfPluginCache(expectedVersion) {
|
|
138
|
+
if (process.env.OPENCODE_RESOLVE_SKIP_CACHE_REFRESH === "1") return
|
|
139
|
+
if (process.env.OPENCODE_RESOLVE_REFRESHING_CACHE === "1") return
|
|
140
|
+
|
|
141
|
+
const forceRefresh = readInstallerOption("force_cache_refresh") === "1"
|
|
142
|
+
const cachedVersion = await readCachedSelfVersion()
|
|
143
|
+
if (!forceRefresh && cachedVersion === expectedVersion) {
|
|
144
|
+
console.log(`[${packageName}] OpenCode plugin cache already at v${expectedVersion}`)
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (forceRefresh && cachedVersion === expectedVersion) {
|
|
149
|
+
console.log(`[${packageName}] forcing OpenCode plugin cache refresh at v${expectedVersion}`)
|
|
150
|
+
} else if (cachedVersion) {
|
|
151
|
+
console.log(`[${packageName}] stale OpenCode plugin cache detected: v${cachedVersion} -> v${expectedVersion}`)
|
|
152
|
+
} else {
|
|
153
|
+
console.log(`[${packageName}] OpenCode plugin cache missing; refreshing cache`)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
await rm(selfPluginCachePath, { recursive: true, force: true })
|
|
157
|
+
const refreshed = await runOpenCodePluginInstall()
|
|
158
|
+
if (!refreshed) {
|
|
159
|
+
console.warn(`[${packageName}] could not refresh OpenCode plugin cache automatically`)
|
|
160
|
+
console.warn(`[${packageName}] run manually: opencode plugin ${packageName} --global --force`)
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const nextVersion = await readCachedSelfVersion()
|
|
165
|
+
if (nextVersion && nextVersion !== expectedVersion) {
|
|
166
|
+
console.warn(`[${packageName}] OpenCode plugin cache refreshed but still reports v${nextVersion}; expected v${expectedVersion}`)
|
|
167
|
+
return
|
|
168
|
+
}
|
|
169
|
+
console.log(`[${packageName}] OpenCode plugin cache refreshed to v${nextVersion ?? expectedVersion}`)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async function readCachedSelfVersion() {
|
|
173
|
+
try {
|
|
174
|
+
const raw = await readFile(selfPluginCachedPackageJson, "utf8")
|
|
175
|
+
const parsed = JSON.parse(raw)
|
|
176
|
+
return typeof parsed?.version === "string" ? parsed.version : undefined
|
|
177
|
+
} catch (error) {
|
|
178
|
+
if (isMissingFileError(error)) return undefined
|
|
179
|
+
return undefined
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async function runOpenCodePluginInstall() {
|
|
184
|
+
return new Promise((resolveSpawn) => {
|
|
185
|
+
const child = spawn("opencode", ["plugin", packageName, "--global", "--force"], {
|
|
186
|
+
stdio: "ignore",
|
|
187
|
+
env: {
|
|
188
|
+
...process.env,
|
|
189
|
+
OPENCODE_RESOLVE_REFRESHING_CACHE: "1",
|
|
190
|
+
OPENCODE_RESOLVE_SKIP_POSTINSTALL: "1",
|
|
191
|
+
OPENCODE_RESOLVE_SKIP_COMPANIONS: "1",
|
|
192
|
+
},
|
|
193
|
+
})
|
|
194
|
+
child.on("exit", (code) => resolveSpawn(code === 0))
|
|
195
|
+
child.on("error", () => resolveSpawn(false))
|
|
196
|
+
})
|
|
197
|
+
}
|
|
198
|
+
|
|
124
199
|
function isPluginRegisteredIn(config) {
|
|
125
200
|
return Array.isArray(config.plugin) && config.plugin.some(isRegisteredPluginEntry)
|
|
126
201
|
}
|
|
@@ -149,8 +224,18 @@ function applyMCPPatches(config, names) {
|
|
|
149
224
|
async function handleExistingResolveConfig(opencodeConfig, scriptedAnswers) {
|
|
150
225
|
const action = await chooseExistingResolveConfigAction(scriptedAnswers)
|
|
151
226
|
if (action === "fresh") {
|
|
227
|
+
const existing = await readExistingResolveConfig()
|
|
152
228
|
await backupResolveConfig()
|
|
153
|
-
|
|
229
|
+
const preserveModels = readInstallerOption("reset_models") !== "1"
|
|
230
|
+
await createAdaptiveResolveConfig(opencodeConfig, scriptedAnswers, {
|
|
231
|
+
preservedModels: preserveModels ? existing?.models : undefined,
|
|
232
|
+
})
|
|
233
|
+
return
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (action === "models") {
|
|
237
|
+
await backupResolveConfig()
|
|
238
|
+
await reconfigureExistingModels(opencodeConfig, scriptedAnswers)
|
|
154
239
|
return
|
|
155
240
|
}
|
|
156
241
|
|
|
@@ -158,18 +243,20 @@ async function handleExistingResolveConfig(opencodeConfig, scriptedAnswers) {
|
|
|
158
243
|
}
|
|
159
244
|
|
|
160
245
|
async function chooseExistingResolveConfigAction(scriptedAnswers) {
|
|
161
|
-
|
|
246
|
+
if (readInstallerOption("configure_models") === "1") return "models"
|
|
247
|
+
const requested = readInstallerOption("reinstall").trim().toLowerCase()
|
|
162
248
|
if (["fresh", "reset", "recreate", "new"].includes(requested)) return "fresh"
|
|
163
249
|
if (["update", "keep", "migrate", "preserve"].includes(requested)) return "update"
|
|
164
250
|
if (requested) {
|
|
165
|
-
console.warn(`[${packageName}] ignoring unknown
|
|
251
|
+
console.warn(`[${packageName}] ignoring unknown reinstall mode ${JSON.stringify(requested)}; use "fresh" or "update".`)
|
|
166
252
|
}
|
|
167
253
|
|
|
168
|
-
const forcePrompt =
|
|
254
|
+
const forcePrompt = readInstallerOption("force_prompt") === "1"
|
|
169
255
|
const canPrompt = Boolean((process.stdin.isTTY && process.stdout.isTTY) || forcePrompt)
|
|
170
256
|
if (!canPrompt) {
|
|
171
257
|
console.log(`[${packageName}] existing ${resolveConfigPath} found; preserving it and applying additive updates.`)
|
|
172
|
-
console.log(`[${packageName}] for
|
|
258
|
+
console.log(`[${packageName}] for model setup, run: ${packageName} setup --models`)
|
|
259
|
+
console.log(`[${packageName}] to force plugin cache reinstall without touching settings, run: ${packageName} setup --force-cache`)
|
|
173
260
|
return "update"
|
|
174
261
|
}
|
|
175
262
|
|
|
@@ -178,14 +265,26 @@ async function chooseExistingResolveConfigAction(scriptedAnswers) {
|
|
|
178
265
|
console.log("")
|
|
179
266
|
console.log(`[${packageName}] Existing resolve config found: ${resolveConfigPath}`)
|
|
180
267
|
console.log(" 1. update existing config — preserve your settings and add missing defaults")
|
|
181
|
-
console.log(" 2.
|
|
182
|
-
|
|
183
|
-
|
|
268
|
+
console.log(" 2. reconfigure models — preserve the rest of resolve.json")
|
|
269
|
+
console.log(" 3. fresh reinstall — back up resolve.json and run setup again, preserving model pins")
|
|
270
|
+
const answer = await askChoice(rl, "Existing config [1=update, 2=models, 3=fresh, default 1]: ", ["1", "2", "3"], "1")
|
|
271
|
+
if (answer === "2") return "models"
|
|
272
|
+
return answer === "3" ? "fresh" : "update"
|
|
184
273
|
} finally {
|
|
185
274
|
rl.close()
|
|
186
275
|
}
|
|
187
276
|
}
|
|
188
277
|
|
|
278
|
+
async function readExistingResolveConfig() {
|
|
279
|
+
try {
|
|
280
|
+
const raw = await readFile(resolveConfigPath, "utf8")
|
|
281
|
+
const parsed = JSON.parse(raw)
|
|
282
|
+
return isObject(parsed) ? parsed : undefined
|
|
283
|
+
} catch {
|
|
284
|
+
return undefined
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
189
288
|
async function backupResolveConfig() {
|
|
190
289
|
const raw = await readFile(resolveConfigPath, "utf8")
|
|
191
290
|
const stamp = new Date().toISOString().replace(/[:.]/g, "-")
|
|
@@ -194,15 +293,16 @@ async function backupResolveConfig() {
|
|
|
194
293
|
console.log(`[${packageName}] backed up existing resolve config to ${backupPath}`)
|
|
195
294
|
}
|
|
196
295
|
|
|
197
|
-
async function createAdaptiveResolveConfig(opencodeConfig, scriptedAnswers) {
|
|
296
|
+
async function createAdaptiveResolveConfig(opencodeConfig, scriptedAnswers, options = {}) {
|
|
198
297
|
await assertReadable(exampleConfigPath)
|
|
199
298
|
const raw = await readFile(exampleConfigPath, "utf8")
|
|
200
299
|
const example = JSON.parse(raw)
|
|
300
|
+
const preservedModels = isObject(options.preservedModels) ? options.preservedModels : undefined
|
|
201
301
|
|
|
202
302
|
const currentModel = detectOpenCodeModel(opencodeConfig)
|
|
203
303
|
const allModels = detectAllModels(opencodeConfig)
|
|
204
304
|
const resolveConfig = { ...example }
|
|
205
|
-
const forcePrompt =
|
|
305
|
+
const forcePrompt = readInstallerOption("force_prompt") === "1"
|
|
206
306
|
const canPrompt = Boolean(
|
|
207
307
|
(process.stdin.isTTY && process.stdout.isTTY) || forcePrompt,
|
|
208
308
|
)
|
|
@@ -215,7 +315,7 @@ async function createAdaptiveResolveConfig(opencodeConfig, scriptedAnswers) {
|
|
|
215
315
|
if (interactivePreset.tier) resolveConfig.tier = interactivePreset.tier
|
|
216
316
|
else delete resolveConfig.tier
|
|
217
317
|
if (interactivePreset.enabled) resolveConfig.enabled = interactivePreset.enabled
|
|
218
|
-
resolveConfig.models = interactivePreset.models
|
|
318
|
+
resolveConfig.models = mergePreservedModels(interactivePreset.models, preservedModels)
|
|
219
319
|
resolveConfig.agents = {
|
|
220
320
|
...resolveConfig.agents,
|
|
221
321
|
...(interactivePreset.agents ?? {}),
|
|
@@ -255,7 +355,9 @@ async function createAdaptiveResolveConfig(opencodeConfig, scriptedAnswers) {
|
|
|
255
355
|
}
|
|
256
356
|
|
|
257
357
|
if (preset && Object.keys(preset).length > 0) {
|
|
258
|
-
resolveConfig.models = preset
|
|
358
|
+
resolveConfig.models = mergePreservedModels(preset, preservedModels)
|
|
359
|
+
} else if (preservedModels) {
|
|
360
|
+
resolveConfig.models = { ...preservedModels }
|
|
259
361
|
} else {
|
|
260
362
|
console.log(`[${packageName}] no GPT/GLM models detected in opencode.json — agents inherit the top-level model`)
|
|
261
363
|
console.log(`[${packageName}] to configure model pinning, rerun setup in a TTY or edit ${resolveConfigPath}`)
|
|
@@ -267,6 +369,59 @@ async function createAdaptiveResolveConfig(opencodeConfig, scriptedAnswers) {
|
|
|
267
369
|
console.log(`[${packageName}] created ${resolveConfigPath} (preset: ${label})`)
|
|
268
370
|
}
|
|
269
371
|
|
|
372
|
+
async function reconfigureExistingModels(opencodeConfig, scriptedAnswers) {
|
|
373
|
+
const existing = await readExistingResolveConfig()
|
|
374
|
+
if (!existing) {
|
|
375
|
+
await createAdaptiveResolveConfig(opencodeConfig, scriptedAnswers)
|
|
376
|
+
return
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const currentModel = detectOpenCodeModel(opencodeConfig)
|
|
380
|
+
const allModels = detectAllModels(opencodeConfig)
|
|
381
|
+
const forcePrompt = readInstallerOption("force_prompt") === "1"
|
|
382
|
+
const canPrompt = Boolean((process.stdin.isTTY && process.stdout.isTTY) || forcePrompt)
|
|
383
|
+
const interactivePreset = canPrompt
|
|
384
|
+
? await buildInteractivePreset(currentModel, allModels, scriptedAnswers)
|
|
385
|
+
: undefined
|
|
386
|
+
const preset = interactivePreset ?? {
|
|
387
|
+
label: getPresetLabel(currentModel),
|
|
388
|
+
profile: inferProfileFromModels(currentModel, allModels),
|
|
389
|
+
models: buildModelPreset(currentModel, allModels),
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (!preset.models || Object.keys(preset.models).length === 0) {
|
|
393
|
+
console.log(`[${packageName}] no GPT/GLM models detected; existing model pins preserved`)
|
|
394
|
+
return
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const updated = { ...existing }
|
|
398
|
+
updated.profile = preset.profile
|
|
399
|
+
if (preset.tier) updated.tier = preset.tier
|
|
400
|
+
else delete updated.tier
|
|
401
|
+
if (preset.enabled) updated.enabled = preset.enabled
|
|
402
|
+
updated.models = preset.models
|
|
403
|
+
updated.agents = {
|
|
404
|
+
...(updated.agents ?? {}),
|
|
405
|
+
...(preset.agents ?? {}),
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
await writeFile(resolveConfigPath, `${JSON.stringify(updated, null, 2)}\n`)
|
|
409
|
+
console.log(`[${packageName}] updated model pins in ${resolveConfigPath} (preset: ${preset.label})`)
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function mergePreservedModels(generated, preserved) {
|
|
413
|
+
if (!preserved) return generated
|
|
414
|
+
return { ...generated, ...preserved }
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function inferProfileFromModels(currentModel, allModels) {
|
|
418
|
+
const hasGLM = allModels.some((m) => isGLMModel(m)) || isGLMModel(currentModel)
|
|
419
|
+
const hasGPT = allModels.some((m) => isGPTModel(m)) || isGPTModel(currentModel)
|
|
420
|
+
if (hasGLM && !hasGPT) return "glm"
|
|
421
|
+
if (hasGPT && !hasGLM) return "gpt"
|
|
422
|
+
return "mix"
|
|
423
|
+
}
|
|
424
|
+
|
|
270
425
|
function detectOpenCodeModel(config) {
|
|
271
426
|
// Prefer top-level `model` as primary signal
|
|
272
427
|
if (typeof config.model === "string" && config.model.length > 0) {
|
|
@@ -421,6 +576,7 @@ async function buildInteractivePreset(currentModel, allModels, scriptedAnswers)
|
|
|
421
576
|
label: "gpt-three-tier",
|
|
422
577
|
profile: "gpt",
|
|
423
578
|
tier: "gold",
|
|
579
|
+
enabled: GPT_ENABLED_AGENTS,
|
|
424
580
|
models: buildGPTThreeTierModels(tiers),
|
|
425
581
|
agents: { gpt: { enabled: true } },
|
|
426
582
|
}
|
|
@@ -437,6 +593,7 @@ async function buildInteractivePreset(currentModel, allModels, scriptedAnswers)
|
|
|
437
593
|
label: "glm-three-tier",
|
|
438
594
|
profile: "glm",
|
|
439
595
|
tier: "gold",
|
|
596
|
+
enabled: GLM_ENABLED_AGENTS,
|
|
440
597
|
models: buildGLMThreeTierModels(tiers),
|
|
441
598
|
agents: { glm: { enabled: true } },
|
|
442
599
|
}
|
|
@@ -449,6 +606,11 @@ async function buildInteractivePreset(currentModel, allModels, scriptedAnswers)
|
|
|
449
606
|
return {
|
|
450
607
|
label: "mix-three-tier",
|
|
451
608
|
profile: "mix",
|
|
609
|
+
enabled: unique([
|
|
610
|
+
...DEFAULT_ENABLED_AGENTS,
|
|
611
|
+
...(useGPT ? ["gpt"] : []),
|
|
612
|
+
...(useGLM ? ["glm"] : []),
|
|
613
|
+
]),
|
|
452
614
|
models: buildMixedThreeTierModels(gptTiers, glmTiers),
|
|
453
615
|
agents: {
|
|
454
616
|
gpt: { enabled: useGPT },
|
|
@@ -576,19 +738,40 @@ function collectModelChoices(allModels, predicate, hints, includeFallbackHints =
|
|
|
576
738
|
const matchingHints = includeFallbackHints
|
|
577
739
|
? hints.filter((model) => providerIds.size === 0 || providerIds.has(model.split("/")[0]) || detected.length < 3)
|
|
578
740
|
: []
|
|
579
|
-
|
|
741
|
+
const choices = unique([...detected, ...matchingHints])
|
|
742
|
+
return predicate === isGLMModel ? sortGLMModelChoices(choices) : choices
|
|
580
743
|
}
|
|
581
744
|
|
|
582
745
|
function chooseThreeTier(models, family, includeFallbackHints = true) {
|
|
583
746
|
const fallback = family === "glm" ? GLM_MODEL_HINTS : OPENAI_MODEL_HINTS
|
|
584
747
|
const choices = unique(includeFallbackHints ? [...models, ...fallback] : models)
|
|
585
748
|
return {
|
|
586
|
-
bronze: preferModel(choices, family === "glm" ? ["
|
|
587
|
-
silver: preferModel(choices, family === "glm" ? ["
|
|
749
|
+
bronze: preferModel(choices, family === "glm" ? ["5.1", "4.5", "5"] : ["spark", "mini", "4o-mini"], choices[0]),
|
|
750
|
+
silver: preferModel(choices, family === "glm" ? ["5.1", "4.5", "5"] : ["codex", "5.3", "5.2"], choices[1] ?? choices[0]),
|
|
588
751
|
gold: preferModel(choices, family === "glm" ? ["5.1", "5", "4.5"] : ["5.5", "5.4", "gpt-5.3-codex"], choices[2] ?? choices[1] ?? choices[0]),
|
|
589
752
|
}
|
|
590
753
|
}
|
|
591
754
|
|
|
755
|
+
function sortGLMModelChoices(models) {
|
|
756
|
+
return [...models].sort((a, b) => rankGLMModel(a) - rankGLMModel(b))
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
function rankGLMModel(model) {
|
|
760
|
+
const lower = model.toLowerCase()
|
|
761
|
+
if (lower.includes("5.1")) return 0
|
|
762
|
+
if (lower.includes("4.5") && !lower.includes("air") && !lower.includes("flash")) return 1
|
|
763
|
+
if (lower.includes("4.5-airx")) return 2
|
|
764
|
+
if (lower.includes("4.5-air")) return 3
|
|
765
|
+
if (/\bglm-5\b/.test(lower)) return 4
|
|
766
|
+
if (lower.includes("4.7-flashx")) return 5
|
|
767
|
+
if (lower.includes("4.7") && !lower.includes("flash")) return 6
|
|
768
|
+
if (lower.includes("4.6")) return 7
|
|
769
|
+
if (lower.includes("4.5-flash")) return 8
|
|
770
|
+
if (lower.includes("4.7-flash")) return 9
|
|
771
|
+
if (lower.includes("flash")) return 10
|
|
772
|
+
return 20
|
|
773
|
+
}
|
|
774
|
+
|
|
592
775
|
function preferModel(models, needles, fallback) {
|
|
593
776
|
return models.find((model) => {
|
|
594
777
|
const lower = model.toLowerCase()
|
|
@@ -609,12 +792,22 @@ async function readAllStdin() {
|
|
|
609
792
|
}
|
|
610
793
|
|
|
611
794
|
async function readScriptedAnswersIfNeeded() {
|
|
612
|
-
if (
|
|
795
|
+
if (readInstallerOption("force_prompt") === "1" && !process.stdin.isTTY) {
|
|
613
796
|
return (await readAllStdin()).split(/\r?\n/)
|
|
614
797
|
}
|
|
615
798
|
return undefined
|
|
616
799
|
}
|
|
617
800
|
|
|
801
|
+
function readInstallerOption(name) {
|
|
802
|
+
const normalized = name.toUpperCase().replace(/-/g, "_")
|
|
803
|
+
const npmName = name.toLowerCase().replace(/-/g, "_")
|
|
804
|
+
return (
|
|
805
|
+
process.env[`OPENCODE_RESOLVE_${normalized}`] ??
|
|
806
|
+
process.env[`npm_config_opencode_resolve_${npmName}`] ??
|
|
807
|
+
""
|
|
808
|
+
)
|
|
809
|
+
}
|
|
810
|
+
|
|
618
811
|
function getPresetLabel(currentModel) {
|
|
619
812
|
if (!currentModel) return "inherited"
|
|
620
813
|
const lower = currentModel.toLowerCase()
|