@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 +114 -3
- package/dist/main.js.map +1 -1
- package/dist/preview.d.ts +63 -0
- package/dist/preview.d.ts.map +1 -0
- package/dist/preview.js +483 -0
- package/dist/preview.js.map +1 -0
- package/package.json +6 -3
- package/skill/SKILL.md +49 -5
- package/skill/reference/code-projects.md +35 -0
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
|
-
|
|
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
|
|
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) ·
|