openzoo 0.48.0 → 0.48.2

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/lib/boxes.js CHANGED
@@ -36,8 +36,15 @@ const CPU_FLAVORS = ['cpu3c', 'cpu3g', 'cpu5c'];
36
36
  // on musl they fail to build because the entrypoint has no toolchain, which is
37
37
  // why alpine boxes sat at 404 forever. Debian-slim runs the shim as published,
38
38
  // and its apt has a real sshd.
39
- const BOX_IMAGE = process.env.OPENZOO_BOX_IMAGE || 'node:22-slim';
40
- const BOX_PORTS = ['8402/http', '1337/http', '6080/http', '1340/http', '6081/http', '22/tcp'];
39
+ // Everything the box runs is ALREADY IN THE IMAGE: the shim, its node_modules,
40
+ // grokui, and sshd. Boxes used to apt-get and `npx openzoo` at boot, which put
41
+ // a package fetch on the critical path from RunPod egress IPs — and those get
42
+ // rate limited. A 429 from raw.githubusercontent arrives as a FILE whose
43
+ // contents are the text "429: Too Many Requests", so node ran it and failed in
44
+ // a way that read as an app bug rather than a download bug.
45
+ // Still glibc, never musl: @solana's native deps don't build on the box.
46
+ const BOX_IMAGE = process.env.OPENZOO_BOX_IMAGE || 'jrsdunn123/openzoo-box:latest';
47
+ const BOX_PORTS = ['8402/http', '4173/http', '1337/http', '6080/http', '1340/http', '6081/http', '22/tcp'];
41
48
  const TTL_MAX_HOURS = 90 * 24;
42
49
 
43
50
  /**
@@ -50,18 +57,27 @@ const TTL_MAX_HOURS = 90 * 24;
50
57
  * host keys, and run sshd in the foreground of the background. Passwordless
51
58
  * root login is NOT enabled — key-only.
52
59
  *
53
- * `npx --yes openzoo` is still fetched at boot so a box always runs the
54
- * published shim; readyBox() polls /v1/models rather than assuming a timing.
60
+ * NOTHING IS FETCHED AT BOOT. sshd, the shim and grokui are baked into
61
+ * BOX_IMAGE by box.Dockerfile; this only copies /opt into the volume RunPod
62
+ * mounts over /workspace and starts things. readyBox() still polls /v1/models
63
+ * rather than assuming a timing.
55
64
  */
