free-coding-models 0.5.77 → 0.5.78
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/v0.5.78.md +17 -0
- package/package.json +1 -1
- package/src/core/endpoint-installer.js +4 -2
- package/src/core/schema-normalizer.js +18 -1
- package/src/core/tool-launchers.js +6 -0
- package/src/tui/key-handler.js +1 -3
- package/src/tui/tui-state.js +11 -4
- package/web/dist/assets/{index-BjEXVUHp.js → index-DV-1tGVj.js} +2 -2
- package/web/dist/index.html +1 -1
- package/web/src/hooks/useToolMode.js +1 -1
- package/web/src/utils/m3.js +2 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Changelog v0.5.78 - 2026-08-23
|
|
2
|
+
|
|
3
|
+
### Fixed
|
|
4
|
+
- **Router forwards `prompt_cache_key` to NVIDIA -> HTTP 400 (fixes #160).** Clients using OpenAI JS SDK (pi agent) set `prompt_cache_key` to session UUID for prompt caching; NVIDIA rejects it with `Unsupported parameter(s): prompt_cache_key`. Added `prompt_cache_key` to `STRIP_PARAMS` in `src/core/schema-normalizer.js` and added `nvidia: normalizeNvidia` (strip + orphan tool drop) to `PROVIDER_NORMALIZERS` so `normalizeRequestBody(body, 'nvidia')` now strips the hint before forwarding. Verified with direct replay against `integrate.api.nvidia.com` and `pnpm test` 813/813. Thanks @pelvity.
|
|
5
|
+
|
|
6
|
+
- **Config `pingInterval` not respected at startup (fixes #155).** TUI always started in speed mode (2s) ignoring `settings.pingInterval` and `--ping-interval`. Now `src/tui/tui-state.js` `createTuiState()` derives `initialInterval` from `config.settings.pingInterval` (already merged from CLI in `src/tui/app.js:283`) and maps to mode via `intervalToPingMode` (30000 -> slow, 10000 -> normal, 4000 -> forced, <=3000 -> speed). Sets `pingModeSource` to `config` vs `startup` and only sets `speedModeUntil` when initial mode is speed. Users with 30s interval no longer burn quota on startup burst. Thanks @BetterToAutomateTheWorld.
|
|
7
|
+
|
|
8
|
+
- **jcode configuration option for CLI and web (fixes #145).** CLI Z cycle already had `jcode` but launch hung (stdin left in raw mode, REPL appeared frozen) and web had no jcode option at all.
|
|
9
|
+
- CLI hang: `src/tui/key-handler.js` `launchSelectedModel` now calls `stopUi({ resetRawMode: true })` (was `stopUi()` with raw mode still enabled) and `src/core/tool-launchers.js` `spawnCommand` defensively resets `stdin.setRawMode(false)` before handing TTY to child.
|
|
10
|
+
- Web: Added `jcode` to `INSTALL_TARGET_MODES` in `src/core/endpoint-installer.js` (now 15 targets, `jcode` after `pi` to match `TOOL_MODE_ORDER` order) with env-based installer (`~/.fcm-jcode-env`), mirrored in `web/src/utils/m3.js` `INSTALL_ENDPOINT_TOOL_MODES`, and fixed `web/src/hooks/useToolMode.js` cycle bug (`TOOL_MODE_ORDER` -> `INSTALL_ENDPOINT_TOOL_MODES`). Web `/api/tool-mode` now accepts `jcode` without 422 and dropdown/cycle shows it. Thanks @plahakps.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- `src/core/schema-normalizer.js` JSDoc now exports `normalizeNvidia` and clarifies per-provider normalization.
|
|
14
|
+
- `web/src/utils/m3.js` and `src/core/endpoint-installer.js` tool order now matches CLI's `TOOL_MODE_ORDER` (opencode -> pi -> jcode -> opencode-desktop ...).
|
|
15
|
+
|
|
16
|
+
### Tests
|
|
17
|
+
- 813/813 passing. Updated `test/test.js` endpoint install target expectation to include `jcode`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "free-coding-models",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.78",
|
|
4
4
|
"description": "Find the fastest coding LLM models in seconds — ping free models from multiple providers, pick the best one for OpenCode, Cursor, or any AI coding assistant.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nvidia",
|
|
@@ -53,8 +53,8 @@ import { ensureDir, readJson as sharedReadJson } from './shared-helpers.js'
|
|
|
53
53
|
// 📖 zai and opencode-zen ARE OpenAI-compatible and CAN be installed into any tool.
|
|
54
54
|
const DIRECT_INSTALL_UNSUPPORTED_PROVIDERS = new Set(['replicate'])
|
|
55
55
|
// 📖 Install Endpoints only lists tools whose persisted config shape is actually supported here.
|
|
56
|
-
// 📖
|
|
57
|
-
const INSTALL_TARGET_MODES = ['opencode', 'opencode-desktop', 'opencode-web', 'openclaw', 'crush', 'goose', '
|
|
56
|
+
// 📖 jcode is CLI-only and uses an env-file helper (same as openhands) — selectable in both CLI and Web.
|
|
57
|
+
const INSTALL_TARGET_MODES = ['opencode', 'pi', 'jcode', 'opencode-desktop', 'opencode-web', 'openclaw', 'crush', 'goose', 'aider', 'qwen', 'openhands', 'amp', 'forgecode', 'fcm_router', 'zcode']
|
|
58
58
|
|
|
59
59
|
function getDefaultPaths() {
|
|
60
60
|
const home = homedir()
|
|
@@ -839,6 +839,8 @@ export function installProviderEndpoints(config, providerKey, toolMode, options
|
|
|
839
839
|
installResult = installIntoAmp(providerKey, models, apiKey, paths)
|
|
840
840
|
} else if (canonicalToolMode === 'qwen') {
|
|
841
841
|
installResult = installIntoQwen(providerKey, models, apiKey, paths)
|
|
842
|
+
} else if (canonicalToolMode === 'jcode') {
|
|
843
|
+
installResult = installIntoEnvBasedTool(providerKey, models, apiKey, canonicalToolMode, paths)
|
|
842
844
|
} else if (canonicalToolMode === 'openhands') {
|
|
843
845
|
installResult = installIntoEnvBasedTool(providerKey, models, apiKey, canonicalToolMode, paths)
|
|
844
846
|
} else if (canonicalToolMode === 'fcm_router') {
|
|
@@ -30,8 +30,9 @@
|
|
|
30
30
|
* → `clampTemperature` — clamps `temperature` to the provider's accepted range
|
|
31
31
|
* → `normalizeZai` — for `zai` (GLM) provider
|
|
32
32
|
* → `normalizeMistral` — for `mistral` and `codestral` providers
|
|
33
|
+
* → `normalizeNvidia` — for `nvidia` provider (strips unsupported params like prompt_cache_key)
|
|
33
34
|
*
|
|
34
|
-
* @exports normalizeRequestBody, normalizeZai, normalizeMistral, PROVIDER_NORMALIZERS
|
|
35
|
+
* @exports normalizeRequestBody, normalizeZai, normalizeMistral, normalizeNvidia, PROVIDER_NORMALIZERS
|
|
35
36
|
*
|
|
36
37
|
* @see src/core/router-daemon.js — calls `normalizeRequestBody` before forwarding upstream
|
|
37
38
|
*/
|
|
@@ -48,6 +49,7 @@ const STRIP_PARAMS = [
|
|
|
48
49
|
'echo', // not in OpenAI spec
|
|
49
50
|
'user', // PII risk; not used by FCM
|
|
50
51
|
'metadata', // not always supported
|
|
52
|
+
'prompt_cache_key', // OpenAI prompt-caching hint; NVIDIA rejects with 400
|
|
51
53
|
'store', // GLM rejects
|
|
52
54
|
]
|
|
53
55
|
|
|
@@ -146,6 +148,20 @@ export function normalizeMistral(body) {
|
|
|
146
148
|
return result
|
|
147
149
|
}
|
|
148
150
|
|
|
151
|
+
/**
|
|
152
|
+
* 📖 `normalizeNvidia` — transforms a request body for the `nvidia` provider.
|
|
153
|
+
*
|
|
154
|
+
* 1. Strips parameters NVIDIA rejects with 400 (e.g. prompt_cache_key)
|
|
155
|
+
* 2. Removes orphan `tool` messages that lack a matching assistant tool_call
|
|
156
|
+
* 3. Strips `stream_options` when not streaming
|
|
157
|
+
*/
|
|
158
|
+
export function normalizeNvidia(body) {
|
|
159
|
+
if (!body || typeof body !== 'object') return body
|
|
160
|
+
let result = stripUnsupportedParams(body)
|
|
161
|
+
result = dropOrphanToolMessages(result)
|
|
162
|
+
return result
|
|
163
|
+
}
|
|
164
|
+
|
|
149
165
|
// 📖 Map of provider key → normalizer function. Providers that don't need
|
|
150
166
|
// 📖 any tweak (most OpenAI-compat ones like Groq, Cerebras, etc.) are not
|
|
151
167
|
// 📖 listed and pass through `normalizeRequestBody` unchanged.
|
|
@@ -153,6 +169,7 @@ export const PROVIDER_NORMALIZERS = {
|
|
|
153
169
|
zai: normalizeZai,
|
|
154
170
|
mistral: normalizeMistral,
|
|
155
171
|
codestral: normalizeMistral,
|
|
172
|
+
nvidia: normalizeNvidia,
|
|
156
173
|
}
|
|
157
174
|
|
|
158
175
|
/**
|
|
@@ -209,6 +209,12 @@ const JCODE_NATIVE_PROVIDERS = {
|
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
function spawnCommand(command, args, env) {
|
|
212
|
+
// 📖 Defensive: ensure stdin is back in cooked mode before handing the TTY to
|
|
213
|
+
// 📖 an interactive child (jcode REPL etc). The TUI leaves raw mode enabled;
|
|
214
|
+
// 📖 if the caller forgot to reset it the child will appear to hang.
|
|
215
|
+
try {
|
|
216
|
+
if (process.stdin.isTTY && process.stdin.isRaw) process.stdin.setRawMode(false)
|
|
217
|
+
} catch {}
|
|
212
218
|
return new Promise((resolve, reject) => {
|
|
213
219
|
const child = spawn(command, args, {
|
|
214
220
|
stdio: 'inherit',
|
package/src/tui/key-handler.js
CHANGED
|
@@ -278,9 +278,7 @@ export function createKeyHandler(ctx) {
|
|
|
278
278
|
userSelected = { modelId: selected.modelId, label: selected.label, tier: selected.tier, providerKey: selected.providerKey, ctx: selected.ctx }
|
|
279
279
|
|
|
280
280
|
if (!uiAlreadyStopped) {
|
|
281
|
-
|
|
282
|
-
if (process.stdin.isTTY) process.stdin.setRawMode(true)
|
|
283
|
-
stopUi()
|
|
281
|
+
stopUi({ resetRawMode: true })
|
|
284
282
|
}
|
|
285
283
|
|
|
286
284
|
// 📖 If router is enabled, push [selected, ...favorites] to daemon as the active set
|
package/src/tui/tui-state.js
CHANGED
|
@@ -89,6 +89,13 @@ export function createTuiState({
|
|
|
89
89
|
|
|
90
90
|
// 📖 Dynamic normal interval from config (default 10s)
|
|
91
91
|
const normalInterval = config.settings?.pingInterval ?? PING_MODE_INTERVALS.normal
|
|
92
|
+
// 📖 Respect config.settings.pingInterval at startup (issue #155).
|
|
93
|
+
// 📖 CLI override is already merged into config.settings.pingInterval in app.js.
|
|
94
|
+
const configuredInterval = config.settings?.pingInterval
|
|
95
|
+
const hasConfiguredInterval =
|
|
96
|
+
typeof configuredInterval === 'number' && Number.isFinite(configuredInterval) && configuredInterval > 0
|
|
97
|
+
const initialInterval = hasConfiguredInterval ? configuredInterval : PING_MODE_INTERVALS.speed
|
|
98
|
+
const initialMode = intervalToPingMode(initialInterval)
|
|
92
99
|
|
|
93
100
|
return {
|
|
94
101
|
// 📖 Core data: model results (mutated in-place by ping loop)
|
|
@@ -107,14 +114,14 @@ export function createTuiState({
|
|
|
107
114
|
sortDirection: (config.settings?.sortAsc ?? true) ? 'asc' : 'desc',
|
|
108
115
|
|
|
109
116
|
// 📖 Ping cadence — drives the interval between background ping cycles
|
|
110
|
-
pingInterval:
|
|
111
|
-
pingMode:
|
|
112
|
-
pingModeSource: 'startup',
|
|
117
|
+
pingInterval: initialInterval,
|
|
118
|
+
pingMode: initialMode,
|
|
119
|
+
pingModeSource: hasConfiguredInterval ? 'config' : 'startup',
|
|
113
120
|
pingModeIntervals: {
|
|
114
121
|
...PING_MODE_INTERVALS,
|
|
115
122
|
normal: normalInterval,
|
|
116
123
|
},
|
|
117
|
-
speedModeUntil: now + SPEED_MODE_DURATION_MS,
|
|
124
|
+
speedModeUntil: initialMode === 'speed' ? now + SPEED_MODE_DURATION_MS : null,
|
|
118
125
|
lastPingTime: now,
|
|
119
126
|
lastUserActivityAt: now,
|
|
120
127
|
resumeSpeedOnActivity: false,
|