@artblocks/abx-cli 0.1.0-alpha.0 → 0.1.0-alpha.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.
@@ -0,0 +1,58 @@
1
+ /** Read the running CLI's own version from its package.json. Resolves the same in dev
2
+ * (src/update-check.ts → packages/cli/package.json) and published (dist/update-check.js →
3
+ * <pkg>/package.json) — npm always ships package.json regardless of the `files` allowlist.
4
+ * Returns '0.0.0' if unreadable, so callers never throw on a self-version read. */
5
+ export declare function readCliVersion(): string;
6
+ /**
7
+ * Compare two semver strings. Returns -1 if a < b, 0 if equal, 1 if a > b. Handles the
8
+ * prerelease rule (1.0.0-alpha < 1.0.0) since our own versions are prereleases today
9
+ * (0.1.0-alpha.0). Build metadata (+…) is ignored per semver. Tolerant of a leading `v`.
10
+ */
11
+ export declare function compareVersions(a: string, b: string): number;
12
+ /** True when `latest` is a strictly newer release than `current`. */
13
+ export declare function isNewer(latest: string, current: string): boolean;
14
+ /** GET the `latest` dist-tag's version from the npm registry. Aborts after {@link TIMEOUT_MS};
15
+ * returns null on any failure (offline, timeout, non-200, malformed body) — never throws. */
16
+ export declare function fetchLatestVersion(pkg?: string): Promise<string | null>;
17
+ /**
18
+ * Resolve the latest published version IF it is newer than `current`, else null. Reads a disk
19
+ * cache first and only hits the network when the cache is older than {@link TTL_MS}. On a network
20
+ * failure it still stamps the cache (backing off a full TTL instead of retrying every command).
21
+ * Swallows every error — the update check must never break or slow a real command.
22
+ *
23
+ * `now` is injectable so tests can drive TTL behavior deterministically.
24
+ */
25
+ export declare function checkForCliUpdate(current: string, now?: number): Promise<string | null>;
26
+ /** The skill's folder name — matches SKILL.md `name`, per the Agent Skills rule that a skill
27
+ * directory must be named for its `name` field. */
28
+ export declare const SKILL_DIR_NAME = "abx-self-host";
29
+ /**
30
+ * Every skills PARENT directory an ABX-capable agent scans for a `SKILL.md`, keyed by agent.
31
+ * These are the discovery locations the agents actually read (verified against each agent's docs):
32
+ * • `.claude/skills` — Claude Code (and Copilot also reads it)
33
+ * • `.agents/skills` — the near-universal neutral dir: Cursor, Codex, Gemini, and Copilot all
34
+ * read it (Gemini/Cursor treat it as the canonical alias over their own dir)
35
+ * `abx skill install` writes the version-locked bundle into these; the drift check reads it back.
36
+ */
37
+ export declare const AGENT_SKILL_PARENTS: Record<string, string>;
38
+ /** Distinct skills-parent dirs to scan for a possibly-installed copy — the union of every agent's
39
+ * discovery dirs (including a few per-agent aliases), so drift detection finds the skill no matter
40
+ * which agent (or install route) put it there. Missing dirs are simply skipped. */
41
+ export declare const ALL_SKILL_PARENTS: string[];
42
+ /**
43
+ * Parse `metadata.version` from a `SKILL.md`'s YAML frontmatter. Dependency-free on purpose: the
44
+ * frontmatter is the small, controlled block between the leading `---` fences, and the version is
45
+ * the only `version:` key we write there. Returns null if the file is missing/unreadable or
46
+ * declares no version.
47
+ */
48
+ export declare function readSkillVersion(skillMdPath: string): string | null;
49
+ /**
50
+ * Versions of any locally-installed skill copy (project-local under CWD, and global under HOME),
51
+ * read from each copy's own `SKILL.md` frontmatter. The skill and CLI are co-versioned, but a
52
+ * separately-installed skill copy does NOT move when the CLI upgrades — this lets the notifier
53
+ * catch that drift. Because the version travels INSIDE `SKILL.md`, this works for every install
54
+ * route (bundled `abx skill install`, git-based `npx skills add`, or a manual copy) — not just
55
+ * the CLI's own installer.
56
+ */
57
+ export declare function installedSkillVersions(): string[];
58
+ //# sourceMappingURL=update-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-check.d.ts","sourceRoot":"","sources":["../src/update-check.ts"],"names":[],"mappings":"AAqBA;;;oFAGoF;AACpF,wBAAgB,cAAc,IAAI,MAAM,CAQvC;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CA+B5D;AAED,qEAAqE;AACrE,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAEhE;AAiBD;8FAC8F;AAC9F,wBAAsB,kBAAkB,CAAC,GAAG,SAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAc1E;AAMD;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA4BzG;AAED;oDACoD;AACpD,eAAO,MAAM,cAAc,kBAAkB,CAAC;AAE9C;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAMtD,CAAC;AAEF;;oFAEoF;AACpF,eAAO,MAAM,iBAAiB,UAO7B,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUnE;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CASjD"}
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Update check — a notify-only "you're behind" nudge, extracted from main.ts so the
3
+ * version comparison and cache logic are unit-testable (importing main executes the CLI).
4
+ *
5
+ * Policy (owner's call): NOTIFY, never self-mutate. The CLI checks npm at most once per
6
+ * {@link TTL_MS} (cached to disk), prints an upgrade one-liner to STDERR (so it never
7
+ * corrupts machine-readable stdout an agent may be parsing), and is a hard no-op when
8
+ * offline, in CI, or opted out (ABX_NO_UPDATE_CHECK / --no-update-check — see main.ts).
9
+ * The check lives in the CLI binary, so it fires no matter which agent (Claude, Codex,
10
+ * Cursor, …) drives `abx` — one implementation, every agent covered.
11
+ */
12
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
13
+ import { dirname, join, resolve } from 'node:path';
14
+ import { fileURLToPath } from 'node:url';
15
+ import { homedir } from 'node:os';
16
+ const REGISTRY = 'https://registry.npmjs.org';
17
+ const PKG = '@artblocks/abx-cli';
18
+ const TTL_MS = 24 * 60 * 60 * 1000; // check npm at most once a day
19
+ const TIMEOUT_MS = 1500; // never hang a command on a slow/offline network
20
+ /** Read the running CLI's own version from its package.json. Resolves the same in dev
21
+ * (src/update-check.ts → packages/cli/package.json) and published (dist/update-check.js →
22
+ * <pkg>/package.json) — npm always ships package.json regardless of the `files` allowlist.
23
+ * Returns '0.0.0' if unreadable, so callers never throw on a self-version read. */
24
+ export function readCliVersion() {
25
+ try {
26
+ const pkgDir = resolve(fileURLToPath(import.meta.url), '..', '..');
27
+ const raw = JSON.parse(readFileSync(join(pkgDir, 'package.json'), 'utf8'));
28
+ return typeof raw.version === 'string' ? raw.version : '0.0.0';
29
+ }
30
+ catch {
31
+ return '0.0.0';
32
+ }
33
+ }
34
+ /**
35
+ * Compare two semver strings. Returns -1 if a < b, 0 if equal, 1 if a > b. Handles the
36
+ * prerelease rule (1.0.0-alpha < 1.0.0) since our own versions are prereleases today
37
+ * (0.1.0-alpha.0). Build metadata (+…) is ignored per semver. Tolerant of a leading `v`.
38
+ */
39
+ export function compareVersions(a, b) {
40
+ const pa = parseSemver(a);
41
+ const pb = parseSemver(b);
42
+ for (let i = 0; i < 3; i++) {
43
+ if (pa.main[i] !== pb.main[i])
44
+ return pa.main[i] < pb.main[i] ? -1 : 1;
45
+ }
46
+ // A version WITH a prerelease is lower than the same version without one.
47
+ if (!pa.pre.length && !pb.pre.length)
48
+ return 0;
49
+ if (!pa.pre.length)
50
+ return 1;
51
+ if (!pb.pre.length)
52
+ return -1;
53
+ const n = Math.max(pa.pre.length, pb.pre.length);
54
+ for (let i = 0; i < n; i++) {
55
+ const x = pa.pre[i];
56
+ const y = pb.pre[i];
57
+ if (x === undefined)
58
+ return -1; // a shorter prerelease set is smaller (1.0.0-a < 1.0.0-a.1)
59
+ if (y === undefined)
60
+ return 1;
61
+ const xn = /^\d+$/.test(x);
62
+ const yn = /^\d+$/.test(y);
63
+ if (xn && yn) {
64
+ const dx = Number(x);
65
+ const dy = Number(y);
66
+ if (dx !== dy)
67
+ return dx < dy ? -1 : 1;
68
+ }
69
+ else if (xn) {
70
+ return -1; // numeric identifiers rank below alphanumeric ones
71
+ }
72
+ else if (yn) {
73
+ return 1;
74
+ }
75
+ else if (x !== y) {
76
+ return x < y ? -1 : 1;
77
+ }
78
+ }
79
+ return 0;
80
+ }
81
+ /** True when `latest` is a strictly newer release than `current`. */
82
+ export function isNewer(latest, current) {
83
+ return compareVersions(latest, current) > 0;
84
+ }
85
+ function parseSemver(v) {
86
+ const clean = String(v).trim().replace(/^v/, '').split('+')[0];
87
+ const dash = clean.indexOf('-');
88
+ const mainStr = dash === -1 ? clean : clean.slice(0, dash);
89
+ const preStr = dash === -1 ? '' : clean.slice(dash + 1);
90
+ const parts = mainStr.split('.').map((n) => {
91
+ const d = Number(n);
92
+ return Number.isFinite(d) ? d : 0;
93
+ });
94
+ return {
95
+ main: [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0],
96
+ pre: preStr ? preStr.split('.') : [],
97
+ };
98
+ }
99
+ /** GET the `latest` dist-tag's version from the npm registry. Aborts after {@link TIMEOUT_MS};
100
+ * returns null on any failure (offline, timeout, non-200, malformed body) — never throws. */
101
+ export async function fetchLatestVersion(pkg = PKG) {
102
+ const ctrl = new AbortController();
103
+ const timer = setTimeout(() => ctrl.abort(), TIMEOUT_MS);
104
+ try {
105
+ // Scoped packages encode the '/' as %2F; the /latest endpoint returns that version's document.
106
+ const res = await fetch(`${REGISTRY}/${pkg.replace('/', '%2F')}/latest`, { signal: ctrl.signal });
107
+ if (!res.ok)
108
+ return null;
109
+ const body = (await res.json());
110
+ return typeof body.version === 'string' ? body.version : null;
111
+ }
112
+ catch {
113
+ return null;
114
+ }
115
+ finally {
116
+ clearTimeout(timer);
117
+ }
118
+ }
119
+ function cachePath() {
120
+ return join(homedir(), '.cache', 'abx', 'update-check.json');
121
+ }
122
+ /**
123
+ * Resolve the latest published version IF it is newer than `current`, else null. Reads a disk
124
+ * cache first and only hits the network when the cache is older than {@link TTL_MS}. On a network
125
+ * failure it still stamps the cache (backing off a full TTL instead of retrying every command).
126
+ * Swallows every error — the update check must never break or slow a real command.
127
+ *
128
+ * `now` is injectable so tests can drive TTL behavior deterministically.
129
+ */
130
+ export async function checkForCliUpdate(current, now = Date.now()) {
131
+ let latest = null;
132
+ try {
133
+ const path = cachePath();
134
+ let cache = {};
135
+ try {
136
+ if (existsSync(path))
137
+ cache = JSON.parse(readFileSync(path, 'utf8'));
138
+ }
139
+ catch {
140
+ cache = {};
141
+ }
142
+ const fresh = typeof cache.checkedAt === 'number' && now - cache.checkedAt < TTL_MS;
143
+ if (fresh) {
144
+ latest = typeof cache.latest === 'string' ? cache.latest : null;
145
+ }
146
+ else {
147
+ const fetched = await fetchLatestVersion();
148
+ latest = fetched ?? (typeof cache.latest === 'string' ? cache.latest : null);
149
+ try {
150
+ mkdirSync(dirname(path), { recursive: true });
151
+ writeFileSync(path, JSON.stringify({ checkedAt: now, latest }));
152
+ }
153
+ catch {
154
+ /* a read-only HOME just means we re-check next run */
155
+ }
156
+ }
157
+ }
158
+ catch {
159
+ return null;
160
+ }
161
+ if (!latest)
162
+ return null;
163
+ return isNewer(latest, current) ? latest : null;
164
+ }
165
+ /** The skill's folder name — matches SKILL.md `name`, per the Agent Skills rule that a skill
166
+ * directory must be named for its `name` field. */
167
+ export const SKILL_DIR_NAME = 'abx-self-host';
168
+ /**
169
+ * Every skills PARENT directory an ABX-capable agent scans for a `SKILL.md`, keyed by agent.
170
+ * These are the discovery locations the agents actually read (verified against each agent's docs):
171
+ * • `.claude/skills` — Claude Code (and Copilot also reads it)
172
+ * • `.agents/skills` — the near-universal neutral dir: Cursor, Codex, Gemini, and Copilot all
173
+ * read it (Gemini/Cursor treat it as the canonical alias over their own dir)
174
+ * `abx skill install` writes the version-locked bundle into these; the drift check reads it back.
175
+ */
176
+ export const AGENT_SKILL_PARENTS = {
177
+ claude: '.claude/skills',
178
+ cursor: '.agents/skills',
179
+ codex: '.agents/skills',
180
+ gemini: '.agents/skills',
181
+ copilot: '.agents/skills',
182
+ };
183
+ /** Distinct skills-parent dirs to scan for a possibly-installed copy — the union of every agent's
184
+ * discovery dirs (including a few per-agent aliases), so drift detection finds the skill no matter
185
+ * which agent (or install route) put it there. Missing dirs are simply skipped. */
186
+ export const ALL_SKILL_PARENTS = [
187
+ '.claude/skills',
188
+ '.agents/skills',
189
+ '.cursor/skills',
190
+ '.gemini/skills',
191
+ '.github/skills',
192
+ '.copilot/skills',
193
+ ];
194
+ /**
195
+ * Parse `metadata.version` from a `SKILL.md`'s YAML frontmatter. Dependency-free on purpose: the
196
+ * frontmatter is the small, controlled block between the leading `---` fences, and the version is
197
+ * the only `version:` key we write there. Returns null if the file is missing/unreadable or
198
+ * declares no version.
199
+ */
200
+ export function readSkillVersion(skillMdPath) {
201
+ try {
202
+ const raw = readFileSync(skillMdPath, 'utf8');
203
+ const fm = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw);
204
+ const front = fm ? fm[1] : '';
205
+ const vm = /(?:^|\n)\s*version:\s*["']?([\w.+-]+)["']?/.exec(front);
206
+ return vm ? vm[1] : null;
207
+ }
208
+ catch {
209
+ return null;
210
+ }
211
+ }
212
+ /**
213
+ * Versions of any locally-installed skill copy (project-local under CWD, and global under HOME),
214
+ * read from each copy's own `SKILL.md` frontmatter. The skill and CLI are co-versioned, but a
215
+ * separately-installed skill copy does NOT move when the CLI upgrades — this lets the notifier
216
+ * catch that drift. Because the version travels INSIDE `SKILL.md`, this works for every install
217
+ * route (bundled `abx skill install`, git-based `npx skills add`, or a manual copy) — not just
218
+ * the CLI's own installer.
219
+ */
220
+ export function installedSkillVersions() {
221
+ const out = new Set();
222
+ for (const root of [process.cwd(), homedir()]) {
223
+ for (const parent of ALL_SKILL_PARENTS) {
224
+ const v = readSkillVersion(join(root, parent, SKILL_DIR_NAME, 'SKILL.md'));
225
+ if (v)
226
+ out.add(v);
227
+ }
228
+ }
229
+ return [...out];
230
+ }
231
+ //# sourceMappingURL=update-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-check.js","sourceRoot":"","sources":["../src/update-check.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@artblocks/abx-cli",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.1.0-alpha.2",
4
4
  "license": "MIT",
5
5
  "description": "ABX CLI ('abx') — the agentic UX surface of the Self-Host Toolkit (Layer 3). Deploy, index, serve, and demo a self-hosted ABX project end to end. Wraps the SDK; runs a different implementation and the protocol works identically.",
6
6
  "type": "module",
@@ -38,10 +38,10 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "viem": "^2.21.0",
41
- "@artblocks/abx-storage": "0.1.0-alpha.0",
42
41
  "@artblocks/abx-sdk": "0.1.0-alpha.0",
43
42
  "@artblocks/abx-indexer": "0.1.0-alpha.0",
44
- "@artblocks/abx-token-api": "0.1.0-alpha.0"
43
+ "@artblocks/abx-token-api": "0.1.0-alpha.0",
44
+ "@artblocks/abx-storage": "0.1.0-alpha.0"
45
45
  },
