@feralfile/cli 1.1.4 → 1.3.0

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.
Files changed (36) hide show
  1. package/README.md +50 -0
  2. package/dist/index.js +4 -2
  3. package/dist/package.json +84 -0
  4. package/dist/src/ai-orchestrator/index.js +14 -2
  5. package/dist/src/commands/build.js +3 -3
  6. package/dist/src/commands/chat.js +3 -3
  7. package/dist/src/commands/config.js +2 -2
  8. package/dist/src/commands/device.js +6 -6
  9. package/dist/src/commands/find.js +502 -0
  10. package/dist/src/commands/helpers/device-discovery.js +1 -1
  11. package/dist/src/commands/play.js +52 -11
  12. package/dist/src/commands/setup.js +1 -1
  13. package/dist/src/commands/status.js +2 -2
  14. package/dist/src/intent-parser/index.js +16 -5
  15. package/dist/src/intent-parser/utils.js +7 -2
  16. package/dist/src/main.js +61 -17
  17. package/dist/src/utilities/ab-marketplace.js +95 -0
  18. package/dist/src/utilities/feral-file-artwork.js +89 -0
  19. package/dist/src/utilities/ff-marketplace.js +115 -0
  20. package/dist/src/utilities/ff1-device.js +4 -4
  21. package/dist/src/utilities/functions.js +1 -1
  22. package/dist/src/utilities/fxhash-marketplace.js +146 -0
  23. package/dist/src/utilities/index.js +80 -1
  24. package/dist/src/utilities/marketplace-url.js +373 -0
  25. package/dist/src/utilities/neort-marketplace.js +102 -0
  26. package/dist/src/utilities/nft-indexer.js +22 -14
  27. package/dist/src/utilities/objkt-marketplace.js +77 -0
  28. package/dist/src/utilities/playlist-publisher.js +9 -42
  29. package/dist/src/utilities/raster-client.js +252 -0
  30. package/dist/src/utilities/user-agent.js +20 -0
  31. package/docs/CONFIGURATION.md +3 -1
  32. package/docs/EXAMPLES.md +23 -0
  33. package/docs/FUNCTION_CALLING.md +3 -1
  34. package/docs/README.md +11 -10
  35. package/docs/RELEASING.md +92 -22
  36. package/package.json +1 -1
package/docs/EXAMPLES.md CHANGED
@@ -70,12 +70,35 @@ npm run dev -- build examples/params-example.json -o playlist.json
70
70
  cat examples/params-example.json | npm run dev -- build -o playlist.json
71
71
  ```
72
72
 
73
+ Build from a Feral File public artwork id or artwork URL:
74
+
75
+ ```bash
76
+ cat > /tmp/ff-artwork.json <<'JSON'
77
+ {
78
+ "requirements": [
79
+ {
80
+ "type": "feral_file_artwork",
81
+ "artworkId": "https://feralfile.com/exhibitions/artwork/f0240e04d64717e319584957f6a83954b029254ad1260b6320472ea8c0c5b1cf"
82
+ }
83
+ ],
84
+ "playlistSettings": {
85
+ "title": "Feral File Artwork"
86
+ }
87
+ }
88
+ JSON
89
+
90
+ npm run dev -- build /tmp/ff-artwork.json -o playlist.json
91
+ ```
92
+
73
93
  ## AI‑Orchestrated Deterministic Flow (prompts)
74
94
 
75
95
  ```bash
76
96
  # Show tool‑call progress and validation
77
97
  npm run dev -- chat "Build a playlist of my works from reas.eth plus 3 from Unsupervised" -v -o playlist.json
78
98
 
99
+ # Build from a Feral File artwork URL
100
+ npm run dev -- chat "Build a playlist from https://feralfile.com/exhibitions/artwork/f0240e04d64717e319584957f6a83954b029254ad1260b6320472ea8c0c5b1cf" -v -o playlist.json
101
+
79
102
  # Switch model if desired
80
103
  npm run dev -- chat "Build playlist from Ethereum address 0xaeE022552B539dB18297D7481b6D547C622488B3 and 2 from Unsupervised" --model gpt -v
