pi-freeflow 1.27.0 → 1.28.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 +18 -0
- package/README.md +6 -6
- package/package.json +1 -1
- package/src/catalog.ts +10 -5
- package/src/commands.ts +24 -2
- package/src/index.ts +1 -1
- package/src/models.ts +36 -23
- package/src/probe.ts +33 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.28.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 526d43f: Refresh the free model catalog to the current live rotation (33 models).
|
|
8
|
+
|
|
9
|
+
- New: Space Bunny Alpha on the KiloCode Gateway free list (1M context window) — pick it with the short name `space-bunny-alpha`.
|
|
10
|
+
- Back: Step 3.7 Flash returns to the KiloCode Gateway free list after a live serving re-check.
|
|
11
|
+
- Retired from the picker: Nex N2.5 Pro, Nex N2.5 Mini, and GLM 5.2 free — all three are gone from the free list (only paid versions remain).
|
|
12
|
+
|
|
13
|
+
- 526d43f: Add LongCat 2.5 Preview Free (`longcat-2.5-preview-free`) from OpenCode Zen with a 1M context window, reasoning, and vision.
|
|
14
|
+
|
|
15
|
+
Fix upstream drift detection to accurately recognize free and alpha models while excluding commercial paid model families from candidate suggestions.
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- 526d43f: Retry relay reachability verification after deployment so edge DNS and routing propagation delays (such as newly created Cloudflare Workers routes) do not show a false unreachable warning.
|
|
20
|
+
|
|
3
21
|
## 1.27.0
|
|
4
22
|
|
|
5
23
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ Thin by design: a model list, a relay proxy, and a log. The host (`pi-ai`) handl
|
|
|
16
16
|
|
|
17
17
|
| Feature | What it does | Cost |
|
|
18
18
|
| :--- | :--- | :--- |
|
|
19
|
-
| **33 free models** |
|
|
19
|
+
| **33 free models** | 10 from OpenCode Zen, 18 from KiloCode Gateway, 5 from Cline, context windows up to 1M. Full list below. | **$0** |
|
|
20
20
|
| **Relay pool** | Route requests through your own Cloudflare Workers and Vercel Edge relays. Requests rotate across the pool. A relay that rate-limits, times out, or drops the connection cools down while healthy ones take its traffic. | **$0** beyond your platforms' free tiers |
|
|
21
21
|
| **Automatic fallback** | When every relay is cooling down, requests go direct to upstream instead of failing. | **$0** |
|
|
22
22
|
| **Short model names** | Every model has a slash-free, colon-free alias, plus an optional `:effort` suffix for thinking depth. You type `freeflow/<name>`. | **$0** |
|
|
@@ -190,7 +190,7 @@ The same command set works identically in OMP and Pi:
|
|
|
190
190
|
/model → freeflow → pick
|
|
191
191
|
```
|
|
192
192
|
|
|
193
|
-
#### OpenCode Zen (
|
|
193
|
+
#### OpenCode Zen (10 models), Responses and Chat API
|
|
194
194
|
|
|
195
195
|
Good defaults for long coding sessions and agentic work.
|
|
196
196
|
|
|
@@ -205,8 +205,9 @@ Good defaults for long coding sessions and agentic work.
|
|
|
205
205
|
| `big-pickle` | Big Pickle | **200K** (200.000) | **32K** (32.000) | `high / max` | ❌ |
|
|
206
206
|
| `ling-3.0-flash-fin-free` | Inclusion AI | **262K** (262.144) | **131K** (131.072) | `minimal … xhigh` | ❌ |
|
|
207
207
|
| `space-bunny-free` | Stealth preview (lab undisclosed) | **1M** (1.048.576) | **512K** (524.288) | `low … max` | ✅ |
|
|
208
|
+
| `longcat-2.5-preview-free` | Meituan LongCat | **1M** (1.000.000) | **131K** (131.072) | `minimal … max` | ✅ |
|
|
208
209
|
|
|
209
|
-
#### KiloCode Gateway (
|
|
210
|
+
#### KiloCode Gateway (18 models), OpenRouter compatible
|
|
210
211
|
|
|
211
212
|
Keyless access. Short aliases work for every row (the full ID is in parentheses).
|
|
212
213
|
|
|
@@ -226,11 +227,10 @@ Keyless access. Short aliases work for every row (the full ID is in parentheses)
|
|
|
226
227
|
| `content-safety` (`nvidia/...:free`) | NVIDIA | **128K** (128.000) | **8K** (8.192) | ❌ *(non-thinking)* | ✅ |
|
|
227
228
|
| `ling-3.0-flash-fin` (`inclusionai/ling-3.0-flash-fin:free`) | Inclusion AI | **262K** (262.144) | **32K** (32.768) | `minimal…xhigh`\* | ❌ |
|
|
228
229
|
| `ling-3.0-flash-sante` (`inclusionai/ling-3.0-flash-sante:free`) | Inclusion AI | **262K** (262.144) | **32K** (32.768) | `minimal…xhigh`\* | ❌ |
|
|
229
|
-
| `
|
|
230
|
-
| `
|
|
230
|
+
| `step-3.7-flash` (`stepfun/...:free`) | StepFun | **262K** (262.144) | **262K** (262.144) | `minimal…xhigh`\* | ✅ |
|
|
231
|
+
| `space-bunny-alpha` (`stealth/...`) | Stealth preview (lab undisclosed) | **1M** (1.000.000) | **512K** (524.288) | `minimal…xhigh`\* | ✅ |
|
|
231
232
|
| `inkling-small` (`thinkingmachines/inkling-small:free`) | Thinking Machines | **1M** (1.048.576) | **262K** (262.144) | `minimal…xhigh`\* | ✅ |
|
|
232
233
|
| `qwen3.8-27b` (`qwen/...:free`) | Alibaba Qwen | **262K** (262.144) | **235K** (235.929) | `minimal…xhigh`\* | ✅ |
|
|
233
|
-
| `glm-5.2` (`z-ai/...:free`) | Zhipu AI | **32K** (32.768) | **29K** (29.491) | `minimal…xhigh`\* | ❌ |
|
|
234
234
|
|
|
235
235
|
\* Levels are forwarded as-is through the OpenRouter-style nested `reasoning` parameter; effort mapping is decided by each model. MiMo collapses `minimal→low` and `xhigh→high` upstream, so its selector shows 5 labels but only 3 distinct effort values.
|
|
236
236
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-freeflow",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.28.0",
|
|
5
5
|
"description": "Thin provider for OMP/Pi — model list + dumb relay proxy + log; host pi-ai owns thinking/normalization",
|
|
6
6
|
"main": "extensions/index.ts",
|
|
7
7
|
"types": "src/index.ts",
|
package/src/catalog.ts
CHANGED
|
@@ -30,9 +30,12 @@ import type {
|
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* Pruned model IDs that must never re-enter the catalog via disk cache or upstream merge.
|
|
33
|
-
* - jev-1.13-free: non-chat decision model, chat-incompatible — never registered.
|
|
34
|
-
* - deepseek-v4-flash-free: listed but
|
|
35
|
-
* -
|
|
33
|
+
* - jev-1.13-free: non-chat decision model, chat-incompatible (live chat 500) — never registered.
|
|
34
|
+
* - deepseek-v4-flash-free: listed but unserved upstream (live chat 400 "Model is unavailable").
|
|
35
|
+
* - nex-agi/nex-n2.5-pro:free + nex-agi/nex-n2.5-mini:free: gone 2026-09-28 — zero nex-agi
|
|
36
|
+
* IDs on the live Kilo list, keyless chat 404 "does not exist".
|
|
37
|
+
* - z-ai/glm-5.2:free: gone 2026-09-28 — :free ID absent (keyless 404); only the paid
|
|
38
|
+
* z-ai/glm-5.2 remains (isFree:false, keyless 401 PAID_MODEL_AUTH_REQUIRED).
|
|
36
39
|
* - inclusionai/ling-3.0-flash-vl:free: gone from the live Kilo free list — only the paid counterpart remains.
|
|
37
40
|
*/
|
|
38
41
|
export const DEAD_MODEL_IDS = new Set<string>([
|
|
@@ -47,7 +50,9 @@ export const DEAD_MODEL_IDS = new Set<string>([
|
|
|
47
50
|
"minimax/minimax-m3:free",
|
|
48
51
|
"thinkingmachines/inkling:free",
|
|
49
52
|
"union-alpha",
|
|
50
|
-
"
|
|
53
|
+
"nex-agi/nex-n2.5-pro:free",
|
|
54
|
+
"nex-agi/nex-n2.5-mini:free",
|
|
55
|
+
"z-ai/glm-5.2:free",
|
|
51
56
|
"inclusionai/ling-3.0-flash-vl:free",
|
|
52
57
|
]);
|
|
53
58
|
/**
|
|
@@ -55,7 +60,7 @@ export const DEAD_MODEL_IDS = new Set<string>([
|
|
|
55
60
|
* Upstream lists paid models alongside free ones (e.g. claude-fable-5-1,
|
|
56
61
|
* claude-opus-4-*, gemini-3-*) so a bare upstream merge leaks paid entries that
|
|
57
62
|
* fail with 401 Missing API key. Known static IDs without a free suffix
|
|
58
|
-
* (e.g. big-pickle) stay allowed via MODEL_MAP.
|
|
63
|
+
* (e.g. big-pickle, stealth/space-bunny-alpha) stay allowed via MODEL_MAP.
|
|
59
64
|
*/
|
|
60
65
|
export function isFreeCatalogId(id: string): boolean {
|
|
61
66
|
if (typeof id !== "string" || id.length === 0) return false;
|
package/src/commands.ts
CHANGED
|
@@ -12,7 +12,7 @@ import path from "node:path";
|
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
13
|
import { getClientPort, isJsRuntimeExecutable, resolveDaemonRuntime } from "./client.ts";
|
|
14
14
|
import { refreshCatalog, setAliveCatalog } from "./catalog.ts";
|
|
15
|
-
import { DAEMON_SPAWN_ENV, DEBUG_STATE_FILE, HOST, LOG_FILE, PORT, RELAY_STATE_FILE } from "./config.ts";
|
|
15
|
+
import { DAEMON_SPAWN_ENV, DATA_DIR_ENV, DEBUG_STATE_FILE, HOST, LOG_FILE, PORT, RELAY_STATE_FILE } from "./config.ts";
|
|
16
16
|
import {
|
|
17
17
|
compareVersions,
|
|
18
18
|
fetchLatestVersion,
|
|
@@ -84,6 +84,17 @@ export function updateStatusBar(ui?: ExtensionUIContext): void {
|
|
|
84
84
|
export const STARTUP_TASK_NAME = "pi-freeflow-daemon";
|
|
85
85
|
export const STARTUP_SERVICE_NAME = "pi-freeflow.service";
|
|
86
86
|
|
|
87
|
+
let deployProbeRetries = 5;
|
|
88
|
+
let deployProbeRetryDelayMs: number | null = null;
|
|
89
|
+
|
|
90
|
+
/** Test-only: override post-deploy probe retry count and delay. */
|
|
91
|
+
export function _setDeployProbeOptionsForTest(
|
|
92
|
+
opts: { retries?: number; retryDelayMs?: number } | null,
|
|
93
|
+
): void {
|
|
94
|
+
deployProbeRetries = opts?.retries ?? 5;
|
|
95
|
+
deployProbeRetryDelayMs = opts?.retryDelayMs ?? null;
|
|
96
|
+
}
|
|
97
|
+
|
|
87
98
|
export interface StartupPlan {
|
|
88
99
|
platform: string;
|
|
89
100
|
taskName: string;
|
|
@@ -755,7 +766,18 @@ export function createCommandSpec(
|
|
|
755
766
|
}
|
|
756
767
|
let probeNote = "";
|
|
757
768
|
try {
|
|
758
|
-
const
|
|
769
|
+
const retryDelay =
|
|
770
|
+
deployProbeRetryDelayMs ?? (process.env[DATA_DIR_ENV] ? 10 : 2000);
|
|
771
|
+
const probe = await probeRelay(finalUrl, auth, {
|
|
772
|
+
retries: deployProbeRetries,
|
|
773
|
+
retryDelayMs: retryDelay,
|
|
774
|
+
onRetry: (attempt, total) => {
|
|
775
|
+
ctx.ui.notify(
|
|
776
|
+
`Waiting for edge routing to propagate (${attempt}/${total})…`,
|
|
777
|
+
"info",
|
|
778
|
+
);
|
|
779
|
+
},
|
|
780
|
+
});
|
|
759
781
|
probeNote = probe.ok
|
|
760
782
|
? ` ✓ reachable (HTTP ${probe.status}, ${probe.latencyMs}ms)`
|
|
761
783
|
: ` ⚠ deployed but unreachable (${probe.error || `HTTP ${probe.status}`}) — run /freeflow test ${finalUrl} to retry, or /freeflow remove ${finalUrl} to drop it`;
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* pi-freeflow — Modular, high-resiliency LLM extension for Pi & Oh My Pi (OMP)
|
|
3
3
|
*
|
|
4
|
-
* Provides access to 33 free models (
|
|
4
|
+
* Provides access to 33 free models (10 OpenCode Zen + 18 KiloCode Gateway + 5 Cline) with:
|
|
5
5
|
* - Single-port daemon reuse on 28180 across concurrent subagents
|
|
6
6
|
* - Multi-cloud rolling egress relays (Vercel Edge, Cloudflare, Deno)
|
|
7
7
|
* - 0ms instant startup with verified static catalog and background live health checks
|
package/src/models.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Static model definitions and upstream routing catalogs for pi-freeflow
|
|
3
3
|
*
|
|
4
|
-
* Defines the 33 verified free models:
|
|
5
|
-
* -
|
|
6
|
-
* -
|
|
4
|
+
* Defines the 33 verified free models (live-verified 2026-09-28):
|
|
5
|
+
* - 10 OpenCode Zen models (2 Responses API + 8 Chat Completions)
|
|
6
|
+
* - 18 KiloCode Keyless Gateway models (OpenRouter format)
|
|
7
7
|
* - 5 Cline direct-only models (per-user pool)
|
|
8
8
|
*/
|
|
9
9
|
|
|
@@ -169,6 +169,23 @@ export const OPENCODE_MODELS: ModelDef[] = [
|
|
|
169
169
|
max: "max",
|
|
170
170
|
},
|
|
171
171
|
},
|
|
172
|
+
{
|
|
173
|
+
id: "longcat-2.5-preview-free",
|
|
174
|
+
name: "LongCat 2.5 Preview (1M) [OpenCode]",
|
|
175
|
+
reasoning: true,
|
|
176
|
+
contextWindow: 1_000_000,
|
|
177
|
+
maxTokens: 131_072,
|
|
178
|
+
input: ["text", "image"],
|
|
179
|
+
thinkingLevelMap: {
|
|
180
|
+
off: null,
|
|
181
|
+
minimal: "minimal",
|
|
182
|
+
low: "low",
|
|
183
|
+
medium: "medium",
|
|
184
|
+
high: "high",
|
|
185
|
+
xhigh: "xhigh",
|
|
186
|
+
max: "max",
|
|
187
|
+
},
|
|
188
|
+
},
|
|
172
189
|
];
|
|
173
190
|
|
|
174
191
|
/**
|
|
@@ -321,21 +338,26 @@ export const KILO_MODELS: ModelDef[] = [
|
|
|
321
338
|
thinkingLevelMap: KILO_REASONING_MAP,
|
|
322
339
|
},
|
|
323
340
|
{
|
|
324
|
-
|
|
325
|
-
|
|
341
|
+
// Resurrected 2026-09-28: back on the live Kilo free list
|
|
342
|
+
// (isFree:true, 0/0 pricing, no expiry) + keyless chat 200.
|
|
343
|
+
id: "stepfun/step-3.7-flash:free",
|
|
344
|
+
name: "Step 3.7 Flash [Kilo]",
|
|
326
345
|
reasoning: true,
|
|
327
346
|
contextWindow: 262_144,
|
|
328
|
-
maxTokens:
|
|
347
|
+
maxTokens: 262_144,
|
|
329
348
|
input: ["text", "image"],
|
|
330
349
|
thinkingFormat: "openrouter",
|
|
331
350
|
thinkingLevelMap: KILO_REASONING_MAP,
|
|
332
351
|
},
|
|
333
352
|
{
|
|
334
|
-
|
|
335
|
-
|
|
353
|
+
// Added 2026-09-28: live on the Kilo free list (isFree:true, 0/0
|
|
354
|
+
// pricing, 1M context, text+image in) + keyless chat 200. Passes
|
|
355
|
+
// isFreeCatalogId via MODEL_MAP (no :free suffix by design).
|
|
356
|
+
id: "stealth/space-bunny-alpha",
|
|
357
|
+
name: "Space Bunny Alpha (1M) [Kilo]",
|
|
336
358
|
reasoning: true,
|
|
337
|
-
contextWindow:
|
|
338
|
-
maxTokens:
|
|
359
|
+
contextWindow: 1_000_000,
|
|
360
|
+
maxTokens: 524_288,
|
|
339
361
|
input: ["text", "image"],
|
|
340
362
|
thinkingFormat: "openrouter",
|
|
341
363
|
thinkingLevelMap: KILO_REASONING_MAP,
|
|
@@ -370,16 +392,6 @@ export const KILO_MODELS: ModelDef[] = [
|
|
|
370
392
|
thinkingFormat: "openrouter",
|
|
371
393
|
thinkingLevelMap: KILO_REASONING_MAP,
|
|
372
394
|
},
|
|
373
|
-
{
|
|
374
|
-
id: "z-ai/glm-5.2:free",
|
|
375
|
-
name: "GLM 5.2 [Kilo]",
|
|
376
|
-
reasoning: true,
|
|
377
|
-
contextWindow: 32_768,
|
|
378
|
-
maxTokens: 29_491,
|
|
379
|
-
input: ["text"],
|
|
380
|
-
thinkingFormat: "openrouter",
|
|
381
|
-
thinkingLevelMap: KILO_REASONING_MAP,
|
|
382
|
-
},
|
|
383
395
|
];
|
|
384
396
|
|
|
385
397
|
/**
|
|
@@ -498,13 +510,14 @@ export const MODEL_ALIASES: Record<string, string> = {
|
|
|
498
510
|
"content-safety": "nvidia/nemotron-3.5-content-safety:free",
|
|
499
511
|
"ling-3.0-flash-fin": "inclusionai/ling-3.0-flash-fin:free",
|
|
500
512
|
"ling-3.0-flash-sante": "inclusionai/ling-3.0-flash-sante:free",
|
|
501
|
-
"
|
|
502
|
-
"
|
|
513
|
+
"step-3.7-flash": "stepfun/step-3.7-flash:free",
|
|
514
|
+
"space-bunny-alpha": "stealth/space-bunny-alpha",
|
|
503
515
|
"inkling-small": "thinkingmachines/inkling-small:free",
|
|
504
516
|
"mimo-v2.6-flash": "mimo-v2.6-flash-free",
|
|
505
517
|
"space-bunny": "space-bunny-free",
|
|
518
|
+
"longcat-2.5-preview": "longcat-2.5-preview-free",
|
|
519
|
+
"longcat": "longcat-2.5-preview-free",
|
|
506
520
|
"qwen3.8-27b": "qwen/qwen3.8-27b:free",
|
|
507
|
-
"glm-5.2": "z-ai/glm-5.2:free",
|
|
508
521
|
// provider-prefixed short aliases (slash-normalized)
|
|
509
522
|
"laguna-s-2.1:free": "poolside/laguna-s-2.1:free",
|
|
510
523
|
"laguna-xs-2.1:free": "poolside/laguna-xs-2.1:free",
|
package/src/probe.ts
CHANGED
|
@@ -24,6 +24,15 @@ export interface RelayProbeOptions {
|
|
|
24
24
|
model?: string;
|
|
25
25
|
/** Timeout in milliseconds (defaults to 5000) */
|
|
26
26
|
timeoutMs?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Number of retry attempts when the probe returns !ok or throws.
|
|
29
|
+
* Defaults to 0 (single attempt). Useful post-deploy for edge DNS propagation.
|
|
30
|
+
*/
|
|
31
|
+
retries?: number;
|
|
32
|
+
/** Delay between retries in milliseconds (defaults to 2000). */
|
|
33
|
+
retryDelayMs?: number;
|
|
34
|
+
/** Optional progress callback before each retry */
|
|
35
|
+
onRetry?: (attempt: number, maxRetries: number, lastResult: RelayProbeResult) => void;
|
|
27
36
|
}
|
|
28
37
|
|
|
29
38
|
/**
|
|
@@ -41,7 +50,7 @@ export interface RelayProbeOptions {
|
|
|
41
50
|
* @param auth Optional per-relay shared secret; sent as x-relay-auth so
|
|
42
51
|
* deployed relays (which enforce it) answer the probe instead of 401.
|
|
43
52
|
*/
|
|
44
|
-
|
|
53
|
+
async function probeRelayOnce(
|
|
45
54
|
url: string,
|
|
46
55
|
auth?: string,
|
|
47
56
|
opts?: RelayProbeOptions,
|
|
@@ -119,3 +128,26 @@ export async function probeRelay(
|
|
|
119
128
|
};
|
|
120
129
|
}
|
|
121
130
|
}
|
|
131
|
+
|
|
132
|
+
export async function probeRelay(
|
|
133
|
+
url: string,
|
|
134
|
+
auth?: string,
|
|
135
|
+
opts?: RelayProbeOptions,
|
|
136
|
+
): Promise<RelayProbeResult> {
|
|
137
|
+
const retries = Math.max(0, opts?.retries ?? 0);
|
|
138
|
+
const retryDelay = Math.max(10, opts?.retryDelayMs ?? 2000);
|
|
139
|
+
|
|
140
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
141
|
+
if (attempt > 0) {
|
|
142
|
+
const { promise, resolve } = Promise.withResolvers<void>();
|
|
143
|
+
setTimeout(resolve, retryDelay);
|
|
144
|
+
await promise;
|
|
145
|
+
}
|
|
146
|
+
const result = await probeRelayOnce(url, auth, opts);
|
|
147
|
+
if (result.ok || attempt === retries) {
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
opts?.onRetry?.(attempt + 1, retries, result);
|
|
151
|
+
}
|
|
152
|
+
return { ok: false, status: 0, latencyMs: 0, error: "Probe failed" };
|
|
153
|
+
}
|