@feralfile/cli 2.2.0 → 2.2.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 CHANGED
@@ -119,7 +119,6 @@ npm run dev -- setup
119
119
  npm run dev -- find https://objkt.com/tokens/hicetnunc/111068 --play
120
120
  npm run dev -- play "https://example.com/video.mp4" --skip-verify
121
121
  ```
122
-
123
122
  ## Documentation
124
123
 
125
124
  - Getting started and usage: `./docs/README.md`
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feralfile/cli",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
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",
@@ -59,7 +59,7 @@
59
59
  "license": "MIT",
60
60
  "dependencies": {
61
61
  "@napi-rs/keyring": "1.3.0",
62
- "@feralfile/source-resolver": "git+https://github.com/feral-file/ff-source-resolver.git#54be943014c8cf7860114510257cdd713bbf4e79",
62
+ "@feralfile/source-resolver": "^1.0.0",
63
63
  "axios": "^1.6.2",
64
64
  "bonjour-service": "^1.3.0",
65
65
  "chalk": "^4.1.2",
@@ -207,8 +207,10 @@ deviceCommand
207
207
  // existingIndex === -1 (no confirmed match, e.g. manual IP → .local migration),
208
208
  // a same-name entry is the upsertDevice case-3 migration path — blocking it would
209
209
  // prevent the user from retaining their existing device name during host migration.
210
+ // Case-insensitive: lookups everywhere else ignore case, so "Kitchen"
211
+ // alongside "kitchen" would make one of them unreachable by name.
210
212
  const nameConflict = existingIndex !== -1
211
- ? existingDevices.find((d, i) => d.name === deviceName && i !== existingIndex)
213
+ ? existingDevices.find((d, i) => d.name?.toLowerCase() === deviceName.toLowerCase() && i !== existingIndex)
212
214
  : undefined;
213
215
  if (nameConflict) {
214
216
  if (options.name) {
@@ -221,7 +223,7 @@ deviceCommand
221
223
  const retryAnswer = await ask('Device name: ');
222
224
  deviceName = retryAnswer || 'ff1';
223
225
  const retryConflict = existingIndex !== -1
224
- ? existingDevices.find((d, i) => d.name === deviceName && i !== existingIndex)
226
+ ? existingDevices.find((d, i) => d.name?.toLowerCase() === deviceName.toLowerCase() && i !== existingIndex)
225
227
  : undefined;
226
228
  if (retryConflict) {
227
229
  console.error(chalk_1.default.red(`\nName "${deviceName}" is also taken. No changes made.`));
@@ -255,21 +257,7 @@ deviceCommand
255
257
  }
256
258
  const config = await (0, config_files_1.readConfigFile)(configPath);
257
259
  const existingDevices = config.ff1Devices?.devices || [];
258
- // Match by name (case-insensitive) or by host URL so unnamed legacy/manual
259
- // entries (stored without a name field) can still be targeted and removed.
260
- const normalizedArg = name.toLowerCase();
261
- let normalizedArgHost = '';
262
- try {
263
- normalizedArgHost = (0, device_normalize_1.normalizeDeviceHost)(name).toLowerCase();
264
- }
265
- catch {
266
- // not a valid URL — host matching will not apply
267
- }
268
- const deviceIndex = existingDevices.findIndex((d) => (d.name && d.name.toLowerCase() === normalizedArg) ||
269
- (d.host && d.host.toLowerCase() === normalizedArg) ||
270
- (normalizedArgHost &&
271
- d.host &&
272
- (0, device_normalize_1.normalizeDeviceHost)(d.host).toLowerCase() === normalizedArgHost));
260
+ const deviceIndex = (0, device_lookup_1.findConfiguredDeviceIndex)(existingDevices, name);
273
261
  if (deviceIndex === -1) {
274
262
  console.error(chalk_1.default.red(`\nDevice "${name}" not found`));
275
263
  if (existingDevices.length > 0) {
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.promoteDeviceToDefault = promoteDeviceToDefault;
4
- const device_normalize_1 = require("./device-normalize");
4
+ const device_lookup_1 = require("./device-lookup");
5
5
  /**
6
6
  * Move the named device to index 0 so it becomes the implicit default.
7
7
  *
@@ -11,19 +11,7 @@ const device_normalize_1 = require("./device-normalize");
11
11
  * @throws {Error} When no device matches the identifier
12
12
  */
13
13
  function promoteDeviceToDefault(devices, identifier) {
14
- const normalizedArg = identifier.toLowerCase();
15
- let normalizedArgHost = '';
16
- try {
17
- normalizedArgHost = (0, device_normalize_1.normalizeDeviceHost)(identifier).toLowerCase();
18
- }
19
- catch {
20
- // not a valid URL — host matching will not apply
21
- }
22
- const index = devices.findIndex((d) => (d.name && d.name.toLowerCase() === normalizedArg) ||
23
- (d.host && d.host.toLowerCase() === normalizedArg) ||
24
- (normalizedArgHost &&
25
- d.host &&
26
- (0, device_normalize_1.normalizeDeviceHost)(d.host).toLowerCase() === normalizedArgHost));
14
+ const index = (0, device_lookup_1.findConfiguredDeviceIndex)(devices, identifier);
27
15
  if (index === -1) {
28
16
  throw new Error(`Device "${identifier}" not found`);
29
17
  }
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.findExistingDeviceEntry = findExistingDeviceEntry;
4
+ exports.findConfiguredDeviceIndex = findConfiguredDeviceIndex;
5
+ const device_normalize_1 = require("./device-normalize");
4
6
  /**
5
7
  * Find an existing configured device that corresponds to a newly discovered host.
6
8
  *
@@ -106,3 +108,29 @@ function findExistingDeviceEntry(existingDevices, newHost, discoveredName, disco
106
108
  }
107
109
  return undefined;
108
110
  }
111
+ /**
112
+ * Find the index of a configured device by a user-supplied identifier.
113
+ *
114
+ * Matches by friendly name (case-insensitive) or by host URL — raw or
115
+ * normalized — so unnamed legacy/manual entries (stored without a name field)
116
+ * can still be targeted. Shared by `device remove`, `device default`,
117
+ * `device rename`, and runtime `-d` resolution so every surface answers to
118
+ * the same identifier the same way.
119
+ *
120
+ * Returns -1 when nothing matches.
121
+ */
122
+ function findConfiguredDeviceIndex(devices, identifier) {
123
+ const normalizedArg = identifier.toLowerCase();
124
+ let normalizedArgHost = '';
125
+ try {
126
+ normalizedArgHost = (0, device_normalize_1.normalizeDeviceHost)(identifier).toLowerCase();
127
+ }
128
+ catch {
129
+ // not a valid URL — host matching will not apply
130
+ }
131
+ return devices.findIndex((d) => (d.name && d.name.toLowerCase() === normalizedArg) ||
132
+ (d.host && d.host.toLowerCase() === normalizedArg) ||
133
+ (normalizedArgHost &&
134
+ d.host &&
135
+ (0, device_normalize_1.normalizeDeviceHost)(d.host).toLowerCase() === normalizedArgHost));
136
+ }
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.renameDevice = renameDevice;
4
- const device_normalize_1 = require("./device-normalize");
4
+ const device_lookup_1 = require("./device-lookup");
5
5
  /**
6
6
  * Rename a configured device without touching host, apiKey, topicID, or list
7
7
  * position — a pure config edit that needs no discovery or network access
@@ -20,19 +20,7 @@ function renameDevice(devices, identifier, newName) {
20
20
  if (!trimmedName) {
21
21
  throw new Error('New device name must not be empty');
22
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));
23
+ const index = (0, device_lookup_1.findConfiguredDeviceIndex)(devices, identifier);
36
24
  if (index === -1) {
37
25
  throw new Error(`Device "${identifier}" not found`);
38
26
  }
@@ -39,6 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.resolveConfiguredDevice = resolveConfiguredDevice;
40
40
  exports.assertFF1CommandCompatibility = assertFF1CommandCompatibility;
41
41
  const config_1 = require("../config");
42
+ const device_lookup_1 = require("./device-lookup");
42
43
  const logger = __importStar(require("../logger"));
43
44
  const FF1_COMMAND_POLICIES = {
44
45
  displayPlaylist: {
@@ -51,7 +52,8 @@ const FF1_COMMAND_POLICIES = {
51
52
  /**
52
53
  * Load and validate the configured FF1 device selected by name.
53
54
  *
54
- * @param {string} [deviceName] - Optional device name, exact match required
55
+ * @param {string} [deviceName] - Optional device name (case-insensitive) or host URL,
56
+ * resolved with the same matching rules as `device remove`/`default`/`rename`
55
57
  * @param {Object} [options] - Optional dependency overrides
56
58
  * @param {Function} [options.getFF1DeviceConfigFn] - Optional config loader override
57
59
  * @returns {FF1DeviceSelectionResult} Selected device or reason for failure
@@ -70,7 +72,8 @@ function resolveConfiguredDevice(deviceName, options = {}) {
70
72
  }
71
73
  let device = deviceConfig.devices[0];
72
74
  if (deviceName) {
73
- device = deviceConfig.devices.find((item) => item.name === deviceName);
75
+ const index = (0, device_lookup_1.findConfiguredDeviceIndex)(deviceConfig.devices, deviceName);
76
+ device = index === -1 ? undefined : deviceConfig.devices[index];
74
77
  if (!device) {
75
78
  const availableNames = deviceConfig.devices
76
79
  .map((item) => item.name)
@@ -151,7 +151,7 @@ Setup and `device add` both preserve existing devices. Adding a device with the
151
151
  Selection rules when sending:
152
152
 
153
153
  - If you omit `-d`, the first configured device is used.
154
- - 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.
154
+ - If you pass `-d <name>`, the CLI matches the device by `name` (case-insensitive) or by host URL — the same rules as `device remove`/`default`/`rename`, so unnamed entries can be targeted by host. If not found, you’ll see an error listing available devices.
155
155
 
156
156
  Device cast contract:
157
157
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feralfile/cli",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
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",
@@ -59,7 +59,7 @@
59
59
  "license": "MIT",
60
60
  "dependencies": {
61
61
  "@napi-rs/keyring": "1.3.0",
62
- "@feralfile/source-resolver": "git+https://github.com/feral-file/ff-source-resolver.git#54be943014c8cf7860114510257cdd713bbf4e79",
62
+ "@feralfile/source-resolver": "^1.0.0",
63
63
  "axios": "^1.6.2",
64
64
  "bonjour-service": "^1.3.0",
65
65
  "chalk": "^4.1.2",