free-coding-models 0.5.91 โ 0.5.93
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.md +13 -11
- package/changelog/v0.5.92.md +40 -0
- package/changelog/v0.5.93.md +21 -0
- package/package.json +1 -1
- package/sources.js +86 -32
- package/src/core/tool-launchers.js +109 -0
- package/src/tui/key-handler.js +5 -2
- package/web/dist/assets/index-rwecx-QO.js +45 -0
- package/web/dist/index.html +1 -1
- package/web/dist/assets/index-CfwMURO5.js +0 -45
|
@@ -21,12 +21,16 @@
|
|
|
21
21
|
* ๐ Continue: writes ~/.continue/config.yaml with provider: openai + apiBase
|
|
22
22
|
* ๐ Cline: writes ~/.cline/globalState.json with openai-compatible provider config
|
|
23
23
|
* ๐ ForgeCode: writes [[providers]] TOML block into ~/.forge/.forge.toml + sets [session] defaults
|
|
24
|
+
* ๐ FCM Router: built-in target with nothing to install or spawn; starts the daemon,
|
|
25
|
+
* pushes the selected model + favorites as the `fast-coding` routing set and prints
|
|
26
|
+
* the /v1 endpoint + model + key trio (issue #184)
|
|
24
27
|
*
|
|
25
28
|
* @functions
|
|
26
29
|
* โ `resolveLauncherModelId` โ choose the provider-specific id for a launch
|
|
27
30
|
* โ `writeGooseConfig` โ install provider + set GOOSE_PROVIDER/GOOSE_MODEL in config.yaml
|
|
28
31
|
* โ `writeCrushConfig` โ write provider + models.large/small to crush.json
|
|
29
32
|
* โ `prepareExternalToolLaunch` โ persist selected-model defaults and compute the launch command
|
|
33
|
+
* โ `startFcmRouterLaunch` โ daemon lifecycle + routing-set push + connection instructions for fcm_router mode
|
|
30
34
|
* โ `startExternalTool` โ configure and launch the selected external tool mode
|
|
31
35
|
*
|
|
32
36
|
* @exports resolveLauncherModelId, buildToolEnv, prepareExternalToolLaunch, startExternalTool
|
|
@@ -1100,6 +1104,21 @@ export function prepareExternalToolLaunch(mode, model, config, options = {}) {
|
|
|
1100
1104
|
}
|
|
1101
1105
|
}
|
|
1102
1106
|
|
|
1107
|
+
if (mode === 'fcm_router') {
|
|
1108
|
+
// ๐ FCM Router is built into FCM: nothing to install and no external binary to
|
|
1109
|
+
// ๐ spawn. Enter in this mode starts the daemon and routes the selected model
|
|
1110
|
+
// ๐ through it (see startFcmRouterLaunch). Issue #184.
|
|
1111
|
+
return {
|
|
1112
|
+
command: null,
|
|
1113
|
+
args: [],
|
|
1114
|
+
env,
|
|
1115
|
+
apiKey,
|
|
1116
|
+
baseUrl,
|
|
1117
|
+
meta,
|
|
1118
|
+
configArtifacts: [],
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1103
1122
|
return {
|
|
1104
1123
|
blocked: true,
|
|
1105
1124
|
exitCode: 1,
|
|
@@ -1109,6 +1128,92 @@ export function prepareExternalToolLaunch(mode, model, config, options = {}) {
|
|
|
1109
1128
|
}
|
|
1110
1129
|
}
|
|
1111
1130
|
|
|
1131
|
+
// ๐ startFcmRouterLaunch: the "launch" flow for the FCM Router target in the Z cycle
|
|
1132
|
+
// ๐ (issue #184). The router ships inside FCM, so there is nothing to install and no
|
|
1133
|
+
// ๐ CLI to spawn. Instead: make sure the daemon is running, push the selected model
|
|
1134
|
+
// ๐ as primary with the user's favorites as failover into the active `fast-coding`
|
|
1135
|
+
// ๐ set, then print the endpoint + model + key trio any OpenAI-compatible tool needs.
|
|
1136
|
+
async function startFcmRouterLaunch(model, config) {
|
|
1137
|
+
const meta = getToolMeta('fcm_router')
|
|
1138
|
+
console.log(chalk.cyan(` โถ Configuring ${meta.label} with ${chalk.bold(model.label)}...`))
|
|
1139
|
+
|
|
1140
|
+
let status = null
|
|
1141
|
+
try {
|
|
1142
|
+
// ๐ Lazy import: router-daemon.js is large and only needed in this mode.
|
|
1143
|
+
const { startRouterDaemonBackground } = await import('./router-daemon.js')
|
|
1144
|
+
status = await startRouterDaemonBackground()
|
|
1145
|
+
} catch (error) {
|
|
1146
|
+
status = { ok: false, error: error instanceof Error ? error.message : String(error) }
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
if (!status?.ok) {
|
|
1150
|
+
console.log(chalk.red(` X Could not start the FCM Router daemon${status?.error ? `: ${status.error}` : '.'}`))
|
|
1151
|
+
console.log(chalk.dim(' Run `free-coding-models --daemon` in another terminal to see the startup error.'))
|
|
1152
|
+
console.log()
|
|
1153
|
+
return 1
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
const routerBaseUrl = `http://localhost:${status.port}`
|
|
1157
|
+
|
|
1158
|
+
// ๐ Selected model first, favorites as failover. Mirrors syncFavoritesToRouter
|
|
1159
|
+
// ๐ in the TUI but without the router.enabled gate: choosing the FCM Router
|
|
1160
|
+
// ๐ target in the Z cycle IS the intent, no extra setting required.
|
|
1161
|
+
const favorites = Array.isArray(config?.favorites) ? config.favorites : []
|
|
1162
|
+
const selKey = `${model.providerKey}/${model.modelId}`
|
|
1163
|
+
const chain = [selKey, ...favorites.filter((f) => f !== selKey)]
|
|
1164
|
+
const routerModels = chain.map((key, index) => {
|
|
1165
|
+
const slashIdx = key.indexOf('/')
|
|
1166
|
+
return {
|
|
1167
|
+
provider: slashIdx >= 0 ? key.slice(0, slashIdx) : '?',
|
|
1168
|
+
model: slashIdx >= 0 ? key.slice(slashIdx + 1) : key,
|
|
1169
|
+
priority: index + 1,
|
|
1170
|
+
}
|
|
1171
|
+
})
|
|
1172
|
+
|
|
1173
|
+
// ๐ POST creates-or-replaces the set (PUT alone 404s when the set is missing),
|
|
1174
|
+
// ๐ then activate makes it the routing target. Best-effort: a failure here
|
|
1175
|
+
// ๐ leaves the daemon on its default set, which still works, so we warn
|
|
1176
|
+
// ๐ instead of failing the whole launch.
|
|
1177
|
+
let setSynced = false
|
|
1178
|
+
try {
|
|
1179
|
+
const payload = JSON.stringify({ name: 'fast-coding', models: routerModels, created: new Date().toISOString() })
|
|
1180
|
+
const createRes = await fetch(`${routerBaseUrl}/sets`, {
|
|
1181
|
+
method: 'POST',
|
|
1182
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1183
|
+
body: payload,
|
|
1184
|
+
signal: AbortSignal.timeout(5000),
|
|
1185
|
+
})
|
|
1186
|
+
if (createRes.ok || (await fetch(`${routerBaseUrl}/sets/fast-coding`, {
|
|
1187
|
+
method: 'PUT',
|
|
1188
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1189
|
+
body: payload,
|
|
1190
|
+
signal: AbortSignal.timeout(5000),
|
|
1191
|
+
})).ok) {
|
|
1192
|
+
await fetch(`${routerBaseUrl}/sets/fast-coding/activate`, {
|
|
1193
|
+
method: 'POST',
|
|
1194
|
+
signal: AbortSignal.timeout(5000),
|
|
1195
|
+
}).catch(() => {})
|
|
1196
|
+
setSynced = true
|
|
1197
|
+
}
|
|
1198
|
+
} catch {}
|
|
1199
|
+
|
|
1200
|
+
console.log(chalk.green(` โ FCM Router ${status.alreadyRunning ? 'is running' : 'started'} at ${chalk.bold(`${routerBaseUrl}/v1`)}`))
|
|
1201
|
+
console.log()
|
|
1202
|
+
console.log(chalk.bold(' Point any OpenAI-compatible coding tool at the router:'))
|
|
1203
|
+
console.log(` ${chalk.dim('Base URL:')} ${routerBaseUrl}/v1`)
|
|
1204
|
+
console.log(` ${chalk.dim('API key:')} fcm-local${process.env.FCM_ROUTER_TOKEN ? ' (or your FCM_ROUTER_TOKEN value)' : ''}`)
|
|
1205
|
+
console.log(` ${chalk.dim('Model:')} fcm`)
|
|
1206
|
+
console.log()
|
|
1207
|
+
if (setSynced) {
|
|
1208
|
+
console.log(chalk.dim(` ๐ Routing chain: ${model.label} first${favorites.length > 0 ? `, ${favorites.length} favorite${favorites.length === 1 ? '' : 's'} as failover` : ''}.`))
|
|
1209
|
+
} else {
|
|
1210
|
+
console.log(chalk.yellow(' โ Could not update the routing set; the daemon keeps its default set, which still works.'))
|
|
1211
|
+
}
|
|
1212
|
+
console.log(chalk.dim(' ๐ Dashboard: `free-coding-models web` ยท Docs: docs/router.md'))
|
|
1213
|
+
console.log()
|
|
1214
|
+
return 0
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1112
1217
|
export async function startExternalTool(mode, model, config) {
|
|
1113
1218
|
const launchPlan = prepareExternalToolLaunch(mode, model, config)
|
|
1114
1219
|
const { meta } = launchPlan
|
|
@@ -1119,6 +1224,10 @@ export async function startExternalTool(mode, model, config) {
|
|
|
1119
1224
|
return launchPlan.exitCode || 1
|
|
1120
1225
|
}
|
|
1121
1226
|
|
|
1227
|
+
// ๐ fcm_router has no binary to spawn: the helper starts the daemon, pushes
|
|
1228
|
+
// ๐ the routing set and prints connection instructions instead. Issue #184.
|
|
1229
|
+
if (mode === 'fcm_router') return startFcmRouterLaunch(model, config)
|
|
1230
|
+
|
|
1122
1231
|
console.log(chalk.cyan(` โถ Launching ${meta.label} with ${chalk.bold(model.label)}...`))
|
|
1123
1232
|
printConfigArtifacts(meta.label, launchPlan.configArtifacts)
|
|
1124
1233
|
|
package/src/tui/key-handler.js
CHANGED
|
@@ -242,12 +242,15 @@ export function createKeyHandler(ctx) {
|
|
|
242
242
|
state.toolInstallPromptErrorMsg = null
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
-
|
|
245
|
+
function shouldCheckMissingTool(mode) {
|
|
246
246
|
// ๐ opencode-desktop doesn't have a binary check (it uses 'open -a').
|
|
247
247
|
// ๐ opencode-web, opencode, and kilo manage their own ENOENT errors in spawn handlers.
|
|
248
248
|
// ๐ xcode uses 'open -a Xcode' which doesn't need a binary path resolution.
|
|
249
249
|
// ๐ zcode is a desktop app with no CLI binary โ the launch handler prints setup instructions.
|
|
250
|
-
|
|
250
|
+
// ๐ fcm_router ships inside FCM (nothing to install) โ the launch handler starts the
|
|
251
|
+
// ๐ daemon itself. Without this exclusion Enter showed a bogus "Missing Tool" prompt
|
|
252
|
+
// ๐ reading "Unknown tool mode: fcm_router" (issue #184).
|
|
253
|
+
return !['opencode-desktop', 'opencode-web', 'opencode', 'kilo', 'xcode', 'zcode', 'fcm_router'].includes(mode)
|
|
251
254
|
}
|
|
252
255
|
|
|
253
256
|
function getModelTelemetryFamily(providerKey) {
|