@feralfile/cli 1.1.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.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +96 -0
  3. package/config.json.example +96 -0
  4. package/dist/index.js +54 -0
  5. package/dist/src/ai-orchestrator/index.js +1019 -0
  6. package/dist/src/ai-orchestrator/registry.js +96 -0
  7. package/dist/src/commands/build.js +69 -0
  8. package/dist/src/commands/chat.js +189 -0
  9. package/dist/src/commands/config.js +68 -0
  10. package/dist/src/commands/device.js +278 -0
  11. package/dist/src/commands/helpers/config-files.js +62 -0
  12. package/dist/src/commands/helpers/device-discovery.js +111 -0
  13. package/dist/src/commands/helpers/playlist-display.js +161 -0
  14. package/dist/src/commands/helpers/prompt.js +65 -0
  15. package/dist/src/commands/helpers/ssh-helpers.js +44 -0
  16. package/dist/src/commands/play.js +110 -0
  17. package/dist/src/commands/publish.js +115 -0
  18. package/dist/src/commands/setup.js +225 -0
  19. package/dist/src/commands/sign.js +41 -0
  20. package/dist/src/commands/ssh.js +108 -0
  21. package/dist/src/commands/status.js +126 -0
  22. package/dist/src/commands/validate.js +18 -0
  23. package/dist/src/config.js +441 -0
  24. package/dist/src/intent-parser/index.js +1382 -0
  25. package/dist/src/intent-parser/utils.js +108 -0
  26. package/dist/src/logger.js +82 -0
  27. package/dist/src/main.js +459 -0
  28. package/dist/src/types.js +5 -0
  29. package/dist/src/utilities/address-validator.js +242 -0
  30. package/dist/src/utilities/device-default.js +36 -0
  31. package/dist/src/utilities/device-lookup.js +107 -0
  32. package/dist/src/utilities/device-normalize.js +62 -0
  33. package/dist/src/utilities/device-upsert.js +91 -0
  34. package/dist/src/utilities/domain-resolver.js +291 -0
  35. package/dist/src/utilities/ed25519-key-derive.js +155 -0
  36. package/dist/src/utilities/feed-fetcher.js +471 -0
  37. package/dist/src/utilities/ff1-compatibility.js +269 -0
  38. package/dist/src/utilities/ff1-device.js +250 -0
  39. package/dist/src/utilities/ff1-discovery.js +330 -0
  40. package/dist/src/utilities/functions.js +308 -0
  41. package/dist/src/utilities/index.js +469 -0
  42. package/dist/src/utilities/nft-indexer.js +1024 -0
  43. package/dist/src/utilities/playlist-builder.js +523 -0
  44. package/dist/src/utilities/playlist-publisher.js +131 -0
  45. package/dist/src/utilities/playlist-send.js +260 -0
  46. package/dist/src/utilities/playlist-signer.js +204 -0
  47. package/dist/src/utilities/playlist-signing-role.js +41 -0
  48. package/dist/src/utilities/playlist-source.js +128 -0
  49. package/dist/src/utilities/playlist-verifier.js +274 -0
  50. package/dist/src/utilities/ssh-access.js +145 -0
  51. package/dist/src/utils.js +48 -0
  52. package/docs/CONFIGURATION.md +206 -0
  53. package/docs/EXAMPLES.md +390 -0
  54. package/docs/FUNCTION_CALLING.md +96 -0
  55. package/docs/PROJECT_SPEC.md +228 -0
  56. package/docs/README.md +348 -0
  57. package/docs/RELEASING.md +73 -0
  58. package/package.json +76 -0
