pi-freeflow 1.7.0 → 1.8.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # pi-freeflow 🌊
2
2
 
3
- > **25 free models. Up to 1M context. Zero API keys. Infinite scale via your own relay pool.**
3
+ > **28 free models. Up to 1M context. Zero API keys. Infinite scale via your own relay pool.**
4
4
 
5
5
  Thin by design: model list + dumb relay + log. Host `pi-ai` owns thinking, normalization & provider magic. We just make it free, fast, and unbreakable.
6
6
 
@@ -18,7 +18,7 @@ Join devs bypassing rate limits with their own relay pools. BYO, add as many as
18
18
 
19
19
  | Feature | Description | Value | Cost |
20
20
  | :--- | :--- | :--- | :--- |
21
- | **25 Curated Free Models** | 7 OpenCode Zen + 18 KiloCode Gateway models, up to 1M context & 512K output | Ceiling Unlocked | **$0** |
21
+ | **28 Curated Free Models** | 8 OpenCode Zen + 20 KiloCode Gateway models, up to 1M context & 512K output | Ceiling Unlocked | **$0** |
22
22
  | **BYO Relay Pool** | Round-robin load balancing across your Cloudflare Workers & Vercel Edges | Zero Rate Limits | **$0** (your free tiers) |
23
23
  | **Adaptive Health & Error Detection** | Auto-cooldown on 429 rate limits, 504 timeouts, and socket drops | 0ms Wasted Latency | **$0** |
24
24
  | **Stream Truncation Resilience** | Stateful SSE terminal tracking (`response.failed` / `response.incomplete` injection) | Zero Host Crashes | **$0** |
@@ -32,7 +32,7 @@ Philosophy: **Thin by design.** We only ship model list + relay proxy + log. Hos
32
32
 
33
33
  ---
34
34
 
35
- ### 25 Curated Models, One Command
35
+ ### 28 Curated Models, One Command
36
36
 
37
37
  ```bash
38
38
  /model → freeflow → pick
@@ -123,33 +123,64 @@ Manage your relay pool directly from the OMP / Pi terminal:
123
123
 
124
124
  ### Quick Start in 30 Seconds
125
125
 
126
- #### 1. Install
127
-
128
- **Oh My Pi (Recommended):**
126
+ **On Oh My Pi (OMP):**
129
127
  ```bash
130
128
  omp plugin install pi-freeflow
131
- # or local dev
129
+ # or local dev (repo checkout)
132
130
  omp plugin link /path/to/pi-freeflow
133
131
  ```
134
132
 
135
- **Pi:**
133
+ **On Pi:**
136
134
  ```bash
137
135
  pi install npm:pi-freeflow
138
136
  ```
139
137
 
138
+ > Both commands fetch the **same npm package** from the registry — pi-freeflow
139
+ > is an extension loaded by the host, not a standalone CLI. It works on **OMP
140
+ > and Pi only** (they share the extension API).
141
+
140
142
  #### 2. Pick a Model
141
143
 
144
+ **OMP — interactive:**
142
145
  ```bash
143
146
  omp
144
147
  /model → freeflow → muse-spark-1.2-contributor-free (1M) → max
148
+ ```
145
149
 
146
- # or CLI
150
+ **OMP one-shot CLI:**
151
+ ```bash
147
152
  omp -p --model freeflow/muse-spark-1.2-contributor-free "build me a SaaS"