81
104
  ```
@@ -24,8 +24,9 @@ Key files:
24
24
  Defined in `src/ai-orchestrator/index.js` as tool schemas for OpenAI‑compatible clients.
25
25
 
26
26
  - `query_requirement(requirement, duration)`
27
- - Types: `build_playlist`, `fetch_feed`, `query_address`
27
+ - Types: `build_playlist`, `feral_file_artwork`, `fetch_feed`, `query_address`
28
28
  - For `build_playlist`: requires `blockchain`, `contractAddress`, `tokenIds`, optional `quantity`
29
+ - For `feral_file_artwork`: requires `artworkId`, either a Feral File public artwork id or `/exhibitions/artwork/{id}` URL
29
30
  - For `query_address`: requires `ownerAddress`, optional `quantity` (random selection)
30
31
  - For `fetch_feed`: requires `playlistName`, `quantity`
31
32
 
@@ -65,6 +66,7 @@ Two options are available:
65
66
  {
66
67
  "requirements": [
67
68
  { "type": "fetch_feed", "playlistName": "Social Codes", "quantity": 3 },
69
+ { "type": "feral_file_artwork", "artworkId": "https://feralfile.com/exhibitions/artwork/..." },
68
70
  {
69
71
  "type": "build_playlist",
70
72
  "blockchain": "ethereum",
package/docs/README.md CHANGED
@@ -144,9 +144,9 @@ Notes:
144
144
  - `verify <file-or-url>` – Validate structure and verify signatures. On failure, the CLI labels structure issues separately from signature verification. dp1-js uses `--public-key` (or a key derived from `playlist.privateKey` / `PLAYLIST_PRIVATE_KEY` when omitted) **only** for legacy flat `signature` verification; DP-1 v1.1.0 `signatures[]` envelopes are verified without relying on that argument. If deriving or normalizing key material fails, the CLI prints a warning on stderr and continues without it (legacy verification still requires a usable key when the playlist uses a flat `signature`). The derived key is emitted as PEM. Supported key forms: hex with optional `0x`, PEM, or 32-byte raw public key as hex or base64
145
145
  - `sign <file>` – Sign playlist with a DP-1 v1.1.0 multi-signature envelope (private key string is forwarded to **`dp1-js`**; same hex or base64 PKCS#8 DER forms as `playlist.privateKey` in `./CONFIGURATION.md`). The command verifies the final envelope before writing output and refuses to persist tampered or otherwise unverifiable `signatures[]`.
146
146
  - Options: `-k, --key <privateKey>`, `-r, --role <role>`, `-o, --output <file>`
147
- - `play <source>` – Play a playlist file, playlist URL, or media URL on an FF1 device (runs `validate`-style structure checks before sending; use `verify` for signatures)
148
- - Options: `-d, --device <name>`, `--skip-verify` (skip structure validation; not recommended)
149
- - `publish <file>` – Publish a playlist to a feed server (`validate`-style structure checks before upload; use `verify` for signatures)
147
+ - `play <source>` – Play a playlist file, playlist URL, or media URL on an FF1 device (runs `verify` before sending; only the CLI-synthesized media URL fallback is auto-signed when a signing key is configured; use `--skip-verify` to bypass the gate)
148
+ - Options: `-d, --device <name>`, `--skip-verify` (skip signature verification; not recommended)
149
+ - `publish <file>` – Publish a playlist to a feed server (runs `verify` before upload and rejects unsigned or broken playlists)
150
150
  - Options: `-s, --server <index>` (server index if multiple configured)
151
151
  - `ssh <enable|disable>` – Manage SSH access on an FF1 device
152
152
  - Options: `-d, --device <name>`, `--pubkey <path>`, `--ttl <duration>`
@@ -184,8 +184,8 @@ npm run dev -- chat "From Ethereum contract 0xb932a70A57673d89f4acfFBE830E8ed7f7
184
184
  How it works (at a glance):
185
185
 
186
186
  - The intent parser maps your text to `requirements` (what to fetch) and `playlistSettings` (e.g., `durationPerItem`, `preserveOrder=false` for shuffle, `deviceName`, `feedServer`).
187
- - Deterministic tools fetch NFT metadata, build a DP‑1 playlist, and validate it.
188
- - If `deviceName` is present, the CLI will send the validated playlist to that FF1 device.
187
+ - Deterministic tools fetch NFT metadata, build a DP‑1 playlist, and verify it.
188
+ - If `deviceName` is present, the CLI will send the verified playlist to that FF1 device.
189
189
  - If `feedServer` is present (via "publish to my feed"), the CLI will publish the playlist to the selected feed server.
190
190
 
191
191
  Use `--model claude|grok|gpt|gemini` to switch models, or set `defaultModel` in `config.json`.
@@ -215,7 +215,7 @@ Publishing keywords: "publish", "publish to my feed", "push to feed", "send to f
215
215
 
216
216
  1. Detect the keyword and call `get_feed_servers`
217
217
  2. If multiple servers → ask which one to use
218
- 3. Build → validate (structure) → publish automatically
218
+ 3. Build → verify → publish automatically
219
219
  4. Display playlist ID and server URL on success
220
220
 
221
221
  If all configured feed servers are unreachable, the CLI now reports a feed availability error instead of "playlist not found".
@@ -238,7 +238,7 @@ npm run dev -- validate playlist.json
238
238
  # Sign (uses key and role from config, or overrides via --key / --role)
239
239
  npm run dev -- sign playlist.json -o signed.json
240
240
 
241
- # Play on configured default device (validates DP-1 structure by default)
241
+ # Play on configured default device (verifies playlists; only media URL fallbacks may be auto-signed)
242
242
  npm run dev -- play playlist.json
243
243
 
244
244
  # Play on a specific named device
@@ -256,7 +256,7 @@ npm run dev -- play "https://cdn.example.com/playlist.json"
256
256
  # Play a media URL directly
257
257
  npm run dev -- play "https://example.com/video.mp4"
258
258
 
259
- # Skip structure validation only if you must send a non-conformant payload (not recommended)
259
+ # Skip signature verification only if you must send a non-conformant payload (not recommended)
260
260
  npm run dev -- play playlist.json --skip-verify
261
261
  ```
