openzoo 0.51.3 → 0.51.5

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.
Files changed (54) hide show
  1. package/README.md +2 -0
  2. package/bin/openzoo.js +14 -37
  3. package/lib/chatgpt.js +328 -0
  4. package/lib/config.js +34 -0
  5. package/lib/pay.js +60 -4
  6. package/lib/proxy.js +8 -7
  7. package/lib/stripeOnramp.js +9 -3
  8. package/package.json +4 -7
  9. package/transmute/README.md +0 -385
  10. package/transmute/bin/openzoo-transmute.js +0 -7
  11. package/transmute/docs/OPENZOO_CLI_PATCH.md +0 -139
  12. package/transmute/docs/VERCEL_TO_SOLANA.md +0 -381
  13. package/transmute/lib/build.js +0 -381
  14. package/transmute/lib/cli.js +0 -293
  15. package/transmute/lib/compile/bytecode.js +0 -591
  16. package/transmute/lib/compile/hostids.js +0 -11
  17. package/transmute/lib/compile/index.js +0 -159
  18. package/transmute/lib/compile/ir.js +0 -1634
  19. package/transmute/lib/compile/parse.js +0 -753
  20. package/transmute/lib/compile/rust.js +0 -781
  21. package/transmute/lib/deploy.js +0 -357
  22. package/transmute/lib/eligibility.js +0 -211
  23. package/transmute/lib/gateway.js +0 -710
  24. package/transmute/lib/hub.js +0 -290
  25. package/transmute/lib/solana.js +0 -387
  26. package/transmute/lib/vercel.js +0 -296
  27. package/transmute/lib/wallet.js +0 -41
  28. package/transmute/lib/wire.js +0 -208
  29. package/transmute/prebuilt/zoo_vm.v0.so +0 -0
  30. package/transmute/runtime/zoo-host/Cargo.lock +0 -588
  31. package/transmute/runtime/zoo-host/Cargo.toml +0 -30
  32. package/transmute/runtime/zoo-host/examples/vmrun.rs +0 -43
  33. package/transmute/runtime/zoo-host/src/assets.rs +0 -214
  34. package/transmute/runtime/zoo-host/src/ctx.rs +0 -350
  35. package/transmute/runtime/zoo-host/src/fmt.rs +0 -188
  36. package/transmute/runtime/zoo-host/src/helpers.rs +0 -245
  37. package/transmute/runtime/zoo-host/src/json.rs +0 -263
  38. package/transmute/runtime/zoo-host/src/kv.rs +0 -206
  39. package/transmute/runtime/zoo-host/src/lib.rs +0 -137
  40. package/transmute/runtime/zoo-host/src/site.rs +0 -94
  41. package/transmute/runtime/zoo-host/src/val.rs +0 -1050
  42. package/transmute/runtime/zoo-host/src/vm.rs +0 -902
  43. package/transmute/runtime/zoo-host/src/wire.rs +0 -239
  44. package/transmute/runtime/zoo-vm/Cargo.lock +0 -596
  45. package/transmute/runtime/zoo-vm/Cargo.toml +0 -23
  46. package/transmute/runtime/zoo-vm/src/lib.rs +0 -92
  47. package/transmute/site/.env.production +0 -2
  48. package/transmute/site/api/echo.js +0 -10
  49. package/transmute/site/api/hello.js +0 -12
  50. package/transmute/site/api/hits.js +0 -13
  51. package/transmute/site/api/time.js +0 -5
  52. package/transmute/site/package.json +0 -6
  53. package/transmute/site/public/app.js +0 -16
  54. package/transmute/site/public/index.html +0 -96
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "openzoo",
3
- "version": "0.51.3",
4
- "description": "Local x402-paying proxy + MCP server for openzoo.fun \u2014 point any OpenAI-compatible harness (Cursor, Claude Code, aider, SDKs) at localhost and it pays per call from a local burner wallet. Solana and Base rails live; Robinhood experimental. `openzoo build|deploy|serve|hub|runtime` transmute a Vercel-shaped app (Next.js / Vite + /api) into Solana accounts on the shared zoo-vm Pinocchio runtime (~0.05 SOL a site) or a dedicated program, mainnet.",
3
+ "version": "0.51.5",
4
+ "description": "Local x402-paying proxy + MCP server for openzoo.fun \u2014 point any OpenAI-compatible harness (Cursor, Claude Code, aider, SDKs) at localhost and it pays per call from a local burner wallet. Solana and Base rails live; Robinhood experimental.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "bin": {
@@ -10,8 +10,7 @@
10
10
  "files": [
11
11
  "bin",
12
12
  "lib",
13
- "README.md",
14
- "transmute"
13
+ "README.md"
15
14
  ],
16
15
  "engines": {
17
16
  "node": ">=18"
@@ -26,9 +25,7 @@
26
25
  "dugite": "^3.2.3",
27
26
  "selfsigned": "^5.5.0",
28
27
  "viem": "^2.21.0",
29
- "zod": "^3.24.0",
30
- "acorn": "^8.14.0",
31
- "acorn-walk": "^8.3.4"
28
+ "zod": "^3.24.0"
32
29
  },
