pi-freeflow 1.28.0 → 1.29.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 +14 -0
- package/README.md +10 -9
- package/package.json +1 -1
- package/src/commands.ts +29 -16
- package/src/opencode-fingerprint.ts +844 -743
- package/src/probe.ts +153 -153
- package/src/proxy.ts +10 -2
- package/src/relay-state.ts +21 -1
- package/src/stream-pipe.ts +720 -306
- package/src/tool-translation.ts +93 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.29.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- e119a71: Sync upstream tracking to v1.18.34 and land compatibility improvements: Vercel relays are now treated as a last resort behind Cloudflare with clear quota guidance, new relays show when a deployment is disabled by quota, and streamed responses plus tool-using callers stay compatible through the local proxy.
|
|
8
|
+
|
|
9
|
+
## 1.28.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Retry relay reachability checks after deployment so edge routing propagation delays do not show a fresh relay as unreachable — the check retries with progress notices and only warns if the relay stays dark.
|
|
14
|
+
|
|
15
|
+
Fix upstream drift alerts to stop flagging commercial paid model families as free candidates, while known alpha and stealth free models keep their free status.
|
|
16
|
+
|
|
3
17
|
## 1.28.0
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -82,17 +82,24 @@ stays in memory and is never written to disk.
|
|
|
82
82
|
|
|
83
83
|
Manual fallback per platform:
|
|
84
84
|
|
|
85
|
-
**Option A: Cloudflare Workers, auto deploy**
|
|
85
|
+
**Option A: Cloudflare Workers, auto deploy (recommended)**
|
|
86
86
|
```bash
|
|
87
87
|
/freeflow deploy cloudflare # prompts token in-memory, auto-adds to pool
|
|
88
|
+
# or shorthand: /freeflow deploy (defaults to Cloudflare)
|
|
88
89
|
```
|
|
89
90
|
Manual fallback: `dash.cloudflare.com` → Workers → Create → Deploy → Edit code → paste the canonical worker source (see below) → Deploy → `/freeflow add https://your.workers.dev cf-worker-1`
|
|
90
91
|
|
|
91
|
-
**Option B:
|
|
92
|
+
**Option B: Deno Deploy, auto deploy**
|
|
93
|
+
```bash
|
|
94
|
+
/freeflow deploy deno # prompts token in-memory, auto-adds to pool
|
|
95
|
+
```
|
|
96
|
+
Manual fallback: `dash.deno.com` → New Project → Playground → paste the canonical worker source (see below) → Deploy → `/freeflow add https://your-project.deno.dev deno-relay-1`
|
|
97
|
+
|
|
98
|
+
**Option C: Vercel Edge Relay (last resort, paid plan only)**
|
|
92
99
|
```bash
|
|
93
100
|
/freeflow deploy vercel # prompts token in-memory, auto-adds to pool
|
|
94
|
-
# or shorthand: /freeflow deploy
|
|
95
101
|
```
|
|
102
|
+
Only use with a paid plan: the Hobby plan caps Fast Origin Transfer at 10 GB (~1.2 MB per streaming turn, about 8,000 turns), then every deployment in the project returns HTTP 402 until the quota resets.
|
|
96
103
|
Manual fallback: push 2 files (`api/relay.js` + `vercel.json`) to GitHub, then Import on `vercel.com`, then `/freeflow add https://your.vercel.app vercel-relay-1`
|
|
97
104
|
|
|
98
105
|
For `api/relay.js`, use the canonical worker source (see below); `vercel.json` stays:
|
|
@@ -101,12 +108,6 @@ For `api/relay.js`, use the canonical worker source (see below); `vercel.json` s
|
|
|
101
108
|
{ "rewrites": [{ "source": "/(.*)", "destination": "/api/relay" }] }
|
|
102
109
|
```
|
|
103
110
|
|
|
104
|
-
**Option C: Deno Deploy, auto deploy**
|
|
105
|
-
```bash
|
|
106
|
-
/freeflow deploy deno # prompts token in-memory, auto-adds to pool
|
|
107
|
-
```
|
|
108
|
-
Manual fallback: `dash.deno.com` → New Project → Playground → paste the canonical worker source (see below) → Deploy → `/freeflow add https://your-project.deno.dev deno-relay-1`
|
|
109
|
-
|
|
110
111
|
**Canonical worker source (all platforms)**
|
|
111
112
|
|
|
112
113
|
The relay worker template is generated per deployment by `/freeflow deploy`, with one shared core adapted for Vercel, Cloudflare, and Deno. Deployed relays accept requests from any pi-freeflow user by default, while only forwarding to an allowlist (`https://opencode.ai`, `https://api.kilo.ai`) with private-host and path checks.
|
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.29.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/commands.ts
CHANGED
|
@@ -514,7 +514,7 @@ export function createCommandSpec(
|
|
|
514
514
|
): Omit<RegisteredCommand, "name"> {
|
|
515
515
|
return {
|
|
516
516
|
description:
|
|
517
|
-
"Relay egress: auto | on | off | hide | show | widget hide/show | status | add <URL> [name] | list | use <URL|name|index> [name] | label <target> <name> | remove <target> | test [target|opencode] [--chat] | export [path] [--include-secrets] | import <path> [--merge|--replace] [--dry-run] | logs [level] [n] | debug on|off | refresh | update | deploy
|
|
517
|
+
"Relay egress: auto | on | off | hide | show | widget hide/show | status | add <URL> [name] | list | use <URL|name|index> [name] | label <target> <name> | remove <target> | test [target|opencode] [--chat] | export [path] [--include-secrets] | import <path> [--merge|--replace] [--dry-run] | logs [level] [n] | debug on|off | refresh | update | deploy cloudflare | deploy deno | deploy vercel | install-startup | uninstall-startup | cline login [--key] [slot] | cline accounts | cline logout [slot]",
|
|
518
518
|
getArgumentCompletions: (prefix: string) =>
|
|
519
519
|
[
|
|
520
520
|
"auto",
|
|
@@ -536,9 +536,9 @@ export function createCommandSpec(
|
|
|
536
536
|
"test opencode",
|
|
537
537
|
"url",
|
|
538
538
|
"deploy",
|
|
539
|
-
"deploy vercel",
|
|
540
539
|
"deploy cloudflare",
|
|
541
540
|
"deploy deno",
|
|
541
|
+
"deploy vercel",
|
|
542
542
|
"install-startup",
|
|
543
543
|
"uninstall-startup",
|
|
544
544
|
"cline",
|
|
@@ -666,20 +666,20 @@ export function createCommandSpec(
|
|
|
666
666
|
};
|
|
667
667
|
|
|
668
668
|
const DEPLOY_OPTIONS: Record<string, DeployPlatform> = {
|
|
669
|
-
"
|
|
670
|
-
"Cloudflare (100k req/day)": "cloudflare",
|
|
669
|
+
"Cloudflare (100k req/day — recommended)": "cloudflare",
|
|
671
670
|
"Deno Deploy (100k req/day)": "deno",
|
|
671
|
+
"Vercel (last resort — Hobby 10GB origin cap)": "vercel",
|
|
672
672
|
};
|
|
673
673
|
|
|
674
674
|
const parseDeployPlatform = (raw?: string): DeployPlatform | null => {
|
|
675
675
|
const v = (raw || "").trim().toLowerCase();
|
|
676
|
-
if (!v || v === "
|
|
677
|
-
if (v === "cloudflare" || v === "cf") return "cloudflare";
|
|
676
|
+
if (!v || v === "cloudflare" || v === "cf") return "cloudflare";
|
|
678
677
|
if (v === "deno") return "deno";
|
|
678
|
+
if (v === "vercel") return "vercel";
|
|
679
679
|
return null;
|
|
680
680
|
};
|
|
681
681
|
|
|
682
|
-
const doDeploy = async (platform: DeployPlatform = "
|
|
682
|
+
const doDeploy = async (platform: DeployPlatform = "cloudflare") => {
|
|
683
683
|
const defaultName = `relay-${Date.now().toString(36)}`;
|
|
684
684
|
const label =
|
|
685
685
|
platform === "cloudflare"
|
|
@@ -832,6 +832,10 @@ export function createCommandSpec(
|
|
|
832
832
|
ctx.ui.notify("No saved relays (direct upstream mode)", "info");
|
|
833
833
|
return;
|
|
834
834
|
}
|
|
835
|
+
const isDeadParked = (url: string): boolean => {
|
|
836
|
+
const h = getRelayHealth(url);
|
|
837
|
+
return h != null && h.lastStatus === 402 && Date.now() < h.cooldownUntil;
|
|
838
|
+
};
|
|
835
839
|
const lines = relayState.relays.map((r, idx) => {
|
|
836
840
|
const star = r.url === relayState.url ? "★" : " ";
|
|
837
841
|
const shortName = r.label ? `[${r.label}]` : `[${shortRelayLabel(r.url, relayState.relays)}]`;
|
|
@@ -839,11 +843,13 @@ export function createCommandSpec(
|
|
|
839
843
|
const health = getRelayHealth(r.url);
|
|
840
844
|
const isCooling = health && Date.now() < health.cooldownUntil;
|
|
841
845
|
const remainingSec = isCooling ? Math.ceil((health.cooldownUntil - Date.now()) / 1000) : 0;
|
|
842
|
-
const healthBadge =
|
|
843
|
-
? `
|
|
844
|
-
:
|
|
845
|
-
? `
|
|
846
|
-
:
|
|
846
|
+
const healthBadge = isDeadParked(r.url)
|
|
847
|
+
? ` ✖ [dead — HTTP 402 disabled deployment, parked ${remainingSec}s]`
|
|
848
|
+
: isCooling
|
|
849
|
+
? ` ⚠️ [cooling ${remainingSec}s: ${health.lastStatus ? `HTTP ${health.lastStatus}` : "error"}]`
|
|
850
|
+
: health?.lastLatencyMs != null && Number.isFinite(health.lastLatencyMs)
|
|
851
|
+
? ` ✓ [${health.lastLatencyMs}ms]`
|
|
852
|
+
: " ✓";
|
|
847
853
|
const counterText = health && (health.successCount != null || health.failureCount != null)
|
|
848
854
|
? ` ${health.successCount ?? 0} ok / ${health.failureCount ?? 0} fail`
|
|
849
855
|
: "";
|
|
@@ -1144,7 +1150,14 @@ export function createCommandSpec(
|
|
|
1144
1150
|
})
|
|
1145
1151
|
.join(" | ");
|
|
1146
1152
|
const clineLine = formatClineStatusSnippet(loadPool());
|
|
1147
|
-
|
|
1153
|
+
const deadParked = relayState.relays.filter((r) => {
|
|
1154
|
+
const h = getRelayHealth(r.url);
|
|
1155
|
+
return h != null && h.lastStatus === 402 && Date.now() < h.cooldownUntil;
|
|
1156
|
+
});
|
|
1157
|
+
const deadLine = deadParked.length
|
|
1158
|
+
? `\nDead relays (HTTP 402 disabled deployment, parked): ${deadParked.map((r) => r.url).join(", ")} — delete the Vercel project, run /freeflow remove <url>, then deploy Cloudflare`
|
|
1159
|
+
: "";
|
|
1160
|
+
ctx.ui.notify(`${modeLine} | ${poolLine}\n${stateFileLine}\nUpstream: ${upstreamLine}\n${clineLine}${deadLine}`, "info");
|
|
1148
1161
|
} else if (sub === "kill" || sub === "stop" || sub === "shutdown") {
|
|
1149
1162
|
const port = getClientPort() || PORT;
|
|
1150
1163
|
try {
|
|
@@ -1815,7 +1828,7 @@ export function createCommandSpec(
|
|
|
1815
1828
|
const pf = parseDeployPlatform(rest);
|
|
1816
1829
|
if (!pf) {
|
|
1817
1830
|
ctx.ui.notify(
|
|
1818
|
-
"Unknown platform. Use:
|
|
1831
|
+
"Unknown platform. Use: cloudflare | deno | vercel",
|
|
1819
1832
|
"warning",
|
|
1820
1833
|
);
|
|
1821
1834
|
} else {
|
|
@@ -1840,9 +1853,9 @@ export function createCommandSpec(
|
|
|
1840
1853
|
"Share relays (export file)…",
|
|
1841
1854
|
"Load relays from file (import)…",
|
|
1842
1855
|
"List saved relays",
|
|
1843
|
-
"Deploy Vercel relay…",
|
|
1844
1856
|
"Deploy Cloudflare relay…",
|
|
1845
1857
|
"Deploy Deno relay…",
|
|
1858
|
+
"Deploy Vercel relay (last resort)…",
|
|
1846
1859
|
"Mode: AUTO (auto-detect on model select)",
|
|
1847
1860
|
"Mode: ON (always relay)",
|
|
1848
1861
|
"Mode: OFF (always direct)",
|
|
@@ -1865,7 +1878,7 @@ export function createCommandSpec(
|
|
|
1865
1878
|
await runImport(picked, "merge", false);
|
|
1866
1879
|
} else if (choice === "List saved relays") {
|
|
1867
1880
|
showList();
|
|
1868
|
-
} else if (choice === "Deploy Vercel relay…") {
|
|
1881
|
+
} else if (choice === "Deploy Vercel relay (last resort)…") {
|
|
1869
1882
|
await doDeploy("vercel");
|
|
1870
1883
|
} else if (choice === "Deploy Cloudflare relay…") {
|
|
1871
1884
|
await doDeploy("cloudflare");
|