262
262
 
@@ -285,9 +285,10 @@ npm run dev -- publish playlist.json -s 1
285
285
 
286
286
  The `publish` command:
287
287
 
288
- - Validates playlist structure (same as `validate`; does not verify signatures)
288
+ - Verifies playlist signatures before upload
289
+ - Rejects unsigned or broken playlists
289
290
  - Shows interactive server selection if multiple are configured
290
- - Sends the validated playlist to the chosen feed server
291
+ - Sends the verified playlist to the chosen feed server
291
292
  - Returns the playlist ID on success
292
293
 
293
294
  Configure feed servers in `config.json`:
package/docs/RELEASING.md CHANGED
@@ -1,8 +1,57 @@
1
- # Releasing Binary Assets
1
+ # Releasing ff-cli
2
2
 
3
- The curl installer downloads prebuilt binaries from GitHub Releases. Build one asset per OS/arch and upload both the archive and its `.sha256` checksum.
3
+ How releases happen, how to cut one, and what to do when things go sideways.
4
4
 
5
- ## Build a Release Asset (local)
5
+ ## Cut a release (day-to-day)
6
+
7
+ 1. **Bump `package.json` version** on `main` (or via a `chore: bump version to X.Y.Z` PR — the project has used both patterns).
8
+ 2. **Push to `main`** so the tag created in step 3 points at the bump commit.
9
+ 3. **Create a GitHub Release** with the tag equal to the version (e.g. tag `1.2.0` → `"version": "1.2.0"`). Use `gh release create 1.2.0 --title "v1.2.0" --notes "..."` or the web UI.
10
+ 4. **The `release.yml` workflow fires automatically**, verifies tag == `package.json` version, runs CI, publishes to npm via OIDC, builds binary assets, uploads them to the release, and verifies the package landed on npm.
11
+
12
+ That's it. No tokens, no manual `npm publish`. The whole thing takes ~4 minutes end-to-end.
13
+
14
+ ### Beta releases
15
+
16
+ For pre-release versions (e.g. `1.2.0-beta.0`):
17
+
18
+ - Mark the GitHub Release as a **pre-release**, and the version string in `package.json` must contain `beta`. Publishes go to the `beta` dist-tag.
19
+ - Or use **workflow dispatch** (Actions → Release → Run workflow) from `main` with a `version` input that contains `beta` and matches `package.json`.
20
+
21
+ ## How publishing actually works
22
+
23
+ The `publish-npm` job in `release.yml` authenticates to npm via **GitHub Actions OIDC** (npm Trusted Publishing). Key pieces:
24
+
25
+ | Piece | Why |
26
+ |---|---|
27
+ | `permissions: id-token: write` on the job | Lets GitHub Actions mint an OIDC token at runtime. |
28
+ | `node-version: '24'` (only this job) | Node 22's bundled npm 10.x doesn't support OIDC publishing. Node 24 ships with npm 11.x. The package's `engines: node >=22` floor for consumers is unrelated. |
29
+ | `npm publish --provenance` | Generates a SLSA provenance attestation signed via sigstore, then submits the package + attestation to npm. |
30
+ | `package.json` `"repository": { "url": "git+https://github.com/feral-file/ff-cli.git" }` | npm cross-checks this against the provenance ("built from feral-file/ff-cli") and rejects the publish if it doesn't match. |
31
+ | **No `NPM_TOKEN` secret** | OIDC replaces the token entirely. The old secret is deleted; do not re-add. |
32
+
33
+ The trust relationship lives at https://www.npmjs.com/package/@feralfile/cli/access — publisher: GitHub Actions, org `feral-file`, repo `ff-cli`, workflow `release.yml`. Changing the workflow filename, repo, or org will break publishes until the trust config is updated.
34
+
35
+ ## Bootstrapping a brand-new npm package
36
+
37
+ You will probably never do this again for `@feralfile/cli`, but if you ever publish a new scoped package (`@feralfile/something-else`), the chicken-and-egg sequence is:
38
+
39
+ 1. **First publish must come from a logged-in human** (npm Trusted Publishing can only be configured against an existing package). Run `npm publish --access public` locally as a user with publish rights to the org. If your account has 2FA, pass `--otp 123456` inline — the browser auth flow needs a real TTY that `gh`/Claude/CI doesn't provide.
40
+ 2. **Configure the trusted publisher** at `https://www.npmjs.com/package/<name>/access` once the package exists. Fields: Publisher = GitHub Actions, Organization = `feral-file`, Repository = `<repo>`, Workflow filename = `release.yml`.
41
+ 3. **Confirm `package.json` has a matching `repository.url`** before letting CI publish, or `--provenance` will 422.
42
+ 4. **Cut a small follow-up release through CI** to confirm the OIDC path works end-to-end before relying on it.
43
+ 5. **Optionally harden**: flip "Publishing access" to "Require 2FA and disallow tokens" so the only way to publish is via the trusted publisher in CI — no laptops, no automation tokens.
44
+
45
+ ## GitHub Actions workflows
46
+
47
+ - **`ci.yml`** — formatting, lint, tests, build on Node 22 across ubuntu/macos/windows. Called by `release.yml` and runs on PRs / pushes to `main`.
48
+ - **`build.yml`** — builds prebuilt binaries (macOS/Linux/Windows) and uploads them as workflow artifacts. Triggered manually or from `release.yml`.
49
+ - **`release.yml`** — orchestrates a release. Verifies version, runs CI, publishes to npm (Node 24 + OIDC + provenance), builds binaries, uploads release assets, verifies the package and assets are live. Triggers on published GitHub Releases or manual dispatch (beta only).
50
+ - **`codeql.yml`**, **`dependency-review.yml`** — security scanning.
51
+
52
+ ## Binary release assets
53
+
54
+ The curl installer (`https://feralfile.com/ff-cli-install`) downloads prebuilt binaries from GitHub Releases. The release workflow builds and uploads them automatically. To build one locally:
6
55
 
