@artblocks/abx-cli 0.1.0-alpha.2 → 0.1.0-alpha.3
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 +74 -79
- package/dist/main.js.map +1 -1
- package/package.json +3 -3
- package/skill/SKILL.md +1 -1
package/dist/main.js
CHANGED
|
@@ -134,7 +134,7 @@ async function maybeNotifyUpdate(flags) {
|
|
|
134
134
|
const latest = await checkForCliUpdate(current);
|
|
135
135
|
if (latest) {
|
|
136
136
|
console.error(`\n ${c.orange}⚠${c.reset} update available: ${bold('abx')} ${dim(current)} → ${g(latest)}\n` +
|
|
137
|
-
` upgrade: ${g('npm i -g @artblocks/abx-cli@latest')} ${dim('· or invoke:')} ${g('npx abx@latest <command>')}\n` +
|
|
137
|
+
` upgrade: ${g('npm i -g @artblocks/abx-cli@latest')} ${dim('· or invoke:')} ${g('npx @artblocks/abx-cli@latest <command>')}\n` +
|
|
138
138
|
` release notes: https://github.com/ArtBlocks/abx/releases ${dim('· silence: ABX_NO_UPDATE_CHECK=1')}\n`);
|
|
139
139
|
}
|
|
140
140
|
}
|
|
@@ -4167,114 +4167,109 @@ async function cmdMigrate(address, flags) {
|
|
|
4167
4167
|
}
|
|
4168
4168
|
// ── doctor ────────────────────────────────────────────────────────────────--
|
|
4169
4169
|
async function cmdDoctor(flags) {
|
|
4170
|
-
console.log(bold('\n abx doctor\n')
|
|
4171
|
-
|
|
4170
|
+
console.log(bold('\n abx doctor') + dim(` · ${CHAIN}`) + '\n');
|
|
4171
|
+
// Two visual tiers: PASS/FAIL checks (✓/✗) for things that are either working or broken, and an
|
|
4172
|
+
// "Optional" block (·) for path-dependent setup that is fine to be unset. We deliberately do NOT
|
|
4173
|
+
// use ⚠ for "unset but often fine" — that read as noise; ⚠ is reserved for a real gotcha (a
|
|
4174
|
+
// range-capped RPC). Labels are padded so both tiers align.
|
|
4175
|
+
const CONT = ' '.repeat(17); // continuation indent: aligns under a check/opt detail column
|
|
4176
|
+
const check = (label, pass, detail = '') => console.log(` ${pass ? g('✓') : `${c.red}✗${c.reset}`} ${label.padEnd(13)}${detail ? dim(detail) : ''}`);
|
|
4177
|
+
const opt = (label, detail) => console.log(` ${dim('·')} ${label.padEnd(13)}${dim(detail)}`);
|
|
4172
4178
|
const hasKey = !!(process.env.ABX_DEPLOYER_PK ?? process.env.SEPOLIA_FUNDED_PK ?? process.env.SEPOLIA_WALLET_PK);
|
|
4173
|
-
//
|
|
4174
|
-
//
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
+
// 1. Agent skill — FIRST and prominent. The primary way to use abx is to let a coding agent drive
|
|
4180
|
+
// it, so a missing/stale skill is a ✗: not broken infra, but the main UX isn't set up. Its
|
|
4181
|
+
// version lives in SKILL.md frontmatter (version-locked to this CLI). Notify-only — no exit code.
|
|
4182
|
+
const cliVersion = readCliVersion();
|
|
4183
|
+
const skillVersions = installedSkillVersions();
|
|
4184
|
+
const staleSkills = skillVersions.filter((v) => compareVersions(cliVersion, v) > 0);
|
|
4185
|
+
if (skillVersions.length === 0) {
|
|
4186
|
+
check('agent skill', false, `not installed — run ${g('abx skill install')}`);
|
|
4187
|
+
console.log(`${CONT}${dim('(recommended: let a coding agent drive abx)')}`);
|
|
4188
|
+
}
|
|
4189
|
+
else if (staleSkills.length > 0) {
|
|
4190
|
+
check('agent skill', false, `v${staleSkills.join(', v')} behind CLI v${cliVersion} — run ${g('abx skill install')}`);
|
|
4191
|
+
}
|
|
4192
|
+
else {
|
|
4193
|
+
check('agent skill', true, `in sync (v${cliVersion})`);
|
|
4194
|
+
}
|
|
4195
|
+
console.log('');
|
|
4196
|
+
// 2. Core environment (✓/✗). Signing-wallet balances are computed here (they need the RPC) but
|
|
4197
|
+
// printed in the Optional block below, so buffer them.
|
|
4198
|
+
let signingOpt = null;
|
|
4199
|
+
let forOpt = null;
|
|
4179
4200
|
try {
|
|
4180
4201
|
const publicClient = makePublicClient({ chainKey: CHAIN });
|
|
4181
4202
|
const bn = await publicClient.getBlockNumber();
|
|
4182
|
-
|
|
4183
|
-
//
|
|
4184
|
-
// so the toolkit uses (and recommends) the one fit for the job — and says so if none are.
|
|
4203
|
+
// Collapse the RPC report to one line (best endpoint + head), and only add a ⚠ when there is a
|
|
4204
|
+
// genuine problem — a range-capped-only set that will grind a resolver under load.
|
|
4185
4205
|
const probes = await probeRpcEndpoints({ chainKey: CHAIN });
|
|
4186
4206
|
const usable = probes.filter((pr) => pr.verdict !== 'unusable');
|
|
4187
4207
|
const best = probes.find((pr) => pr.verdict === 'best') ?? usable[0];
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
console.log(` ${glyph} ${pr.label} ${dim('— ' + (pr.verdict === 'best' ? 'wide getLogs range + archive' : pr.reason ?? pr.verdict))}`);
|
|
4194
|
-
}
|
|
4195
|
-
if (usable.length === 0) {
|
|
4196
|
-
console.log(` ${c.orange}↳${c.reset} ${dim('research a current free archive RPC with a wide getLogs range and add it to ABX_RPC_URLS — see the skill’s “Choosing an RPC”')}`);
|
|
4197
|
-
}
|
|
4198
|
-
else if (!probes.some((pr) => pr.verdict === 'best')) {
|
|
4199
|
-
// Every usable endpoint is range-capped. Indexing still works (chunking), but a first
|
|
4200
|
-
// reconstruction — and a resolver under marketplace load — is slow + rate-limit-prone. That's
|
|
4201
|
-
// a serious infra signal, NOT "a paid plan is required": flag it. A normal deploy→index only
|
|
4202
|
-
// scans from the deploy block, so this bites first reconstructions and busy resolvers most.
|
|
4203
|
-
console.log(` ${c.orange}⚠ every usable RPC is getLogs-range-capped${c.reset} ${dim('— add a wide-range archive endpoint to ABX_RPC_URLS before running a resolver under load; capped ones grind on wide scans (skill → “Choosing an RPC”).')}`);
|
|
4204
|
-
}
|
|
4205
|
-
if (hasKey) {
|
|
4206
|
-
const { account } = makeWalletClient({ chainKey: CHAIN });
|
|
4207
|
-
const bal = await publicClient.getBalance({ address: account.address });
|
|
4208
|
-
check('deployer funded', bal > 0n, `${account.address} · ${formatEther(bal)} ETH`);
|
|
4209
|
-
}
|
|
4210
|
-
// Wallet lane has no env key — let a creator preflight THEIR OWN signing wallet's balance,
|
|
4211
|
-
// the one gap where an unfunded wallet otherwise only surfaces at the signing step.
|
|
4212
|
-
if (flags.for) {
|
|
4213
|
-
const bal = await publicClient.getBalance({ address: flags.for });
|
|
4214
|
-
check('wallet funded (--for)', bal > 0n, `${flags.for} · ${formatEther(bal)} ETH${bal > 0n ? '' : ` — ${faucetHint(CHAIN)}`}`);
|
|
4208
|
+
if (usable.length > 0) {
|
|
4209
|
+
check('RPC', true, `${best.label} · head ${bn} · ${best.verdict === 'best' ? 'wide range + archive' : 'range-capped'}`);
|
|
4210
|
+
if (!probes.some((pr) => pr.verdict === 'best')) {
|
|
4211
|
+
console.log(`${CONT}${c.orange}⚠${c.reset}${dim(' every endpoint is getLogs-range-capped — add a wide-range archive RPC to ABX_RPC_URLS before running a resolver under load')}`);
|
|
4212
|
+
}
|
|
4215
4213
|
}
|
|
4216
|
-
else
|
|
4217
|
-
|
|
4214
|
+
else {
|
|
4215
|
+
check('RPC', false, `${CHAIN} — no endpoint usable for reconstruction; add a wide-range archive RPC to ABX_RPC_URLS`);
|
|
4218
4216
|
}
|
|
4219
4217
|
const factory = factoryAddress();
|
|
4220
4218
|
if (factory) {
|
|
4221
4219
|
const code = await publicClient.getCode({ address: factory });
|
|
4222
|
-
if (!code || code === '0x')
|
|
4223
|
-
check('
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
else {
|
|
4229
|
-
check('canonical factory deployed', false, `${factory} — older/incompatible version; \`abx deploy\` redeploys`);
|
|
4230
|
-
}
|
|
4220
|
+
if (!code || code === '0x')
|
|
4221
|
+
check('factory', false, `${factory} — no code on ${CHAIN}; \`abx deploy\` redeploys`);
|
|
4222
|
+
else if (await isCurrentFactory(publicClient, factory))
|
|
4223
|
+
check('factory', true, factory);
|
|
4224
|
+
else
|
|
4225
|
+
check('factory', false, `${factory} — older/incompatible; \`abx deploy\` redeploys`);
|
|
4231
4226
|
}
|
|
4232
4227
|
else {
|
|
4233
|
-
check('
|
|
4228
|
+
check('factory', false, 'none yet — `abx demo` deploys one');
|
|
4229
|
+
}
|
|
4230
|
+
if (hasKey) {
|
|
4231
|
+
const { account } = makeWalletClient({ chainKey: CHAIN });
|
|
4232
|
+
const bal = await publicClient.getBalance({ address: account.address });
|
|
4233
|
+
signingOpt = `env key ${account.address} · ${bal > 0n ? `funded ${formatEther(bal)} ETH` : `empty — fund it (${faucetHint(CHAIN)})`}`;
|
|
4234
|
+
}
|
|
4235
|
+
if (flags.for) {
|
|
4236
|
+
const bal = await publicClient.getBalance({ address: flags.for });
|
|
4237
|
+
forOpt = `${flags.for} · ${bal > 0n ? `funded ${formatEther(bal)} ETH` : `empty — ${faucetHint(CHAIN)}`}`;
|
|
4234
4238
|
}
|
|
4235
4239
|
}
|
|
4236
4240
|
catch (err) {
|
|
4237
|
-
check('RPC
|
|
4241
|
+
check('RPC', false, err.message);
|
|
4238
4242
|
}
|
|
4239
|
-
// public base URL — baked into on-chain URIs at deploy; unset is fine for a demo
|
|
4240
|
-
// but a silent footgun for a real launch, so surface it explicitly.
|
|
4241
|
-
const baseUrl = process.env.ABX_PUBLIC_BASE_URL;
|
|
4242
|
-
if (baseUrl)
|
|
4243
|
-
check('public base URL set (baked into on-chain URIs)', true, baseUrl);
|
|
4244
|
-
else
|
|
4245
|
-
console.log(` ${c.orange}⚠${c.reset} public base URL${dim(' ABX_PUBLIC_BASE_URL unset → resolver-served deploys REFUSE (localhost on-chain resolves for no one). Set it (or --public-base-url) for a hosted resolver. IRRELEVANT if you go fully on-chain: --onchain-image (1/1) or --onchain-uri (code project) bake no resolver base.')}`);
|
|
4246
4243
|
// storage backend — resolve it (catches missing config), then probe liveness/creds
|
|
4247
4244
|
const backendId = activeBackendId();
|
|
4248
4245
|
try {
|
|
4249
4246
|
const backend = resolveBackend(storageOptions());
|
|
4250
4247
|
const h = await backend.health?.();
|
|
4251
4248
|
if (h)
|
|
4252
|
-
check(
|
|
4249
|
+
check('storage', h.ok, `${backend.id} · ${h.detail ?? ''}`);
|
|
4253
4250
|
else
|
|
4254
|
-
check(
|
|
4251
|
+
check('storage', true, `${backend.id} · configured`);
|
|
4255
4252
|
}
|
|
4256
4253
|
catch (err) {
|
|
4257
|
-
check(
|
|
4254
|
+
check('storage', false, `${backendId} · ${err.message}`);
|
|
4258
4255
|
}
|
|
4259
|
-
//
|
|
4256
|
+
// 3. Optional — path-dependent setup. Unset is fine; these say WHEN you'll need each, so an unset
|
|
4257
|
+
// value never reads as a warning.
|
|
4258
|
+
console.log(`\n ${dim('Optional — depends how you deploy:')}`);
|
|
4259
|
+
if (signingOpt)
|
|
4260
|
+
opt('signing', signingOpt);
|
|
4261
|
+
else
|
|
4262
|
+
opt('signing', 'no env key → sign in your browser wallet (--sign). Preflight yours: `abx doctor --for 0x<addr>`. Set ABX_DEPLOYER_PK for the unattended hot lane.');
|
|
4263
|
+
if (forOpt)
|
|
4264
|
+
opt('wallet --for', forOpt);
|
|
4265
|
+
const baseUrl = process.env.ABX_PUBLIC_BASE_URL;
|
|
4266
|
+
if (baseUrl)
|
|
4267
|
+
opt('resolver URL', baseUrl);
|
|
4268
|
+
else
|
|
4269
|
+
opt('resolver URL', 'ABX_PUBLIC_BASE_URL unset → needed for off-chain/resolver-served deploys; skip if fully on-chain (--onchain-image / --onchain-uri).');
|
|
4260
4270
|
if (!process.env.ARWEAVE_JWK && existsSync(arweaveKeyFilePath())) {
|
|
4261
4271
|
const jwk = loadArweaveJwk();
|
|
4262
|
-
|
|
4263
|
-
}
|
|
4264
|
-
// Agent skill co-versioning — the skill drives this CLI and is version-locked to it (its version
|
|
4265
|
-
// lives in SKILL.md frontmatter). Report any installed copy and flag drift; notify-only, so a
|
|
4266
|
-
// stale/absent skill never fails doctor (`abx skill install` resyncs).
|
|
4267
|
-
const cliVersion = readCliVersion();
|
|
4268
|
-
const skillVersions = installedSkillVersions();
|
|
4269
|
-
const staleSkills = skillVersions.filter((v) => compareVersions(cliVersion, v) > 0);
|
|
4270
|
-
if (skillVersions.length === 0) {
|
|
4271
|
-
console.log(` ${dim('↳ agent skill: not installed here — `abx skill install` so a coding agent can drive abx')}`);
|
|
4272
|
-
}
|
|
4273
|
-
else if (staleSkills.length > 0) {
|
|
4274
|
-
console.log(` ${c.orange}⚠${c.reset} agent skill behind CLI${dim(` installed v${staleSkills.join(', v')} · CLI v${cliVersion} — refresh: `)}${g('abx skill install')}`);
|
|
4275
|
-
}
|
|
4276
|
-
else {
|
|
4277
|
-
check(`agent skill in sync (v${cliVersion})`, true);
|
|
4272
|
+
opt('arweave key', `${jwk ? arweaveAddress(jwk) + ' ' : ''}holds upload credits — back it up: \`abx storage backup-key --out <path>\``);
|
|
4278
4273
|
}
|
|
4279
4274
|
console.log('');
|
|
4280
4275
|
}
|