@feralfile/cli 2.1.0 → 2.2.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/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feralfile/cli",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "CLI for building, signing, and playing DP-1 playlists of digital art on Feral File devices",
5
5
  "repository": {
6
6
  "type": "git",
@@ -11,6 +11,7 @@ const device_lookup_1 = require("../utilities/device-lookup");
11
11
  const device_normalize_1 = require("../utilities/device-normalize");
12
12
  const device_upsert_1 = require("../utilities/device-upsert");
13
13
  const device_default_1 = require("../utilities/device-default");
14
+ const device_rename_1 = require("../utilities/device-rename");
14
15
  const config_files_1 = require("./helpers/config-files");
15
16
  const device_discovery_1 = require("./helpers/device-discovery");
16
17
  const prompt_1 = require("./helpers/prompt");
@@ -291,6 +292,50 @@ deviceCommand
291
292
  process.exit(1);
292
293
  }
293
294
  });
295
+ deviceCommand
296
+ .command('rename')
297
+ .description('Rename a configured FF1 device (host, API key, and default status are untouched)')
298
+ .argument('<name>', 'Current device name or host')
299
+ .argument('<new-name>', 'New device name')
300
+ .action(async (name, newName) => {
301
+ try {
302
+ const configPath = await (0, config_files_1.resolveExistingConfigPath)();
303
+ if (!configPath) {
304
+ console.log(chalk_1.default.red('config.json not found'));
305
+ console.log(chalk_1.default.dim('Run: ff-cli setup'));
306
+ process.exit(1);
307
+ }
308
+ const config = await (0, config_files_1.readConfigFile)(configPath);
309
+ const existingDevices = config.ff1Devices?.devices || [];
310
+ if (existingDevices.length === 0) {
311
+ console.log(chalk_1.default.yellow('\nNo devices configured'));
312
+ console.log(chalk_1.default.dim('Run: ff-cli device add\n'));
313
+ process.exit(1);
314
+ }
315
+ let result;
316
+ try {
317
+ result = (0, device_rename_1.renameDevice)(existingDevices, name, newName);
318
+ }
319
+ catch (error) {
320
+ console.error(chalk_1.default.red(`\n${error.message}`));
321
+ const names = existingDevices.map((d) => d.name || d.host).join(', ');
322
+ console.log(chalk_1.default.dim(`Available devices: ${names}\n`));
323
+ process.exit(1);
324
+ }
325
+ if (result.unchanged) {
326
+ console.log(chalk_1.default.dim(`\n"${result.renamed.name}" is already named that.\n`));
327
+ return;
328
+ }
329
+ config.ff1Devices = { devices: result.devices };
330
+ await fs_1.promises.writeFile(configPath, `${JSON.stringify(config, null, 2)}\n`, 'utf-8');
331
+ const from = result.previousName || result.renamed.host;
332
+ console.log(chalk_1.default.green(`\nRenamed device: ${from} → ${result.renamed.name}\n`));
333
+ }
334
+ catch (error) {
335
+ console.error(chalk_1.default.red('\nError:'), error.message);
336
+ process.exit(1);
337
+ }
338
+ });
294
339
  deviceCommand
295
340
  .command('default')
