@feralfile/cli 2.10.1 → 4.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.
Files changed (59) hide show
  1. package/README.md +31 -23
  2. package/config.json.example +6 -1
  3. package/dist/index.js +10 -2
  4. package/dist/package.json +5 -2
  5. package/dist/src/commands/build.js +40 -0
  6. package/dist/src/commands/config.js +1 -0
  7. package/dist/src/commands/device.js +85 -40
  8. package/dist/src/commands/enrich.js +18 -23
  9. package/dist/src/commands/find.js +144 -106
  10. package/dist/src/commands/helpers/device-control.js +937 -0
  11. package/dist/src/commands/helpers/hidden-alias-help.js +37 -0
  12. package/dist/src/commands/helpers/playlist-display.js +2 -15
  13. package/dist/src/commands/helpers/prompt.js +85 -10
  14. package/dist/src/commands/play.js +162 -84
  15. package/dist/src/commands/report.js +251 -0
  16. package/dist/src/commands/setup.js +82 -7
  17. package/dist/src/commands/status.js +4 -1
  18. package/dist/src/config.js +256 -6
  19. package/dist/src/main.js +14 -5
  20. package/dist/src/utilities/control/codec.js +578 -0
  21. package/dist/src/utilities/control/control-errors.js +157 -0
  22. package/dist/src/utilities/control/controller-store.js +964 -0
  23. package/dist/src/utilities/control/jwk.js +141 -0
  24. package/dist/src/utilities/control/pairing-channel.js +52 -0
  25. package/dist/src/utilities/control/pairing.js +416 -0
  26. package/dist/src/utilities/control/registration.js +391 -0
  27. package/dist/src/utilities/control/relay-pairing-channel.js +191 -0
  28. package/dist/src/utilities/control/secure-transport.js +850 -0
  29. package/dist/src/utilities/control/strict-json.js +376 -0
  30. package/dist/src/utilities/device-lookup.js +36 -11
  31. package/dist/src/utilities/device-network.js +129 -0
  32. package/dist/src/utilities/device-upsert.js +1 -1
  33. package/dist/src/utilities/feral-file-artwork.js +2 -2
  34. package/dist/src/utilities/ff1-compatibility.js +87 -11
  35. package/dist/src/utilities/ff1-device.js +161 -78
  36. package/dist/src/utilities/ff1-relayer.js +34 -14
  37. package/dist/src/utilities/handoff-client.js +23 -9
  38. package/dist/src/utilities/index.js +270 -259
  39. package/dist/src/utilities/media-url.js +117 -0
  40. package/dist/src/utilities/neort-marketplace.js +3 -3
  41. package/dist/src/utilities/nft-indexer.js +12 -376
  42. package/dist/src/utilities/playlist-builder.js +11 -2
  43. package/dist/src/utilities/playlist-cast.js +6 -6
  44. package/dist/src/utilities/playlist-source.js +223 -34
  45. package/dist/src/utilities/raster-client.js +101 -14
  46. package/dist/src/utilities/relay-client.js +774 -0
  47. package/dist/src/utilities/ssh-access.js +192 -58
  48. package/dist/src/utilities/support-report.js +511 -0
  49. package/dist/src/utilities/temporary-channel.js +139 -0
  50. package/dist/src/utilities/token-item.js +428 -0
  51. package/dist/src/utilities/token-source-contract.js +55 -0
  52. package/dist/src/utilities/token-source-indexer.js +99 -0
  53. package/dist/src/utilities/token-source-raster.js +378 -0
  54. package/dist/src/utilities/token-source.js +48 -0
  55. package/docs/CONFIGURATION.md +331 -28
  56. package/docs/EXAMPLES.md +33 -33
  57. package/docs/PROJECT_SPEC.md +55 -36
  58. package/docs/README.md +145 -43
  59. package/package.json +5 -2
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ /**
3
+ * User-facing messages for FF1 control protocol v1 failures.
4
+ *
5
+ * Codes come from three places and are mapped here in one table so the three
6
+ * device commands and `device pair` tell the user the same thing:
7
+ * - public errors (§4.4): LAN HTTP 401/403 `{"error":{"code"}}`, or the
8
+ * relayer RPC `message`;
9
+ * - encrypted reply codes (§4.3), only after the reply verified against the
10
+ * pinned devSig;
11
+ * - local conditions (no controller file, reply failed verification).
12
+ *
13
+ * A public error is unauthenticated (anyone on the path can forge one), so
14
+ * the advice it triggers is never destructive: it tells the user what to run,
15
+ * it never deletes keys on its own.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.pairCommand = pairCommand;
19
+ exports.unpairCommand = unpairCommand;
20
+ exports.describeControlCode = describeControlCode;
21
+ exports.replayAfterJumpFailure = replayAfterJumpFailure;
22
+ exports.unverifiedReplyFailure = unverifiedReplyFailure;
23
+ /** PAIRING_CODES are the codes whose fix is `ff-cli device pair` + an owner adding it. */
24
+ const PAIRING_CODES = new Set([
25
+ 'enrollment_required',
26
+ 'envelope_required',
27
+ 'unknown_controller',
28
+ 'access_revoked',
29
+ 'not_approved',
30
+ 'pending',
31
+ ]);
32
+ /**
33
+ * pairCommand renders the pair command for a device label.
34
+ *
35
+ * @param label - Device name or ID
36
+ * @returns Shell-ready command text
37
+ */
38
+ function pairCommand(label) {
39
+ return `ff-cli device pair ${shellArg(label)}`;
40
+ }
41
+ /**
42
+ * unpairCommand renders the unpair command for a device label.
43
+ *
44
+ * @param label - Device name or ID
45
+ * @returns Shell-ready command text
46
+ */
47
+ function unpairCommand(label) {
48
+ return `ff-cli device unpair ${shellArg(label)}`;
49
+ }
50
+ function shellArg(label) {
51
+ return /[\s"'$`\\]/.test(label) ? JSON.stringify(label) : label;
52
+ }
53
+ const HEADLINES = {
54
+ enrollment_required: 'This FF1 is secured and only accepts requests from paired controllers.',
55
+ envelope_required: 'This FF1 is secured and only accepts requests from paired controllers.',
56
+ unknown_controller: 'This FF1 does not know this ff-cli controller.',
57
+ access_revoked: "This ff-cli controller's access to the FF1 was revoked or has expired.",
58
+ not_approved: 'This ff-cli controller has not been approved on the FF1.',
59
+ pending: 'This ff-cli controller is still waiting for approval.',
60
+ permission_denied: 'This command needs owner access, and ff-cli is paired as a controller.',
61
+ bad_envelope: 'The FF1 could not open the request.',
62
+ bad_signature: 'The FF1 rejected the request signature.',
63
+ locked: 'The FF1 control store is locked (its identity or grants could not be read).',
64
+ rate_limited: 'The FF1 is rate-limiting requests.',
65
+ busy: 'The FF1 is busy; try again.',
66
+ expired: 'The FF1 rejected the request as expired.',
67
+ replay: 'The FF1 rejected the request counter as already used.',
68
+ invalid_request: 'The FF1 rejected the request as malformed.',
69
+ internal_error: 'The FF1 could not confirm it recorded the request; a command was not run, but a pairing or controller change may have been applied.',
70
+ invitation_invalid: 'The FF1 rejected the invitation.',
71
+ command_failed: 'The FF1 ran the command, but it failed.',
72
+ };
73
+ /**
74
+ * describeControlCode maps a protocol code to an actionable message.
75
+ *
76
+ * @param code - Public or encrypted error code
77
+ * @param deviceLabel - Device name used in commands
78
+ * @returns The message and follow-up details
79
+ */
80
+ function describeControlCode(code, deviceLabel) {
81
+ const headline = HEADLINES[code] ?? `The FF1 refused the request (${code}).`;
82
+ const pair = pairCommand(deviceLabel);
83
+ if (PAIRING_CODES.has(code)) {
84
+ const details = `Run \`${pair}\` and have an owner scan the code it shows with Add controller in the Feral File app.`;
85
+ if (code !== 'access_revoked') {
86
+ return { code, error: headline, details };
87
+ }
88
+ // access_revoked has two causes: an owner revoked this controller (the
89
+ // FF1 still has an owner, so pairing again is the fix), or the owner
90
+ // released the FF1 (Forget I exist, spec §5.5) and nobody is left to add
91
+ // it. A released FF1 is legacy and takes plaintext, so unpair is what
92
+ // works until someone sets it up again; after that, pair does. Unpair
93
+ // is offered beside pair, never instead of it.
94
+ return {
95
+ code,
96
+ error: headline,
97
+ details: `${details} If the FF1 shows a setup code on its own screen, or no one has set it up since its owner gave it up, there is no owner to add this controller: run \`${unpairCommand(deviceLabel)}\` to use it without pairing, or set it up in the Feral File app and then pair with that owner.`,
98
+ };
99
+ }
100
+ return { code, error: headline, details: followUp(code, pair) };
101
+ }
102
+ /** followUp returns the next step for codes that are not fixed by pairing. */
103
+ function followUp(code, pair) {
104
+ switch (code) {
105
+ case 'permission_denied':
106
+ return 'Ask an owner to give this controller the owner role in the Feral File app (Controllers), or run the command from an owner device.';
107
+ case 'bad_envelope':
108
+ case 'bad_signature':
109
+ // Public errors are unauthenticated: anyone on the path can send one
110
+ // (or the address now reaches another FF1), so the advice never
111
+ // deletes keys: a new pairing keeps the current ones until it is finished.
112
+ return `Check that the device address still reaches this FF1. If it was factory reset, run \`${pair}\`; the current keys are kept until the new pairing is finished. Do not unpair.`;
113
+ case 'locked':
114
+ return 'The FF1 needs attention: restart it, and if the problem stays, factory reset it from the Feral File app.';
115
+ case 'rate_limited':
116
+ case 'internal_error':
117
+ return 'Wait a few seconds and try again.';
118
+ case 'expired':
119
+ return "Check that this computer's clock is correct, then try again.";
120
+ case 'replay':
121
+ return `Run the command again. If this keeps happening, check that no other computer uses a copy of this controller file; if it still fails, run \`${pair}\`; the current keys are kept until the new pairing is finished. Do not unpair.`;
122
+ case 'invalid_request':
123
+ return 'Check that the clocks of this computer and the FF1 are correct (the FF1 refuses a request dated more than 5 minutes ahead of its clock). If they are, this is an ff-cli bug; please report it with `ff-cli --version`.';
124
+ default:
125
+ return undefined;
126
+ }
127
+ }
128
+ /**
129
+ * replayAfterJumpFailure is the message for a verified `replay` that came
130
+ * back again after ff-cli moved its counter ahead and retried once (§14.4).
131
+ *
132
+ * @param deviceLabel - Device name used in commands
133
+ * @returns The failure
134
+ */
135
+ function replayAfterJumpFailure(deviceLabel) {
136
+ return {
137
+ code: 'replay',
138
+ error: HEADLINES.replay,
139
+ details: `ff-cli moved its request counter ahead and tried once more, and the FF1 still refused it. Run the command again. If it keeps failing, check that no other computer uses a copy of this controller file, then run \`${pairCommand(deviceLabel)}\`; the current keys are kept until the new pairing is finished. Do not unpair.`,
140
+ };
141
+ }
142
+ /**
143
+ * unverifiedReplyFailure is the message for a reply that failed §4.2 checks.
144
+ * It is not retried and not trusted at all: on the relayer it may be a forged
145
+ * "first reply wins" answer.
146
+ *
147
+ * @param reason - Verification failure reason
148
+ * @returns The failure
149
+ */
150
+ function unverifiedReplyFailure(reason, deviceLabel) {
151
+ const pair = deviceLabel ? pairCommand(deviceLabel) : 'ff-cli device pair <name>';
152
+ return {
153
+ code: 'invalid_reply',
154
+ error: 'The reply did not verify against the paired FF1 identity and was discarded.',
155
+ details: `${reason}. Check that the device address still reaches this FF1. If it was factory reset, run \`${pair}\`; the current keys are kept until the new pairing is finished. Do not unpair.`,
156
+ };
157
+ }