7
56
  **macOS / Linux:**
8
57
 
@@ -16,28 +65,12 @@ The curl installer downloads prebuilt binaries from GitHub Releases. Build one a
16
65
  .\scripts\release\build-asset-windows.ps1
17
66
  ```
18
67
 
19
- This produces (names vary by OS/arch):
68
+ Produces (names vary by OS/arch):
20
69
 
21
70
  - `release/ff-cli-darwin-arm64.tar.gz` (and `.sha256`) on macOS
22
71
  - `release/ff-cli-linux-x64.tar.gz` (and `.sha256`) on Linux
23
72
  - `release/ff-cli-windows-x64.zip` (and `.sha256`) on Windows
24
73
 
25
- Run the appropriate script on each target platform and upload each pair to the GitHub release.
26
-
27
- ## GitHub Actions
28
-
29
- - **Build** (`build.yml`): Trigger manually (Actions → Build → Run workflow) or on pull requests. Builds binaries on macOS, Linux, and Windows and uploads them as workflow artifacts for download.
30
- - **Release** (`release.yml`): On **published** GitHub Releases, validates that `package.json` matches the tag, publishes to npm, then builds binaries and uploads assets. A GitHub Release marked as a pre-release publishes to the **`beta`** dist-tag; the version must also contain `beta`. A manual workflow dispatch can also publish a beta version when you need to bypass the normal release flow, but run it from `main`.
31
-
32
- ## npm Publish Requirements
33
-
34
- - Publishes use **npm Trusted Publishing (OIDC)** — no `NPM_TOKEN` secret is required or used. The trust is configured at https://www.npmjs.com/package/@feralfile/cli/access (publisher: GitHub Actions, org: `feral-file`, repo: `ff-cli`, workflow: `release.yml`).
35
- - Ensure `package.json` version matches the release tag (e.g. tag `1.0.2` → `"version": "1.0.2"`). The release job fails fast when they differ.
36
- - A **regular** (non-prerelease) GitHub Release publishes with the default dist-tag **`latest`**.
37
- - A GitHub Release marked **Set as a pre-release** publishes to the **`beta`** dist-tag, and its version must contain `beta` so the package version stays aligned with the beta channel.
38
- - Manual workflow dispatch runs publish to the **`beta`** dist-tag too, and the `version` input must contain `beta`, match `package.json`, and be dispatched from `main`.
39
- - Example beta version: `1.0.18-beta.0`.
40
-
41
74
  ## Release notes and breaking changes
42
75
 
43
76
  GitHub Release text (and any user-facing summary you publish with the version) should state compatibility changes in plain language. **Do not rely on `package.json` `engines` alone**; npm and installers surface it inconsistently, and operators skim release notes first.
@@ -56,7 +89,7 @@ GitHub Release text (and any user-facing summary you publish with the version) s
56
89
 
57
90
  Later releases only need to repeat this block if the engine floor changes again.
58
91
 
59
- ## Installer Redirect
92
+ ## Installer redirect
60
93
 
61
94
  `https://feralfile.com/ff-cli-install` should redirect to:
