openzoo 0.39.0 → 0.39.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
@@ -32,29 +32,40 @@ function podAgentB64() {
32
32
  const REST = 'https://rest.runpod.io/v1';
33
33
  export const BOX_PREFIX = 'openzoo-box-';
34
34
  const CPU_FLAVORS = ['cpu3c', 'cpu3g', 'cpu5c'];
35
- const BOX_IMAGE = process.env.OPENZOO_BOX_IMAGE || 'node:22-alpine';
35
+ // GLIBC, NOT ALPINE. `npx openzoo` pulls native deps (ed25519 / ws helpers);
36
+ // on musl they fail to build because the entrypoint has no toolchain, which is
37
+ // why alpine boxes sat at 404 forever. Debian-slim runs the shim as published,
38
+ // and its apt has a real sshd.
39
+ const BOX_IMAGE = process.env.OPENZOO_BOX_IMAGE || 'node:22-slim';
36
40
  const BOX_PORTS = ['8402/http', '1337/http', '6080/http', '1340/http', '6081/http', '22/tcp'];
37
41
  const TTL_MAX_HOURS = 90 * 24;
38
42
 
39
43
  /**
40
- * Boot: write the wallet, start the x402 proxy, keep the container alive.
44
+ * Boot: write the wallet, START SSHD, start the x402 proxy + capture agent.
41
45
  *
42
- * The proxy is the point everything the agent asks for is paid per request
43
- * from this box's own wallet. `npx --yes openzoo` is fetched at boot rather
44
- * than baked, so a box always runs the published shim; the tradeoff is a hard
45
- * dependency on npm being reachable in the first ~30s, which readyBox() waits
46
- * for explicitly instead of assuming.
46
+ * SSHD IS NOT OPTIONAL and was the second bug: our dockerStartCmd replaces the
47
+ * image's own init, so nothing starts sshd and every `execInBox` got
48
+ * "connection refused" the agent had no hands. RunPod injects the account
49
+ * key at $PUBLIC_KEY / $SSH_PUBLIC_KEY; we write it to authorized_keys, generate
50
+ * host keys, and run sshd in the foreground of the background. Passwordless
51
+ * root login is NOT enabled — key-only.
52
+ *
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.
47
55
  */
48
56
  const ENTRYPOINT = [
49
- 'sh', '-c',
50
- 'set -e; mkdir -p /workspace /root/.openzoo /var/log/openzoo; '
57
+ 'bash', '-lc',
58
+ 'set +e; mkdir -p /workspace /root/.openzoo /var/log/openzoo /run/sshd /root/.ssh; '
51
59
  + 'if [ -n "$OPENZOO_WALLET_JSON" ]; then printf %s "$OPENZOO_WALLET_JSON" > /root/.openzoo/wallet.json; chmod 600 /root/.openzoo/wallet.json; fi; '
52
- + 'apk add --no-cache openssh bash git curl >/dev/null 2>&1 || true; '
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
+ // RunPod hands the operator pubkey in one of these — accept whichever is set
62
+ + '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
+ + '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 & '
53
64
  + 'npx --yes openzoo > /var/log/openzoo/proxy.log 2>&1 & '
54
65
  // the capture agent answers the ports Grok Bot expects a Cursor sandbox on,
55
66
  // logging the protocol we do not yet speak (see lib/podagent.mjs)
56
67
  + '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; '
57
- + 'wait',
68
+ + 'sleep infinity',
58
69
  ];
59
70
 
60
71
  function key() {
@@ -185,6 +196,8 @@ export async function spawnBox({ name = 'bot', days = 1, vcpu = 2, walletJson, p
185
196
  OPENZOO_BOX: '1',
186
197
  OPENZOO_EXPIRES_UNIX: String(unix),
187
198
  OPENZOO_NO_TUNNEL: '1', // the runpod proxy already fronts it
199
+ OPENZOO_BIND: '0.0.0.0', // else the runpod proxy can't reach :8402
200
+ OPENZOO_TUNNEL_TOKEN: `oz-${Math.random().toString(36).slice(2)}${Math.random().toString(36).slice(2)}`,
188
201
  ...(walletJson ? { OPENZOO_WALLET_JSON: walletJson } : {}),
189
202
  ...(pubkey ? { OPENZOO_WALLET_PUBKEY: pubkey } : {}),
190
203
  OZ_PODAGENT_B64: podAgentB64(),
package/lib/proxy.js CHANGED
@@ -715,9 +715,17 @@ export async function startProxy({ silent = false, requireToken = null, sessionM
715
715
  }
716
716
  });
717
717
 
718
+ // BIND HOST. Default 127.0.0.1 — the keyless localhost path must never be
719
+ // world-reachable on an ordinary machine. A RunPod box is the exception: its
720
+ // HTTP proxy reaches the container over the pod network, so a localhost bind
721
+ // shows "Initializing…" forever (MEASURED: podagent's 0.0.0.0 ports went
722
+ // Ready, the 127.0.0.1 proxy never did). The box sets OPENZOO_BIND=0.0.0.0
723
+ // AND a tunnel token, so the RunPod-fronted port stays gated exactly like the
724
+ // public tunnel path.
725
+ const bindHost = process.env.OPENZOO_BIND || '127.0.0.1';
718
726
  await new Promise((resolve, reject) => {
719
727
  server.on('error', reject);
720
- server.listen(config.port, '127.0.0.1', resolve);
728
+ server.listen(config.port, bindHost, resolve);
721
729
  });
722
730
 
723
731
  if (!silent) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzoo",
3
- "version": "0.39.0",
3
+ "version": "0.39.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",