56
65
  const ENTRYPOINT = [
57
66
  'bash', '-lc',
58
67
  'set +e; mkdir -p /workspace /root/.openzoo /var/log/openzoo /run/sshd /root/.ssh; '
59
68
  + 'if [ -n "$OPENZOO_WALLET_JSON" ]; then printf %s "$OPENZOO_WALLET_JSON" > /root/.openzoo/wallet.json; chmod 600 /root/.openzoo/wallet.json; fi; '
60
- + 'export DEBIAN_FRONTEND=noninteractive; apt-get update >/dev/null 2>&1; apt-get install -y --no-install-recommends openssh-server git ca-certificates >/dev/null 2>&1; '
61
69
  // RunPod hands the operator pubkey in one of these — accept whichever is set
62
70
  + 'for K in "$PUBLIC_KEY" "$SSH_PUBLIC_KEY"; do [ -n "$K" ] && echo "$K" >> /root/.ssh/authorized_keys; done; chmod 600 /root/.ssh/authorized_keys 2>/dev/null; '
63
71
  + 'ssh-keygen -A >/dev/null 2>&1; sed -i "s/^#\\?PermitRootLogin.*/PermitRootLogin prohibit-password/" /etc/ssh/sshd_config 2>/dev/null; /usr/sbin/sshd -e >/var/log/openzoo/sshd.log 2>&1 & '
64
- + 'npx --yes openzoo > /var/log/openzoo/proxy.log 2>&1 & '
72
+ // RUN FROM /opt, never from /workspace. Staging the app onto the volume and
73
+ // gating on `[ ! -f .../bin/openzoo.js ]` is a trap: one interrupted copy
74
+ // leaves that file behind, every later boot skips the copy, and the box
75
+ // crash-loops on a half-copied node_modules forever (seen in production as
76
+ // "Cannot find module .../viem/accounts" every 15s, which also cost the pod
77
+ // its HTTP port mappings). The app is read-only at runtime; mutable state
78
+ // lives in /root/.openzoo.
79
+ + 'until OPENZOO_BIND=0.0.0.0 node /opt/openzoo/bin/openzoo.js >> /var/log/openzoo/proxy.log 2>&1; do echo "proxy exited, restarting" >> /var/log/openzoo/proxy.log; sleep 2; done & '
80
+ + 'until OZ_GROKUI_BIND=0.0.0.0 OZ_GROKUI_PORT=4173 node /opt/grokui/grokui.mjs >> /var/log/openzoo/grokui.log 2>&1; do echo "grokui exited, restarting" >> /var/log/openzoo/grokui.log; sleep 2; done & '
65
81
  // the capture agent answers the ports Grok Bot expects a Cursor sandbox on,
66
82
  // logging the protocol we do not yet speak (see lib/podagent.mjs)
67
83
  + 'if [ -n "$OZ_PODAGENT_B64" ]; then printf %s "$OZ_PODAGENT_B64" | base64 -d > /opt/podagent.mjs; node /opt/podagent.mjs > /var/log/openzoo/agent.log 2>&1 & fi; '
package/lib/grokui.mjs CHANGED
@@ -14,6 +14,12 @@ import path from 'node:path';
14
14
  import { brain, brainStream, PROXY } from './podagent.mjs';
15
15
 
16
16
  const PORT = Number(process.env.OZ_GROKUI_PORT || 4173);
17
+ // BIND HOST. Default 127.0.0.1 so the desktop app never exposes a shell-capable
18
+ // UI to the LAN. A container MUST override it: binding the container's loopback
19
+ // makes `docker run -p 4173:4173` refuse the connection, because the published
20
+ // port forwards to the container's external interface, which nothing is on.
21
+ // Mirrors OPENZOO_BIND in lib/proxy.js — same reasoning, same default.
22
+ const BIND = process.env.OZ_GROKUI_BIND || process.env.OPENZOO_BIND || '127.0.0.1';
17
23
  const STORE_DIR = path.join(homedir(), '.openzoo');
18
24
  const STORE_FILE = path.join(STORE_DIR, 'grokui-threads.json');
19
25
 
@@ -368,7 +374,12 @@ function parseRun(reply) {
368
374
  // several such calls were dropped. Same failure as the old /^RUN:/ anchor,
369
375
  // different shape: a silently discarded directive reads to the model as a
370
376
  // tool that does nothing, and it fabricates rather than reporting failure.
371
- const dsml = /<[|\s]*DSML[^>]*\bparameter\b[^>]*name="command"[^>]*>([\s\S]*?)<\/[|\s]*DSML/i.exec(reply);
377
+ // The separator is NOT always an ASCII pipe. DeepSeek's real special tokens
378
+ // use U+FF5C FULLWIDTH VERTICAL LINE (|) — matching only `|` parsed the
379
+ // pretty-printed form in tests while missing what the model actually emits,
380
+ // which is exactly how this survived one round of "fixed".
381
+ const SEP = '[||\\s]*';
382
+ const dsml = new RegExp(`<${SEP}DSML[^>]*\\bparameter\\b[^>]*name="command"[^>]*>([\\s\\S]*?)<\\/${SEP}DSML`, 'i').exec(reply);
372
383
  if (dsml) return sanitizeRunCommand(dsml[1]);
373
384
 
374
385
  const m = /^[ \t>*-]*RUN:[ \t]*([\s\S]+)/m.exec(reply);
@@ -1514,4 +1525,4 @@ const server = http.createServer((req, res) => {
1514
1525
  res.end(APP_HTML);
1515
1526
  });
1516
1527
 
1517
- server.listen(PORT, '127.0.0.1', () => console.log(`[grokui] http://localhost:${PORT}`));
1528
+ server.listen(PORT, BIND, () => console.log(`[grokui] http://${BIND === '0.0.0.0' ? 'localhost' : BIND}:${PORT}`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzoo",
3
- "version": "0.48.0",
3
+ "version": "0.48.2",
4
4
  "description": "Local x402-paying proxy + MCP server for openzoo.fun — 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",