62
95
 
@@ -66,8 +99,45 @@ https://raw.githubusercontent.com/feral-file/ff-cli/main/scripts/install.sh
66
99
 
67
100
  The installer script then fetches the release assets from GitHub Releases.
68
101
 
69
- ## Environment Overrides
102
+ ## Environment overrides
70
103
 
71
104
  - `FF_CLI_VERSION`: overrides the version label in logs
72
105
  - `FF_CLI_NODE_VERSION`: Reserved in script headers for future use; current CI, npm `engines`, and release wrappers assume **Node.js 22+** (required by `dp1-js`).
73
106
  - `FF_CLI_OUTPUT_DIR`: output directory (default: `./release`)
107
+
108
+ ## Troubleshooting
109
+
110
+ ### `404 Not Found - PUT https://registry.npmjs.org/@feralfile%2fcli`
111
+
112
+ npm hides "you don't have publish access" as a 404. Check:
113
+
114
+ 1. The trusted publisher at https://www.npmjs.com/package/@feralfile/cli/access matches the *running* workflow: organization, repository, workflow filename, environment (or lack thereof).
115
+ 2. The job has `permissions: id-token: write`.
116
+ 3. You're not somehow still passing an `NPM_TOKEN` / `NODE_AUTH_TOKEN` env var — that flips npm into token-auth mode, and if the token isn't authorized for the scope you'll 404.
117
+
118
+ ### `422 Unprocessable Entity ... "repository.url" is "", expected to match "https://github.com/feral-file/ff-cli" from provenance`
119
+
120
+ The SLSA provenance attestation records the source repo; npm requires `package.json`'s `repository.url` to match before accepting the publish. Add:
121
+
122
+ ```json
123
+ "repository": {
124
+ "type": "git",
125
+ "url": "git+https://github.com/feral-file/ff-cli.git"
126
+ }
127
+ ```
128
+
129
+ ### `npm error code MODULE_NOT_FOUND ... Cannot find module 'promise-retry'`
130
+
131
+ You're trying to `npm install -g npm@latest` on top of a running npm. It overwrites its own files mid-execution and corrupts. Don't upgrade in place — use a Node version that already ships the npm you need. The `publish-npm` job runs Node 24 specifically for this reason.
132
+
133
+ ### `EOTP — This operation requires a one-time password`
134
+
135
+ You're publishing manually from a laptop, and your npm account has 2FA enforced. Either pass `--otp 123456` inline (TOTP code from your authenticator), or run `npm publish` directly in a real terminal where the browser auth flow can complete. **CI never hits this** — OIDC is its own auth path and bypasses 2FA.
136
+
137
+ ### `Version mismatch: package.json is X but tag is Y`
138
+
139
+ The `verify-version` job catches this. Fix `package.json` to match the tag and create a new release tag, or delete and recreate the release with the correct tag.
140
+
141
+ ### Release fired but never reached npm
142
+
143
+ The tag is consumed even when publish fails (you can't republish the same version). Bump to the next patch version and cut a fresh release. Optionally `gh release delete <tag> --cleanup-tag` to remove the never-published tag from the release page.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feralfile/cli",
3
- "version": "1.1.4",
3
+ "version": "1.3.0",
4
4
  "description": "CLI for building DP-1 playlists of digital art using AI (Claude, Grok, ChatGPT, Gemini)",
5
5
  "repository": {
6
6
  "type": "git",