openzoo 0.51.4 → 0.51.6
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 +12 -1
- package/bin/openzoo.js +21 -46
- package/lib/chatgpt-install.js +106 -0
- package/lib/chatgpt.js +108 -90
- package/lib/config.js +34 -0
- package/lib/modelcommand.js +443 -0
- package/lib/pay.js +60 -4
- package/lib/proxy.js +39 -0
- package/package.json +4 -7
- package/transmute/README.md +0 -385
- package/transmute/bin/openzoo-transmute.js +0 -7
- package/transmute/docs/OPENZOO_CLI_PATCH.md +0 -139
- package/transmute/docs/VERCEL_TO_SOLANA.md +0 -381
- package/transmute/lib/build.js +0 -381
- package/transmute/lib/cli.js +0 -293
- package/transmute/lib/compile/bytecode.js +0 -591
- package/transmute/lib/compile/hostids.js +0 -11
- package/transmute/lib/compile/index.js +0 -159
- package/transmute/lib/compile/ir.js +0 -1634
- package/transmute/lib/compile/parse.js +0 -753
- package/transmute/lib/compile/rust.js +0 -781
- package/transmute/lib/deploy.js +0 -357
- package/transmute/lib/eligibility.js +0 -211
- package/transmute/lib/gateway.js +0 -710
- package/transmute/lib/hub.js +0 -290
- package/transmute/lib/solana.js +0 -387
- package/transmute/lib/vercel.js +0 -296
- package/transmute/lib/wallet.js +0 -41
- package/transmute/lib/wire.js +0 -208
- package/transmute/prebuilt/zoo_vm.v0.so +0 -0
- package/transmute/runtime/zoo-host/Cargo.lock +0 -588
- package/transmute/runtime/zoo-host/Cargo.toml +0 -30
- package/transmute/runtime/zoo-host/examples/vmrun.rs +0 -43
- package/transmute/runtime/zoo-host/src/assets.rs +0 -214
- package/transmute/runtime/zoo-host/src/ctx.rs +0 -350
- package/transmute/runtime/zoo-host/src/fmt.rs +0 -188
- package/transmute/runtime/zoo-host/src/helpers.rs +0 -245
- package/transmute/runtime/zoo-host/src/json.rs +0 -263
- package/transmute/runtime/zoo-host/src/kv.rs +0 -206
- package/transmute/runtime/zoo-host/src/lib.rs +0 -137
- package/transmute/runtime/zoo-host/src/site.rs +0 -94
- package/transmute/runtime/zoo-host/src/val.rs +0 -1050
- package/transmute/runtime/zoo-host/src/vm.rs +0 -902
- package/transmute/runtime/zoo-host/src/wire.rs +0 -239
- package/transmute/runtime/zoo-vm/Cargo.lock +0 -596
- package/transmute/runtime/zoo-vm/Cargo.toml +0 -23
- package/transmute/runtime/zoo-vm/src/lib.rs +0 -92
- package/transmute/site/.env.production +0 -2
- package/transmute/site/api/echo.js +0 -10
- package/transmute/site/api/hello.js +0 -12
- package/transmute/site/api/hits.js +0 -13
- package/transmute/site/api/time.js +0 -5
- package/transmute/site/package.json +0 -6
- package/transmute/site/public/app.js +0 -16
- package/transmute/site/public/index.html +0 -96
package/transmute/lib/cli.js
DELETED
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
// The openzoo-transmute command line.
|
|
2
|
-
//
|
|
3
|
-
// The openzoo package can mount these subcommands in its own bin without
|
|
4
|
-
// duplicating anything:
|
|
5
|
-
//
|
|
6
|
-
// case 'build': case 'deploy': case 'serve':
|
|
7
|
-
// await (await import('openzoo-transmute/lib/cli.js')).run(process.argv.slice(2));
|
|
8
|
-
//
|
|
9
|
-
// `run(argv)` resolves to the exit code (0 = ok) and also sets
|
|
10
|
-
// `process.exitCode` when non-zero; it never calls `process.exit` itself so a
|
|
11
|
-
// `serve` keeps the event loop (and the http server) alive.
|
|
12
|
-
import fs from 'node:fs';
|
|
13
|
-
import path from 'node:path';
|
|
14
|
-
import { PublicKey } from '@solana/web3.js';
|
|
15
|
-
import { readDeployment } from './vercel.js';
|
|
16
|
-
import { build } from './build.js';
|
|
17
|
-
import { deploy, deployRuntime, resolveOutDir, clusterLabel } from './deploy.js';
|
|
18
|
-
import { startGateway, DEFAULT_PORT } from './gateway.js';
|
|
19
|
-
import { startHub, DEFAULT_HUB_PORT } from './hub.js';
|
|
20
|
-
import { connect, getProgramInfo, getSiteInfo, readManifest } from './solana.js';
|
|
21
|
-
import { loadWallet, rpcUrl } from './wallet.js';
|
|
22
|
-
import { MANIFEST_PATH } from './wire.js';
|
|
23
|
-
|
|
24
|
-
export const USAGE = `openzoo-transmute — Vercel app → Solana program + asset accounts
|
|
25
|
-
|
|
26
|
-
usage:
|
|
27
|
-
openzoo-transmute build [dir] [--target shared|program] [--out .zoo-out] [--name <crate>] [--arch v0|v3] [--cluster <c>] [--skip-cargo]
|
|
28
|
-
--target shared (default): bytecode for the shared zoo-vm runtime — a site is data accounts only (≈0.05–0.1 SOL)
|
|
29
|
-
--target program: your own Pinocchio program per site (≈1 SOL+)
|
|
30
|
-
openzoo-transmute deploy [dir|outDir] [--cluster mainnet|devnet|localnet|<url>] [--keypair <path>] [--yes] [--runtime <zoo-vm id>]
|
|
31
|
-
[--program <id>] [--concurrency 4] [--skip-assets] [--force] [--headroom 1.5]
|
|
32
|
-
openzoo-transmute serve <siteId|programId> [--runtime <zoo-vm id>] [--cluster <c>] [--port ${DEFAULT_PORT}] [--keypair <path>] [--host 127.0.0.1] [--quiet]
|
|
33
|
-
openzoo-transmute runtime deploy [--cluster <c>] [--keypair <path>] [--yes] [--program-keypair zoo-vm-keypair.json] [--so <path>]
|
|
34
|
-
deploy (or upgrade) the shared zoo-vm runtime once per cluster
|
|
35
|
-
openzoo-transmute runtime status <zoo-vm id> [--cluster <c>]
|
|
36
|
-
openzoo-transmute hub [--cluster mainnet] [--port 8080] [--host 0.0.0.0] [--public-url <https://…>]
|
|
37
|
-
hosted explorer for EVERY program on the cluster (/s/<programId>), read-only
|
|
38
|
-
openzoo-transmute inspect [dir] print the Vercel model + eligibility report
|
|
39
|
-
openzoo-transmute status <programId> [--cluster <c>]
|
|
40
|
-
openzoo-transmute help
|
|
41
|
-
|
|
42
|
-
clusters: mainnet (default, needs --yes to deploy), devnet, testnet, localnet, or an RPC URL.
|
|
43
|
-
env: OPENZOO_CLUSTER, OPENZOO_RPC (mainnet RPC), OPENZOO_KEYPAIR / OPENZOO_WALLET (signer), OPENZOO_VM_PROGRAM (shared runtime id).
|
|
44
|
-
`;
|
|
45
|
-
|
|
46
|
-
/** `--k v`, `--k=v`, `--flag`, `--no-flag`; everything else is positional. */
|
|
47
|
-
export function parseArgs(argv) {
|
|
48
|
-
const flags = {};
|
|
49
|
-
const positionals = [];
|
|
50
|
-
const boolFlags = new Set(['yes', 'skip-cargo', 'skip-assets', 'force', 'quiet', 'help', 'json', 'version']);
|
|
51
|
-
for (let i = 0; i < argv.length; i++) {
|
|
52
|
-
const a = argv[i];
|
|
53
|
-
if (a === '--') { positionals.push(...argv.slice(i + 1)); break; }
|
|
54
|
-
if (a.startsWith('--')) {
|
|
55
|
-
const eq = a.indexOf('=');
|
|
56
|
-
let key = eq >= 0 ? a.slice(2, eq) : a.slice(2);
|
|
57
|
-
let val;
|
|
58
|
-
if (eq >= 0) val = a.slice(eq + 1);
|
|
59
|
-
else if (key.startsWith('no-')) { key = key.slice(3); val = false; }
|
|
60
|
-
else if (boolFlags.has(key) || i + 1 >= argv.length || argv[i + 1].startsWith('--')) val = true;
|
|
61
|
-
else val = argv[++i];
|
|
62
|
-
flags[key] = val;
|
|
63
|
-
flags[key.replace(/-([a-z])/g, (_, c) => c.toUpperCase())] = val;
|
|
64
|
-
} else if (a === '-h') flags.help = true;
|
|
65
|
-
else if (a === '-y') flags.yes = true;
|
|
66
|
-
else positionals.push(a);
|
|
67
|
-
}
|
|
68
|
-
return { cmd: positionals[0] || 'help', positionals: positionals.slice(1), flags };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function fmtBytes(n) { return n == null ? '?' : n.toLocaleString() + ' B'; }
|
|
72
|
-
|
|
73
|
-
export function inspectText(deployment, report = null) {
|
|
74
|
-
const lines = [];
|
|
75
|
-
lines.push(`${deployment.root}`);
|
|
76
|
-
lines.push(`source: ${deployment.source}${deployment.framework ? ` · framework: ${deployment.framework}` : ''} · static dir: ${deployment.staticDir ? path.relative(deployment.root, deployment.staticDir) || '.' : '-'}`);
|
|
77
|
-
for (const n of deployment.notes) lines.push(`note: ${n}`);
|
|
78
|
-
lines.push('');
|
|
79
|
-
lines.push(`functions (${deployment.functions.length}):`);
|
|
80
|
-
const inel = new Map((report?.ineligible || []).map((i) => [i.name, i]));
|
|
81
|
-
const elig = new Set((report?.eligible || []).map((e) => (typeof e === 'string' ? e : e.name)));
|
|
82
|
-
for (const f of deployment.functions) {
|
|
83
|
-
const mark = report ? (inel.has(f.name) ? '✗' : elig.has(f.name) ? '✓' : '?') : ' ';
|
|
84
|
-
const meth = f.methods ? f.methods.join(',') : 'any';
|
|
85
|
-
lines.push(` ${mark} ${f.routePath.padEnd(32)} ${f.style.padEnd(12)} ${f.runtime.padEnd(11)} ${String(f.maxDuration + 's').padEnd(5)} ${String(f.memory + 'MB').padEnd(7)} ${meth}${f.params.length ? ` params=${f.params.join(',')}` : ''}${f.middleware ? ' middleware' : ''}${f.prerender ? ' ISR' : ''}`);
|
|
86
|
-
lines.push(` ${f.sourceFile ? path.relative(deployment.root, f.sourceFile) : '(no source)'}${Object.keys(f.environment || {}).length ? ` env=${Object.keys(f.environment).join(',')}` : ''}`);
|
|
87
|
-
const r = inel.get(f.name);
|
|
88
|
-
if (r) lines.push(` ineligible: ${r.reason}${r.line ? ` (line ${r.line})` : ''}`);
|
|
89
|
-
}
|
|
90
|
-
if (deployment.crons.length) { lines.push(''); lines.push(`crons (${deployment.crons.length}) — not transmuted, Solana has no scheduler:`); for (const c of deployment.crons) lines.push(` ${c.schedule || '?'} ${c.path || ''}`); }
|
|
91
|
-
lines.push('');
|
|
92
|
-
const total = deployment.staticFiles.reduce((n, f) => n + f.size, 0);
|
|
93
|
-
lines.push(`static files (${deployment.staticFiles.length}, ${fmtBytes(total)}):`);
|
|
94
|
-
for (const f of deployment.staticFiles.slice(0, 60)) lines.push(` ${f.path.padEnd(48)} ${f.contentType.padEnd(32)} ${fmtBytes(f.size)}`);
|
|
95
|
-
if (deployment.staticFiles.length > 60) lines.push(` … ${deployment.staticFiles.length - 60} more`);
|
|
96
|
-
if (report) {
|
|
97
|
-
lines.push('');
|
|
98
|
-
lines.push(`eligibility: ${report.eligible.length} eligible, ${report.ineligible.length} ineligible, ${report.warnings.length} warning(s)`);
|
|
99
|
-
for (const w of report.warnings) lines.push(` ! ${typeof w === 'string' ? w : w.message || JSON.stringify(w)}`);
|
|
100
|
-
}
|
|
101
|
-
const routes = deployment.config?.routes || [];
|
|
102
|
-
if (routes.length) {
|
|
103
|
-
lines.push('');
|
|
104
|
-
lines.push(`routes (${routes.length}):`);
|
|
105
|
-
for (const r of routes) lines.push(' ' + (r.handle ? `[handle: ${r.handle}]` : `${r.src} → ${r.dest || (r.status ? `status ${r.status}` : '(headers)')}${r.continue ? ' (continue)' : ''}`));
|
|
106
|
-
}
|
|
107
|
-
return lines.join('\n');
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async function tryTransmute(deployment) {
|
|
111
|
-
try {
|
|
112
|
-
const mod = await import('./compile/index.js');
|
|
113
|
-
if (typeof mod.transmute !== 'function') return null;
|
|
114
|
-
const r = await mod.transmute(deployment, { name: 'inspect', dryRun: true });
|
|
115
|
-
return r?.report || null;
|
|
116
|
-
} catch (e) {
|
|
117
|
-
if (e?.code === 'ERR_MODULE_NOT_FOUND') return null;
|
|
118
|
-
return { eligible: [], ineligible: [], warnings: [`compiler failed: ${e?.message || e}`] };
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export const DEFAULT_TARGET = 'shared';
|
|
123
|
-
|
|
124
|
-
/** `--runtime` is the zoo-vm program id (shared) — or, for `--target program`, a zoo-host checkout path. */
|
|
125
|
-
function runtimeFlag(f) {
|
|
126
|
-
const v = f.runtime;
|
|
127
|
-
if (typeof v !== 'string') return { runtime: process.env.OPENZOO_VM_PROGRAM || null, runtimePath: f.runtimePath || undefined };
|
|
128
|
-
if (fs.existsSync(v) && fs.existsSync(path.join(v, 'Cargo.toml'))) return { runtime: process.env.OPENZOO_VM_PROGRAM || null, runtimePath: v };
|
|
129
|
-
new PublicKey(v);
|
|
130
|
-
return { runtime: v, runtimePath: f.runtimePath || undefined };
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
async function cmdBuild(p, f, log) {
|
|
134
|
-
const target = f.target || process.env.OPENZOO_TARGET || DEFAULT_TARGET;
|
|
135
|
-
if (!['shared', 'program'].includes(target)) throw new Error(`--target must be shared or program (got ${target})`);
|
|
136
|
-
const { runtime, runtimePath } = runtimeFlag(f);
|
|
137
|
-
const r = await build(p[0] || '.', { out: f.out, name: f.name, arch: f.arch, cluster: f.cluster, target, runtime, runtimePath, skipCargo: !!f.skipCargo, log });
|
|
138
|
-
if (f.json) log(JSON.stringify({ outDir: r.outDir, target: r.target || 'program', soPath: r.soPath, codePath: r.codePath || null, codeSize: r.codeSize || null, arch: r.arch, report: r.report, costEstimate: { sol: r.costEstimate.sol, totalSol: r.costEstimate.totalSol, source: r.costEstimate.source } }, null, 2));
|
|
139
|
-
return r.report.ineligible.length && !r.report.eligible.length && r.manifest.routes.length === 0 && r.deployment.functions.length ? 2 : 0;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
async function cmdDeploy(p, f, log) {
|
|
143
|
-
const { runtime } = runtimeFlag(f);
|
|
144
|
-
const r = await deploy({ outDir: p[0] || '.', cluster: f.cluster, keypair: f.keypair, programId: f.program, runtime, yes: !!f.yes, concurrency: f.concurrency ? Number(f.concurrency) : undefined, skipAssets: !!f.skipAssets, force: !!f.force, headroom: f.headroom, log });
|
|
145
|
-
if (f.json) log(JSON.stringify(r, null, 2));
|
|
146
|
-
return 0;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
async function cmdServe(p, f, log) {
|
|
150
|
-
let programId = p[0];
|
|
151
|
-
let { runtime } = runtimeFlag(f);
|
|
152
|
-
if (!programId) {
|
|
153
|
-
// Fall back to the last deploy in ./ or ./.zoo-out.
|
|
154
|
-
try {
|
|
155
|
-
const d = JSON.parse(fs.readFileSync(path.join(resolveOutDir('.'), '.zoo', 'deploy.json'), 'utf8'));
|
|
156
|
-
programId = d.target === 'shared' ? d.site : d.programId;
|
|
157
|
-
if (d.target === 'shared' && !runtime) runtime = d.runtime;
|
|
158
|
-
} catch { /* none */ }
|
|
159
|
-
if (!programId) throw new Error('usage: serve <siteId|programId> [--runtime <zoo-vm id>] (no .zoo/deploy.json found to infer it from)');
|
|
160
|
-
}
|
|
161
|
-
new PublicKey(programId); // validate
|
|
162
|
-
// Shared runtime: the positional is the site id, the program is zoo-vm.
|
|
163
|
-
let site = null;
|
|
164
|
-
if (runtime) { site = programId; programId = runtime; }
|
|
165
|
-
let keypair = null, walletPath = null;
|
|
166
|
-
if (f.keypair !== false) {
|
|
167
|
-
try { const w = loadWallet({ keypair: typeof f.keypair === 'string' ? f.keypair : undefined }); keypair = w.keypair; walletPath = w.path; }
|
|
168
|
-
catch (e) { if (typeof f.keypair === 'string') throw e; log(`no wallet found (${String(e.message).split(';')[0]}); mutating requests will answer 402`); }
|
|
169
|
-
}
|
|
170
|
-
const port = f.port != null ? Number(f.port) : DEFAULT_PORT;
|
|
171
|
-
const host = f.host || '127.0.0.1';
|
|
172
|
-
if (keypair && !/^(127\.0\.0\.1|localhost|::1|\[::1\])$/.test(host)) log(`warning: binding ${host} with a signer: anyone who can reach this gateway can send transactions paid by ${keypair.publicKey.toBase58()}; use --no-keypair unless that is intended`);
|
|
173
|
-
const gw = await startGateway({ programId, site, cluster: f.cluster, port, host, keypair, log, quiet: !!f.quiet });
|
|
174
|
-
const m = gw.state.manifest;
|
|
175
|
-
log(`openzoo-transmute gateway → ${gw.url}/ (explorer ${gw.url}/.zoo/, manifest ${gw.url}${MANIFEST_PATH})`);
|
|
176
|
-
log(`${site ? `site ${site} on runtime ${programId}` : `program ${programId}`} on ${gw.state.cluster} · ${m.routes.length} function(s), ${m.static ? m.static.length : '?'} asset(s) · signer ${keypair ? keypair.publicKey.toBase58() + (walletPath ? ` (${walletPath})` : '') : 'none (reads only)'}`);
|
|
177
|
-
for (const r of m.routes) log(` ${(r.methods && r.methods.length ? r.methods.join('|') : 'ANY').padEnd(16)} ${r.routePath}`);
|
|
178
|
-
const stop = () => gw.close().then(() => process.exit(0));
|
|
179
|
-
process.once('SIGINT', stop);
|
|
180
|
-
process.once('SIGTERM', stop);
|
|
181
|
-
return 0;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
async function cmdInspect(p, f, log) {
|
|
185
|
-
const deployment = readDeployment(p[0] || '.');
|
|
186
|
-
const report = await tryTransmute(deployment);
|
|
187
|
-
if (f.json) log(JSON.stringify({ ...deployment, report }, null, 2));
|
|
188
|
-
else {
|
|
189
|
-
log(inspectText(deployment, report));
|
|
190
|
-
if (!report) log('\n(compiler not available: eligibility not checked)');
|
|
191
|
-
}
|
|
192
|
-
return 0;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
async function cmdStatus(p, f, log) {
|
|
196
|
-
if (!p[0]) throw new Error('usage: status <siteId|programId> [--runtime <zoo-vm id>] [--cluster <c>]');
|
|
197
|
-
const { runtime } = runtimeFlag(f);
|
|
198
|
-
const rpc = rpcUrl(f.cluster);
|
|
199
|
-
const connection = connect(rpc);
|
|
200
|
-
if (runtime) {
|
|
201
|
-
const site = new PublicKey(p[0]);
|
|
202
|
-
const [info, manifest] = await Promise.all([getSiteInfo(connection, new PublicKey(runtime), site), readManifest(connection, new PublicKey(runtime), site).catch(() => null)]);
|
|
203
|
-
const out = { site: site.toBase58(), runtime, cluster: clusterLabel(f.cluster), rpc: rpc.replace(/\?.*$/, ''), exists: info.exists, authority: info.authority ? info.authority.toBase58() : null, account: info.pda.toBase58(), manifest: manifest ? { name: manifest.name, routes: manifest.routes?.length ?? 0, static: manifest.static?.length ?? 0, deployedAt: manifest.deployedAt } : null };
|
|
204
|
-
if (f.json) { log(JSON.stringify(out, null, 2)); return info.exists ? 0 : 1; }
|
|
205
|
-
log(`site ${out.site} on runtime ${runtime} (${out.cluster})`);
|
|
206
|
-
if (!info.exists) { log('status NOT DEPLOYED'); return 1; }
|
|
207
|
-
log(`authority ${out.authority}`);
|
|
208
|
-
log(`account ${out.account}`);
|
|
209
|
-
if (manifest) { log(`manifest ${manifest.name || ''} · ${out.manifest.routes} function(s), ${out.manifest.static} asset(s) · deployed ${manifest.deployedAt || '?'}`); for (const r of manifest.routes || []) log(` ${(r.methods && r.methods.length ? r.methods.join('|') : 'ANY').padEnd(16)} ${r.routePath}`); }
|
|
210
|
-
else log(`manifest none at ${MANIFEST_PATH}`);
|
|
211
|
-
return 0;
|
|
212
|
-
}
|
|
213
|
-
const programId = new PublicKey(p[0]);
|
|
214
|
-
const [info, slot, manifest] = await Promise.all([getProgramInfo(connection, programId), connection.getSlot('confirmed'), readManifest(connection, programId).catch(() => null)]);
|
|
215
|
-
const out = {
|
|
216
|
-
programId: programId.toBase58(), cluster: clusterLabel(f.cluster), rpc: rpc.replace(/\?.*$/, ''), slot,
|
|
217
|
-
exists: info.exists, executable: info.executable ?? null, authority: info.authority ? info.authority.toBase58() : null,
|
|
218
|
-
deploySlot: info.slot, maxDataLen: info.maxDataLen, programData: info.programData.toBase58(),
|
|
219
|
-
manifest: manifest ? { name: manifest.name, framework: manifest.framework, routes: manifest.routes?.length ?? 0, static: manifest.static?.length ?? 0, deployedAt: manifest.deployedAt, arch: manifest.arch } : null,
|
|
220
|
-
};
|
|
221
|
-
if (f.json) { log(JSON.stringify(out, null, 2)); return info.exists ? 0 : 1; }
|
|
222
|
-
log(`program ${out.programId} on ${out.cluster} (${out.rpc}) @ slot ${slot}`);
|
|
223
|
-
if (!info.exists) { log('status NOT DEPLOYED'); return 1; }
|
|
224
|
-
log(`status deployed at slot ${out.deploySlot}${out.executable === false ? ' (not executable!)' : ''}`);
|
|
225
|
-
log(`authority ${out.authority || 'none (immutable)'}`);
|
|
226
|
-
log(`programData ${out.programData} (max ${out.maxDataLen?.toLocaleString()} B)`);
|
|
227
|
-
if (manifest) {
|
|
228
|
-
log(`manifest ${manifest.name || ''} ${manifest.framework || ''} · ${out.manifest.routes} function(s), ${out.manifest.static} asset(s) · deployed ${manifest.deployedAt || '?'}${manifest.arch ? ` (${manifest.arch})` : ''}`);
|
|
229
|
-
for (const r of manifest.routes || []) log(` ${(r.methods && r.methods.length ? r.methods.join('|') : 'ANY').padEnd(16)} ${r.routePath}`);
|
|
230
|
-
} else log(`manifest none at ${MANIFEST_PATH}`);
|
|
231
|
-
return 0;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Run the CLI. Returns the exit code; prints errors to stderr.
|
|
236
|
-
* @param {string[]} argv arguments after the program name
|
|
237
|
-
* @param {{log?:Function, error?:Function}} io
|
|
238
|
-
*/
|
|
239
|
-
async function cmdHub(p, f, log) {
|
|
240
|
-
// The hosted explorer: every program on the cluster from one public host,
|
|
241
|
-
// read-only by default (a public signer would be a drain). --keypair opts in.
|
|
242
|
-
const cluster = f.cluster || process.env.OPENZOO_CLUSTER || 'mainnet';
|
|
243
|
-
// A signer makes the demo real (visitors can POST). It is bounded: a per-IP
|
|
244
|
-
// rate limit and a daily SOL budget (OPENZOO_HUB_WRITE_BUDGET_SOL), so a
|
|
245
|
-
// public hub can sign without being drained. Keypair from --keypair or the
|
|
246
|
-
// OPENZOO_HUB_KEYPAIR secret (JSON byte array).
|
|
247
|
-
let keypair = null;
|
|
248
|
-
if (typeof f.keypair === 'string') keypair = loadWallet({ keypair: f.keypair }).keypair;
|
|
249
|
-
else if (process.env.OPENZOO_HUB_KEYPAIR) { const { Keypair } = await import('@solana/web3.js'); keypair = Keypair.fromSecretKey(Uint8Array.from(JSON.parse(process.env.OPENZOO_HUB_KEYPAIR))); }
|
|
250
|
-
if (keypair) log(`hub signs writes with ${keypair.publicKey.toBase58()} (budget ${process.env.OPENZOO_HUB_WRITE_BUDGET_SOL || '0.05'} SOL/day, ${process.env.OPENZOO_HUB_WRITES_PER_MIN || '3'} writes/min/ip)`);
|
|
251
|
-
const port = f.port != null ? Number(f.port) : Number(process.env.PORT || DEFAULT_HUB_PORT);
|
|
252
|
-
const h = await startHub({ cluster, port, host: f.host || '0.0.0.0', keypair, log, quiet: !!f.quiet, publicUrl: f.publicUrl || process.env.OPENZOO_HUB_URL || null, maxSites: f.maxSites ? Number(f.maxSites) : undefined });
|
|
253
|
-
log(`openzoo hub → ${h.url}/.hub (cluster ${cluster}; sites at ${h.url}/s/<programId>)`);
|
|
254
|
-
const stop = () => h.close().then(() => process.exit(0));
|
|
255
|
-
process.once('SIGINT', stop);
|
|
256
|
-
process.once('SIGTERM', stop);
|
|
257
|
-
return 0;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
async function cmdRuntime(p, f, log) {
|
|
261
|
-
const sub = p[0];
|
|
262
|
-
if (sub === 'deploy') {
|
|
263
|
-
const r = await deployRuntime({ cluster: f.cluster, keypair: f.keypair, yes: !!f.yes, programKeypair: f.programKeypair, so: f.so, headroom: f.headroom, log });
|
|
264
|
-
if (f.json) log(JSON.stringify(r, null, 2));
|
|
265
|
-
return 0;
|
|
266
|
-
}
|
|
267
|
-
if (sub === 'status') {
|
|
268
|
-
if (!p[1]) throw new Error('usage: runtime status <zoo-vm id> [--cluster <c>]');
|
|
269
|
-
return cmdStatus([p[1]], { ...f, runtime: undefined }, log);
|
|
270
|
-
}
|
|
271
|
-
throw new Error('usage: runtime deploy|status');
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
export async function run(argv = process.argv.slice(2), io = {}) {
|
|
275
|
-
const log = io.log ?? console.log;
|
|
276
|
-
const error = io.error ?? console.error;
|
|
277
|
-
const { cmd, positionals, flags } = parseArgs(argv);
|
|
278
|
-
if (flags.version) { log(JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version); return 0; }
|
|
279
|
-
if (flags.help || cmd === 'help' || cmd === '--help') { log(USAGE); return 0; }
|
|
280
|
-
const commands = { build: cmdBuild, deploy: cmdDeploy, serve: cmdServe, inspect: cmdInspect, status: cmdStatus, hub: cmdHub, runtime: cmdRuntime };
|
|
281
|
-
const fn = commands[cmd];
|
|
282
|
-
if (!fn) { error(`unknown command: ${cmd}\n`); error(USAGE); process.exitCode = 1; return 1; }
|
|
283
|
-
try {
|
|
284
|
-
const code = (await fn(positionals, flags, log)) || 0;
|
|
285
|
-
if (code) process.exitCode = code;
|
|
286
|
-
return code;
|
|
287
|
-
} catch (e) {
|
|
288
|
-
error(`error: ${e?.message || e}`);
|
|
289
|
-
if (process.env.OPENZOO_DEBUG && e?.stack) error(e.stack);
|
|
290
|
-
process.exitCode = 1;
|
|
291
|
-
return 1;
|
|
292
|
-
}
|
|
293
|
-
}
|