296
341
  .description('Set the default FF1 device (reorders so this device is used when -d is omitted)')
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renameDevice = renameDevice;
4
+ const device_normalize_1 = require("./device-normalize");
5
+ /**
6
+ * Rename a configured device without touching host, apiKey, topicID, or list
7
+ * position — a pure config edit that needs no discovery or network access
8
+ * (unlike `device add`, whose update path requires the host or mDNS).
9
+ *
10
+ * Matches by name (case-insensitive) or by host URL, mirroring `device remove`
11
+ * so unnamed legacy entries can still be targeted (renaming gives them a name
12
+ * for the first time).
13
+ *
14
+ * @throws {Error} When no device matches the identifier, the new name is
15
+ * empty, or the new name is already used by a different device
16
+ * (case-insensitive, so lookups elsewhere stay unambiguous).
17
+ */
18
+ function renameDevice(devices, identifier, newName) {
19
+ const trimmedName = newName.trim();
20
+ if (!trimmedName) {
21
+ throw new Error('New device name must not be empty');
22
+ }
23
+ const normalizedArg = identifier.toLowerCase();
24
+ let normalizedArgHost = '';
25
+ try {
26
+ normalizedArgHost = (0, device_normalize_1.normalizeDeviceHost)(identifier).toLowerCase();
27
+ }
28
+ catch {
29
+ // not a valid URL — host matching will not apply
30
+ }
31
+ const index = devices.findIndex((d) => (d.name && d.name.toLowerCase() === normalizedArg) ||
32
+ (d.host && d.host.toLowerCase() === normalizedArg) ||
33
+ (normalizedArgHost &&
34
+ d.host &&
35
+ (0, device_normalize_1.normalizeDeviceHost)(d.host).toLowerCase() === normalizedArgHost));
36
+ if (index === -1) {
37
+ throw new Error(`Device "${identifier}" not found`);
38
+ }
39
+ const conflict = devices.find((d, i) => i !== index && d.name && d.name.toLowerCase() === trimmedName.toLowerCase());
40
+ if (conflict) {
41
+ throw new Error(`Device name "${trimmedName}" is already used by another device (${conflict.host})`);
42
+ }
43
+ const target = devices[index];
44
+ if (target.name === trimmedName) {
45
+ return { devices: [...devices], renamed: target, previousName: target.name, unchanged: true };
46
+ }
47
+ const updated = [...devices];
48
+ updated[index] = { ...target, name: trimmedName };
49
+ return { devices: updated, renamed: updated[index], previousName: target.name, unchanged: false };
50
+ }
@@ -139,6 +139,7 @@ You can also manage devices independently with:
139
139
  non-interactively with `--host`, `--name`, and the physical `--id` shown in
140
140
  Feral File mobile. Manual devices need the ID before relayer pairing.
141
141
  - `ff-cli device list` – Show all configured devices.
142
+ - `ff-cli device rename <name> <new-name>` – Rename a device without touching its host, API key, or default status. Works offline; also accepts the host URL as the identifier, so unnamed legacy entries can be given a name.
142
143
  - `ff-cli device remove <name>` – Remove a device by name.
143
144
  - `ff-cli device default <name>` – Promote a device to the top of the list so it is used when `-d` is omitted.
144
145
  - `ff-cli device pair [name]` – Display a one-time handoff code and key-bound
package/docs/EXAMPLES.md CHANGED
@@ -385,6 +385,9 @@ npm run dev -- device add
385
385
  # Add a device non-interactively
386
386
  npm run dev -- device add --host 192.168.1.100 --name kitchen
387
387
 
388
+ # Rename a device (host, API key, and default status are untouched)
389
+ npm run dev -- device rename kitchen gallery
390
+
388
391
  # Remove a device by name
389
392
  npm run dev -- device remove kitchen
390
393
 
package/docs/README.md CHANGED
@@ -120,6 +120,7 @@ Both paths run the same deterministic pipeline: fetch metadata, assemble a DP-1
120
120
  - `device add` – Add a device with mDNS or manual `--host`, `--name`, and `--id`
121
121
  - Options: `--host <host>`, `--name <name>`, `--id <id>`
122
122
  - `device pair [name]` – Securely receive relayer access from Feral File mobile
123
+ - `device rename <name> <new-name>` – Rename a configured FF1 device (host, API key, and default status are untouched)
123
124
  - `device remove <name>` – Remove a configured FF1 device
124
125
  - `device default <name>` – Set the default FF1 device (used when `-d` is omitted)
125
126
  - `config <init|show|validate>` – Manage configuration
@@ -274,6 +275,9 @@ ff-cli device add
274
275
  # Add a device non-interactively
275
276
  ff-cli device add --host 192.168.1.100 --name kitchen --id FF1-XXXXXXXX
276
277
 
278
+ # Rename a device (host, API key, and default status are untouched)
279
+ ff-cli device rename kitchen gallery
280
+
277
281
  # Remove a device by name
278
282
  ff-cli device remove kitchen
279
283
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feralfile/cli",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "CLI for building, signing, and playing DP-1 playlists of digital art on Feral File devices",
5
5
  "repository": {
6
6
  "type": "git",