148
- # or with short alias & thinking level
149
- omp -p --model freeflow/step-3.7-flash:high "solve this bug"
153
+ omp -p --model freeflow/step-3.7-flash:high "solve this bug" # alias + thinking level
154
+ ```
155
+
156
+ **Pi — interactive:**
157
+ ```bash
158
+ pi
159
+ /model → freeflow → pick
160
+ ```
161
+
162
+ **Pi — one-shot CLI:**
163
+ ```bash
164
+ pi -p --model freeflow/step-3.7-flash:high "solve this bug"
165
+ ```
166
+
167
+ > Model IDs accept a full canonical ID, a short alias (see the tables above),
168
+ > and an optional `:effort` suffix (`:minimal` … `:xhigh`, `:max` where
169
+ > supported). The host resolves the rest — you only type `freeflow/<name>`.
170
+
171
+ #### 3. Manage Your Relay Pool (OMP & Pi both)
172
+
173
+ ```bash
174
+ /freeflow status # active relay, pool status, candidates
175
+ /freeflow list # relays with health badges
176
+ /freeflow add <url> [label]
177
+ /freeflow deploy # guided deploy: vercel|cloudflare|deno
178
+ /freeflow logs [n] # tail proxy logs
150
179
  ```
151
180
 
152
- #### 3. Add Your Free Relays (Scale Infinitely)
181
+ These slash commands work **identically in OMP and Pi** — the extension
182
+ registers the same `/freeflow` command set in both hosts.
183
+ #### 4. Add Your Free Relays (Scale Infinitely)
153
184
 
154
185
  Default ships direct. Add relays via `/freeflow add <url> [label]`.
155
186
 
@@ -231,7 +262,7 @@ Log rotation at 10MB. Clean, parseable, real-time HTTP lifecycle tracking.
231
262
 
232
263
  This package stays thin. It ships three things: a model catalog, a relay proxy, and a log. There is no build step. The only runtime dependency is `undici`, which powers the upstream fetch agent. Thinking and prompt normalization stay with the host (`pi-ai`).
233
264
 
234
- Current size: about 11.3k lines including tests. 228 tests pass, typecheck clean.
265
+ Current size: about 11.3k lines including tests. 230 tests pass, typecheck clean.
235
266
 
236
267
  ---
237
268
 
@@ -251,6 +282,18 @@ Deleted in 1.3.0. If zai/qwen/deepseek thinking broke before, it's fixed now bec
251
282
 
252
283
  **Why is context free?**
253
284
  We use OpenCode Zen & Kilo free tiers. You pay only with your own Cloudflare/Vercel free tiers for egress.
285
+ **Why is it installed via npm?**
286
+ The npm package is the **distribution channel** only — both hosts resolve it internally:
287
+ `omp plugin install pi-freeflow` and `pi install npm:pi-freeflow` install the same
288
+ package from the npm registry. pi-freeflow is an **extension, not a standalone CLI** —
289
+ the host (OMP or Pi) loads and runs it. A plain `npm install` just downloads the
290
+ files; it is not a supported way to run the extension.
291
+
292
+ **Which hosts can use it?**
293
+ Oh My Pi (OMP) and Pi only. They share the same extension API
294
+ (`extensions/index.ts` declares both `omp` and `pi` extension entries), so one
295
+ package serves both. Other AI agents (OpenCode, KiloCode, Cursor, ...) have their
296
+ own plugin systems and do not load this extension.
254
297
 
255
298
  ---
256
299
 
@@ -281,7 +324,7 @@ pnpm smoke # verifies extensions/index.ts loads without crashing
281
324
  ```
282
325
  src/
283
326
  ├── index.ts # extension entry, lifecycle hooks
284
- ├── models.ts # 25-model catalog definitions
327
+ ├── models.ts # 28-model catalog definitions
285
328
  ├── catalog.ts # model catalog cache (24h disk)
286
329
  ├── proxy.ts # local proxy server (127.0.0.1:28180)
287
330
  ├── relay.ts # relay selection & round-robin