46
46
  "optionalDependencies": {
47
47
  "@artblocks/abx-effects": "0.1.0-alpha.0"
package/skill/SKILL.md CHANGED
@@ -1,6 +1,9 @@
1
1
  ---
2
2
  name: abx-self-host
3
3
  description: Launch and operate a self-hosted ABX NFT end to end with the ABX CLI (`abx`) on testnet — a 1/1 (`abx deploy`), a multi-token Series from a folder of media (`abx deploy-series`), or a generative/code drop (`abx deploy-code`). Covers on-chain vs off-chain metadata, storage custody (local disk, S3/R2, IPFS, Arweave), deploy + mint (now or pre-warmed at a predicted address), rendered thumbnails and on-chain traits for code art, primary sales via the shared fixed-price minter, and owner ops (transfer, refresh, re-point URIs, royalties, lock fields, pause/unpause, supply cap, delegate minting). Use when the user wants to self-host an ABX project, take an image to an NFT on testnet, deploy a collection from a folder of images, launch generative/code art, mint or run a primary sale, refresh a listing, operate a project they launched, choose a storage backend, or stand up hosting they own.
4
+ compatibility: Drives the abx CLI (@artblocks/abx-cli). Co-versioned with it — install/refresh with `abx skill install` so this skill matches the CLI's `abx version`. Requires Node 22.5+.
5
+ metadata:
6
+ version: "0.1.0-alpha.2"
4
7
  ---
5
8
 
6
9
  # ABX Self-Host Toolkit (`abx`)
@@ -12,6 +15,7 @@ L3 agentic surface: image → live self-hosted NFT the creator owns — a **1/1*
12
15
  ## Read first (every session)
13
16
 
14
17
  - **YOU run the `abx` commands — never tell the creator to run one.** You have a shell; use it. Run `doctor`, `ls`, `--dry-run`, `tokenuri`, `state`, `refresh`, `balance`, etc. yourself and read the output — don't paste a command and wait for them to run it or copy back results. The creator's *only* hands-on step is approving in their **browser wallet** (`--sign`) or giving you a value you asked for (their address, a name). Even "next steps" after a deploy: **run the read-only ones** (`tokenuri` to prove it resolves) and **offer to run** the actions (`refresh`, `unpause`, `mint`) — don't hand over a list of commands to run. Exceptions: a genuinely interactive/again-in-their-env command (an OS login, `gcloud auth`), and the **in-chain Solidity lane's Foundry step** (`forge build/test/deploy` a renderer — `abx` never compiles/deploys Solidity; see [Code projects](#code-projects-generative--code-based-drops)) — then run it if you have the tool, else hand it over.
18
+ - **Skill ⇄ CLI version must match.** This skill is co-versioned with the `abx` CLI. Run `abx version` and compare it to this file's frontmatter `metadata.version` (top of SKILL.md). If they differ, this skill is stale for the installed CLI — run `abx skill install` to resync, then reload the skill before continuing. (`abx` also prints a drift nudge on its own when it notices.)
15
19
  - **`abx doctor` first, always** — full preflight (Node, pnpm, RPC, signing key, storage). Fix any ✗ before deploying ([Setup](#setup--environment)). A missing public-base-url is not a "set up IPFS" signal: for tiny art go on-chain, for larger art pick an off-chain backend — see [Quick start](#quick-start).
16
20
  - **Never collect secrets in chat.** Keys, `PINATA_JWT`, S3 secrets → the project's `.env`. The Arweave/Turbo key is a CLI-managed file (`.abx-self-host/arweave-key.json`) — never paste it. Name the var/file; never take the value.
17
21
  - **Testnet only today** — every launch is on a testnet: **Base Sepolia by default** (`ABX_CHAIN` unset), with **Sepolia** also shipped (`ABX_CHAIN=sepolia`). Say "testnet"; don't imply mainnet. **Testnet IS the preview + e2e environment**: it runs the *real* wiring (renderers, generator, on-chain tokenURI assembly), so a creator should deploy there, inspect the actual result (`abx tokenuri` / the live view / `abx verify`), confirm it looks right, and only *then* go to mainnet — no separate local "preview" is as faithful as the real testnet drop, and a testnet deploy is ~free + ~minutes. **One cross-chain gotcha: on-chain library deps (`--dep p5@…`) resolve to on-chain bytes only where an Art Blocks dependency registry exists — that's Sepolia, NOT Base Sepolia.** A no-dependency script (vanilla JS/GLSL) goes fully on-chain on either; a drop that needs a registry-hosted library on-chain must target `ABX_CHAIN=sepolia` (or run the resolver lane).
@@ -62,7 +62,7 @@ A failed upload is the #1 place an agent goes off the rails: it invents a cause
62
62
 
63
63
  For the large/mutable default. `abx deploy-resolver --provider <fly|render|vps> --domain <meta.you.xyz>` scaffolds the artifact and prints the exact next steps, the DNS record, and the bake reminder. Providers: **fly.io** / **render** (Docker PaaS, free tier, custom domain) and a **VPS** (compose + Caddy auto-TLS). The host is **read-only** — serves + accepts admin index-control, no signing key on it (writes are signed locally), so a compromised host can at worst serve wrong bytes (the keccak catches it). Prefer **a domain you control** (move = DNS, not a tx) — but without one the scaffold now bakes the **real platform hostname** (`<app>.fly.dev` / `<app>.onrender.com`) so the resolver works out of the box (add a custom domain later). It NO LONGER bakes a dead `<app>.example` placeholder, and the resolver **refuses to serve** an `.example`/placeholder base (or a localhost base in a hosted image, `ABX_HOSTED=1`) — a loud fail beats silently serving dead image/animation links. The one sub-decision is *which provider* (ask + recommend); you scaffold, the human owns the cloud account + domain.
64
64
 
65
- **The artifact is fully self-contained.** `deploy-resolver` writes `deploy/<provider>/` with its OWN `Dockerfile` + `.dockerignore` + config (a production image installs the published CLI: `npm i -g --no-optional @artblocks/abx-cli`). **Run every next step from that dir.** You never supply, copy, or hand-edit a Dockerfile. **If a step seems to need a file from elsewhere (`../Dockerfile`, a `packages/` dir, the repo), STOP — that's a scaffold bug, not something to work around.** Report it; don't MacGyver it. (Local pre-publish dev sets `ABX_RESOLVER_SOURCE=1` → a build-from-source artifact instead, still self-contained; you don't set this.)
65
+ **The artifact is fully self-contained.** `deploy-resolver` writes `deploy/<provider>/` with its OWN `Dockerfile` + `.dockerignore` + config (a production image installs the published CLI: `npm i -g --no-optional @artblocks/abx-cli`). **Run every next step from that dir.** You never supply, copy, or hand-edit a Dockerfile. **If a step seems to need a file from elsewhere (`../Dockerfile`, a `packages/` dir, the repo), STOP — that's a scaffold bug, not something to work around.** Report it; don't MacGyver it. (Local from-source/contributor dev sets `ABX_RESOLVER_SOURCE=1` → a build-from-source artifact instead, still self-contained; you don't set this.)
66
66
 
67
67
  **Never inline a secret into a command.** The generated steps **source from `.env`** (`set -a; . ../../.env; set +a` then `fly secrets set ABX_RPC_URLS="$ABX_RPC_URLS"`) — keep that shape. Never substitute a literal secret into command text; it must never appear in output or shell history.
68
68