@feralfile/cli 2.10.1 → 3.0.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.
- package/README.md +16 -11
- package/config.json.example +5 -0
- package/dist/package.json +1 -1
- package/dist/src/commands/build.js +40 -0
- package/dist/src/commands/config.js +1 -0
- package/dist/src/commands/device.js +1 -1
- package/dist/src/commands/enrich.js +16 -21
- package/dist/src/commands/find.js +83 -79
- package/dist/src/commands/helpers/prompt.js +85 -10
- package/dist/src/commands/play.js +138 -73
- package/dist/src/commands/setup.js +81 -6
- package/dist/src/commands/status.js +4 -1
- package/dist/src/config.js +94 -0
- package/dist/src/main.js +14 -5
- package/dist/src/utilities/device-lookup.js +36 -11
- package/dist/src/utilities/feral-file-artwork.js +2 -2
- package/dist/src/utilities/index.js +270 -259
- package/dist/src/utilities/media-url.js +117 -0
- package/dist/src/utilities/nft-indexer.js +12 -376
- package/dist/src/utilities/playlist-builder.js +10 -1
- package/dist/src/utilities/playlist-source.js +222 -33
- package/dist/src/utilities/raster-client.js +100 -13
- package/dist/src/utilities/token-item.js +428 -0
- package/dist/src/utilities/token-source-contract.js +55 -0
- package/dist/src/utilities/token-source-indexer.js +99 -0
- package/dist/src/utilities/token-source-raster.js +378 -0
- package/dist/src/utilities/token-source.js +48 -0
- package/docs/CONFIGURATION.md +63 -2
- package/docs/EXAMPLES.md +2 -2
- package/docs/PROJECT_SPEC.md +23 -17
- package/docs/README.md +40 -15
- package/package.json +1 -1
package/docs/README.md
CHANGED
|
@@ -29,7 +29,10 @@ ff-cli setup --non-interactive --key-file /run/secrets/ff-signing.key \
|
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
`--key` and `--key-file` together is an error, and an empty or unreadable key file is refused rather
|
|
32
|
-
than quietly generating a new identity for the machine.
|
|
32
|
+
than quietly generating a new identity for the machine. Setup also never replaces a signing key that is
|
|
33
|
+
already configured: `--generate-key`, `--key`, and `--key-file` refuse when a different key is in place,
|
|
34
|
+
because that key owns every playlist you published (`publish --replace` and `unpublish` accept only it).
|
|
35
|
+
Add `--force` to replace it anyway. See
|
|
33
36
|
[Where the signing key can come from](./CONFIGURATION.md#where-the-signing-key-can-come-from).
|
|
34
37
|
|
|
35
38
|
See the full configuration reference here: `./CONFIGURATION.md`.
|
|
@@ -105,8 +108,9 @@ Both paths run the same deterministic pipeline: fetch metadata, assemble a DP-1
|
|
|
105
108
|
|
|
106
109
|
## Commands (cheat sheet)
|
|
107
110
|
|
|
108
|
-
- `build [params.json]` – Deterministic build from JSON or stdin
|
|
111
|
+
- `build [params.json]` – Deterministic build from JSON or stdin. `ff-cli build --help` lists the requirement types and settings
|
|
109
112
|
- Options: `-o, --output <file>`, `-v, --verbose`
|
|
113
|
+
- Exits 1 when any requirement finds nothing or fails, when named `tokenIds` do not all resolve, or when the send to `playlistSettings.deviceName` fails. The playlist is still saved when anything was collected; when nothing was, no file is written
|
|
110
114
|
- `validate <file-or-url>` – Validate playlist structure only
|
|
111
115
|
- `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
|
|
112
116
|
- `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[]`.
|
|
@@ -116,14 +120,19 @@ Both paths run the same deterministic pipeline: fetch metadata, assemble a DP-1
|
|
|
116
120
|
- Signing **appends** by default, so repeated runs accumulate endorsements. That is only correct while the signed content is unchanged: editing a signed playlist moves bytes the earlier signatures cover, and the envelope check then refuses to write the result. `--replace-signatures` discards every existing entry — including a feed's own co-signature — and signs the document as it stands. It is the required step before `publish --replace`
|
|
117
121
|
- **An in-place run refuses when it would discard a still-valid signature from another key**, since only its holder could make that signature again: write the result elsewhere with `-o, --output`, which leaves the original where it is. The refusal is about what cannot be recovered — an in-place run still proceeds when the only entries dropped are your own, or ones that no longer verify
|
|
118
122
|
- `--replace-signatures` names each discarded entry by role and the last 8 characters of its `kid`, and verifies it against the document as it now stands. Your own earlier signature is replaced by the same run. Another key's entry that **still verifies** — which is every entry when the content was not edited, since the signing payload excludes `signatures` — is reported as removed; keep the previous file if you want it back. An entry that does **not** verify is reported as exactly that and no more: the command has only the document as it stands, so it cannot tell an edit from an entry that was never valid, and it does not guess. Roles are carried through as found (`agent`, `institution` and `licensor` are as valid as `curator`), and a flat legacy `signature` is reported as not checkable, since it carries no key or role at all
|
|
119
|
-
- `play <source>` – Play a playlist file, playlist URL,
|
|
120
|
-
-
|
|
123
|
+
- `play <source>` – Play a playlist file, playlist URL, media URL, or web page on an FF1 device (runs `verify` before sending; only the CLI-synthesized media/web URL fallback is auto-signed when a signing key is configured; use `--skip-verify` to bypass the gate)
|
|
124
|
+
- Any URL that does not load as a playlist is sent as a single item: a media file plays as media, and anything else plays as a web page
|
|
125
|
+
- A URL that answers 404 or 410, a host that does not resolve, and a `.json` URL that does not load as a playlist are refused, so a missing page is never cast as an error page. `--web` checks the first two as well. Other failures to read the URL (a 403 from bot protection, a 5xx, a timeout) still send it, with a note, because the device fetches it itself
|
|
126
|
+
- Marketplace links (Art Blocks, OpenSea, objkt, Feral File, …) are sent to `find`, which resolves the artwork. `--web` plays the page itself instead
|
|
127
|
+
- Options: `-d, --device <name>`, `--web` (play the URL as a web page, skipping the marketplace redirect), `--skip-verify` (skip signature verification; not recommended)
|
|
121
128
|
- `find <input>` – Resolve a marketplace URL, raw `chain:contract:tokenId`, or wallet address into a playable DP-1 playlist
|
|
122
129
|
- Sources: Art Blocks, Objkt, fxhash, OpenSea, SuperRare, Feral File, Neort, Verse, raw on-chain coordinates, and wallet addresses
|
|
123
130
|
- OpenSea forms: `/assets/ethereum/{contract}/{tokenId}`, `/item/ethereum/{contract}/{tokenId}`, and `/collection/{slug}` (Ethereum collections; the slug is resolved from the public collection page, no API key needed)
|
|
124
131
|
- Verse forms: `/items/ethereum/{contract}/{tokenId}` and `/series/{slug}`
|
|
125
132
|
- Options: `-o, --output <path>`, `-l, --limit <n>`, `-p, --play`, `--no-play` (never cast; combine with `-y` to build and save without playing), `-d, --device <name>`, `--publish`, `-s, --server <index>`, `-y, --yes` (skip prompts; without `-o` or `--publish` it plays the playlist on a device, so add `--no-play` to skip casting), `--skip-verify`
|
|
126
|
-
-
|
|
133
|
+
- A wallet address picks one work by the artist at that address. For what a wallet owns, use `build` with a `query_address` requirement
|
|
134
|
+
- Without a terminal, pass `-y`: a question with no terminal to answer it fails with exit 1 and names the flag, rather than waiting or exiting 0 with nothing built
|
|
135
|
+
- `enrich <file>` – Add missing artist, title, and thumbnail metadata to a playlist that already exists. Resolves each item by its `provenance.contract` and writes an inline Ref Manifest; `source`, `duration`, `title`, and `id` are never touched. Drops the signature when it changes anything, so re-sign afterwards. With the opt-in Raster token source no still image is supplied, so `enrich` adds artist and description but no thumbnail; thumbnails come from the default indexer source
|
|
127
136
|
- DP-1 `evm` names a chain family rather than a network, so those items are skipped unless `--assume-ethereum` asserts which one they are. Getting that wrong attaches another artwork's metadata
|
|
128
137
|
- Options: `-o, --output <file>`, `--force` (replace existing manifests), `--assume-ethereum`, `-v, --verbose`
|
|
129
138
|
- `publish <file>` – Publish a playlist to a feed server (runs `verify` before upload and rejects unsigned or broken playlists)
|
|
@@ -160,7 +169,8 @@ ff-cli find https://objkt.com/tokens/hicetnunc/111068 --play
|
|
|
160
169
|
# Save without playing
|
|
161
170
|
ff-cli find ethereum:0xababababab20053426ad1c782de9ea8444358070:5001410 -o send-receive.json
|
|
162
171
|
|
|
163
|
-
# Wallet address
|
|
172
|
+
# Wallet address: one work by the artist at that address; cap the number of tokens with --limit
|
|
173
|
+
# (for what a wallet owns, use build with a query_address requirement)
|
|
164
174
|
ff-cli find 0xaeE022552B539dB18297D7481b6D547C622488B3 -l 10 -o wallet.json
|
|
165
175
|
|
|
166
176
|
# Build and publish to a configured feed server
|
|
@@ -178,7 +188,7 @@ cat params.json | ff-cli build -o playlist.json
|
|
|
178
188
|
|
|
179
189
|
`params.json` should include `requirements` and optional `playlistSettings`. See `examples/params-example.json`. Each requirement has a `type`:
|
|
180
190
|
|
|
181
|
-
- `build_playlist` — `blockchain`, `contractAddress`, `tokenIds`, optional `quantity`
|
|
191
|
+
- `build_playlist` — `blockchain`, `contractAddress`, `tokenIds`, optional `quantity`. Omitting `tokenIds` asks for random tokens from the contract, which only the default indexer token source supports.
|
|
182
192
|
- `feral_file_artwork` — `artworkId` (a Feral File public artwork id or `/exhibitions/artwork/{id}` URL)
|
|
183
193
|
- `query_address` — `ownerAddress`, optional `quantity` (random selection)
|
|
184
194
|
- `fetch_feed` — `playlistName`, `quantity`
|
|
@@ -211,8 +221,8 @@ copied from another document, or written before inline manifests existed has no
|
|
|
211
221
|
and an empty tile in the app's grid — the app can draw a thumbnail from an
|
|
212
222
|
image, video, or SVG source, but not from a live HTML work.
|
|
213
223
|
|
|
214
|
-
`ff-cli enrich` fills that in from the
|
|
215
|
-
`provenance.contract`:
|
|
224
|
+
`ff-cli enrich` fills that in from the configured token source (the Feral
|
|
225
|
+
File indexer by default), keyed on each item's `provenance.contract`:
|
|
216
226
|
|
|
217
227
|
```bash
|
|
218
228
|
ff-cli enrich playlist.json --assume-ethereum
|
|
@@ -222,8 +232,8 @@ ff-cli enrich playlist.json --assume-ethereum --force # replace existing manif
|
|
|
222
232
|
|
|
223
233
|
`--assume-ethereum` is needed for most playlists. DP-1 records an EVM
|
|
224
234
|
coordinate as `chain: "evm"`, which names Ethereum, Polygon, Arbitrum,
|
|
225
|
-
Optimism, Base and Zora alike, and the
|
|
226
|
-
same word. Enrichment will not pick one for you, because the wrong member
|
|
235
|
+
Optimism, Base and Zora alike, and the token source's answer collapses back to
|
|
236
|
+
the same word. Enrichment will not pick one for you, because the wrong member
|
|
227
237
|
returns a different artwork's artist and still — into a document you then sign.
|
|
228
238
|
|
|
229
239
|
It writes `inlineManifest` and nothing else. Items with no
|
|
@@ -265,13 +275,28 @@ npm run dev -- play playlist.json -d "Living Room Display"
|
|
|
265
275
|
# Play a hosted DP-1 playlist
|
|
266
276
|
npm run dev -- play "https://cdn.example.com/playlist.json"
|
|
267
277
|
|
|
268
|
-
# Play a media URL directly
|
|
278
|
+
# Play a media URL or any web page directly
|
|
269
279
|
npm run dev -- play "https://example.com/video.mp4"
|
|
280
|
+
npm run dev -- play "https://example.com/"
|
|
281
|
+
|
|
282
|
+
# Play a page on a marketplace site as a page (play would otherwise send it to find)
|
|
283
|
+
npm run dev -- play --web "https://feralfile.com/"
|
|
270
284
|
|
|
271
285
|
# Skip signature verification only if you must send a non-conformant payload (not recommended)
|
|
272
286
|
npm run dev -- play playlist.json --skip-verify
|
|
273
287
|
```
|
|
274
288
|
|
|
289
|
+
### Owner remote maintenance
|
|
290
|
+
|
|
291
|
+
FFOS images containing `feral-tailscale` support opt-in persistent owner SSH on
|
|
292
|
+
port 2222 through Tailscale. It requires the owner's authorized computer and SSH
|
|
293
|
+
key and remains available independently of controld. The procedure checks for
|
|
294
|
+
a complete installation. If support is absent, check the normal firmware release
|
|
295
|
+
channel; enrollment waits until a version containing it is published and installed. Follow the [owner-access procedure](https://github.com/feral-file/ffos/blob/36e25cec84505271a7232d6936bb46a19fab5cfd/docs/OWNER_TAILSCALE.md)
|
|
296
|
+
for enrollment, diagnostics, revocation and validation. The normal `ff-cli ssh`
|
|
297
|
+
commands below continue to manage temporary support SSH on port 22; disabling
|
|
298
|
+
support SSH does not revoke persistent owner access.
|
|
299
|
+
|
|
275
300
|
### SSH access
|
|
276
301
|
|
|
277
302
|
```bash
|
|
@@ -287,8 +312,8 @@ ff-cli ssh disable -d "Living Room Display"
|
|
|
287
312
|
### Publish to feed server
|
|
288
313
|
|
|
289
314
|
```bash
|
|
290
|
-
# Publish to first configured feed server
|
|
291
|
-
npm run dev -- publish playlist.json
|
|
315
|
+
# Publish to the first configured feed server (-s is required when more than one is configured)
|
|
316
|
+
npm run dev -- publish playlist.json -s 0
|
|
292
317
|
|
|
293
318
|
# Publish to specific server (if multiple configured)
|
|
294
319
|
npm run dev -- publish playlist.json -s 0
|
|
@@ -442,7 +467,7 @@ Setup preserves existing devices when adding new ones. See selection rules and e
|
|
|
442
467
|
|
|
443
468
|
## Constraints
|
|
444
469
|
|
|
445
|
-
-
|
|
470
|
+
- Per-requirement caps: `build_playlist` up to 20 with `tokenIds` (100 without), `query_address` up to 100 when `quantity` is a number (omitted or `"all"` takes the whole wallet), `fetch_feed` up to 20. Requirements add up; there is no total cap beyond DP-1's 1024 items
|
|
446
471
|
- Per-source caps enforced in utilities
|
|
447
472
|
- Per-item timing: when no duration is given, video/audio items carry **no** `duration` and `display.loop: false`, so the player advances at end-of-stream (DP-1 §4.1) — the media plays its natural length. Static items use `defaultDuration` (10s unless configured). An explicit duration always wins.
|
|
448
473
|
|