@artblocks/abx-cli 0.1.0-alpha.3 → 0.1.0-alpha.4

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/dist/main.js CHANGED
@@ -14,6 +14,7 @@
14
14
  * abx demo deploy a 1/1 to Sepolia, index it, and serve it
15
15
  * abx deploy [--image ..] deploy + index a 1/1 (--image to custody your own bytes; --no-mint to defer)
16
16
  * abx predict [--salt ..] pre-compute a deploy address (reserve / vanity it before signing)
17
+ * abx preview run a code project on localhost while it's still being made (no chain)
17
18
  * abx deploy-code deploy a code project (SeriesCode): --script <file> (on-chain template)
18
19
  * or --code-dir <dir> (build directory → ipfs/arweave `code` field)
19
20
  * abx add <address> register + index a project (--remote: on a hosted resolver, not this machine)
@@ -57,6 +58,7 @@ import { composeParamsKeys, expectedChainComplete, hasOnChainUriLane, onchainUri
57
58
  import { parseFlags, unknownFlags } from './flags.js';
58
59
  import { AGENT_SKILL_PARENTS, checkForCliUpdate, compareVersions, installedSkillVersions, readCliVersion, readSkillVersion, SKILL_DIR_NAME, } from './update-check.js';
59
60
  import { analyzeScript, recommendLane } from './inspect.js';
61
+ import { previewConfigFromFlags, previewDepTags, shootPreview, startPreviewServer, DEFAULT_PREVIEW_PORT, PREVIEW_FLAGS } from './preview.js';
60
62
  import { parseSchemaSpecs, describeSchema } from './schema.js';
61
63
  import { parseSeriesTraits, looksPerTokenAttributes, parseSeriesTraitsById } from './series-traits.js';
62
64
  /** The `--schema` type/auth/format catalog, shown wherever the CLI nudges `--schema`. Kept accurate
@@ -166,6 +168,7 @@ async function main() {
166
168
  case 'deploy-series': return cmdDeploySeries(flags);
167
169
  case 'deploy-code': return cmdDeployCode(flags);
168
170
  case 'inspect': return cmdInspect(rest[0], flags);
171
+ case 'preview': return cmdPreview(flags);
169
172
  case 'scaffold-renderer': return cmdScaffoldRenderer(rest, flags);
170
173
  case 'predict': return cmdPredict(flags);
171
174
  case 'add': return cmdAdd(rest[0], flags);
@@ -984,7 +987,24 @@ async function cmdDeploy(flags, serveAfter) {
984
987
  // lockable) — mirrors the description model. On-chain wins if both ever set the same trait.
985
988
  const traits = parseDeployTraits(flags);
986
989
  console.log(bold(`\n ABX Self-Host Toolkit — ${serveAfter ? 'demo' : 'deploy'}\n ${dim('a project, served from chain alone')}`));
987
- const lane = serveAfter ? 'send' : laneFromFlags(flags); // demo is always hot (one-shot)
990
+ // Signing lane. `demo` used to hard-force the hot lane, which SILENTLY dropped `--sign`: with no
991
+ // env key it died confusingly, and WITH one it signed from that key while the operator had asked
992
+ // for their browser wallet — a signing choke point that ignored the lane it was handed. The demo
993
+ // deploy is a single tx, so the wallet lane works here exactly as it does for `deploy`.
994
+ const lane = laneFromFlags(flags);
995
+ // The cold lane only PRINTS a tx; demo's whole point is to index + serve what it just deployed,
996
+ // and there is nothing to index until someone broadcasts. Refuse the combo instead of doing
997
+ // half the job — `abx deploy --unsigned` is the command for that lane.
998
+ if (serveAfter && lane === 'unsigned') {
999
+ throw new Error('`abx demo --unsigned` can\'t work: the cold lane only prints a transaction, and the demo ' +
1000
+ 'indexes + serves the contract it just deployed. Use `abx demo` (hot key) or `abx demo --sign` ' +
1001
+ '(browser wallet) — or `abx deploy --unsigned` if you only want the raw tx.');
1002
+ }
1003
+ // Same shape for --dry-run: previewing sends nothing, so there is nothing to serve.
1004
+ if (serveAfter && flags['dry-run']) {
1005
+ throw new Error('`abx demo --dry-run` can\'t work: a dry run sends nothing, and the demo indexes + serves what ' +
1006
+ 'it deployed. Use `abx deploy --dry-run` to preview a 1/1 deploy without sending.');
1007
+ }
988
1008
  const dryRun = !serveAfter && !!flags['dry-run']; // preview only — no send, no custody, no factory deploy
989
1009
  const publicClient = makePublicClient({ chainKey: CHAIN });
990
1010
  // Verify the RPC really is CHAIN before any send (factory/renderer/staging/deploy). A dry run
@@ -2227,6 +2247,78 @@ async function ensureSeedSource(publicClient) {
2227
2247
  * Schemas: --schema key:Type:Auth[,key:Type:Auth…] (e.g. palette:HexColor:TokenOwner).
2228
2248
  * Seeds: canonical randomizer by default; --no-seed opts out.
2229
2249
  */
2250
+ /**
2251
+ * `abx preview` — serve the program on localhost, live, for as long as the work is being made.
2252
+ *
2253
+ * The studio lane, and deliberately the FIRST thing to reach for on a code project: it renders the
2254
+ * same document the generator serves (real `abx.js`, real tokenData shape, real dependency tags)
2255
+ * with a synthetic seed, so a creator can refresh for new seeds, drive their PostParams from real
2256
+ * inputs, and watch an animated piece actually move — none of which a still-image sweep can show.
2257
+ * No chain, no key, no deploy. `--shoot` renders the same document headlessly for an agent that
2258
+ * can't open a browser.
2259
+ */
2260
+ async function cmdPreview(flags) {
2261
+ warnStrayFlags(flags, PREVIEW_FLAGS, 'preview');
2262
+ const cfg = previewConfigFromFlags(flags);
2263
+ const shootDir = flags.shoot && flags.shoot !== 'true' ? String(flags.shoot) : flags.shoot === 'true' ? 'abx-preview' : undefined;
2264
+ const count = Math.min(Math.max(Number(flags.count ?? 9) || 9, 1), 64);
2265
+ console.log(bold(`\n ABX Self-Host Toolkit — preview\n ${dim('the program, running locally — no chain, no deploy')}`));
2266
+ const { notes } = previewDepTags(cfg.deps);
2267
+ step('Program');
2268
+ info(cfg.source.kind === 'dir' ? `directory build ${cfg.source.path}/ (its own index.html + abx.js)` : `script ${cfg.source.path} ${dim('(re-read from disk on every render)')}`);
2269
+ if (cfg.schemas.length)
2270
+ info(`params: ${cfg.schemas.map(describeSchema).join(' · ')}`);
2271
+ else
2272
+ info(dim('params: none declared — add --schema key:Type:Auth to drive them from the studio'));
2273
+ for (const n of notes)
2274
+ info(`dep ${n}`);
2275
+ // A raw on-chain dependency can't be fetched without a chain, so the preview would render a
2276
+ // sketch missing its runtime and look broken for the wrong reason. Say so rather than let them
2277
+ // debug their own art.
2278
+ if (cfg.deps.some((d) => d.display.startsWith('0x'))) {
2279
+ warn('an on-chain data-contract dep is NOT loaded in preview — the sketch will run without it here. Use a name@version ref to preview against the CDN copy.');
2280
+ }
2281
+ const server = await startPreviewServer(cfg, shootDir ? 0 : Number(flags.port ?? DEFAULT_PREVIEW_PORT));
2282
+ if (shootDir) {
2283
+ step(`Render ${count} seeds headlessly`);
2284
+ try {
2285
+ const shots = await shootPreview(server.url, shootDir, count, {
2286
+ width: Number(flags.width ?? 1000) || 1000,
2287
+ timeoutMs: Number(flags['timeout-ms'] ?? 10_000) || 10_000,
2288
+ });
2289
+ ok(`${shots.length} frames → ${shootDir}/ ${dim('(traits in traits.json)')}`);
2290
+ for (const s of shots) {
2291
+ const t = s.traits && Object.keys(s.traits).length
2292
+ ? Object.entries(s.traits).map(([k, v]) => `${k} ${String(v)}`).join(' · ')
2293
+ : `${c.orange}no traits reported${c.reset}`;
2294
+ console.log(` ${dim(s.seed.slice(0, 10) + '…')} ${t}${s.done ? '' : dim(' (no abx.done())')}`);
2295
+ }
2296
+ const silent = shots.filter((s) => !s.traits || !Object.keys(s.traits).length).length;
2297
+ if (silent === shots.length) {
2298
+ warn('NO frame reported traits — `abx.traits({…})` is the only thing that becomes marketplace `attributes`. Verify with `abx inspect`.');
2299
+ }
2300
+ else if (shots.length > 1 && new Set(shots.map((s) => JSON.stringify(s.traits))).size === 1) {
2301
+ // Only meaningful when traits DID come back: identical values across seeds is the signature
2302
+ // of a sketch that never reads `abx.tokenData.seed` (prototyped on Math.random()), which
2303
+ // deploys as N visually identical tokens. Skipped when nothing reported at all — the
2304
+ // warning above already covers that, and firing both reads as noise.
2305
+ warn('every seed produced identical traits — check the sketch actually reads `abx.tokenData.seed` (the silent "all tokens the same" failure).');
2306
+ }
2307
+ }
2308
+ finally {
2309
+ await server.close();
2310
+ }
2311
+ return;
2312
+ }
2313
+ step('Studio');
2314
+ console.log(`\n ${g('●')} ${bold('preview')} ${server.url}`);
2315
+ console.log(` ${dim('studio ')}${server.url} ${dim('seed + params + live traits')}`);
2316
+ console.log(` ${dim('grid ')}${server.url}/grid ${dim('9 seeds at once, all live')}`);
2317
+ console.log(` ${dim('bare view ')}${server.url}/view ${dim('the generator document itself')}`);
2318
+ console.log(`\n ${dim('Edit the program and refresh — it is re-read from disk. Ctrl-C to stop.')}`);
2319
+ console.log(` ${dim('This is a preview: `abx inspect` is still the wiring check, and a testnet deploy is the faithful end-to-end.')}\n`);
2320
+ await new Promise(() => { }); // block like `serve` — the creator drives it
2321
+ }
2230
2322
  /**
2231
2323
  * `abx inspect <script.js>` — read a generative script and, WITHOUT executing it, report what it
2232
2324
  * needs (traits + their on-chain reproducibility, dependency hints, size → assembled-document size →
@@ -4634,6 +4726,20 @@ const COMMAND_HELP = {
4634
4726
  ${g('abx deploy-series')} --dir ./photos --name "My Series" --symbol MS --onchain-uri --backend ipfs --mint-all --sign
4635
4727
  ${dim('# tiny SVGs → fully on-chain (no storage at all):')}
4636
4728
  ${g('abx deploy-series')} --dir ./svgs --name "My Series" --symbol MS --onchain-image --compress fastlz --mint-all --sign`,
4729
+ preview: `
4730
+ ${bold('abx preview')} (--script <file.js> | --code-dir <dir>) ${dim('— run the program on localhost, live. No chain, no key, no deploy.')}
4731
+ ${g('--schema key:Type:Auth')}[,…] declare PostParams so the studio gives you real inputs for them (e.g. palette:HexColor:TokenOwner)
4732
+ ${g('--dep <name@version>')}[,…] load a library the way the resolver would (built-in CDN map; on-chain refs can't be fetched offline)
4733
+ ${g('--port')} <n> studio port (default ${DEFAULT_PREVIEW_PORT}; the resolver's ${DEFAULT_PORT} stays free)
4734
+ ${g('--shoot')} <dir> render headlessly to PNGs + traits.json and EXIT ${dim('(for an agent that has no browser)')}
4735
+ ${g('--count')} <n> seeds to shoot / show in the grid (default 9) ${g('--width')} <px> ${g('--timeout-ms')} <n>
4736
+ Serves the ${bold('same document the generator serves')} — real ${g('abx.js')}, real tokenData shape, real dep tags — with a synthetic
4737
+ seed, so what you iterate on is what deploys. Routes: ${g('/')} studio (seed + params + live traits) · ${g('/grid')} N seeds at once,
4738
+ all live · ${g('/view')} the bare document. The program is re-read from disk per render, so ${bold('edit and refresh')} — no watcher.
4739
+ Unlike a still-image sweep this shows ${bold('animation')}, which is most of what a screenshot throws away.
4740
+ ${dim('Still do both after the art settles:')} ${g('abx inspect')} ${dim('(is it wired right?) and a testnet deploy (the faithful end-to-end).')}
4741
+ ${g('abx preview')} --script art.js --schema palette:HexColor:TokenOwner
4742
+ ${g('abx preview')} --script art.js --shoot ./frames --count 12`,
4637
4743
  inspect: `
4638
4744
  ${bold('abx inspect')} <script.js> ${dim('— static analysis of a generative script + a lane recommendation. Read-only; the script is never executed.')}
4639
4745
  ${g('--dep <name@version>[,…]')} the on-chain deps you plan to declare, so the assembled-document size estimate is realistic (e.g. --dep p5@1.0.0)
@@ -4891,7 +4997,10 @@ const COMMAND_HELP = {
4891
4997
  ${g('upload')} <path> upload ONE file → prints its locator (the URI ${g('abx attach')} wants) [--backend …] [--dry-run]
4892
4998
  ${g('balance')} · ${g('topup')} --usd <n> Turbo (arweave) upload credits · ${g('backup-key')} --out <path> copy the managed key`,
4893
4999
  demo: `
4894
- ${bold('abx demo')} <${dim('no args')}> ${dim('— deploy a throwaway 1/1 to the testnet, index it, and serve it — a guided first run. Sends a tx.')}`,
5000
+ ${bold('abx demo')} <${dim('no args')}> ${dim('— deploy a throwaway 1/1 to the testnet, index it, and serve it — a guided first run. Sends a tx.')}
5001
+ ${g('--sign')} ${dim('approve in your browser wallet instead of a hot env key (no key needed)')}
5002
+ ${g('--for')} <0x…> ${dim('pin who must connect on --sign (owner + royalty receiver + mint recipient)')}
5003
+ ${dim('--unsigned / --dry-run are refused here: both skip the broadcast, and the demo indexes + serves what it deployed.')}`,
4895
5004
  minter: `
4896
5005
  ${bold('abx minter')} <configure|show|buy> <token> ${dim('— sell a Series via the shared fixed-price minter (Minter spine).')}
4897
5006
  ${g('configure')} <token> (--price <eth> | --price-raw <units>) --allocation <n> [--erc20 0x..] ${dim('(token-owner only)')}
@@ -4985,7 +5094,9 @@ function help() {
4985
5094
  project + notify the effects layer on change (${g('ABX_WATCH_INTERVAL_MS')}; 0 = off)
4986
5095
 
4987
5096
  ${bold('code / generative projects')} ${dim('— a program is the content; output is a function of live on-chain state')}
4988
- ${g('abx inspect')} <script.js> ${bold('start here')} — static analysis (traits + on-chain reproducibility, deps, doc size RPC viability) + a lane recommendation
5097
+ ${g('abx preview')} (--script <f> | --code-dir <d>) ${bold('while you are still making it')} — run the program on localhost, live: refresh for new seeds,
5098
+ drive your PostParams, watch it animate. Same document the generator serves. ${g('--shoot <dir>')} for headless frames. No chain.
5099
+ ${g('abx inspect')} <script.js> ${bold('before you pick a lane')} — static analysis (traits + on-chain reproducibility, deps, doc size → RPC viability) + a lane recommendation
4989
5100
  ${g('abx scaffold-renderer')} [<dir>] write a buildable Foundry project for the ${bold('in-chain Solidity art lane')} (seed + PostParam → on-chain SVG + traits; you forge build/test/deploy)
4990
5101
  ${g('abx deploy-code')} (--script <file> | --code-dir <dir> | ${g('--image-renderer 0x..')}) deploy a ${bold('generative / code project')} (on-chain script, a build directory, or a Solidity SVG renderer — in-chain art)
4991
5102
  ${bold('--public-base-url <url>')} OR ${bold('--onchain-uri')} · --schema key:Type:Auth · ${g('--dep')} name@version|0x.. (ordered; index 0 = the runtime) ·