33
30
  "repository": {
34
31
  "type": "git",
@@ -1,385 +0,0 @@
1
- # openzoo-transmute
2
-
3
- Transmute a Vercel-shaped app — Next.js `pages/api`, app-router `route.ts`, or
4
- a Vite repo with `api/*.js` — into **(a)** a Rust [Pinocchio](https://github.com/anza-xyz/pinocchio)
5
- Solana program that hosts the `/api/*` Lambdas as instruction routes and
6
- **(b)** the static build stored in program-derived accounts; deploy it to
7
- Solana; serve it through a local gateway that speaks the same routes Vercel
8
- would.
9
-
10
- ```
11
- vercel build ─► .vercel/output/{config.json, static/, functions/*.func}
12
- │
13
- openzoo build ─► .zoo-out/.zoo/{crate/ (Rust), manifest.json, deploy/<site>.so, static-plan.json}
14
- openzoo deploy ─► program id + one asset account per file + /.zoo/manifest.json
15
- openzoo serve ─► http://127.0.0.1:4402/ (GET = free simulation, POST = signed transaction)
16
- ```
17
-
18
- The whole reverse-engineering — what `vercel build` emits, what the Lambda
19
- receives from `@vercel/node-bridge`, what Fluid Compute changes, the term-by-term
20
- mapping, the limits, the cost model and the security notes — is in
21
- [`docs/VERCEL_TO_SOLANA.md`](docs/VERCEL_TO_SOLANA.md). The patch that mounts
22
- these commands in `npx openzoo` is [`docs/OPENZOO_CLI_PATCH.md`](docs/OPENZOO_CLI_PATCH.md).
23
-
24
- ## How it works
25
-
26
- * **The Lambda contract becomes instruction data.** Vercel's bridge hands a
27
- function `{method, path, headers, body}` and expects `{statusCode, headers,
28
- body}`. `lib/wire.js` and `runtime/zoo-host/src/wire.rs` are that pair as
29
- bytes: `[tag][route][method][path][query][headers][body]` in, `[status][headers][body]`
30
- out via `set_return_data` plus `sol_log_data` chunks.
31
- * **Each handler becomes a Rust route.** The compiler (`lib/compile/`) lowers
32
- the JS subset it accepts onto `zoo_host::{Ctx, Val}` — a JS-semantics dynamic
33
- value with `req.query`/`req.body`/`res.json`/`Response.json`/`kv.*`/`process.env`
34
- implemented in `runtime/zoo-host/src/{ctx,val,json,kv}.rs`. Anything outside
35
- the subset (network, fs, timers, regex, classes…) is reported with `file:line`
36
- and the rest of the app still builds.
37
- * **`static/` becomes asset PDAs.** One rent-exempt account per file, written in
38
- 900-byte chunks, writes gated by the program's upgrade authority
39
- (`runtime/zoo-host/src/assets.rs`). `@vercel/kv` becomes KV PDAs the program
40
- discovers by dry run.
41
- * **Reads are free, writes are transactions.** `GET/HEAD/OPTIONS` run as
42
- `simulateTransaction` (no signature, no fee); everything else is a signed
43
- transaction paid by the gateway wallet. Without a wallet the gateway answers
44
- `402` — the seam where openzoo's x402 flow plugs in.
45
-
46
- ## Install
47
-
48
- ```sh
49
- # from this repository — the package is not on npm yet (`npm view openzoo-transmute` → 404)
50
- cd openzoo-transmute && npm install --no-audit --no-fund
51
- node bin/openzoo-transmute.js help # or: npm link && openzoo-transmute help
52
-
53
- # once published: npx openzoo-transmute help
54
- # through the openzoo CLI once docs/OPENZOO_CLI_PATCH.md is applied:
55
- npx openzoo build | deploy | serve | inspect
56
- ```
57
-
58
- Until it is published, read `npx openzoo-transmute` in the examples below as
59
- `node <repo>/openzoo-transmute/bin/openzoo-transmute.js` (or the `npm link`ed
60
- `openzoo-transmute`).
61
-
62
- Requirements: Node ≥ 18, and for `build`/`deploy` the Solana toolchain
63
- (`cargo build-sbf`): `sh -c "$(curl -sSfL https://release.anza.xyz/stable/install)"`.
64
- `build --skip-cargo` works without it (you get the crate and the cost sheet, no `.so`).
65
-
66
- ## Quickstart (shared runtime — the default)
67
-
68
- A site does not need its own program. The `zoo-vm` runtime
69
- (`runtime/zoo-vm`, prebuilt at `prebuilt/zoo_vm.v0.so`) is deployed once per
70
- cluster; every site is then just data accounts under it — a 66-byte site
71
- account, its bytecode module (`/.zoo/code.bin`, ~1–2 KB for a handful of
72
- routes), its static files and its manifest. Rent for a small site is
73
- **≈0.03–0.1 SOL** instead of ≈1 SOL+ for a dedicated program.
74
-
75
- ```sh
76
- cd my-next-app # or a Vite repo with api/*.js, or a .vercel/output
77
- npx openzoo-transmute inspect # the app in Vercel terms + eligibility report
78
- npx openzoo-transmute build # → .zoo-out/.zoo/{code.bin,manifest.json,…}; no cargo needed
79
- npx openzoo-transmute deploy --cluster mainnet --runtime <zoo-vm id> --yes
80
- npx openzoo-transmute serve # site id + runtime read from .zoo/deploy.json
81
- open http://127.0.0.1:4402/ # explorer at /.zoo/, manifest at /.zoo/manifest.json
82
- ```
83
-
84
- The site id is the public key of `.zoo/site-keypair.json` (looks like a
85
- program id, costs nothing; keep it — redeploys update the same site). The hub
86
- serves it at `https://sites.openzoo.fun/s/<siteId>` once its
87
- `OPENZOO_VM_PROGRAM` points at the same runtime.
88
-
89
- Deploying the runtime itself (once, ≈2.2 SOL on mainnet for the 313 KB .so,
90
- upgradeable by the deployer's key):
91
-
92
- ```sh
93
- npx openzoo-transmute runtime deploy --cluster mainnet --yes # keypair → ./zoo-vm-keypair.json
94
- export OPENZOO_VM_PROGRAM=<printed id>
95
- ```
96
-
97
- `--target program` keeps the original path: one Pinocchio program per site,
98
- compiled from the generated crate with `cargo build-sbf`.
99
-
100
- ## Quickstart (dedicated program)
101
-
102
- ```sh
103
- npx openzoo-transmute build --target program # → .zoo-out/.zoo/{crate,manifest.json,deploy/*.so}
104
- npx openzoo-transmute deploy --cluster mainnet --yes
105
- npx openzoo-transmute serve <programId> --cluster mainnet
106
- ```
107
-
108
- `inspect` prints one line per function with its route, style, runtime,
109
- `maxDuration`, `memory` and methods, marks each `✓`/`✗` and says why:
110
-
111
- ```
112
- functions (2):
113
- ✓ /api/hello pages nodejs20.x 10s 1024MB any
114
- pages/api/hello.js env=GREETING
115
- ✓ /api/counter app nodejs20.x 10s 1024MB POST
116
- app/api/counter/route.js env=GREETING
117
- ```
118
-
119
- `deploy` prints the rent sheet before spending anything and refuses mainnet
120
- without `--yes` (this one was measured on a local validator for the two-route
121
- sample in [The generated crate](#the-generated-crate), `.so` built with `--arch v3`):
122
-
123
- ```
124
- item bytes SOL
125
- program account 36 0.0011
126
- program data (2×.so = 416,672 B) 416,717 2.9012
127
- /app.js 52 0.0013
128
- /index.html 44 0.0012
129
- /.zoo/manifest.json (manifest) 939 0.0074
130
- rent total 2.9123
131
- + tx fees (approx.) 0.0012
132
- TOTAL 2.9135
133
- (rent per http://127.0.0.1:8899)
134
- ```
135
-
136
- Then `serve` maps HTTP onto the program:
137
-
138
- ```
139
- GET / → 200 text/html (asset PDA, etag, 304 on If-None-Match)
140
- GET /api/hello?name=zoo&n=21 → 200 {"hello":"zoo","n":42,"greeting":"hi"} x-zoo-simulated: true, x-zoo-cu: 11067
141
- POST /api/counter → 200 {"hits":1} x-zoo-signature: <tx>, x-zoo-simulated: false, x-zoo-cu: 10634
142
- POST /api/counter → 402 payment required (gateway started without a keypair)
143
- ```
144
-
145
- ## CLI reference
146
-
147
- | command | what it does |
148
- |---|---|
149
- | `build [dir] [--target shared\|program] [--runtime <id>] [--out .zoo-out] [--name <crate>] [--arch v0\|v3] [--cluster <c>] [--skip-cargo] [--json]` | `--target shared` (default): transmute to a `ZOOB` bytecode module at `<out>/.zoo/code.bin` for the shared runtime, no cargo, site-sized cost sheet. `--target program`: read the app (`lib/vercel.js`), transmute (`lib/compile/`), write `<out>/.zoo/{crate/, manifest.json, report.json, static-plan.json, build.json}`, run `cargo build-sbf --arch <arch>` → `.zoo/deploy/<name>.so`, print the cost sheet (`--cluster` only chooses the RPC whose rent parameters price it, default `localnet`; unreachable → the 6.96 SOL/MB rule). Default arch is `v0` (mainnet); `deploy` re-detects the cluster's SBPF version and rebuilds if needed. Exit code 2 when nothing was eligible |
150
- | `deploy [dir\|outDir] [--cluster mainnet\|devnet\|testnet\|localnet\|<url>] [--keypair <path>] [--yes] [--runtime <id>] [--program <id>] [--concurrency 4] [--skip-assets] [--force] [--json]` | shared build: create the site account under `--runtime` (or `OPENZOO_VM_PROGRAM`), upload `code.bin`, assets and manifest as PDAs namespaced by the site id (keypair at `.zoo/site-keypair.json`). Program build: deploy or upgrade the program (keypair kept at `.zoo/program-keypair.json`, so redeploys upgrade in place), upload every static file (unchanged ones skipped by comparing bytes on chain), write `/.zoo/manifest.json`, record `.zoo/deploy.json` |
151
- | `serve [siteId\|programId] [--runtime <id>] [--cluster <c>] [--port 4402] [--host 127.0.0.1] [--keypair <path>\|--no-keypair] [--quiet]` | the local gateway; with `--runtime` the positional is a site id on the shared runtime. It defaults to the `.zoo/deploy.json` under the current directory (or its `.zoo-out/`). Reads are simulated, writes are signed by the wallet; without a wallet (or with `--no-keypair`) writes answer 402. Without `--cluster`/`OPENZOO_CLUSTER` this command assumes `localnet`, unlike the others |
152
- | `inspect [dir] [--json]` | the Vercel model (functions, static files, routes, crons) plus the eligibility report |
153
- | `status <siteId\|programId> [--runtime <id>] [--cluster <c>] [--json]` | program account, authority, `maxDataLen`, deploy slot, and the on-chain manifest; with `--runtime`, the site account and its manifest |
154
- | `runtime deploy [--cluster <c>] [--keypair <path>] [--yes] [--program-keypair zoo-vm-keypair.json] [--so <path>]` | deploy (or upgrade in place) the shared `zoo-vm` runtime: `prebuilt/zoo_vm.<arch>.so`, or built from `runtime/zoo-vm` when no prebuilt matches the cluster's SBPF version |
155
- | `runtime status <id> [--cluster <c>]` | the runtime program's account |
156
- | `help`, `--version` | |
157
-
158
- Environment: `OPENZOO_VM_PROGRAM` (shared runtime id; default for `--runtime`), `OPENZOO_TARGET` (`shared`/`program`), `OPENZOO_CLUSTER` (default `mainnet` for `deploy`/`status`, `localnet` for `serve`), `OPENZOO_RPC` (mainnet RPC
159
- URL), `OPENZOO_KEYPAIR` / `OPENZOO_WALLET` (signer path), `OPENZOO_DEBUG=1`
160
- (stack traces). Signer discovery order (`lib/wallet.js`): `--keypair` →
161
- `OPENZOO_KEYPAIR` → `OPENZOO_WALLET` or `~/.openzoo/wallet.json` (the openzoo
162
- burner, `{solana:[64 bytes]}` or a bare `solana-keygen` array) → `~/.config/solana/id.json`.
163
-
164
- Gateway responses carry `x-zoo-program`, `x-zoo-route`, `x-zoo-simulated`,
165
- `x-zoo-cu`, `x-zoo-signature` (writes) and `x-zoo-asset` (static); route params
166
- reach the handler as `x-zoo-param-<name>` headers. `413` above 900 body bytes,
167
- `405` when an app-router file does not export the method, `502` with the last
168
- program log lines when the instruction fails, `404` JSON (or the app's
169
- `handle: error` route) otherwise.
170
-
171
- ## The generated crate
172
-
173
- `build` writes a crate that depends on the runtime by path. For this two-route
174
- sample —
175
-
176
- ```js
177
- // pages/api/hello.js
178
- export default function handler(req, res) {
179
- res.status(200).json({ hello: req.query.name || 'world', n: Number(req.query.n) * 2, greeting: process.env.GREETING })
180
- }
181
- // app/api/counter/route.js
182
- import { kv } from '@vercel/kv'
183
- export async function POST() { const n = await kv.incr('hits'); return Response.json({ hits: n }) }
184
- ```
185
-
186
- — with `GREETING=hi` in `.env.production`, the compiler (`lib/compile/index.js`,
187
- `transmute()`) emits exactly this; only the ~240-line prelude of helper
188
- functions between the entrypoint and the routes is elided:
189
-
190
- ```toml
191
- # .zoo-out/.zoo/crate/Cargo.toml (crate name = the app directory, underscored)
192
- [package]
193
- name = "my_site"
194
- version = "0.1.0"
195
- edition = "2021"
196
- description = "Generated by openzoo-transmute: Vercel Lambdas re-hosted as a Solana program"
197
- publish = false
198
-
199
- [lib]
200
- crate-type = ["cdylib", "lib"]
201
-
202
- [dependencies]
203
- pinocchio = "0.11.2"
204
- libm = "0.2"
205
- zoo-host = { path = "<openzoo-transmute>/runtime/zoo-host" }
206
-
207
- [profile.release]
208
- overflow-checks = false
209
- lto = "fat"
210
- codegen-units = 1
211
- opt-level = 3
212
- ```
213
-
214
- ```rust
215
- // .zoo-out/.zoo/crate/src/lib.rs
216
- //! Generated by openzoo-transmute — do not edit.
217
- //! nextjs (nextjs) — 2 route(s), 2 static file(s)
218
- //! route 0: /api/hello ← api/hello
219
- //! route 1: /api/counter [POST] ← api/counter
220
- #![no_std]
221
- #![allow(unused_mut, unused_variables, unused_assignments, dead_code, unused_parens, unreachable_code, unused_braces, unused_labels, unused_imports, non_snake_case, non_upper_case_globals, unused_unsafe, clippy::all)]
222
- extern crate alloc;
223
- use alloc::{format, string::String, vec::Vec};
224
- use pinocchio::{AccountView, Address, ProgramResult};
225
- use zoo_host::{Ctx, Route, Val};
226
- use zoo_host::val as zv;
227
- use zoo_host::json as zjson;
228
-
229
- pinocchio::program_entrypoint!(process_instruction);
230
- pinocchio::default_allocator!();
231
- pinocchio::nostd_panic_handler!();
232
-
233
- /// The Lambda environment table, baked at build time (public on chain).
234
- const ENV: &[(&str, &str)] = &[("GREETING", "hi")];
235
- /// Route table: instruction byte 1 indexes it.
236
- const ROUTES: &[Route] = &[route_0, route_1];
237
-
238
- pub fn process_instruction(program_id: &Address, accounts: &mut [AccountView], data: &[u8]) -> ProgramResult {
239
- zoo_host::dispatch(program_id, accounts, data, ROUTES, ENV)
240
- }
241
-
242
- // … prelude: __zoo_query (req.query + route params), __zoo_resp / __zoo_send (Response objects),
243
- // __zoo_cookies, __zoo_num, __zoo_set_headers, … …
244
-
245
- // ---- route 0: api/hello (pages/api/hello.js)
246
- const PARAMS_0: &[(&str, bool)] = &[];
247
- fn route_0(cx: &mut Ctx) -> Result<(), Val> {
248
- let _ = { let __a1 = Val::Num(200.0); cx.res_status(&__a1); Val::Undef };
249
- let _ = { let __a2 = { let mut __o3 = Val::obj(); __o3.set_str("hello", { let __x4 = (__zoo_query(cx, PARAMS_0)).get_str("name"); if !__x4.truthy() { Val::str("world") } else { __x4 } }); __o3.set_str("n", ({ let __a: Vec<Val> = alloc::vec![(__zoo_query(cx, PARAMS_0)).get_str("n")]; zv::global_call("Number", &__a)? }).mul(&(Val::Num(2.0)))); __o3.set_str("greeting", { let __a5 = Val::str("GREETING"); cx.env(&__a5) }); __o3 }; cx.res_json(&__a2); Val::Undef };
250
- Ok(())
251
- }
252
-
253
- // ---- route 1: api/counter (app/api/counter/route.js)
254
- const PARAMS_1: &[(&str, bool)] = &[];
255
- fn route_1(cx: &mut Ctx) -> Result<(), Val> {
256
- let __m = cx.req_method();
257
- match __m.as_str() {
258
- Some("POST") => route_1_post(cx),
259
- Some("OPTIONS") => { cx.res_status(&Val::Num(204.0)); cx.res_header(&Val::str("allow"), &Val::str("POST, OPTIONS")); cx.res_end(&Val::Undef); Ok(()) }
260
- _ => { cx.res_status(&Val::Num(405.0)); cx.res_header(&Val::str("allow"), &Val::str("POST, OPTIONS")); cx.res_end(&Val::Undef); Ok(()) }
261
- }
262
- }
263
- fn route_1_post(cx: &mut Ctx) -> Result<(), Val> {
264
- let mut v_n: Val = { let __a6 = Val::str("hits"); let __a7 = Val::Num(1.0); cx.kv_incrby(&__a6, &__a7)? };
265
- { let __rv = { let __b = { let mut __o8 = Val::obj(); __o8.set_str("hits", v_n.clone()); __o8 }; let __i = Val::Undef; __zoo_resp("json", __b, __i) }; __zoo_send(cx, &__rv)?; }
266
- return Ok(());
267
- Ok(())
268
- }
269
- ```
270
-
271
- `Route = fn(&mut Ctx) -> Result<(), Val>`; `Err(v)` is a thrown JS value and
272
- answers 500 like a crashed Lambda; returning without responding answers 504 like
273
- a Lambda that never ended its response. The `?` after `kv_incrby` is the KV
274
- account-discovery path: a missing account is logged as `ZOOK` and the gateway
275
- retries with it. Built with `--arch v3` this crate is a 208,336-byte `.so`
276
- (a 416,717-byte program data account, 2.90 SOL of rent); on a local validator
277
- `GET /api/hello?name=zoo&n=21` costs 11,067 CU and `POST /api/counter`
278
- 10,634 CU — nowhere near the 400 k limit.
279
-
280
- ## Tests
281
-
282
- ```sh
283
- cd openzoo-transmute
284
- npm install --no-audit --no-fund
285
- npm test # node --test test/*.test.js
286
- ```
287
-
288
- The unit tests need no chain. The end-to-end test in `test/gateway.test.js`
289
- deploys a pre-built site program and drives the gateway against a **local
290
- validator**; it skips itself with a reason when either is missing:
291
-
292
- ```sh
293
- # 1. toolchain (once)
294
- sh -c "$(curl -sSfL https://release.anza.xyz/stable/install)"
295
- export PATH="$HOME/.local/share/solana/install/active_release/bin:$PATH"
296
-
297
- # 2. a validator with a funded wallet (keep it running in another shell)
298
- solana-test-validator --reset
299
- solana airdrop 100 --url http://127.0.0.1:8899 # ~/.config/solana/id.json
300
-
301
- # 3. a site program with route 0 = /api/hello (pages style) and route 1 =
302
- # /api/counter (POST, kv key "hits") — e.g. the two-route sample below
303
- # (test validator = SBPF v3; mainnet = v0):
304
- openzoo-transmute build <sample> --arch v3 # → <sample>/.zoo-out/.zoo/deploy/<name>.so
305
- # then point the SAMPLE_SO constant in test/gateway.test.js at that .so
306
- # (it is a fixed path today; no sample crate ships with the package yet)
307
-
308
- # 4. run
309
- OPENZOO_TEST_RPC=http://127.0.0.1:8899 npm test
310
- ```
311
-
312
- `test/gateway.test.js` looks for the `.so` at `SAMPLE_SO` and a validator at
313
- `OPENZOO_TEST_RPC` (default `http://127.0.0.1:8899`); `test/build.test.js`
314
- probes `localnet` for real rent numbers and falls back to the 6.96 SOL/MB rule.
315
- The runtime crate also builds on the host (`cd runtime/zoo-host && cargo build`;
316
- its `Cargo.toml` pulls the sha2/curve25519 fallbacks for non-SBF targets), and
317
- for the chain: `cargo build-sbf --arch v3` (test validator) or `--arch v0` (mainnet).
318
-
319
- ## Repository layout
320
-
321
- ```
322
- bin/openzoo-transmute.js the executable (→ lib/cli.js run())
323
- lib/cli.js build · deploy · serve · inspect · status
324
- lib/vercel.js the Vercel deployment model (Build Output API v3 reader / synthesizer)
325
- lib/eligibility.js what can run on chain, with reasons
326
- lib/compile/ JS → Rust: parse.js (acorn + TS stripping) → ir.js (lowering) → rust.js (printer, crate skeleton, prelude)
327
- lib/build.js crate + manifest + cargo build-sbf + cost sheet
328
- lib/deploy.js deploy / upgrade + assets + manifest
329
- lib/gateway.js the local HTTP front (routes, static, 402/413 seams, explorer)
330
- lib/solana.js loader (deploy/upgrade in pure JS), assets, KV, invoke + discovery
331
- lib/wire.js the bridge contract as bytes, PDAs
332
- lib/wallet.js signer discovery, cluster URLs
333
- runtime/zoo-host/ the no_std pinocchio runtime the generated crate links
334
- docs/VERCEL_TO_SOLANA.md the reverse-engineering document
335
- docs/OPENZOO_CLI_PATCH.md mounting build/deploy/serve in npx openzoo
336
- ```
337
-
338
- ## Limits at a glance
339
-
340
- | | |
341
- |---|---|
342
- | request body | 900 B (one transaction is 1232 B) |
343
- | response | ≤ 1024 B in return data, ≈ 7 KB through log chunks |
344
- | KV value | 10 000 B JSON per key |
345
- | compute | 400 k CU requested, 1.4 M max |
346
- | clock | seconds |
347
- | rent | 6.96 SOL per MB of static + program bytes, 2 × the `.so` reserved |
348
- | not supported | network, fs, timers, randomness, regex, classes, streaming, `waitUntil`, ISR, crons, middleware, local imports |
349
-
350
- ## The hosted explorer (`hub`)
351
-
352
- `serve` fronts one program on localhost. `hub` fronts every program on a
353
- cluster from one public host, read-only:
354
-
355
- ```
356
- openzoo-transmute hub --cluster mainnet --port 8080 # or: fly deploy (Dockerfile + fly.toml here)
357
- https://<host>/s/<programId> pins that site (cookie) and opens its /
358
- https://<host>/s/<programId>/api/x one page of it, absolute
359
- https://<host>/.hub paste a program id; recently served sites
360
- ```
361
-
362
- Reads are free simulations, exactly as in `serve`. Writes answer 402 on a
363
- public host (a shared signer would be drained); use `serve` with your wallet
364
- locally, or the x402-paid lane once the openzoo proxy fronts it. The site's
365
- own root-relative links keep working because the pinned program rides a
366
- cookie, so a transmuted app needs no base-path changes.
367
-
368
- ## Roadmap
369
-
370
- * **Browser fork / `sol://` scheme** — a browser that resolves
371
- `sol://<programId>/path` straight from the RPC (assets from PDAs, `/api/*`
372
- through `simulateTransaction`), so a site needs no gateway and no domain.
373
- * **x402-paid writes through the openzoo proxy** — the gateway already answers
374
- `402` with an `x402` stub for mutating requests when it has no signer; next is
375
- letting the openzoo proxy pay per request from the burner wallet so a public
376
- gateway never holds keys.
377
- * **Chunked request bodies** — bodies > 900 B staged into a request account over
378
- several transactions (the same mechanism assets already use), then referenced
379
- by the invoke.
380
- * **Response accounts** — responses > ~7 KB written into a per-request account
381
- and read back by the gateway, instead of the log buffer.
382
- * Compiler coverage: local imports, `RegExp`, base64 (`atob`/`btoa`), `cookies()`,
383
- `URLSearchParams`, more of `@vercel/kv` (`hget`, lists, `expire`).
384
-
385
- License: MIT.
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env node
2
- // `openzoo-transmute build|deploy|serve|inspect|status|help` — see lib/cli.js.
3
- import { run } from '../lib/cli.js';
4
-
5
- const code = await run(process.argv.slice(2));
6
- // `serve` returns 0 while its server keeps the loop alive; only bail on failure.
7
- if (code) process.exit(code);
@@ -1,139 +0,0 @@
1
- # Mounting `build` / `deploy` / `serve` in `npx openzoo`
2
-
3
- The openzoo CLI (`staccDOTsol/openzoo`, `bin/openzoo.js`) gains four
4
- subcommands that delegate to this package. Nothing is duplicated: `openzoo`
5
- forwards its argv to `openzoo-transmute/lib/cli.js`, whose `run(argv)` resolves
6
- to the exit code, sets `process.exitCode` when non-zero and never calls
7
- `process.exit` itself (so `serve` keeps the event loop — and its HTTP server — alive).
8
-
9
- Two files change.
10
-
11
- **Status.** openzoo's `transmute-cli` branch carries hunk 1 (`bin/openzoo.js`,
12
- byte-identical to the diff below) and nothing else. Hunk 2 waits for
13
- `openzoo-transmute` to be published: `npm view openzoo-transmute` is a 404
14
- today, so both the `"openzoo-transmute": "^0.1.0"` dependency and the
15
- `npx --yes openzoo-transmute@latest` fallback fail on a fresh machine. Until
16
- then, install the package from its path so the `import('openzoo-transmute/lib/cli.js')`
17
- branch resolves: `npm install /path/to/leCore/openzoo-transmute` inside the
18
- openzoo checkout (or `npm link` in that directory, then `npm link openzoo-transmute`).
19
-
20
- ## 1. `bin/openzoo.js`
21
-
22
- ```diff
23
- --- a/bin/openzoo.js
24
- +++ b/bin/openzoo.js
25
- @@ -169,6 +169,16 @@
26
- npx openzoo contexts --forget <hash|all> drop manifest entries
27
- npx openzoo balance wallet balance on every rail — Solana (USDC/TOKEN/SOL),
28
- Base (USDC/ETH), Robinhood Chain (USDG/memecoins/ETH)
29
- + npx openzoo build [dir] transmute a Vercel-shaped app (Next.js pages/api,
30
- + app/**/route, Vite + api/*) into a Pinocchio Rust
31
- + program + asset plan (--out .zoo-out --arch v0|v3)
32
- + npx openzoo deploy [dir] deploy the program and the static frontend to
33
- + Solana accounts (--cluster mainnet|devnet|localnet,
34
- + --yes to accept the rent estimate; burner wallet)
35
- + npx openzoo serve <program> local gateway/explorer for a deployed site
36
- + (--cluster, --port 4402); /api/* reads are free
37
- + simulations, writes are signed by the burner
38
- + npx openzoo inspect [dir] print the app in Vercel terms + eligibility report
39
- npx openzoo address print both funding addresses (Solana + EVM)
40
- npx openzoo help this text
41
-
42
- @@ -403,6 +413,33 @@
43
- if (bal <= 0) console.log('buy some with: npx openzoo topup 10');
44
- break;
45
- }
46
- + case 'build':
47
- + case 'deploy':
48
- + case 'serve':
49
- + case 'inspect':
50
- + case 'transmute': {
51
- + // VERCEL-SHAPED APP -> SOLANA MAINNET. `openzoo build|deploy|serve` hand
52
- + // the argv to openzoo-transmute (a sibling npm package): it reads the
53
- + // app the way `vercel build` would (pages/api, app/**/route, api/*,
54
- + // .vercel/output), transmutes each Lambda into a Pinocchio Rust program
55
- + // route, stores the static build in asset accounts, deploys with the
56
- + // burner wallet at ~/.openzoo/wallet.json, and `serve` runs the local
57
- + // gateway/explorer that maps http://localhost:4402/... onto the program.
58
- + // Dynamic import so a machine without the package still runs every other
59
- + // command; the fallback shells out to npx so the very first `openzoo
60
- + // deploy` works before the dependency is pinned in package.json.
61
- + const argv = process.argv.slice(2).map((a, i) => (i === 0 && a === 'transmute' ? 'help' : a));
62
- + let cli = null;
63
- + try { cli = await import('openzoo-transmute/lib/cli.js'); } catch { /* not installed alongside */ }
64
- + if (cli) {
65
- + await cli.run(argv);
66
- + } else {
67
- + const { spawnSync } = await import('child_process');
68
- + const r = spawnSync(process.platform === 'win32' ? 'npx.cmd' : 'npx', ['--yes', 'openzoo-transmute@latest', ...argv], { stdio: 'inherit' });
69
- + process.exit(r.status === null ? 1 : r.status);
70
- + }
71
- + break;
72
- + }
73
- case 'address':
74
- (await import('../lib/info.js')).printAddress();
75
- break;
76
- ```
77
-
78
- The first hunk lands inside the `HELP` template literal, right after the
79
- `balance` lines and before `address`; the second goes into `main()`'s `switch
80
- (cmd)` between the `credits` and `address` cases. `openzoo transmute` with no
81
- further arguments is rewritten to `help` so it prints this package's usage.
82
-
83
- ## 2. `package.json`
84
-
85
- ```diff
86
- --- a/package.json
87
- +++ b/package.json
88
- @@ -21,9 +21,10 @@
89
- "dependencies": {
90
- "@modelcontextprotocol/sdk": "^1.12.0",
91
- "@solana/spl-token": "^0.4.14",
92
- "@solana/web3.js": "^1.98.4",
93
- "dugite": "^3.2.3",
94
- + "openzoo-transmute": "^0.1.0",
95
- "selfsigned": "^5.5.0",
96
- "viem": "^2.21.0",
97
- "zod": "^3.24.0"
98
- },
99
- ```
100
-
101
- `openzoo-transmute`'s `package.json` has no `exports` map, so the deep import
102
- `openzoo-transmute/lib/cli.js` resolves as a plain file; both packages already
103
- depend on `@solana/web3.js ^1.98.4`, so one copy is installed. Until the
104
- dependency is pinned, the `catch` branch above shells out to
105
- `npx --yes openzoo-transmute@latest …`, so `npx openzoo deploy` works on a
106
- fresh machine either way — once the package is on npm (see *Status* above).
107
-
108
- ## The wallet
109
-
110
- Nothing to configure. `openzoo-transmute/lib/wallet.js::loadWallet()` looks, in order, at
111
-
112
- 1. `--keypair <path>`
113
- 2. `$OPENZOO_KEYPAIR`
114
- 3. `$OPENZOO_WALLET`, else **`~/.openzoo/wallet.json`** — the openzoo burner
115
- wallet, in either of its shapes: `{ "solana": [64 bytes], ... }` (what
116
- `npx openzoo address` creates) or a bare `solana-keygen` array
117
- 4. `~/.config/solana/id.json` (the Solana CLI keypair)
118
-
119
- so a user who has run `npx openzoo address` already has the signer `deploy` and
120
- `serve` will use, and the same `OPENZOO_WALLET` / `OPENZOO_RPC` variables
121
- listed in openzoo's `HELP` apply. `OPENZOO_CLUSTER` selects the cluster
122
- (`mainnet` by default; `deploy` refuses mainnet without `--yes` after printing
123
- the rent sheet). The mainnet RPC default is the one the openzoo proxy uses
124
- (`lib/config.js` `rpcUrl`), overridable with `OPENZOO_RPC`.
125
-
126
- ## Checking the patch
127
-
128
- ```sh
129
- npm install # pulls openzoo-transmute (or: npm install /path/to/leCore/openzoo-transmute)
130
- node bin/openzoo.js help | grep -A1 'openzoo build'
131
- node bin/openzoo.js transmute # prints openzoo-transmute's usage
132
- node bin/openzoo.js inspect ./my-app # Vercel model + eligibility, no chain access
133
- node bin/openzoo.js build ./my-app --skip-cargo # crate + cost sheet without the Solana toolchain
134
- node bin/openzoo.js deploy ./my-app --cluster devnet
135
- node bin/openzoo.js serve --cluster devnet # program id from ./my-app/.zoo-out/.zoo/deploy.json
136
- ```
137
-
138
- Exit codes come back from `run()`: `0` ok, `1` error (message on stderr,
139
- `OPENZOO_DEBUG=1` for the stack), `2` when `build` found nothing eligible.