@feralfile/cli 1.6.0 → 2.0.1
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 +6 -11
- package/config.json.example +0 -65
- package/dist/index.js +1 -3
- package/dist/package.json +3 -4
- package/dist/src/commands/config.js +15 -13
- package/dist/src/commands/play.js +63 -0
- package/dist/src/commands/setup.js +1 -73
- package/dist/src/commands/status.js +0 -13
- package/dist/src/config.js +3 -126
- package/dist/src/main.js +2 -361
- package/dist/src/utilities/ff1-device.js +55 -0
- package/dist/src/utilities/playlist-builder.js +79 -17
- package/docs/CONFIGURATION.md +1 -35
- package/docs/EXAMPLES.md +206 -215
- package/docs/PROJECT_SPEC.md +20 -18
- package/docs/README.md +51 -104
- package/package.json +3 -4
- package/dist/src/ai-orchestrator/index.js +0 -1040
- package/dist/src/ai-orchestrator/registry.js +0 -96
- package/dist/src/commands/chat.js +0 -189
- package/dist/src/intent-parser/index.js +0 -1393
- package/dist/src/intent-parser/utils.js +0 -116
- package/docs/FUNCTION_CALLING.md +0 -100
package/docs/CONFIGURATION.md
CHANGED
|
@@ -21,35 +21,10 @@ npm run dev -- config show
|
|
|
21
21
|
|
|
22
22
|
## Top‑level fields
|
|
23
23
|
|
|
24
|
-
- **defaultModel** (string)
|
|
25
|
-
- The default AI model key to use. Must match a key under `models`.
|
|
26
|
-
- Used by orchestration to pick API, timeouts, and model identifier.
|
|
27
|
-
|
|
28
24
|
- **defaultDuration** (number, seconds)
|
|
29
25
|
- Default per‑item display duration for media without an intrinsic runtime (images, code-based works). Falls back to 10s when unset.
|
|
30
26
|
- Does NOT apply to video/audio items: with no explicit duration those are emitted without `duration` and with `display.loop: false`, so a DP-1 player advances at end-of-stream (§4.1) — the media plays its natural length.
|
|
31
27
|
|
|
32
|
-
## models
|
|
33
|
-
|
|
34
|
-
Each key under `models` defines a model configuration used by the AI orchestrator.
|
|
35
|
-
|
|
36
|
-
- `<modelName>.apiKey` (string): API key for the provider.
|
|
37
|
-
- `<modelName>.baseURL` (string): Base API URL.
|
|
38
|
-
- `<modelName>.model` (string): Model identifier (e.g., `grok-beta`, `gpt-4o`).
|
|
39
|
-
- `<modelName>.availableModels` (string[], optional): Display/help only.
|
|
40
|
-
- `<modelName>.timeout` (number, ms): HTTP timeout for requests.
|
|
41
|
-
- `<modelName>.maxRetries` (number): Retry count for requests.
|
|
42
|
-
- `<modelName>.temperature` (number): Generation temperature.
|
|
43
|
-
- `<modelName>.maxTokens` (number): Token cap.
|
|
44
|
-
- `<modelName>.supportsFunctionCalling` (boolean): Must be true; otherwise the CLI rejects the model.
|
|
45
|
-
|
|
46
|
-
Environment variable helpers:
|
|
47
|
-
|
|
48
|
-
- Anthropic (Claude): `ANTHROPIC_API_KEY`
|
|
49
|
-
- Grok: `GROK_API_KEY`, `GROK_MODEL`, `GROK_API_BASE_URL`
|
|
50
|
-
- OpenAI: `OPENAI_API_KEY`
|
|
51
|
-
- Gemini: `GEMINI_API_KEY`
|
|
52
|
-
|
|
53
28
|
## browser
|
|
54
29
|
|
|
55
30
|
Optional settings used where headless/browser‑like behavior is needed.
|
|
@@ -61,7 +36,7 @@ Optional settings used where headless/browser‑like behavior is needed.
|
|
|
61
36
|
|
|
62
37
|
Used for signing DP‑1 playlists.
|
|
63
38
|
|
|
64
|
-
- `playlist.privateKey` (string, Ed25519 private key in hex or base64): Used by the `sign` command to create DP-1 v1.1.0 multi-signatures. The `verify` command may derive the matching public key from this value (or `PLAYLIST_PRIVATE_KEY`) when you omit `--public-key`; **dp1-js applies that derived key only when verifying legacy flat `signature` strings**, not when checking `signatures[]` envelopes. If that derivation fails, `verify` prints a warning on stderr and continues without derived key material. The derived public key is emitted as PEM so Node can decode it without ambiguity. Hex may include or omit the `0x` prefix. You can also set this via `PLAYLIST_PRIVATE_KEY` in `.env`. `play` verifies playlists before delivery and only auto-signs the synthesized media URL fallback when signing is configured. `
|
|
39
|
+
- `playlist.privateKey` (string, Ed25519 private key in hex or base64): Used by the `sign` command to create DP-1 v1.1.0 multi-signatures. The `verify` command may derive the matching public key from this value (or `PLAYLIST_PRIVATE_KEY`) when you omit `--public-key`; **dp1-js applies that derived key only when verifying legacy flat `signature` strings**, not when checking `signatures[]` envelopes. If that derivation fails, `verify` prints a warning on stderr and continues without derived key material. The derived public key is emitted as PEM so Node can decode it without ambiguity. Hex may include or omit the `0x` prefix. You can also set this via `PLAYLIST_PRIVATE_KEY` in `.env`. `play` verifies playlists before delivery and only auto-signs the synthesized media URL fallback when signing is configured. `play` and `publish` verify before delivery or upload and reject unsigned or broken playlists.
|
|
65
40
|
|
|
66
41
|
**Signing and key encoding:** Signing paths (`sign`, deterministic `build` when configured, and `-k/--key` overrides) pass the private key string through to **`dp1-js`** (`SignMultiEd25519`) without an extra decoding step in ff-cli. `dp1-js` recognizes **hex** (optional `0x`) or **base64** encodings of the PKCS#8 DER blob produced by the OpenSSL examples below, then loads the key for Ed25519. Use those formats; ff-cli does not add a separate normalizer ahead of the library.
|
|
67
42
|
|
|
@@ -182,15 +157,6 @@ Minimal `config.json` example (selected fields):
|
|
|
182
157
|
|
|
183
158
|
```json
|
|
184
159
|
{
|
|
185
|
-
"defaultModel": "claude",
|
|
186
|
-
"models": {
|
|
187
|
-
"claude": {
|
|
188
|
-
"apiKey": "sk-ant-your-api-key-here",
|
|
189
|
-
"baseURL": "https://api.anthropic.com/v1/",
|
|
190
|
-
"model": "claude-sonnet-4-6",
|
|
191
|
-
"supportsFunctionCalling": true
|
|
192
|
-
}
|
|
193
|
-
},
|
|
194
160
|
"defaultDuration": 10,
|
|
195
161
|
"playlist": {
|
|
196
162
|
"privateKey": "your_ed25519_private_key_hex_or_base64_here",
|
package/docs/EXAMPLES.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Examples
|
|
2
2
|
|
|
3
|
-
Copy‑pasteable commands that work with the current CLI.
|
|
3
|
+
Copy‑pasteable commands that work with the current CLI. ff-cli is deterministic: there's no built-in chat. Natural language belongs in your coding agent (Claude Code, Codex), which drives these commands via the `ff-control` skill. No LLM API key required.
|
|
4
4
|
|
|
5
5
|
## Setup
|
|
6
6
|
|
|
@@ -16,190 +16,249 @@ npm run dev -- config init
|
|
|
16
16
|
npm run dev -- config validate
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
##
|
|
19
|
+
## Use with a coding agent
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
`skills/ff-control/SKILL.md`
|
|
21
|
+
ff-cli ships a [Claude Code](https://docs.claude.com/en/docs/claude-code) / Codex skill at `skills/ff-control/SKILL.md`. Once installed, ask your agent in plain language ("Get 3 works from reas.eth and play on Living Room", "Build a playlist from this Objkt URL and publish to my feed") and it translates the request into `ff-cli find` or `ff-cli build` and runs the validate → play/publish flow for you.
|
|
24
22
|
|
|
25
23
|
Recommended local install path:
|
|
26
24
|
|
|
27
25
|
```bash
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
git clone --depth=1 https://github.com/feral-file/ff-cli /tmp/ff-cli \
|
|
27
|
+
&& mkdir -p ~/.claude/skills \
|
|
28
|
+
&& cp -r /tmp/ff-cli/skills/ff-control ~/.claude/skills/
|
|
30
29
|
```
|
|
31
30
|
|
|
32
|
-
|
|
31
|
+
The rest of this doc shows the deterministic commands the skill (or you) drives directly.
|
|
33
32
|
|
|
34
|
-
|
|
33
|
+
## Find an artwork
|
|
35
34
|
|
|
36
|
-
|
|
35
|
+
`ff-cli find` resolves a marketplace URL, raw `chain:contract:tokenId`, or wallet address into a playable DP-1 playlist. Sources: Art Blocks, Objkt, fxhash, OpenSea, SuperRare, Feral File, Neort, Verse, raw on-chain coordinates, and wallet addresses.
|
|
37
36
|
|
|
38
37
|
```bash
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
# Paste a URL and play it on your FF1
|
|
39
|
+
npm run dev -- find https://www.artblocks.io/collection/ringers-by-dmitri-cherniak --play
|
|
40
|
+
|
|
41
|
+
# Tezos / hic et nunc via Objkt (the alias resolves to a KT1 contract)
|
|
42
|
+
npm run dev -- find https://objkt.com/tokens/hicetnunc/111068 --play
|
|
43
|
+
|
|
44
|
+
# Feral File artwork (public id may be hex or numeric)
|
|
45
|
+
npm run dev -- find https://feralfile.com/exhibitions/artwork/f0240e04d64717e319584957f6a83954b029254ad1260b6320472ea8c0c5b1cf --play
|
|
46
|
+
|
|
47
|
+
# Save without playing
|
|
48
|
+
npm run dev -- find ethereum:0xababababab20053426ad1c782de9ea8444358070:5001410 -o send-receive.json
|
|
49
|
+
|
|
50
|
+
# Single on-chain token by coordinates
|
|
51
|
+
npm run dev -- find ethereum:0xb932a70A57673d89f4acfFBE830E8ed7f75Fb9e0:52932 -o token.json
|
|
52
|
+
|
|
53
|
+
# Tezos token by coordinates
|
|
54
|
+
npm run dev -- find tezos:KT1BcNnzWze3vCviwiETYNwcFSwjv6RihZEQ:22 -o tez-token.json
|
|
55
|
+
|
|
56
|
+
# Wallet address (owner lookup); cap the number of tokens with --limit
|
|
57
|
+
npm run dev -- find 0xaeE022552B539dB18297D7481b6D547C622488B3 -l 10 -o wallet.json
|
|
58
|
+
|
|
59
|
+
# Limit a large series to the first N tokens
|
|
60
|
+
npm run dev -- find https://www.artblocks.io/collection/ringers-by-dmitri-cherniak -l 5 -o ringers.json
|
|
61
|
+
|
|
62
|
+
# Build and publish to a configured feed server
|
|
63
|
+
npm run dev -- find https://objkt.com/tokens/hicetnunc/111068 --publish
|
|
64
|
+
|
|
65
|
+
# Build, play on a named device, and skip interactive prompts
|
|
66
|
+
npm run dev -- find https://objkt.com/tokens/hicetnunc/111068 --play -d "Living Room Display" -y
|
|
41
67
|
```
|
|
42
68
|
|
|
43
|
-
|
|
69
|
+
OpenSea and Verse accept both item and collection/series URLs:
|
|
44
70
|
|
|
45
71
|
```bash
|
|
46
|
-
#
|
|
47
|
-
npm run dev
|
|
72
|
+
# OpenSea collection (Ethereum); slug resolved from the public page, no API key
|
|
73
|
+
npm run dev -- find https://opensea.io/collection/your-collection-slug -l 5 -o opensea.json
|
|
48
74
|
|
|
49
|
-
#
|
|
50
|
-
npm run dev --
|
|
51
|
-
|
|
52
|
-
npm run dev -- chat "Get tokens 52932,52457 from Ethereum contract 0xb932a70A57673d89f4acfFBE830E8ed7f75Fb9e0" -v
|
|
53
|
-
npm run dev -- chat "Make a playlist of 10 works from 0xaeE022552B539dB18297D7481b6D547C622488B3" -v
|
|
75
|
+
# Verse series
|
|
76
|
+
npm run dev -- find https://verse.works/series/your-series-slug -l 5 -o verse.json
|
|
77
|
+
```
|
|
54
78
|
|
|
55
|
-
|
|
56
|
-
npm run dev -- chat "your request" --model grok
|
|
57
|
-
npm run dev -- chat "your request" --model gpt
|
|
58
|
-
npm run dev -- chat "your request" --model gemini
|
|
79
|
+
Run `npm run dev -- find --help` for the full input list and options.
|
|
59
80
|
|
|
60
|
-
|
|
61
|
-
```
|
|
81
|
+
## Build from structured params
|
|
62
82
|
|
|
63
|
-
|
|
83
|
+
Use `ff-cli build` when you already have the contract, token IDs, feed names, or artwork ids and want explicit control over ordering, durations, and title. The input is a JSON object with `requirements` and optional `playlistSettings`.
|
|
64
84
|
|
|
65
85
|
```bash
|
|
66
|
-
# From file
|
|
86
|
+
# From a file
|
|
67
87
|
npm run dev -- build examples/params-example.json -o playlist.json
|
|
68
88
|
|
|
69
89
|
# From stdin
|
|
70
90
|
cat examples/params-example.json | npm run dev -- build -o playlist.json
|
|
71
91
|
```
|
|
72
92
|
|
|
73
|
-
|
|
93
|
+
Each requirement has a `type`:
|
|
94
|
+
|
|
95
|
+
- `build_playlist` — `blockchain`, `contractAddress`, `tokenIds`, optional `quantity`
|
|
96
|
+
- `feral_file_artwork` — `artworkId` (a Feral File public artwork id or `/exhibitions/artwork/{id}` URL)
|
|
97
|
+
- `query_address` — `ownerAddress`, optional `quantity` (random selection)
|
|
98
|
+
- `fetch_feed` — `playlistName`, `quantity`
|
|
99
|
+
|
|
100
|
+
`playlistSettings` may set `title`, `durationPerItem`, `preserveOrder` (set `false` to shuffle), and `deviceName`.
|
|
101
|
+
|
|
102
|
+
### From a contract + token IDs
|
|
74
103
|
|
|
75
104
|
```bash
|
|
76
|
-
cat > /tmp/
|
|
105
|
+
cat > /tmp/eth-tokens.json <<'JSON'
|
|
77
106
|
{
|
|
78
107
|
"requirements": [
|
|
79
108
|
{
|
|
80
|
-
"type": "
|
|
81
|
-
"
|
|
109
|
+
"type": "build_playlist",
|
|
110
|
+
"blockchain": "ethereum",
|
|
111
|
+
"contractAddress": "0xb932a70A57673d89f4acfFBE830E8ed7f75Fb9e0",
|
|
112
|
+
"tokenIds": ["52932", "52457"]
|
|
82
113
|
}
|
|
83
114
|
],
|
|
84
115
|
"playlistSettings": {
|
|
85
|
-
"title": "
|
|
116
|
+
"title": "ETH Tokens",
|
|
117
|
+
"preserveOrder": false,
|
|
118
|
+
"durationPerItem": 7
|
|
86
119
|
}
|
|
87
120
|
}
|
|
88
121
|
JSON
|
|
89
122
|
|
|
90
|
-
npm run dev -- build /tmp/
|
|
123
|
+
npm run dev -- build /tmp/eth-tokens.json -o playlist-eth.json -v
|
|
91
124
|
```
|
|
92
125
|
|
|
93
|
-
|
|
126
|
+
Tezos works the same way with `"blockchain": "tezos"` and a `KT1...` contract:
|
|
94
127
|
|
|
95
128
|
```bash
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
129
|
+
cat > /tmp/tez-tokens.json <<'JSON'
|
|
130
|
+
{
|
|
131
|
+
"requirements": [
|
|
132
|
+
{
|
|
133
|
+
"type": "build_playlist",
|
|
134
|
+
"blockchain": "tezos",
|
|
135
|
+
"contractAddress": "KT1BcNnzWze3vCviwiETYNwcFSwjv6RihZEQ",
|
|
136
|
+
"tokenIds": ["22", "8"]
|
|
137
|
+
}
|
|
138
|
+
],
|
|
139
|
+
"playlistSettings": { "title": "Tezos Tokens", "preserveOrder": false }
|
|
140
|
+
}
|
|
141
|
+
JSON
|
|
101
142
|
|
|
102
|
-
|
|
103
|
-
npm run dev -- chat "Build playlist from Ethereum address 0xaeE022552B539dB18297D7481b6D547C622488B3 and 2 from Unsupervised" --model gpt -v
|
|
143
|
+
npm run dev -- build /tmp/tez-tokens.json -o playlist-tez.json -v
|
|
104
144
|
```
|
|
105
145
|
|
|
106
|
-
###
|
|
107
|
-
|
|
108
|
-
The CLI can parse rich requests and do it all in one go: fetch, build a DP‑1 playlist, shuffle, set durations, and send to a named device.
|
|
146
|
+
### From a wallet address
|
|
109
147
|
|
|
110
148
|
```bash
|
|
111
|
-
|
|
112
|
-
|
|
149
|
+
cat > /tmp/wallet.json <<'JSON'
|
|
150
|
+
{
|
|
151
|
+
"requirements": [
|
|
152
|
+
{
|
|
153
|
+
"type": "query_address",
|
|
154
|
+
"ownerAddress": "0xaeE022552B539dB18297D7481b6D547C622488B3",
|
|
155
|
+
"quantity": 5
|
|
156
|
+
}
|
|
157
|
+
],
|
|
158
|
+
"playlistSettings": { "title": "From a wallet", "preserveOrder": false }
|
|
159
|
+
}
|
|
160
|
+
JSON
|
|
161
|
+
|
|
162
|
+
npm run dev -- build /tmp/wallet.json -o playlist-wallet.json -v
|
|
113
163
|
```
|
|
114
164
|
|
|
115
|
-
|
|
165
|
+
### From a feed playlist
|
|
116
166
|
|
|
117
|
-
|
|
118
|
-
`https://github.com/feral-file/dp1-feed/tree/main/playlists`
|
|
167
|
+
Feed playlist queries require reachable feed servers in your config. Reference exhibition titles from the official playlist repository: `https://github.com/feral-file/dp1-feed/tree/main/playlists`.
|
|
119
168
|
|
|
120
169
|
```bash
|
|
121
|
-
|
|
122
|
-
|
|
170
|
+
cat > /tmp/feed.json <<'JSON'
|
|
171
|
+
{
|
|
172
|
+
"requirements": [
|
|
173
|
+
{ "type": "fetch_feed", "playlistName": "Unsupervised", "quantity": 3 }
|
|
174
|
+
],
|
|
175
|
+
"playlistSettings": { "title": "From a feed", "preserveOrder": false }
|
|
176
|
+
}
|
|
177
|
+
JSON
|
|
123
178
|
|
|
124
|
-
|
|
125
|
-
npm run dev -- chat "Get 3 from Unsupervised and 2 from reas.eth" -v -o playlist.json
|
|
179
|
+
npm run dev -- build /tmp/feed.json -o playlist-feed.json -v
|
|
126
180
|
```
|
|
127
181
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
- Feed playlist queries require reachable feed servers in your config.
|
|
131
|
-
- If no feeds are reachable, feed playlist examples will fail even if the title exists.
|
|
132
|
-
- Bare EVM prompts (`from 0x...`) now fall back from owner lookup to contract lookup when needed.
|
|
133
|
-
|
|
134
|
-
## Natural Language: Display and Publish
|
|
135
|
-
|
|
136
|
-
The CLI recognizes publishing keywords like "publish", "publish to my feed", "push to feed", "send to feed" and automatically publishes after building.
|
|
137
|
-
|
|
138
|
-
### Basic Publishing
|
|
182
|
+
### From a Feral File artwork
|
|
139
183
|
|
|
140
184
|
```bash
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
185
|
+
cat > /tmp/ff-artwork.json <<'JSON'
|
|
186
|
+
{
|
|
187
|
+
"requirements": [
|
|
188
|
+
{
|
|
189
|
+
"type": "feral_file_artwork",
|
|
190
|
+
"artworkId": "https://feralfile.com/exhibitions/artwork/f0240e04d64717e319584957f6a83954b029254ad1260b6320472ea8c0c5b1cf"
|
|
191
|
+
}
|
|
192
|
+
],
|
|
193
|
+
"playlistSettings": { "title": "Feral File Artwork" }
|
|
194
|
+
}
|
|
195
|
+
JSON
|
|
151
196
|
|
|
152
|
-
|
|
153
|
-
npm run dev chat
|
|
154
|
-
# Then type: "publish the playlist ./playlist-temp.json"
|
|
197
|
+
npm run dev -- build /tmp/ff-artwork.json -o playlist.json
|
|
155
198
|
```
|
|
156
199
|
|
|
157
|
-
###
|
|
200
|
+
### Mixing sources in one playlist
|
|
201
|
+
|
|
202
|
+
Combine multiple requirements; shuffle with `preserveOrder: false` and set per-item timing with `durationPerItem`.
|
|
158
203
|
|
|
159
204
|
```bash
|
|
160
|
-
|
|
161
|
-
|
|
205
|
+
cat > /tmp/mixed.json <<'JSON'
|
|
206
|
+
{
|
|
207
|
+
"requirements": [
|
|
208
|
+
{
|
|
209
|
+
"type": "build_playlist",
|
|
210
|
+
"blockchain": "tezos",
|
|
211
|
+
"contractAddress": "KT1BcNnzWze3vCviwiETYNwcFSwjv6RihZEQ",
|
|
212
|
+
"tokenIds": ["22", "8"]
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
"type": "build_playlist",
|
|
216
|
+
"blockchain": "ethereum",
|
|
217
|
+
"contractAddress": "0xb932a70A57673d89f4acfFBE830E8ed7f75Fb9e0",
|
|
218
|
+
"tokenIds": ["52932", "52457"]
|
|
219
|
+
},
|
|
220
|
+
{ "type": "fetch_feed", "playlistName": "Unsupervised", "quantity": 3 },
|
|
221
|
+
{ "type": "query_address", "ownerAddress": "reas.eth", "quantity": 1 }
|
|
222
|
+
],
|
|
223
|
+
"playlistSettings": {
|
|
224
|
+
"title": "Mixed",
|
|
225
|
+
"preserveOrder": false,
|
|
226
|
+
"durationPerItem": 6,
|
|
227
|
+
"deviceName": "Living Room Display"
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
JSON
|
|
162
231
|
|
|
163
|
-
|
|
164
|
-
npm run dev -- chat "Get 5 from Unsupervised, shuffle, display on 'Living Room', and publish to feed" -v
|
|
232
|
+
npm run dev -- build /tmp/mixed.json -o playlist-mixed.json -v
|
|
165
233
|
```
|
|
166
234
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
**Mode 1: Build and Publish** (when sources are mentioned)
|
|
170
|
-
|
|
171
|
-
1. Intent parser detects "publish" keywords with sources/requirements
|
|
172
|
-
2. Calls `get_feed_servers` to retrieve configured servers
|
|
173
|
-
3. If 1 server → uses it automatically; if 2+ servers → asks user to pick
|
|
174
|
-
4. Builds playlist → validates structure → publishes automatically
|
|
235
|
+
## Validate / Sign / Play
|
|
175
236
|
|
|
176
|
-
|
|
237
|
+
```bash
|
|
238
|
+
# Validate playlist structure
|
|
239
|
+
npm run dev -- validate playlist.json
|
|
240
|
+
npm run dev -- validate "https://cdn.example.com/playlist.json"
|
|
177
241
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
3. If 1 server → uses it automatically; if 2+ servers → asks user to pick
|
|
181
|
-
4. Publishes the playlist from `./playlist.json` (or specified path)
|
|
242
|
+
# Validate structure AND verify signatures
|
|
243
|
+
npm run dev -- verify playlist.json
|
|
182
244
|
|
|
183
|
-
|
|
245
|
+
# Sign playlist (uses key/role from config, or override via --key / --role)
|
|
246
|
+
npm run dev -- sign playlist.json -o signed.json
|
|
184
247
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
- Publishing status: `✓ Published to feed server`
|
|
188
|
-
- Playlist ID: `Playlist ID: 84e028f8-...`
|
|
248
|
+
# Play on the configured default device
|
|
249
|
+
npm run dev -- play playlist.json
|
|
189
250
|
|
|
190
|
-
|
|
251
|
+
# Play on a specific named device
|
|
252
|
+
npm run dev -- play signed.json -d "Living Room Display"
|
|
191
253
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
npm run dev -- validate playlist.json
|
|
195
|
-
npm run dev -- validate "https://cdn.example.com/playlist.json"
|
|
254
|
+
# Play a hosted DP-1 playlist
|
|
255
|
+
npm run dev -- play "https://cdn.example.com/playlist.json"
|
|
196
256
|
|
|
197
|
-
#
|
|
198
|
-
npm run dev --
|
|
257
|
+
# Play a media URL directly
|
|
258
|
+
npm run dev -- play "https://example.com/video.mp4"
|
|
199
259
|
|
|
200
|
-
#
|
|
201
|
-
npm run dev --
|
|
202
|
-
npm run dev -- send "https://cdn.example.com/playlist.json" -d "Living Room Display"
|
|
260
|
+
# Skip verification only if you must send a non-conformant payload (not recommended)
|
|
261
|
+
npm run dev -- play playlist.json --skip-verify
|
|
203
262
|
```
|
|
204
263
|
|
|
205
264
|
## Publish to Feed Server
|
|
@@ -240,9 +299,9 @@ npm run dev -- publish --help
|
|
|
240
299
|
|
|
241
300
|
### Flow
|
|
242
301
|
|
|
243
|
-
1. **
|
|
302
|
+
1. **Verify** - Playlist structure and signatures checked; unsigned or broken playlists are rejected
|
|
244
303
|
2. **Select Server** - If multiple servers, choose which one (interactive or via `-s` flag)
|
|
245
|
-
3. **Publish** - Send
|
|
304
|
+
3. **Publish** - Send the verified playlist to the selected feed server
|
|
246
305
|
4. **Confirm** - Returns playlist ID and server details
|
|
247
306
|
|
|
248
307
|
### Example Output
|
|
@@ -287,11 +346,11 @@ Select server (0-based index): 0
|
|
|
287
346
|
Failed to publish: {"error":"unauthorized","message":"Invalid API key"}
|
|
288
347
|
```
|
|
289
348
|
|
|
290
|
-
##
|
|
349
|
+
## Complete Flow (build → validate → sign → play → publish)
|
|
291
350
|
|
|
292
351
|
```bash
|
|
293
|
-
# 1.
|
|
294
|
-
npm run dev --
|
|
352
|
+
# 1. Build a playlist (via find or build)
|
|
353
|
+
npm run dev -- find https://objkt.com/tokens/hicetnunc/111068 -o playlist.json
|
|
295
354
|
|
|
296
355
|
# 2. Validate it
|
|
297
356
|
npm run dev -- validate playlist.json
|
|
@@ -299,13 +358,38 @@ npm run dev -- validate playlist.json
|
|
|
299
358
|
# 3. Sign it
|
|
300
359
|
npm run dev -- sign playlist.json -o signed.json
|
|
301
360
|
|
|
302
|
-
# 4.
|
|
303
|
-
npm run dev --
|
|
361
|
+
# 4. Play it on a device
|
|
362
|
+
npm run dev -- play signed.json -d "Living Room Display"
|
|
304
363
|
|
|
305
|
-
# 5. Publish to feed server
|
|
364
|
+
# 5. Publish to a feed server
|
|
306
365
|
npm run dev -- publish signed.json -s 0
|
|
307
366
|
```
|
|
308
367
|
|
|
368
|
+
`ff-cli find` can collapse build + play + publish into one command:
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
npm run dev -- find https://objkt.com/tokens/hicetnunc/111068 --play -d "Living Room Display" --publish
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
## FF1 device management
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
# List configured devices
|
|
378
|
+
npm run dev -- device list
|
|
379
|
+
|
|
380
|
+
# Add a device interactively (with mDNS discovery)
|
|
381
|
+
npm run dev -- device add
|
|
382
|
+
|
|
383
|
+
# Add a device non-interactively
|
|
384
|
+
npm run dev -- device add --host 192.168.1.100 --name kitchen
|
|
385
|
+
|
|
386
|
+
# Remove a device by name
|
|
387
|
+
npm run dev -- device remove kitchen
|
|
388
|
+
|
|
389
|
+
# Set the default device (used when -d is omitted)
|
|
390
|
+
npm run dev -- device default office
|
|
391
|
+
```
|
|
392
|
+
|
|
309
393
|
## Troubleshooting
|
|
310
394
|
|
|
311
395
|
```bash
|
|
@@ -314,100 +398,7 @@ npm run dev -- config show
|
|
|
314
398
|
|
|
315
399
|
# Reinitialize config
|
|
316
400
|
npm run dev -- config init
|
|
317
|
-
```
|
|
318
401
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
- Format:
|
|
323
|
-
```bash
|
|
324
|
-
npm run dev -- chat "Compose a playlist from Ethereum contract <0x...> with tokens <id> and <id>; [shuffle|mix]; [send to device|send to '<device>']" -o <output.json> -v
|
|
325
|
-
```
|
|
326
|
-
- Example:
|
|
327
|
-
```bash
|
|
328
|
-
npm run dev -- chat "Compose a playlist from Ethereum contract 0xb932a70A57673d89f4acfFBE830E8ed7f75Fb9e0 with tokens 52932 and 52457; mix them up; send to device" -o playlist-eth.json -v
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
- **TEZ contract + token IDs (shuffle, named device)**
|
|
332
|
-
- Format:
|
|
333
|
-
```bash
|
|
334
|
-
npm run dev -- chat "Build a playlist from Tezos contract <KT1...> with tokens <id> and <id>; shuffle; send to '<device>'" -o <output.json> -v
|
|
335
|
-
```
|
|
336
|
-
- Example:
|
|
337
|
-
```bash
|
|
338
|
-
npm run dev -- chat "Build a playlist from Tezos contract KT1BcNnzWze3vCviwiETYNwcFSwjv6RihZEQ with tokens 22 and 8; shuffle; send to 'Living Room'" -o playlist-tez.json -v
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
- **Owner address (ENS → ETH), shuffled**
|
|
342
|
-
- Format:
|
|
343
|
-
```bash
|
|
344
|
-
npm run dev -- chat "Create a playlist from address <ens> (<n> items); [shuffle|mix]; [send/push to my device]" -o <output.json> -v
|
|
345
|
-
```
|
|
346
|
-
- Example:
|
|
347
|
-
```bash
|
|
348
|
-
npm run dev -- chat "Create a playlist from address reas.eth (5 items); shuffle; push to my device" -o playlist-ens.json -v
|
|
349
|
-
```
|
|
350
|
-
|
|
351
|
-
- **Owner address (Tezos tz1), shuffled**
|
|
352
|
-
- Format:
|
|
353
|
-
```bash
|
|
354
|
-
npm run dev -- chat "Create a playlist from Tezos address <tz1...> (<n> items); [shuffle|mix]; [send to device]" -o <output.json> -v
|
|
355
|
-
```
|
|
356
|
-
- Example:
|
|
357
|
-
```bash
|
|
358
|
-
npm run dev -- chat "Create a playlist from Tezos address tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb (3 items); mix them up; send to device" -o playlist-tz1.json -v
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
- **Feed playlists (named), shuffled**
|
|
362
|
-
- Format:
|
|
363
|
-
```bash
|
|
364
|
-
npm run dev -- chat "[Create|Build] a playlist from feed '<name>' (<n> items); shuffle; [send to device]" -o <output.json> -v
|
|
365
|
-
```
|
|
366
|
-
- Examples:
|
|
367
|
-
```bash
|
|
368
|
-
npm run dev -- chat "Create a playlist from feed 'Unsupervised' (3 items); shuffle; send to device" -o playlist-feed1.json -v
|
|
369
|
-
npm run dev -- chat "Build a playlist from feed 'Unsupervised' (3 items); shuffle; send to device" -o playlist-feed2.json -v
|
|
370
|
-
```
|
|
371
|
-
|
|
372
|
-
- **Mixed in one prompt (ETH + TEZ + feed + ENS), shuffled, named device**
|
|
373
|
-
- Format:
|
|
374
|
-
```bash
|
|
375
|
-
npm run dev -- chat "Compose a playlist: Tezos <KT1...> tokens <id>, <id>; Ethereum <0x...> tokens <id>, <id>; <n> from '<feed>'; <m> from <ens>; shuffle; send to '<device>'" -o <output.json> -v
|
|
376
|
-
```
|
|
377
|
-
- Example:
|
|
378
|
-
```bash
|
|
379
|
-
npm run dev -- chat "Compose a playlist: Tezos KT1BcNnzWze3vCviwiETYNwcFSwjv6RihZEQ tokens 22, 8; Ethereum 0xb932a70A57673d89f4acfFBE830E8ed7f75Fb9e0 tokens 52932, 52457; 3 from 'Unsupervised'; 1 from reas.eth; shuffle; send to 'Living Room'" -o playlist-mixed.json -v
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
- **Multiple instructions in one prompt (incremental), shuffled**
|
|
383
|
-
- Format:
|
|
384
|
-
```bash
|
|
385
|
-
npm run dev -- chat "Create a playlist from Ethereum contract <0x...> tokens <id>, <id>; then add <n> from '<feed>'; then add <m> from <ens>; shuffle; [send/push to my device]" -o <output.json> -v
|
|
386
|
-
```
|
|
387
|
-
- Example:
|
|
388
|
-
```bash
|
|
389
|
-
npm run dev -- chat "Create a playlist from Ethereum contract 0xb932a70A57673d89f4acfFBE830E8ed7f75Fb9e0 tokens 52932, 52457; then add 2 from 'Unsupervised'; then add 1 from reas.eth; shuffle; push to my device" -o playlist-multi.json -v
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
- **Synonym variants for the same ETH case**
|
|
393
|
-
- Format:
|
|
394
|
-
```bash
|
|
395
|
-
npm run dev -- chat "[Build|Create|Compose] a playlist from Ethereum contract <0x...> tokens <id> and <id>; send to device" -o <output.json> -v
|
|
396
|
-
```
|
|
397
|
-
- Examples:
|
|
398
|
-
```bash
|
|
399
|
-
npm run dev -- chat "Build a playlist from Ethereum contract 0xb932a70A57673d89f4acfFBE830E8ed7f75Fb9e0 tokens 52932 and 52457; send to device" -o playlist-eth-build.json -v
|
|
400
|
-
npm run dev -- chat "Create a playlist from Ethereum contract 0xb932a70A57673d89f4acfFBE830E8ed7f75Fb9e0 tokens 52932 and 52457; send to device" -o playlist-eth-create.json -v
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
- **Device targeting: generic vs named**
|
|
404
|
-
- Format:
|
|
405
|
-
```bash
|
|
406
|
-
npm run dev -- chat "Compose a playlist from <ens/address> (<n> items); send to device" -o <output.json> -v
|
|
407
|
-
npm run dev -- chat "Compose a playlist from <ens/address> (<n> items); send to '<device>'" -o <output.json> -v
|
|
408
|
-
```
|
|
409
|
-
- Examples:
|
|
410
|
-
```bash
|
|
411
|
-
npm run dev -- chat "Compose a playlist from reas.eth (3 items); send to device" -o playlist-generic-device.json -v
|
|
412
|
-
npm run dev -- chat "Compose a playlist from reas.eth (3 items); send to 'Living Room'" -o playlist-named-device.json -v
|
|
413
|
-
```
|
|
402
|
+
# Validate configuration
|
|
403
|
+
npm run dev -- config validate
|
|
404
|
+
```
|