@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.
- package/dist/flags.d.ts +6 -0
- package/dist/flags.d.ts.map +1 -1
- package/dist/flags.js +7 -1
- package/dist/flags.js.map +1 -1
- package/dist/main.js +128 -20
- package/dist/main.js.map +1 -1
- package/dist/update-check.d.ts +58 -0
- package/dist/update-check.d.ts.map +1 -0
- package/dist/update-check.js +231 -0
- package/dist/update-check.js.map +1 -0
- package/package.json +3 -3
- package/skill/SKILL.md +4 -0
- package/skill/reference/hosting.md +1 -1
|
@@ -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.
|
|
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
|
|
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
|
|