@@ -0,0 +1,228 @@
1
+ # ff-cli Project Spec
2
+
3
+ This document defines the current product role, boundaries, and constraints for `ff-cli`.
4
+
5
+ It is derived from the behavior and interfaces implemented in this repository as of March 2026. It serves as the planning entry point for substantial changes and should be updated as the CLI evolves.
6
+
7
+ ## Why this doc exists
8
+
9
+ - Give future contributors and coding agents a stable current-state spec before implementation starts.
10
+ - Make the CLI's role in the broader FF1 and DP-1 system explicit.
11
+ - Record the constraints that should shape changes even when the codebase is refactored.
12
+
13
+ ## Product summary
14
+
15
+ - Project: `ff-cli`
16
+ - Type: Node.js CLI
17
+ - System role: a control and integration surface for FF1 and DP-1 workflows
18
+ - Primary purpose: turn user intent or structured parameters into valid DP-1 playlists, then validate them and optionally sign, publish, and send them through FF1 and feed paths
19
+
20
+ In the Feral File architecture bands, `ff-cli` sits primarily in the presentation and control layer. It is not the canonical source of truth for exhibitions, ownership, device runtime, or protocol evolution. It is a practical operator and developer surface that bridges those systems.
21
+
22
+ ## Strategic role
23
+
24
+ The CLI supports the broader Feral File goal of making it effortless to live with digital art every day.
25
+
26
+ Its value is reducing friction in the publish-to-play path:
27
+
28
+ - assemble playlists quickly
29
+ - keep outputs DP-1 conformant
30
+ - make FF1 playback and feed publishing easier to exercise
31
+ - provide deterministic tooling around model-assisted workflows
32
+
33
+ The CLI should strengthen the Gold Path, not invent a parallel product model.
34
+
35
+ ## Users and primary use cases
36
+
37
+ Current likely users:
38
+
39
+ - internal engineers working on FF1, DP-1, feed, and device flows
40
+ - operators and launch teammates validating publish-to-play behavior
41
+ - advanced users or partners building and testing playlists
42
+ - developers using the CLI as a reference implementation for DP-1 and FF1 command flows
43
+
44
+ Primary use cases:
45
+
46
+ - build a playlist from structured JSON inputs
47
+ - build a playlist from natural-language prompts using model orchestration plus deterministic tools
48
+ - validate or verify a local or hosted DP-1 playlist
49
+ - sign a playlist with an Ed25519 key
50
+ - publish a validated playlist to a configured feed server
51
+ - send a playlist or direct media URL to a configured FF1 device
52
+ - manage local config and FF1 SSH access
53
+ - exercise compatibility checks against FF1 OS versions before risky commands
54
+
55
+ ## Domain language
56
+
57
+ Use these terms consistently:
58
+
59
+ - `FF1`
60
+ - `FF1 device`
61
+ - `DP-1`
62
+ - `DP-1 envelope`
63
+ - `DP-1 conformance`
64
+ - `computational art playlist`
65
+ - `channel endorsement`
66
+ - `feed server`
67
+ - `playlist`
68
+ - `work`
69
+
70
+ ## Product goals
71
+
72
+ The current code and internal context imply these practical goals:
73
+
74
+ - Make playlist creation and playback testing fast enough for daily use.
75
+ - Keep the path from intent to valid DP-1 output deterministic and inspectable.
76
+ - Preserve openness by relying on DP-1 as the compatibility layer instead of a CLI-specific format.
77
+ - Support FF1 as the reference playback target without making correctness depend on proprietary-only infrastructure.
78
+ - Serve as a reference surface for publish, verify, and play flows used elsewhere in the Feral File stack.
79
+
80
+ ### Current behavior
81
+
82
+ The current implementation still depends on Feral File-operated infrastructure for some data retrieval paths, including the hardcoded production indexer endpoint used for NFT lookup.
83
+
84
+ ### Design direction
85
+
86
+ Long-term interoperability should continue to reduce hard infrastructure coupling where practical, especially when a dependency blocks portability without adding protocol value.
87
+
88
+ ## Non-goals
89
+
90
+ `ff-cli` should not become:
91
+
92
+ - the source of truth for exhibitions, channels, or artwork metadata
93
+ - the source of truth for ownership, passkeys, rights, or trust registry state
94
+ - a replacement for the mobile app as the primary user-facing controller
95
+ - a place to define new DP-1 protocol semantics by convenience
96
+ - a long-running backend service with hidden state
97
+
98
+ ## Current system responsibilities
99
+
100
+ Based on the code today, the CLI is responsible for:
101
+
102
+ - loading configuration from `config.json`, `.env`, and defaults, with `config.json` taking precedence
103
+ - parsing natural-language requests into structured playlist requirements and settings
104
+ - orchestrating tool calls for feed fetches, address queries, contract-based NFT queries, domain resolution, playlist building, verification, publishing, and sending
105
+ - supporting a deterministic non-AI build path from structured JSON
106
+ - building DP-1 playlist envelopes from NFT metadata or direct media URLs
107
+ - validating and verifying playlist structure and signatures
108
+ - signing playlists when a private key is configured
109
+ - publishing validated playlists to configured feed servers
110
+ - discovering configured FF1 devices and sending playlists or direct media playback requests
111
+ - performing FF1 OS compatibility preflight checks before display and SSH flows
112
+
113
+ ## Architecture boundaries
114
+
115
+ ### What the CLI owns
116
+
117
+ - command-line UX and command routing
118
+ - local config loading and validation
119
+ - intent parsing and orchestration glue
120
+ - deterministic playlist assembly, verification, and signing helpers
121
+ - device and feed integration calls from the client side
122
+
123
+ ### What the CLI depends on but does not own
124
+
125
+ - DP-1 protocol shape and evolution
126
+ - feed server behavior and data persistence
127
+ - FF1 runtime and OS behavior
128
+ - ownership and identity systems
129
+ - trust-path policy, licensing policy, and key registry policy
130
+
131
+ ### Boundary rules
132
+
133
+ - The CLI may assemble, validate, and transmit DP-1 objects, but it should not silently fork the protocol.
134
+ - The CLI may call feed and device endpoints, but it should not become their compatibility abstraction layer of last resort.
135
+ - The CLI may use models for orchestration, but deterministic utilities remain the source of truth for output correctness.
136
+ - Trust-sensitive correctness must stay vendor-neutral and portable. The CLI can use cloud APIs for model orchestration, but the trust path cannot depend on cloud-specific guarantees.
137
+ - Current implementation note: some retrieval paths still use Feral File-operated services directly, so portability here is an intended direction rather than a fully achieved property.
138
+
139
+ ## Functional shape
140
+
141
+ Today the CLI groups into these workflow areas:
142
+
143
+ ### Setup and configuration
144
+
145
+ - `setup`
146
+ - `status`
147
+ - `config init|show|validate`
148
+
149
+ ### Build and orchestration
150
+
151
+ - `chat`
152
+ - `build`
153
+
154
+ ### DP-1 output integrity
155
+
156
+ - `verify`
157
+ - `validate`
158
+ - `sign`
159
+
160
+ ### Delivery
161
+
162
+ - `play` (handles playlist files, playlist URLs, and media URLs)
163
+ - `publish`
164
+
165
+ ### Device operations
166
+
167
+ - `ssh enable|disable`
168
+
169
+ ## Deterministic-first behavior
170
+
171
+ The CLI supports model-assisted workflows, but the implementation posture should remain deterministic-first:
172
+
173
+ - models interpret intent
174
+ - utilities perform the real data fetching, playlist building, validation, signing, and delivery work
175
+ - invalid or malformed outputs should fail validation rather than being accepted because they were model-produced
176
+
177
+ ## Trust, protocol, and rights assumptions
178
+
179
+ Important constraints from the broader FF system:
180
+
181
+ - DP-1 should evolve additively and remain forward-compatible where practical.
182
+ - Trust-path correctness must remain portable and key-controlled.
183
+ - Ownership and stewardship should not be confused with access gating in the CLI.
184
+ - The CLI may surface signatures, verification, and publishing, but it should not absorb licensing or identity policy that belongs elsewhere in the system.
185
+
186
+ ## Reliability expectations
187
+
188
+ ### Current behavior
189
+
190
+ - reliability matters more than novelty
191
+ - the publish-to-play path should stay simple and testable
192
+ - the CLI should help prove the path from canonical JSON to FF1 playback
193
+ - compatibility checks fail clearly when incompatibility is confirmed
194
+ - if FF1 OS version cannot be determined during preflight, the CLI currently proceeds and logs a warning instead of hard-blocking the command
195
+
196
+ ### Design direction
197
+
198
+ - reliability matters more than novelty
199
+ - the publish-to-play path should stay simple and testable
200
+ - the CLI should help prove the path from canonical JSON to FF1 playback
201
+ - compatibility messaging should remain explicit enough that operators can distinguish confirmed incompatibility from uncertain device state
202
+
203
+ ## Code and design constraints
204
+
205
+ - behavior changes should follow a spec-driven, test-first workflow when practical
206
+ - TypeScript is preferred for new or updated source
207
+ - comments should preserve durable maintenance context when the code encodes non-obvious design choices, trade-offs, invariants, or external constraints
208
+ - docs should be updated when user-facing behavior changes
209
+ - legacy compatibility paths should not be preserved unless explicitly required
210
+
211
+ ## Verification expectations
212
+
213
+ The current repo verification path is:
214
+
215
+ ```bash
216
+ npm run lint:fix
217
+ npm test
218
+ npm run build
219
+ ANTHROPIC_API_KEY=dummy node dist/index.js validate examples/sample-playlist.json
220
+ ANTHROPIC_API_KEY=dummy node dist/index.js config validate
221
+ ```
222
+
223
+ ## Open questions
224
+
225
+ - Which CLI commands are considered stable public interface versus internal reference tooling?
226
+ - How much of the feed and trust workflow should remain directly exposed in the CLI?
227
+ - Which FF1 operations deserve stronger compatibility policies or broader smoke coverage?
228
+ - How much of the mobile app's long-term control model should also be mirrored in CLI form?
package/docs/README.md ADDED
@@ -0,0 +1,348 @@
1
+ # ff-cli Documentation
2
+
3
+ Build DP-1 (Display Protocol 1) playlists from NFT data with either natural language (AI‑driven) or deterministic parameters. This doc covers install, config, and day‑to‑day usage.
4
+
5
+ For project-level planning and future agentic work, see `./PROJECT_SPEC.md`.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm i -g @feralfile/cli
11
+ ```
12
+
13
+ `npm` and `npx` require **Node.js 22 or newer** (see `package.json` `engines`). When a release raises the Node floor, that is a **breaking** change for Node 18/20 users; the GitHub Release for that version should say so explicitly (see `./RELEASING.md` for maintainer guidance).
14
+
15
+ ## Install (curl)
16
+
17
+ ```bash
18
+ curl -fsSL https://feralfile.com/ff-cli-install | bash
19
+ ```
20
+
21
+ Installs a prebuilt binary for macOS/Linux (no Node.js required).
22
+
23
+ ## Configure
24
+
25
+ ```bash
26
+ # Guided setup (recommended)
27
+ ff-cli setup
28
+ ```
29
+
30
+ See the full configuration reference here: `./CONFIGURATION.md`.
31
+
32
+ During setup, you can pick FF1 devices to add. Use `ff-cli device add` to add more devices later, and `ff-cli device list` to see what's configured. The first device is the default for `play` commands (override with `-d`).
33
+
34
+ Manual config path:
35
+
36
+ ```bash
37
+ ff-cli config init
38
+ ff-cli config validate
39
+ ```
40
+
41
+ ### config.json structure (minimal)
42
+
43
+ ```json
44
+ {
45
+ "defaultModel": "claude",
46
+ "models": {
47
+ "claude": {
48
+ "apiKey": "sk-ant-your-api-key-here",
49
+ "baseURL": "https://api.anthropic.com/v1/",
50
+ "model": "claude-sonnet-4-6",
51
+ "supportsFunctionCalling": true
52
+ },
53
+ "grok": {
54
+ "apiKey": "xai-your-api-key-here",
55
+ "baseURL": "https://api.x.ai/v1",
56
+ "model": "grok-beta",
57
+ "supportsFunctionCalling": true
58
+ },
59
+ "gpt": {
60
+ "apiKey": "sk-your-openai-key-here",
61
+ "baseURL": "https://api.openai.com/v1",
62
+ "model": "gpt-4o",
63
+ "supportsFunctionCalling": true
64
+ },
65
+ "gemini": {
66
+ "apiKey": "your-gemini-key-here",
67
+ "baseURL": "https://generativelanguage.googleapis.com/v1beta/openai/",
68
+ "model": "gemini-2.5-flash",
69
+ "supportsFunctionCalling": true
70
+ }
71
+ },
72
+ "defaultDuration": 10,
73
+ "playlist": {
74
+ "privateKey": "your_ed25519_private_key_hex_or_base64_here"
75
+ },
76
+ "feed": { "baseURLs": ["https://dp1-feed-operator-api-prod.autonomy-system.workers.dev/api/v1"] },
77
+ "ff1Devices": {
78
+ "devices": [
79
+ {
80
+ "name": "Living Room Display",
81
+ "host": "http://192.168.1.100:1111"
82
+ }
83
+ ]
84
+ }
85
+ }
86
+ ```
87
+
88
+ ### Environment variables (optional)
89
+
90
+ See `./CONFIGURATION.md` for environment variable mappings.
91
+
92
+ ## Quick Start
93
+
94
+ ```bash
95
+ # Chat
96
+ ff-cli chat
97
+
98
+ # Or natural language in one shot
99
+ ff-cli chat "Get 3 works from reas.eth" -o playlist.json
100
+
101
+ # Deterministic (no AI)
102
+ ff-cli build examples/params-example.json -o playlist.json
103
+ ```
104
+
105
+ For development in this repo:
106
+
107
+ ```bash
108
+ npm run build
109
+ node dist/index.js chat
110
+ ```
111
+
112
+ If you're running from source without a build, use:
113
+
114
+ ```bash
115
+ npm run dev -- chat
116
+ ```
117
+
118
+ ## Recommended Deterministic Flow (LLM + Tools)
119
+
120
+ The model orchestrates; deterministic tools keep us honest and DP1‑conformant.
121
+
122
+ 1. Input: Share the essentials (contract + token IDs, or feed/URL names).
123
+ 2. Orchestration: The LLM parses your prompt and calls tools that:
124
+ - Fetch NFT metadata via OSS libs (`viem` for Ethereum, `@taquito/taquito` for Tezos)
125
+ - Validate DP1 schema with `dp1-js`
126
+ - Build a DP1 playlist envelope deterministically
127
+ - Optionally sign with Ed25519 (canonical JSON via `dp1-js`)
128
+ 3. Preview/send: Send to an FF1 on your LAN over HTTP (recommended). Point `ff1Devices.devices[].host` at a local relay if needed.
129
+ 4. Publish: Optional feed/registry publishing via the `publish` command.
130
+
131
+ Notes:
132
+
133
+ - **Deterministic by design**: Validation rejects bad or hallucinated data; we loop until it's valid or stop.
134
+ - **OSS‑first**: `viem` and `@taquito/taquito`, with room for local caching.
135
+ - **Relay**: Swap the example host for a local Node/Hono relay; avoid vendor lock‑in.
136
+
137
+ ## Commands (cheat sheet)
138
+
139
+ - `chat [content]` – AI-driven natural language playlists
140
+ - Options: `-o, --output <file>`, `-m, --model <name>`, `-d, --device <name>`, `-v, --verbose`
141
+ - `build [params.json]` – Deterministic build from JSON or stdin
142
+ - Options: `-o, --output <file>`, `-v, --verbose`
143
+ - `validate <file-or-url>` – Validate playlist structure only
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
+ - `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
+ - 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)
150
+ - Options: `-s, --server <index>` (server index if multiple configured)
151
+ - `ssh <enable|disable>` – Manage SSH access on an FF1 device
152
+ - Options: `-d, --device <name>`, `--pubkey <path>`, `--ttl <duration>`
153
+ - `device list` – List all configured FF1 devices
154
+ - `device add` – Add a new FF1 device (with mDNS discovery)
155
+ - Options: `--host <host>`, `--name <name>`
156
+ - `device remove <name>` – Remove a configured FF1 device
157
+ - `device default <name>` – Set the default FF1 device (used when `-d` is omitted)
158
+ - `config <init|show|validate>` – Manage configuration
159
+
160
+ ## Usage Highlights
161
+
162
+ ### Natural language (AI)
163
+
164
+ ```bash
165
+ npm run dev -- chat "Get 3 works from reas.eth"
166
+ npm run dev -- chat "Get 3 works from einstein-rosen.tez"
167
+ npm run dev -- chat "Get tokens 52932,52457 from Ethereum contract 0xb932a70A57673d89f4acfFBE830E8ed7f75Fb9e0" -o playlist.json
168
+ ```
169
+
170
+ Feed playlists (for example `Unsupervised`, `Social Codes`) depend on your configured feed servers and network reachability.
171
+ Use exact or near-exact playlist titles for best results.
172
+
173
+ If you prompt with a bare EVM address (for example `from 0x...`), the CLI now tries owner-address lookup first, then automatically falls back to contract lookup when no owned tokens are found.
174
+
175
+ ### One-shot complex prompt
176
+
177
+ The model reads your request via the intent parser and turns it into structured `requirements` and `playlistSettings` (including shuffle, durations, and device). You can do everything in one line:
178
+
179
+ ```bash
180
+ # Mix sources, shuffle order, set per-item duration, and send to a named device
181
+ npm run dev -- chat "From Ethereum contract 0xb932a70A57673d89f4acfFBE830E8ed7f75Fb9e0 get tokens 52932,52457 and from reas.eth get 2 works; shuffle the order; 7 seconds per item; send to device 'Living Room Display'." -o playlist.json -v
182
+ ```
183
+
184
+ How it works (at a glance):
185
+
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.
189
+ - If `feedServer` is present (via "publish to my feed"), the CLI will publish the playlist to the selected feed server.
190
+
191
+ Use `--model claude|grok|gpt|gemini` to switch models, or set `defaultModel` in `config.json`.
192
+
193
+ ### Natural language publishing
194
+
195
+ The intent parser recognizes publishing keywords and can both display and publish in one command:
196
+
197
+ ```bash
198
+ # Build and publish
199
+ npm run dev -- chat "Build playlist from Ethereum contract 0xb932a70A57673d89f4acfFBE830E8ed7f75Fb9e0 with tokens 52932 and 52457; publish to my feed" -o playlist.json -v
200
+
201
+ # Display on Art Computer AND publish to feed
202
+ npm run dev -- chat "Get 3 from Unsupervised; shuffle; send to my Art Computer and publish to feed" -o playlist.json -v
203
+ ```
204
+
205
+ For Feral File built playlists, you can reference titles listed in the repository:
206
+ `https://github.com/feral-file/dp1-feed/tree/main/playlists`
207
+
208
+ Example:
209
+
210
+ ```bash
211
+ npm run dev -- chat "Get 3 from Unsupervised"
212
+ ```
213
+
214
+ Publishing keywords: "publish", "publish to my feed", "push to feed", "send to feed". The CLI will:
215
+
216
+ 1. Detect the keyword and call `get_feed_servers`
217
+ 2. If multiple servers → ask which one to use
218
+ 3. Build → validate (structure) → publish automatically
219
+ 4. Display playlist ID and server URL on success
220
+
221
+ If all configured feed servers are unreachable, the CLI now reports a feed availability error instead of "playlist not found".
222
+
223
+ ### Deterministic (no AI)
224
+
225
+ ```bash
226
+ npm run dev -- build params.json -o playlist.json
227
+ cat params.json | npm run dev -- build -o playlist.json
228
+ ```
229
+
230
+ `params.json` should include `requirements` and optional `playlistSettings`. See `examples/params-example.json`.
231
+
232
+ ### Validate, sign, and play
233
+
234
+ ```bash
235
+ # Optional explicit validation (build flows already validate)
236
+ npm run dev -- validate playlist.json
237
+
238
+ # Sign (uses key and role from config, or overrides via --key / --role)
239
+ npm run dev -- sign playlist.json -o signed.json
240
+
241
+ # Play on configured default device (validates DP-1 structure by default)
242
+ npm run dev -- play playlist.json
243
+
244
+ # Play on a specific named device
245
+ npm run dev -- play playlist.json -d "Living Room Display"
246
+
247
+ # The play path performs a compatibility preflight check against the target FF1.
248
+ # If the device reports an unsupported FF1 OS version, the command fails with
249
+ # a clear version message before any cast request is sent.
250
+ # It also retries transient local-network errors (for example intermittent
251
+ # mDNS/Wi-Fi resolver failures) with a short backoff before returning a final error.
252
+
253
+ # Play a hosted DP-1 playlist
254
+ npm run dev -- play "https://cdn.example.com/playlist.json"
255
+
256
+ # Play a media URL directly
257
+ npm run dev -- play "https://example.com/video.mp4"
258
+
259
+ # Skip structure validation only if you must send a non-conformant payload (not recommended)
260
+ npm run dev -- play playlist.json --skip-verify
261
+ ```
262
+
263
+ ### SSH access
264
+
265
+ ```bash
266
+ # Enable SSH access for 30 minutes
267
+ ff-cli ssh enable --pubkey ~/.ssh/id_ed25519.pub --ttl 30m -d "Living Room Display"
268
+
269
+ # Disable SSH access
270
+ ff-cli ssh disable -d "Living Room Display"
271
+
272
+ # `ff-cli ssh` also performs the same FF1 OS compatibility preflight used by `play`.
273
+ ```
274
+
275
+ ### Publish to feed server
276
+
277
+ ```bash
278
+ # Publish to first configured feed server
279
+ npm run dev -- publish playlist.json
280
+
281
+ # Publish to specific server (if multiple configured)
282
+ npm run dev -- publish playlist.json -s 0
283
+ npm run dev -- publish playlist.json -s 1
284
+ ```
285
+
286
+ The `publish` command:
287
+
288
+ - Validates playlist structure (same as `validate`; does not verify signatures)
289
+ - Shows interactive server selection if multiple are configured
290
+ - Sends the validated playlist to the chosen feed server
291
+ - Returns the playlist ID on success
292
+
293
+ Configure feed servers in `config.json`:
294
+
295
+ ```json
296
+ {
297
+ "feedServers": [
298
+ {
299
+ "baseUrl": "http://localhost:8787/api/v1",
300
+ "apiKey": "your-api-key-optional"
301
+ },
302
+ {
303
+ "baseUrl": "https://feed.example.com/api/v1",
304
+ "apiKey": "your-api-key-optional"
305
+ }
306
+ ]
307
+ }
308
+ ```
309
+
310
+ ### FF1 device management
311
+
312
+ ```bash
313
+ # List configured devices
314
+ ff-cli device list
315
+
316
+ # Add a device (interactive with mDNS discovery)
317
+ ff-cli device add
318
+
319
+ # Add a device non-interactively
320
+ ff-cli device add --host 192.168.1.100 --name kitchen
321
+
322
+ # Remove a device by name
323
+ ff-cli device remove kitchen
324
+
325
+ # Set the default device (used when -d is omitted)
326
+ ff-cli device default office
327
+ ```
328
+
329
+ Setup preserves existing devices when adding new ones. See selection rules and examples in `./CONFIGURATION.md`.
330
+
331
+ ### Playlist signing (optional)
332
+
333
+ - Add `playlist.privateKey` (Ed25519 PKCS#8 DER as **hex** or **base64**, per `./CONFIGURATION.md`) and, optionally, `playlist.role` to `config.json`, or set `PLAYLIST_PRIVATE_KEY` and `PLAYLIST_ROLE`.
334
+ - The CLI passes that string to **`dp1-js`** for signing; the dependency decodes hex (`0x` optional) or base64 before loading the key.
335
+ - Signed playlists include a `signatures[]` envelope compliant with DP-1 v1.1.0 (via **`dp1-js`**).
336
+
337
+ ## Constraints
338
+
339
+ - Max 20 items total across all requirements
340
+ - Per-source caps enforced in utilities
341
+ - Duration per item defaults to 10s (configurable)
342
+
343
+ ## Links
344
+
345
+ - Function calling details: `./FUNCTION_CALLING.md`
346
+ - Examples: `./EXAMPLES.md`
347
+ - Release assets: `./RELEASING.md`
348
+ - DP1 spec: `https://github.com/display-protocol/dp1`
@@ -0,0 +1,73 @@
1
+ # Releasing Binary Assets
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.
4
+
5
+ ## Build a Release Asset (local)
6
+
7
+ **macOS / Linux:**
8
+
9
+ ```bash
10
+ ./scripts/release/build-asset.sh
11
+ ```
12
+
13
+ **Windows (PowerShell):**
14
+
15
+ ```powershell
16
+ .\scripts\release\build-asset-windows.ps1
17
+ ```
18
+
19
+ This produces (names vary by OS/arch):
20
+
21
+ - `release/ff-cli-darwin-arm64.tar.gz` (and `.sha256`) on macOS
22
+ - `release/ff-cli-linux-x64.tar.gz` (and `.sha256`) on Linux
23
+ - `release/ff-cli-windows-x64.zip` (and `.sha256`) on Windows
24
+
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
+ - Set `NPM_TOKEN` in GitHub Actions secrets with an npm automation token.
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
+ ## Release notes and breaking changes
42
+
43
+ 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.
44
+
45
+ ### Node.js engine floor (breaking)
46
+
47
+ `package.json` declares `"engines": { "node": ">=22" }`. Raising the floor from Node 18 (or 20) is a **breaking change** for:
48
+
49
+ - global installs and `npx @feralfile/cli` on older runtimes
50
+ - CI jobs and images pinned to Node 18 or 20
51
+ - anyone developing from source without upgrading Node
52
+
53
+ **For the release that first ships this requirement**, copy or adapt the following into the GitHub Release description (and repeat in the upgrade section of internal comms if needed):
54
+
55
+ > **Breaking — Node.js:** ff-cli now requires **Node.js 22 or newer** (`package.json` `engines`). Node 18 and Node 20 are no longer supported. Upgrade Node on your machines and in CI, or stay on an older ff-cli version until you can migrate.
56
+
57
+ Later releases only need to repeat this block if the engine floor changes again.
58
+
59
+ ## Installer Redirect
60
+
61
+ `https://feralfile.com/ff-cli-install` should redirect to:
62
+
63
+ ```
64
+ https://raw.githubusercontent.com/feral-file/ff-cli/main/scripts/install.sh
65
+ ```
66
+
67
+ The installer script then fetches the release assets from GitHub Releases.
68
+
69
+ ## Environment Overrides
70
+
71
+ - `FF_CLI_VERSION`: overrides the version label in logs
72
+ - `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
+ - `FF_CLI_OUTPUT_DIR`: output directory (default: `./release`)