package/package.json CHANGED
@@ -1,62 +1,62 @@
1
- {
2
- "name": "pi-freeflow",
3
- "type": "module",
4
- "version": "1.7.0",
5
- "description": "Thin provider for OMP/Pi — model list + dumb relay proxy + log; host pi-ai owns thinking/normalization",
6
- "main": "extensions/index.ts",
7
- "types": "src/index.ts",
8
- "keywords": [
9
- "pi-package",
10
- "pi-extension",
11
- "oh-my-pi",
12
- "omp",
13
- "free-models",
14
- "opencode",
15
- "kilocode",
16
- "ai-models",
17
- "relay"
18
- ],
19
- "author": "trefeon",
20
- "license": "MIT",
21
- "repository": {
22
- "type": "git",
23
- "url": "git+https://github.com/trefeon/pi-freeflow.git"
24
- },
25
- "homepage": "https://github.com/trefeon/pi-freeflow#readme",
26
- "engines": {
27
- "node": ">=22.19.0"
28
- },
29
- "omp": {
30
- "extensions": [
31
- "./extensions"
32
- ]
33
- },
34
- "pi": {
35
- "extensions": [
36
- "./extensions"
37
- ]
38
- },
39
- "files": [
40
- "extensions",
41
- "src",
42
- "README.md",
43
- "LICENSE"
44
- ],
45
- "scripts": {
46
- "test": "node --experimental-strip-types --test --test-concurrency=1 test/**/*.test.ts",
47
- "typecheck": "tsc --noEmit",
48
- "smoke": "node --experimental-strip-types -e \"import('./extensions/index.ts').then(() => console.log('✓ Smoke test passed: extensions/index.ts loaded successfully')).catch(err => { console.error(err); process.exit(1); })\"",
49
- "changeset": "changeset",
50
- "docs:dev": "vitepress dev docs",
51
- "docs:build": "vitepress build docs"
52
- },
53
- "devDependencies": {
54
- "@changesets/cli": "^2.27.0",
55
- "@types/node": "^22.13.9",
56
- "typescript": "^5.8.2",
57
- "vitepress": "^1.6.4"
58
- },
59
- "dependencies": {
60
- "undici": "^8.10.0"
61
- }
62
- }
1
+ {
2
+ "name": "pi-freeflow",
3
+ "type": "module",
4
+ "version": "1.8.0",
5
+ "description": "Thin provider for OMP/Pi — model list + dumb relay proxy + log; host pi-ai owns thinking/normalization",
6
+ "main": "extensions/index.ts",
7
+ "types": "src/index.ts",
8
+ "keywords": [
9
+ "pi-package",
10
+ "pi-extension",
11
+ "oh-my-pi",
12
+ "omp",
13
+ "free-models",
14
+ "opencode",
15
+ "kilocode",
16
+ "ai-models",
17
+ "relay"
18
+ ],
19
+ "author": "trefeon",
20
+ "license": "MIT",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/trefeon/pi-freeflow.git"
24
+ },
25
+ "homepage": "https://github.com/trefeon/pi-freeflow#readme",
26
+ "engines": {
27
+ "node": ">=22.19.0"
28
+ },
29
+ "omp": {
30
+ "extensions": [
31
+ "./extensions"
32
+ ]
33
+ },
34
+ "pi": {
35
+ "extensions": [
36
+ "./extensions"
37
+ ]
38
+ },
39
+ "files": [
40
+ "extensions",
41
+ "src",
42
+ "README.md",
43
+ "LICENSE"
44
+ ],
45
+ "scripts": {
46
+ "test": "node --experimental-strip-types --test --test-concurrency=1 test/**/*.test.ts",
47
+ "typecheck": "tsc --noEmit",
48
+ "smoke": "node --experimental-strip-types -e \"import('./extensions/index.ts').then(() => console.log('✓ Smoke test passed: extensions/index.ts loaded successfully')).catch(err => { console.error(err); process.exit(1); })\"",
49
+ "changeset": "changeset",
50
+ "docs:dev": "vitepress dev docs",
51
+ "docs:build": "vitepress build docs"
52
+ },
53
+ "devDependencies": {
54
+ "@changesets/cli": "^2.27.0",
55
+ "@types/node": "^22.13.9",
56
+ "typescript": "^5.8.2",
57
+ "vitepress": "^1.6.4"
58
+ },
59
+ "dependencies": {
60
+ "undici": "^8.10.0"
61
+ }
62
+ }
package/src/catalog.ts CHANGED
@@ -34,7 +34,7 @@ import type {
34
34
  export const DEAD_MODEL_IDS = new Set<string>(["deepseek-v4-flash-free", "x-preview-f-free"]);
35
35
  /**
36
36
  * In-memory cache of currently active/available free models.
37
- * Initialized with all 25 verified models for 0ms instant availability.
37
+ * Initialized with all 28 verified models for 0ms instant availability.
38
38
  */
39
39
  let aliveCatalog: RegisteredModel[] = ALL_MODELS.map((m) => ({
40
40
  ...m,
@@ -328,6 +328,6 @@ export async function refreshCatalog(force = false): Promise<RegisteredModel[]>
328
328
  } catch (err) {
329
329
  logDebug("Failed reading stale catalog cache", { error: String(err) });
330
330
  }
331
- // No valid cache — return in-memory static 25 (host will refresh if needed)
331
+ // No valid cache — return in-memory static 28 (host will refresh if needed)
332
332
  return aliveCatalog;
333
333
  }
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 25 free models (7 OpenCode Zen + 18 KiloCode Gateway) with:
4
+ * Provides access to 28 free models (8 OpenCode Zen + 20 KiloCode Gateway) 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
@@ -114,7 +114,7 @@ export function buildProviderConfig(
114
114
  ): ProviderConfig {
115
115
  return {
116
116
  baseUrl: `http://${HOST}:${port}/v1`,
117
- apiKey: "placeholder",
117
+ apiKey: "public",
118
118
  api: "openai-completions",
119
119
  compat: { supportsDeveloperRole: false },
120
120
  models: models.map((m) => {
@@ -310,7 +310,7 @@ export default async function (pi: ExtensionAPI): Promise<void> {
310
310
  ? `Relay pool ready: ${freshRelayState.relays.length} relay(s). Run /freeflow for pool management.`
311
311
  : "Relay pool empty — direct mode. Run /freeflow deploy to add your own egress.";
312
312
  ctx.ui?.notify?.(
313
- `freeflow ready: 25 free models via local proxy 127.0.0.1:28180. ${hint}`,
313
+ `freeflow ready: 28 free models via local proxy 127.0.0.1:28180. ${hint}`,
314
314
  "info",
315
315
  );
316
316
  }
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 25 verified free models:
5
- * - 7 OpenCode Zen models (1 Responses API + 6 Chat Completions)
6
- * - 18 KiloCode Keyless Gateway models (17 OpenRouter format + 1 Standard format)
4
+ * Defines the 28 verified free models:
5
+ * - 8 OpenCode Zen models (1 Responses API + 7 Chat Completions)
6
+ * - 20 KiloCode Keyless Gateway models (19 OpenRouter format + 1 Standard format)
7
7
  */
8
8
 
9
9
  import type { ModelDef, ThinkingLevelMap, Upstream } from "./types.ts";
@@ -126,6 +126,22 @@ export const OPENCODE_MODELS: ModelDef[] = [
126
126
  xhigh: "xhigh",
127
127
  max: null,},
128
128
  },
129
+ {
130
+ id: "ling-3.0-flash-fin-free",
131
+ name: "Ling 3.0 Flash Fin (256K)",
132
+ reasoning: true,
133
+ contextWindow: 262_144,
134
+ maxTokens: 131_072,
135
+ input: ["text"],
136
+ thinkingLevelMap: {
137
+ off: null,
138
+ minimal: "minimal",
139
+ low: "low",
140
+ medium: "medium",
141
+ high: "high",
142
+ xhigh: "xhigh",
143
+ max: null,},
144
+ },
129
145
  ];
130
146
 
131
147
  /**
@@ -328,6 +344,26 @@ export const KILO_MODELS: ModelDef[] = [
328
344
  thinkingFormat: "openrouter",
329
345
  thinkingLevelMap: KILO_REASONING_MAP,
330
346
  },
347
+ {
348
+ id: "thinkingmachines/inkling:free",
349
+ name: "Inkling (1M)",
350
+ reasoning: true,
351
+ contextWindow: 1_048_576,
352
+ maxTokens: 262_144,
353
+ input: ["text", "image"],
354
+ thinkingFormat: "openrouter",
355
+ thinkingLevelMap: KILO_REASONING_MAP,
356
+ },
357
+ {
358
+ id: "thinkingmachines/inkling-small:free",
359
+ name: "Inkling Small (1M)",
360
+ reasoning: true,
361
+ contextWindow: 1_048_576,
362
+ maxTokens: 262_144,
363
+ input: ["text", "image"],
364
+ thinkingFormat: "openrouter",
365
+ thinkingLevelMap: KILO_REASONING_MAP,
366
+ },
331
367
  ];
332
368
 
333
369
  /**
@@ -352,6 +388,8 @@ export const MODEL_ALIASES: Record<string, string> = {
352
388
  "minimax-m2.7": "minimax/minimax-m2.7:free",
353
389
  "minimax-m3": "minimax/minimax-m3:free",
354
390
  "ling-3.0-flash-fin": "inclusionai/ling-3.0-flash-fin:free",
391
+ "inkling": "thinkingmachines/inkling:free",
392
+ "inkling-small": "thinkingmachines/inkling-small:free",
355
393
  // provider-prefixed short aliases (slash-normalized)
356
394
  "hy3:free": "tencent/hy3:free",
357
395
  "laguna-s-2.1:free": "poolside/laguna-s-2.1:free",
@@ -379,7 +417,7 @@ export const KILO_MODEL_IDS = new Set<string>([
379
417
  ]);
380
418
 
381
419
  /**
382
- * Combined list of all 25 static free models (canonical)
420
+ * Combined list of all 28 static free models (canonical)
383
421
  */
384
422
  export const ALL_MODELS: ModelDef[] = [...OPENCODE_MODELS, ...KILO_MODELS];
385
423
 
package/src/proxy.ts CHANGED
@@ -85,13 +85,12 @@ export function sanitizeHeaders(
85
85
  incoming: http.IncomingHttpHeaders,
86
86
  targetHost: string,
87
87
  ): Record<string, string> {
88
- // Positive allowlist: only headers the proxy is meant to relay. Everything
89
- // else (host, cookie, x-forwarded-*, client x-relay-*) is dropped so a
90
- // browser/extension cannot smuggle headers into the upstream request.
88
+ // authorization is deliberately NOT forwarded: the host provider registers
89
+ // with a dummy key (placeholder), and zen free models are keyless sending
90
+ // that fake key upstream gets 401 Invalid API key. Kilo injects its own key.
91
91
  const allowed: Record<string, true> = {
92
92
  "content-type": true,
93
93
  accept: true,
94
- authorization: true,
95
94
  "x-request-id": true,
96
95
  };
97
96
  const sanitized: Record<string, string> = {};