ff1-cli 1.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.
@@ -0,0 +1,178 @@
1
+ # Configuration Guide
2
+
3
+ This guide explains how to configure FF1‑CLI, field by field. Configuration priority is:
4
+
5
+ - `config.json` (highest)
6
+ - `.env`
7
+ - built‑in defaults (lowest)
8
+
9
+ ## Getting started
10
+
11
+ ```bash
12
+ # Create example config and edit it
13
+ npm run dev -- config init
14
+
15
+ # Validate your configuration
16
+ npm run dev -- config validate
17
+
18
+ # Show current config summary
19
+ npm run dev -- config show
20
+ ```
21
+
22
+ ## Top‑level fields
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
+ - **defaultDuration** (number, seconds)
29
+ - Intended default per‑item display duration. Some flows pass an explicit duration; when omitted, utilities fall back to 10s.
30
+
31
+ ## models
32
+
33
+ Each key under `models` defines a model configuration used by the AI orchestrator.
34
+
35
+ - `<modelName>.apiKey` (string): API key for the provider.
36
+ - `<modelName>.baseURL` (string): Base API URL.
37
+ - `<modelName>.model` (string): Model identifier (e.g., `grok-beta`, `gpt-4o`).
38
+ - `<modelName>.availableModels` (string[], optional): Display/help only.
39
+ - `<modelName>.timeout` (number, ms): HTTP timeout for requests.
40
+ - `<modelName>.maxRetries` (number): Retry count for requests.
41
+ - `<modelName>.temperature` (number): Generation temperature.
42
+ - `<modelName>.maxTokens` (number): Token cap.
43
+ - `<modelName>.supportsFunctionCalling` (boolean): Must be true; otherwise the CLI rejects the model.
44
+
45
+ Environment variable helpers:
46
+
47
+ - Grok: `GROK_API_KEY`, `GROK_MODEL`, `GROK_API_BASE_URL`
48
+ - OpenAI: `OPENAI_API_KEY`
49
+ - Gemini: `GEMINI_API_KEY`
50
+
51
+ ## browser
52
+
53
+ Optional settings used where headless/browser‑like behavior is needed.
54
+
55
+ - `browser.timeout` (number, ms): Operation timeout (default 90000).
56
+ - `browser.sanitizationLevel` ("none" | "low" | "medium" | "high" | 0‑3): Converted to numeric via `sanitizationLevelToNumber()`; invalid values are flagged during validation.
57
+
58
+ ## playlist
59
+
60
+ Used for signing DP‑1 playlists.
61
+
62
+ - `playlist.privateKey` (string, Ed25519 private key in hex or base64): Used by the `sign` command. Hex may include or omit the `0x` prefix. You can also set this via `PLAYLIST_PRIVATE_KEY` in `.env`.
63
+
64
+ ### Generate an Ed25519 private key
65
+
66
+ You can generate a key locally. The CLI accepts either base64 (preferred) or hex
67
+
68
+ OpenSSL (recommended):
69
+
70
+ ```bash
71
+ # Base64 (preferred)
72
+ openssl genpkey -algorithm ED25519 -outform DER | base64 | tr -d '\n'
73
+
74
+ # Hex (alternative)
75
+ openssl genpkey -algorithm ED25519 -outform DER | xxd -p -c 256
76
+ ```
77
+
78
+ Paste either value into `playlist.privateKey`:
79
+
80
+ - Hex example (either is valid):
81
+ - `0xabc123...` (with prefix)
82
+ - `abc123...` (without prefix)
83
+ - Base64 example: `uQd9m8S...==`
84
+
85
+ If you already have a base64 key and want hex, convert it:
86
+
87
+ ```bash
88
+ echo -n "<BASE64_KEY>" | base64 -d | xxd -p -c 256
89
+ ```
90
+
91
+ ## feed
92
+
93
+ DP‑1 Feed API configuration.
94
+
95
+ - `feed.baseURLs` (string[]): Array of DP‑1 Feed Operator API v1 base URLs. The CLI queries all feeds in parallel.
96
+ - Legacy support: `feed.baseURL` (string) is still accepted and normalized to an array.
97
+ - Default: `https://feed.feralfile.com/api/v1` if not set.
98
+ - Compatibility: API v1 of the DP‑1 Feed Operator server. See the repository for endpoints and behavior: [dp1-feed](https://github.com/display-protocol/dp1-feed).
99
+
100
+ Endpoints used by the CLI:
101
+
102
+ - `GET /api/v1/playlists` (supports `limit`, `offset`, and sorting)
103
+ - `GET /api/v1/playlists/{id}`
104
+
105
+ Environment variable alternative:
106
+
107
+ ```env
108
+ FEED_BASE_URLS=https://feed.feralfile.com/api/v1,https://dp1-feed-operator-api-prod.autonomy-system.workers.dev/api/v1
109
+ ```
110
+
111
+ ## ff1Devices
112
+
113
+ Configure devices you want to send playlists to.
114
+
115
+ - `ff1Devices.devices` (array of objects):
116
+ - `name` (string): Friendly device label. Free‑form; pick anything memorable.
117
+ - `host` (string): Device base URL. For LAN devices, use `http://<ip>:1111`. The device typically listens on port `1111`.
118
+
119
+ Selection rules when sending:
120
+
121
+ - If you omit `-d`, the first configured device is used.
122
+ - If you pass `-d <name>`, the CLI matches the device by `name` (exact match). If not found, you’ll see an error listing available devices.
123
+
124
+ Examples:
125
+
126
+ ```bash
127
+ # Send to first device
128
+ npm run dev -- send playlist.json
129
+
130
+ # Send to a specific device by exact name
131
+ npm run dev -- send playlist.json -d "Living Room Display"
132
+ ```
133
+
134
+ Minimal `config.json` example (selected fields):
135
+
136
+ ```json
137
+ {
138
+ "defaultModel": "grok",
139
+ "models": {
140
+ "grok": {
141
+ "apiKey": "xai-your-api-key-here",
142
+ "baseURL": "https://api.x.ai/v1",
143
+ "model": "grok-beta",
144
+ "supportsFunctionCalling": true
145
+ }
146
+ },
147
+ "defaultDuration": 10,
148
+ "playlist": {
149
+ "privateKey": "your_ed25519_private_key_hex_or_base64_here"
150
+ },
151
+ "feed": {
152
+ "baseURLs": [
153
+ "https://feed.feralfile.com/api/v1"
154
+ ]
155
+ },
156
+ "ff1Devices": {
157
+ "devices": [
158
+ {
159
+ "name": "Living Room Display",
160
+ "host": "http://192.168.1.100:1111"
161
+ }
162
+ ]
163
+ }
164
+ }
165
+ ```
166
+
167
+ ## Security and validation
168
+
169
+ - Do not commit secrets. Keep `config.json`, `.env`, and keys out of version control.
170
+ - Validate changes regularly:
171
+
172
+ ```bash
173
+ npm run dev -- config validate
174
+ ```
175
+
176
+ If configuration is invalid, the CLI prints actionable errors and a non‑zero exit code.
177
+
178
+
@@ -0,0 +1,331 @@
1
+ # Examples
2
+
3
+ Copy‑pasteable commands that work with the current CLI.
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ npm install
9
+ npm run dev -- config init
10
+ npm run dev -- config validate
11
+ ```
12
+
13
+ ## Natural Language
14
+
15
+ ```bash
16
+ # Interactive chat
17
+ npm run dev chat
18
+
19
+ # One-shot requests
20
+ npm run dev -- chat "Get tokens 1,2,3 from Ethereum contract 0xabc" -o playlist.json
21
+ npm run dev -- chat "Get token 42 from Tezos contract KT1abc"
22
+ npm run dev -- chat "Get 3 items from Social Codes and 2 from 0xdef" -v
23
+
24
+ # Switch model
25
+ npm run dev -- chat "your request" --model grok
26
+ npm run dev -- chat "your request" --model chatgpt
27
+ npm run dev -- chat "your request" --model gemini
28
+ ```
29
+
30
+ ## Deterministic Build (no AI)
31
+
32
+ ```bash
33
+ # From file
34
+ npm run dev -- build examples/params-example.json -o playlist.json
35
+
36
+ # From stdin
37
+ cat examples/params-example.json | npm run dev -- build -o playlist.json
38
+ ```
39
+
40
+ ## AI‑Orchestrated Deterministic Flow (prompts)
41
+
42
+ ```bash
43
+ # Show tool‑call progress and validation
44
+ npm run dev -- chat "Build a playlist of my Tezos works from address tz1... plus 3 from Social Codes" -v -o playlist.json
45
+
46
+ # Switch model if desired
47
+ npm run dev -- chat "Build playlist from Ethereum address 0x... and 2 from Social Codes" --model chatgpt -v
48
+ ```
49
+
50
+ ### One‑shot complex prompt
51
+
52
+ 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.
53
+
54
+ ```bash
55
+ # Example: combine sources, shuffle, set 6s per item, and send to device
56
+ npm run dev -- chat "Get tokens 1,2 from contract 0xabc and token 42 from KT1xyz; shuffle; 6 seconds each; send to 'Living Room Display'." -o playlist.json -v
57
+ ```
58
+
59
+ ## Natural Language: Display and Publish
60
+
61
+ The CLI recognizes publishing keywords like "publish", "publish to my feed", "push to feed", "send to feed" and automatically publishes after building.
62
+
63
+ ### Basic Publishing
64
+
65
+ ```bash
66
+ # Build and publish
67
+ npm run dev -- chat "Build playlist from Ethereum contract 0xb932a70A57673d89f4acfFBE830E8ed7f75Fb9e0 with tokens 52932 and 52457; publish to my feed" -o playlist.json -v
68
+
69
+ # With feed selection (if multiple servers configured)
70
+ # The CLI will ask: "Which feed server? 1) https://feed.feralfile.com/api/v1 2) http://localhost:8787"
71
+ npm run dev -- chat "Get 3 from Social Codes and publish to feed" -v
72
+
73
+ # Publish existing playlist (defaults to ./playlist.json)
74
+ npm run dev chat
75
+ # Then type: "publish playlist"
76
+
77
+ # Publish specific playlist file
78
+ npm run dev chat
79
+ # Then type: "publish the playlist ./playlist-temp.json"
80
+ ```
81
+
82
+ ### Combined: Display + Publish
83
+
84
+ ```bash
85
+ # Display on FF1 AND publish to feed
86
+ npm run dev -- chat "Build playlist from contract 0xb932a70A57673d89f4acfFBE830E8ed7f75Fb9e0 with tokens 52932 and 52457; mix them up; send to my FF1 and publish to my feed" -o playlist.json -v
87
+
88
+ # With explicit device name
89
+ npm run dev -- chat "Get 5 from Social Codes, shuffle, display on 'Living Room', and publish to feed" -v
90
+ ```
91
+
92
+ ### How It Works
93
+
94
+ **Mode 1: Build and Publish** (when sources are mentioned)
95
+ 1. Intent parser detects "publish" keywords with sources/requirements
96
+ 2. Calls `get_feed_servers` to retrieve configured servers
97
+ 3. If 1 server → uses it automatically; if 2+ servers → asks user to pick
98
+ 4. Builds playlist → verifies → publishes automatically
99
+
100
+ **Mode 2: Publish Existing File** (e.g., "publish playlist")
101
+ 1. Intent parser detects "publish playlist" or similar phrases
102
+ 2. Calls `get_feed_servers` to retrieve configured servers
103
+ 3. If 1 server → uses it automatically; if 2+ servers → asks user to pick
104
+ 4. Publishes the playlist from `./playlist.json` (or specified path)
105
+
106
+ Output shows:
107
+ - Playlist build progress (Mode 1 only)
108
+ - Device sending (if requested): `✓ Sent to device: Living Room`
109
+ - Publishing status: `✓ Published to feed server`
110
+ - Playlist ID: `Playlist ID: 84e028f8-...`
111
+
112
+ ## Validate / Sign / Send
113
+
114
+ ```bash
115
+ # Validate playlist
116
+ npm run dev -- validate playlist.json
117
+
118
+ # Sign playlist
119
+ npm run dev -- sign playlist.json -o signed.json
120
+
121
+ # Send to device
122
+ npm run dev -- send playlist.json -d "Living Room Display"
123
+ ```
124
+
125
+ ## Publish to Feed Server
126
+
127
+ Publish validated playlists to a DP-1 feed server for sharing and discovery.
128
+
129
+ ### Configuration
130
+
131
+ Add feed servers to `config.json`:
132
+
133
+ ```json
134
+ {
135
+ "feedServers": [
136
+ {
137
+ "baseUrl": "http://localhost:8787/api/v1",
138
+ "apiKey": "your-api-key"
139
+ },
140
+ {
141
+ "baseUrl": "https://feed.example.com/api/v1",
142
+ "apiKey": "your-api-key"
143
+ }
144
+ ]
145
+ }
146
+ ```
147
+
148
+ ### Publish Commands
149
+
150
+ ```bash
151
+ # Interactive: list servers and ask which to use
152
+ npm run dev -- publish playlist.json
153
+
154
+ # Direct: publish to specific server (server index 0)
155
+ npm run dev -- publish playlist.json -s 0
156
+
157
+ # Show help
158
+ npm run dev -- publish --help
159
+ ```
160
+
161
+ ### Flow
162
+
163
+ 1. **Validate** - Playlist verified against DP-1 specification
164
+ 2. **Select Server** - If multiple servers, choose which one (interactive or via `-s` flag)
165
+ 3. **Publish** - Send validated playlist to selected feed server
166
+ 4. **Confirm** - Returns playlist ID and server details
167
+
168
+ ### Example Output
169
+
170
+ ```
171
+ $ npm run dev -- publish playlist.json
172
+
173
+ 📡 Publishing playlist to feed server...
174
+
175
+ Multiple feed servers found. Select one:
176
+ 0: http://localhost:8787/api/v1
177
+ 1: https://feed.example.com/api/v1
178
+
179
+ Select server (0-based index): 0
180
+
181
+ ✅ Playlist published successfully!
182
+ Playlist ID: 84e028f8-ea12-4779-a496-64f95f0486cd
183
+ Server: http://localhost:8787/api/v1
184
+ Status: Published to feed server (created)
185
+ ```
186
+
187
+ ### Error Handling
188
+
189
+ **Validation failed:**
190
+ ```
191
+ ❌ Failed to publish playlist
192
+ Playlist validation failed: dpVersion: Required; id: Required
193
+ ```
194
+
195
+ **File not found:**
196
+ ```
197
+ ❌ Failed to publish playlist
198
+ Playlist file not found: /path/to/playlist.json
199
+ ```
200
+
201
+ **API error:**
202
+ ```
203
+ ❌ Failed to publish playlist
204
+ Failed to publish: {"error":"unauthorized","message":"Invalid API key"}
205
+ ```
206
+
207
+ ## Validate / Sign / Send / Publish (Complete Flow)
208
+
209
+ ```bash
210
+ # 1. Create a playlist (via chat or build)
211
+ npm run dev -- chat "Get tokens 1,2,3 from contract 0xabc" -o playlist.json
212
+
213
+ # 2. Validate it
214
+ npm run dev -- validate playlist.json
215
+
216
+ # 3. Sign it
217
+ npm run dev -- sign playlist.json -o signed.json
218
+
219
+ # 4. Send to device
220
+ npm run dev -- send signed.json -d "My Display"
221
+
222
+ # 5. Publish to feed server
223
+ npm run dev -- publish signed.json -s 0
224
+ ```
225
+
226
+ ## Troubleshooting
227
+
228
+ ```bash
229
+ # Show current configuration
230
+ npm run dev -- config show
231
+
232
+ # Reinitialize config
233
+ npm run dev -- config init
234
+ ```
235
+
236
+
237
+ ### Natural‑language one‑shot examples (proven)
238
+
239
+ - **ETH contract + token IDs (shuffle/mix, generic device)**
240
+ - Format:
241
+ ```bash
242
+ 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
243
+ ```
244
+ - Example:
245
+ ```bash
246
+ 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
247
+ ```
248
+
249
+ - **TEZ contract + token IDs (shuffle, named device)**
250
+ - Format:
251
+ ```bash
252
+ npm run dev -- chat "Build a playlist from Tezos contract <KT1...> with tokens <id> and <id>; shuffle; send to '<device>'" -o <output.json> -v
253
+ ```
254
+ - Example:
255
+ ```bash
256
+ 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
257
+ ```
258
+
259
+ - **Owner address (ENS → ETH), shuffled**
260
+ - Format:
261
+ ```bash
262
+ npm run dev -- chat "Create a playlist from address <ens> (<n> items); [shuffle|mix]; [send/push to my device]" -o <output.json> -v
263
+ ```
264
+ - Example:
265
+ ```bash
266
+ npm run dev -- chat "Create a playlist from address reas.eth (5 items); shuffle; push to my device" -o playlist-ens.json -v
267
+ ```
268
+
269
+ - **Owner address (Tezos tz1), shuffled**
270
+ - Format:
271
+ ```bash
272
+ npm run dev -- chat "Create a playlist from Tezos address <tz1...> (<n> items); [shuffle|mix]; [send to device]" -o <output.json> -v
273
+ ```
274
+ - Example:
275
+ ```bash
276
+ npm run dev -- chat "Create a playlist from Tezos address tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb (3 items); mix them up; send to device" -o playlist-tz1.json -v
277
+ ```
278
+
279
+ - **Feed playlists (named), shuffled**
280
+ - Format:
281
+ ```bash
282
+ npm run dev -- chat "[Create|Build] a playlist from feed '<name>' (<n> items); shuffle; [send to device]" -o <output.json> -v
283
+ ```
284
+ - Examples:
285
+ ```bash
286
+ npm run dev -- chat "Create a playlist from feed 'Unsupervised' (3 items); shuffle; send to device" -o playlist-feed1.json -v
287
+ npm run dev -- chat "Build a playlist from feed 'Social Codes' (3 items); shuffle; send to device" -o playlist-feed2.json -v
288
+ ```
289
+
290
+ - **Mixed in one prompt (ETH + TEZ + feed + ENS), shuffled, named device**
291
+ - Format:
292
+ ```bash
293
+ 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
294
+ ```
295
+ - Example:
296
+ ```bash
297
+ 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
298
+ ```
299
+
300
+ - **Multiple instructions in one prompt (incremental), shuffled**
301
+ - Format:
302
+ ```bash
303
+ 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
304
+ ```
305
+ - Example:
306
+ ```bash
307
+ npm run dev -- chat "Create a playlist from Ethereum contract 0xb932a70A57673d89f4acfFBE830E8ed7f75Fb9e0 tokens 52932, 52457; then add 2 from 'Social Codes'; then add 1 from reas.eth; shuffle; push to my device" -o playlist-multi.json -v
308
+ ```
309
+
310
+ - **Synonym variants for the same ETH case**
311
+ - Format:
312
+ ```bash
313
+ npm run dev -- chat "[Build|Create|Compose] a playlist from Ethereum contract <0x...> tokens <id> and <id>; send to device" -o <output.json> -v
314
+ ```
315
+ - Examples:
316
+ ```bash
317
+ npm run dev -- chat "Build a playlist from Ethereum contract 0xb932a70A57673d89f4acfFBE830E8ed7f75Fb9e0 tokens 52932 and 52457; send to device" -o playlist-eth-build.json -v
318
+ npm run dev -- chat "Create a playlist from Ethereum contract 0xb932a70A57673d89f4acfFBE830E8ed7f75Fb9e0 tokens 52932 and 52457; send to device" -o playlist-eth-create.json -v
319
+ ```
320
+
321
+ - **Device targeting: generic vs named**
322
+ - Format:
323
+ ```bash
324
+ npm run dev -- chat "Compose a playlist from <ens/address> (<n> items); send to device" -o <output.json> -v
325
+ npm run dev -- chat "Compose a playlist from <ens/address> (<n> items); send to '<device>'" -o <output.json> -v
326
+ ```
327
+ - Examples:
328
+ ```bash
329
+ npm run dev -- chat "Compose a playlist from reas.eth (3 items); send to device" -o playlist-generic-device.json -v
330
+ npm run dev -- chat "Compose a playlist from reas.eth (3 items); send to 'Living Room'" -o playlist-named-device.json -v
331
+ ```
@@ -0,0 +1,92 @@
1
+ # Function Calling Architecture
2
+
3
+ How FF1‑CLI uses AI function calling to build playlists deterministically. The model orchestrates function calls; tools enforce schema and assemble the DP-1 envelope.
4
+
5
+ ## Overview
6
+
7
+ Natural language requests become structured parameters. An AI orchestrator then calls functions to fetch items, build a DP-1 playlist, verify it, optionally sign, and send to a device.
8
+
9
+ Pipeline:
10
+
11
+ ```
12
+ Intent Parser → Orchestrator (function calls) → Utilities → DP-1 Playlist
13
+ ```
14
+
15
+ Key files:
16
+
17
+ - `src/intent-parser/` – Parses user text into `requirements` + `playlistSettings`
18
+ - `src/ai-orchestrator/index.js` – Function schemas and orchestration logic
19
+ - `src/utilities/` – Concrete implementations used by the orchestrator
20
+ - `src/main.ts` – Bridges CLI commands to parser/orchestrator/utilities
21
+
22
+ ## Function Schemas (AI‑visible)
23
+
24
+ Defined in `src/ai-orchestrator/index.js` as tool schemas for OpenAI‑compatible clients.
25
+
26
+ - `query_requirement(requirement, duration)`
27
+ - Types: `build_playlist`, `fetch_feed`, `query_address`
28
+ - For `build_playlist`: requires `blockchain`, `contractAddress`, `tokenIds`, optional `quantity`
29
+ - For `query_address`: requires `ownerAddress`, optional `quantity` (random selection)
30
+ - For `fetch_feed`: requires `playlistName`, `quantity`
31
+
32
+ - `search_feed_playlist(playlistName)` → fuzzy-match across configured feeds
33
+ - `fetch_feed_playlist_items(playlistName, quantity, duration)`
34
+ - `build_playlist(items, title?, slug?, shuffle?)` → returns DP-1 playlist
35
+ - `verify_playlist(playlist)` → validates DP-1 compliance (must precede send)
36
+ - `verify_addresses(addresses[])` → validates Ethereum (0x...) and Tezos (tz.../KT1) address formats
37
+ - `send_to_device(playlist, deviceName?)`
38
+ - `resolve_domains(domains[], displayResults?)` → ENS/TNS resolution
39
+
40
+ Notes enforced by the orchestrator:
41
+
42
+ - Always pass complete requirement objects (no truncating addresses/token IDs)
43
+ - Resolve domains (`.eth`, `.tez`) before `query_address`
44
+ - Build, then verify before sending to devices
45
+ - Shuffle is controlled by `playlistSettings.preserveOrder`
46
+
47
+ ## Implementations (server‑side)
48
+
49
+ Located in `src/utilities/` and wired in `src/ai-orchestrator/index.js`:
50
+
51
+ - `buildDP1Playlist({ items, title, slug })` → `src/utilities/playlist-builder.js`
52
+ - `sendPlaylistToDevice({ playlist, deviceName })` → `src/utilities/ff1-device.ts`
53
+ - `resolveDomains({ domains, displayResults })` → `src/utilities/domain-resolver.ts`
54
+ - `verifyPlaylist({ playlist })` → `src/utilities/playlist-verifier.ts`
55
+ - `verifyAddresses({ addresses })` → `src/utilities/functions.js` (uses `address-validator.ts`)
56
+ - Feed utilities: `feed-fetcher.js`
57
+
58
+ ## Deterministic Paths
59
+
60
+ Two options are available:
61
+
62
+ 1) No-AI deterministic build (recommended for automation): Use CLI `build` command with a JSON file or stdin containing:
63
+
64
+ ```json
65
+ {
66
+ "requirements": [
67
+ { "type": "fetch_feed", "playlistName": "Social Codes", "quantity": 3 },
68
+ { "type": "build_playlist", "blockchain": "ethereum", "contractAddress": "0x...", "tokenIds": ["1","2"], "quantity": 2 }
69
+ ],
70
+ "playlistSettings": { "durationPerItem": 10, "preserveOrder": true, "title": "My Mix" }
71
+ }
72
+ ```
73
+
74
+ This path bypasses the intent parser/orchestrator and calls utilities directly. Validation and sensible defaults are applied in `src/main.ts`.
75
+
76
+ 2) AI‑orchestrated deterministic build (recommended for prompts): Use `chat` with `--verbose` to see tool calls. The orchestrator enforces complete requirement objects, then validates the result with `verify_playlist` before sending.
77
+
78
+ ## Extending Functionality (OSS‑first)
79
+
80
+ 1. Add a new function in `src/utilities/` (prefer OSS libs: `viem` for Ethereum, `@taquito/taquito` for Tezos; add local caching where it helps)
81
+ 2. Export and wire it in `src/utilities/index.js`
82
+ 3. Add a corresponding schema in `src/ai-orchestrator/index.js`
83
+ 4. Update this doc if user‑facing behavior changes
84
+ 5. Run `npm run lint:fix`
85
+
86
+ ## Validation & Constraints
87
+
88
+ - Verify via `dp1-js` for DP-1 conformance (canonical JSON + Ed25519 signing supported)
89
+ - Enforce max item counts and ordering/shuffle rules during build
90
+ - Batch domain resolution; report failures without crashing the flow
91
+
92
+