@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 +0 -1
- package/dist/package.json +2 -2
- package/dist/src/commands/device.js +5 -17
- package/dist/src/utilities/device-default.js +2 -14
- package/dist/src/utilities/device-lookup.js +28 -0
- package/dist/src/utilities/device-rename.js +2 -14
- package/dist/src/utilities/ff1-compatibility.js +5 -2
- package/docs/CONFIGURATION.md +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@feralfile/cli",
|
|
3
|
-
"version": "2.2.
|
|
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": "
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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)
|
package/docs/CONFIGURATION.md
CHANGED
|
@@ -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` (
|
|
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.
|
|
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": "
|
|
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",
|