sinfactura-types 1.10.7 → 1.10.8

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.
@@ -22,7 +22,7 @@
22
22
  * Management API → client. Nothing posts to a connection directly.
23
23
  */
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.isSocketKeepAlive = exports.SOCKET_KEEPALIVE = exports.SOCKET_AUTH_FAIL_REASONS = exports.LIVE_CLIENT_SOCKET_ACTIONS = exports.CLIENT_SOCKET_ACTIONS = exports.isSocketAction = exports.SOCKET_ACTIONS = void 0;
25
+ exports.isSocketKeepAlive = exports.SOCKET_KEEPALIVE = exports.SOCKET_AUTH_FAIL_REASONS = exports.LIVE_CLIENT_SOCKET_ACTIONS = exports.CLIENT_SOCKET_ACTIONS = exports.DESTRUCTIVE_AGENT_COMMANDS = exports.isAgentCommand = exports.AGENT_COMMANDS = exports.isSocketAction = exports.SOCKET_ACTIONS = void 0;
26
26
  /* -------------------------------------------------------------------------- */
27
27
  /* Server → client data frames */
28
28
  /* -------------------------------------------------------------------------- */
@@ -109,6 +109,18 @@ exports.SOCKET_ACTIONS = [
109
109
  // operator's pause toggle (api#2028). Payload is `PrintersActiveData`.
110
110
  // Full replacement, never a delta; both unknowns fail OPEN (see that type).
111
111
  'printers_active',
112
+ // BE → agent, scoped to ONE agent's own connections (like `printers_active`,
113
+ // never fanned to the store): run a local diagnostic/recovery action
114
+ // (print#224). Payload is `AgentCommandData`.
115
+ //
116
+ // ⚠️ NOT understood by any released agent. The agent routes inbound frames
117
+ // through a closed control-frame switch (`request_printers`,
118
+ // `printers_active`) and then a 4-member print-job enum; anything else lands
119
+ // in its "Unhandled frame" warn and is never executed or acknowledged. As of
120
+ // v2.2.2 that includes this action — print#224's agent lane must add the case
121
+ // before a producer can rely on it. Publishing it here first is deliberate, so
122
+ // all three lanes build against one `.d.ts`.
123
+ 'agent_command',
112
124
  // Operations / operator surfaces.
113
125
  'logs',
114
126
  'maintenance',
@@ -121,6 +133,55 @@ exports.SOCKET_ACTIONS = [
121
133
  const isSocketAction = (value) => typeof value === 'string' && exports.SOCKET_ACTIONS.includes(value);
122
134
  exports.isSocketAction = isSocketAction;
123
135
  /* -------------------------------------------------------------------------- */
136
+ /* Agent diagnostic commands (print#224) */
137
+ /* -------------------------------------------------------------------------- */
138
+ /**
139
+ * The remote-triggerable diagnostic actions, in the agent's OWN spelling.
140
+ *
141
+ * **Kebab-case, verbatim from the agent's `DiagnosticActionId`** (print#223,
142
+ * shipped v2.2.2) — not the snake_case the rest of this file's ACTION names use.
143
+ * That is a deliberate trade. `sinfactura/print` does not depend on this package,
144
+ * so any renaming here has to be re-implemented as a mapping table on the agent
145
+ * side, where a drift is invisible to both ends until an operator reports that a
146
+ * button does nothing. Passing the agent's own ids through untouched removes the
147
+ * mapping layer entirely: the BE forwards a string the agent already keys on.
148
+ * The frame's ACTION (`agent_command`) stays snake_case like its siblings; this
149
+ * is the payload vocabulary, a different namespace.
150
+ *
151
+ * A command the agent does not recognise is safe by construction — its
152
+ * `runDiagnosticAction` returns a structured "unknown command" result instead of
153
+ * throwing — so an older agent degrades to a visible failure, not a crash.
154
+ *
155
+ * Two members of the originally-proposed set are absent on purpose:
156
+ *
157
+ * - **`view-logs`** exists on the agent but is not remotely meaningful. It opens
158
+ * the log FOLDER on the agent's own machine and resolves `ok: true`, so a
159
+ * remote operator would read "logs delivered" while a window opened on an
160
+ * unattended PC. Shipping logs BACK to the backend is a genuinely different
161
+ * capability that the agent does not have; it needs its own contract.
162
+ * - **`test_print`** is not an agent-local action at all. The backend dispatches
163
+ * a real job to an explicit printer (api#2041, shipped), so routing it through
164
+ * here would be a second way to do one thing — and the agent would reject it as
165
+ * an unknown command.
166
+ */
167
+ exports.AGENT_COMMANDS = ['redetect-printers', 'reconnect-socket', 'flush-acks', 'clear-queue'];
168
+ /** Runtime guard — narrows an untrusted string to a known command. */
169
+ const isAgentCommand = (value) => typeof value === 'string' && exports.AGENT_COMMANDS.includes(value);
170
+ exports.isAgentCommand = isAgentCommand;
171
+ /**
172
+ * Commands that destroy operator data and MUST be confirmed before dispatch.
173
+ *
174
+ * Mirrors the agent's own `destructive` flag. The agent deliberately does not
175
+ * prompt inside `run()` — its confirmation lives in the DOM layer, which a remote
176
+ * trigger never reaches — so the "are you sure?" is the CALLER's job. For a
177
+ * remote command that means the app, before it asks the backend to dispatch.
178
+ *
179
+ * `clear-queue` deletes queued work that then never prints. print#235 (agent
180
+ * v2.2.3) stopped it destroying in-flight ACKs, but queued jobs are still lost,
181
+ * and the operator who loses them is not the one who pressed the button.
182
+ */
183
+ exports.DESTRUCTIVE_AGENT_COMMANDS = ['clear-queue'];
184
+ /* -------------------------------------------------------------------------- */
124
185
  /* Client → server frames */
125
186
  /* -------------------------------------------------------------------------- */
126
187
  /**
@@ -141,6 +202,14 @@ exports.isSocketAction = isSocketAction;
141
202
  * shipped in 1.10.x, but the action string was never added to these two arrays —
142
203
  * so `isClientSocketAction`-style checks and exhaustive switches keyed off
143
204
  * `ClientSocketAction` silently excluded a live action. Corrected in 1.10.5.
205
+ *
206
+ * `agent_command_result` is declared but **NOT live** (print#224, 1.10.8): no
207
+ * `$default` union entry or handler exists for it yet, so the api answers it with
208
+ * `400 Invalid message`. It is in this array — the full vocabulary — and out of
209
+ * `LIVE_CLIENT_SOCKET_ACTIONS` below, which is exactly the distinction the two
210
+ * arrays exist to carry. Do not "fix the inconsistency" by adding it there; that
211
+ * is the inverse of the 1.10.5 bug, and it would tell a consumer the frame is
212
+ * accepted when it is rejected.
144
213
  */
145
214
  exports.CLIENT_SOCKET_ACTIONS = [
146
215
  'auth',
@@ -149,8 +218,13 @@ exports.CLIENT_SOCKET_ACTIONS = [
149
218
  'ack',
150
219
  'register_printers',
151
220
  'export_local_rules',
221
+ 'agent_command_result',
152
222
  ];
153
- /** Client→server actions the backend accepts **today**. */
223
+ /**
224
+ * Client→server actions the backend accepts **today** — a strict subset of
225
+ * `CLIENT_SOCKET_ACTIONS`. Anything declared but absent here is published ahead
226
+ * of its handler and will be rejected `400 Invalid message` on the wire.
227
+ */
154
228
  exports.LIVE_CLIENT_SOCKET_ACTIONS = [
155
229
  'auth',
156
230
  'logs',
@@ -70,66 +70,6 @@ declare global {
70
70
  currency: string;
71
71
  paidAt?: number;
72
72
  }
73
- type GtinRequirementTag = "not_required" | "conditional_required" | "new_required";
74
- interface MlAttribute {
75
- id: string;
76
- name?: string;
77
- value_id?: string;
78
- value_name?: string;
79
- attribute_group_id?: string;
80
- attribute_group_name?: string;
81
- }
82
- interface MlRequiredAttribute {
83
- id: string;
84
- name?: string;
85
- required: boolean;
86
- tags?: string[];
87
- }
88
- interface MlCategoryPrediction {
89
- domainName: string;
90
- categoryId: string;
91
- categoryName: string;
92
- attributes: MlAttribute[];
93
- requiredAttributes: MlRequiredAttribute[];
94
- immediatePayment?: "required" | "optional";
95
- maxTitleLength?: number;
96
- gtinRequirement: GtinRequirementTag;
97
- }
98
- interface MlCategoryCandidate {
99
- domainId?: string;
100
- domainName: string;
101
- categoryId: string;
102
- categoryName: string;
103
- }
104
- interface PublishPrediction extends MlCategoryPrediction {
105
- isUpMigrated: boolean;
106
- candidates: MlCategoryCandidate[];
107
- }
108
- interface MlCategoryAttributeSchema {
109
- categoryId: string;
110
- requiredAttributes: MlRequiredAttribute[];
111
- gtinRequirement: GtinRequirementTag;
112
- maxTitleLength?: number;
113
- immediatePayment?: "required" | "optional";
114
- }
115
- interface MlPublishRequest {
116
- productId: string;
117
- categoryId: string;
118
- attributes: MlAttribute[];
119
- listingTypeId: string;
120
- saleTerms?: Record<string, unknown>[];
121
- pictures?: {
122
- url: string;
123
- }[];
124
- description?: string;
125
- }
126
- interface MlPublishResponse {
127
- productId: string;
128
- itemId: string;
129
- userProductId?: string;
130
- isUpMigrated: boolean;
131
- status: "linked";
132
- }
133
73
  interface MlFieldError {
134
74
  field: string;
135
75
  code?: string;
package/dist/print.d.ts CHANGED
@@ -1,6 +1,17 @@
1
1
  declare global {
2
2
  type PrintJobState = 'queued' | 'sent' | 'printed' | 'error';
3
3
  type PrintContentType = 'pdf_uri' | 'pdf_base64' | 'raw_uri' | 'raw_base64';
4
+ /**
5
+ * A raw (non-PDF) control language a printer can be sent.
6
+ *
7
+ * Deliberately NOT encoded into `PrintContentType`: that field says how the
8
+ * bytes ARRIVE (`raw_uri` vs `raw_base64`), not what dialect they are, and the
9
+ * two axes are independent — every format is transportable both ways. Widening
10
+ * `PrintContentType` to `raw_zpl_base64`-style members would multiply out to a
11
+ * member per (format × transport) pair and break every existing consumer that
12
+ * switches on it.
13
+ */
14
+ type PrintRawFormat = 'zpl' | 'escpos';
4
15
  interface PrintOptions {
5
16
  bin?: string;
6
17
  collate?: boolean;
@@ -157,6 +168,29 @@ declare global {
157
168
  * survive a re-registration that omits it. `false` = do not route jobs here.
158
169
  */
159
170
  active: boolean;
171
+ /**
172
+ * Raw control languages the OPERATOR declares this device understands.
173
+ * BE-owned and operator-set, exactly like `active` — never agent-reported.
174
+ *
175
+ * ⚠️ **Absent or empty means REFUSE all raw content.** This is the one field
176
+ * in the printer registry where absence is a "no" rather than a "not
177
+ * reported", and it inverts `PrintPrinterCapabilities`' core rule on purpose
178
+ * (`api/stacks/services/printRules.ts`: "Absent capabilities = not reported —
179
+ * accept everything"). That default is right for `paper`/`bins`/`dpis`, where
180
+ * guessing wrong wastes one job. Here, guessing wrong sends a ZPL stream to a
181
+ * laser printer, which does not error — it renders every command as text and
182
+ * puts a ream through the machine until someone physically cancels it. The
183
+ * spooler accepts the bytes, the agent ACKs `printed`, and the BE records
184
+ * success. So it cannot live in `capabilities`, or the next person to "fix
185
+ * the inconsistency" reopens the hole.
186
+ *
187
+ * It is also not something the agent could report honestly: it can read a
188
+ * driver string (`ZDesigner ZD420`), but a generic driver, a renamed queue or
189
+ * a shared network printer defeats that heuristic. And an agent-reported
190
+ * field is overwritten on every re-registration — the same bug `active` is
191
+ * shaped to avoid, with a worse outcome than an un-paused printer.
192
+ */
193
+ rawFormats?: PrintRawFormat[];
160
194
  /** ms epoch of the last `register_printers` report that included this printer. */
161
195
  reportedAt: number;
162
196
  /**
@@ -177,7 +211,7 @@ declare global {
177
211
  * wholesale would silently reset the operator's per-printer pause toggle on
178
212
  * every agent restart.
179
213
  */
180
- type PrintPrinterReport = Omit<PrintPrinter, 'agentId' | 'active' | 'reportedAt' | 'online'>;
214
+ type PrintPrinterReport = Omit<PrintPrinter, 'agentId' | 'active' | 'reportedAt' | 'online' | 'rawFormats'>;
181
215
  /**
182
216
  * payload of the agent → BE `register_printers` WSS frame. ⚠️ That frame is
183
217
  * **FLAT** — `{ action, printers }`, NOT `{ action, data }` (api#2017); this type
package/dist/socket.d.ts CHANGED
@@ -34,7 +34,7 @@
34
34
  * (`wsPostStore`). A client must ignore actions it does not own rather than
35
35
  * assume it receives all of them.
36
36
  */
37
- export declare const SOCKET_ACTIONS: readonly ["account", "baskets", "brands", "cash", "categories", "customers", "globals", "invoices", "literals", "orders", "products", "shifts", "stores", "suppliers", "supplier-invoice", "support", "surveys", "users", "favorites", "account-delete", "baskets-delete", "log-delete", "notifications", "support-message", "whatsapp-message", "payment_received", "payment_linked", "payment_unlinked", "mercadopago", "mercadopago_dynamic_qr_created", "mercadopago_static_qr_created", "mp_hook_log_appended", "mp_ipn_log_appended", "stripe", "subscription", "caea", "print", "print-order", "print-invoice", "print-product", "print-tag", "print_job_failed", "request_printers", "printers_changed", "print_rules_changed", "printers_active", "logs", "maintenance", "currency_auto_updated", "drain_progress", "integration_event_appended", "userActivityRecorded"];
37
+ export declare const SOCKET_ACTIONS: readonly ["account", "baskets", "brands", "cash", "categories", "customers", "globals", "invoices", "literals", "orders", "products", "shifts", "stores", "suppliers", "supplier-invoice", "support", "surveys", "users", "favorites", "account-delete", "baskets-delete", "log-delete", "notifications", "support-message", "whatsapp-message", "payment_received", "payment_linked", "payment_unlinked", "mercadopago", "mercadopago_dynamic_qr_created", "mercadopago_static_qr_created", "mp_hook_log_appended", "mp_ipn_log_appended", "stripe", "subscription", "caea", "print", "print-order", "print-invoice", "print-product", "print-tag", "print_job_failed", "request_printers", "printers_changed", "print_rules_changed", "printers_active", "agent_command", "logs", "maintenance", "currency_auto_updated", "drain_progress", "integration_event_appended", "userActivityRecorded"];
38
38
  /** Union of every server→client data-frame action. */
39
39
  export type SocketAction = (typeof SOCKET_ACTIONS)[number];
40
40
  /** Runtime guard — narrows an untrusted string to a known action. */
@@ -50,6 +50,106 @@ export interface SocketMessage<T = unknown> {
50
50
  action: SocketAction;
51
51
  data: T;
52
52
  }
53
+ /**
54
+ * The remote-triggerable diagnostic actions, in the agent's OWN spelling.
55
+ *
56
+ * **Kebab-case, verbatim from the agent's `DiagnosticActionId`** (print#223,
57
+ * shipped v2.2.2) — not the snake_case the rest of this file's ACTION names use.
58
+ * That is a deliberate trade. `sinfactura/print` does not depend on this package,
59
+ * so any renaming here has to be re-implemented as a mapping table on the agent
60
+ * side, where a drift is invisible to both ends until an operator reports that a
61
+ * button does nothing. Passing the agent's own ids through untouched removes the
62
+ * mapping layer entirely: the BE forwards a string the agent already keys on.
63
+ * The frame's ACTION (`agent_command`) stays snake_case like its siblings; this
64
+ * is the payload vocabulary, a different namespace.
65
+ *
66
+ * A command the agent does not recognise is safe by construction — its
67
+ * `runDiagnosticAction` returns a structured "unknown command" result instead of
68
+ * throwing — so an older agent degrades to a visible failure, not a crash.
69
+ *
70
+ * Two members of the originally-proposed set are absent on purpose:
71
+ *
72
+ * - **`view-logs`** exists on the agent but is not remotely meaningful. It opens
73
+ * the log FOLDER on the agent's own machine and resolves `ok: true`, so a
74
+ * remote operator would read "logs delivered" while a window opened on an
75
+ * unattended PC. Shipping logs BACK to the backend is a genuinely different
76
+ * capability that the agent does not have; it needs its own contract.
77
+ * - **`test_print`** is not an agent-local action at all. The backend dispatches
78
+ * a real job to an explicit printer (api#2041, shipped), so routing it through
79
+ * here would be a second way to do one thing — and the agent would reject it as
80
+ * an unknown command.
81
+ */
82
+ export declare const AGENT_COMMANDS: readonly ["redetect-printers", "reconnect-socket", "flush-acks", "clear-queue"];
83
+ /** Union of every remote-triggerable diagnostic command. */
84
+ export type AgentCommand = (typeof AGENT_COMMANDS)[number];
85
+ /** Runtime guard — narrows an untrusted string to a known command. */
86
+ export declare const isAgentCommand: (value: unknown) => value is AgentCommand;
87
+ /**
88
+ * Commands that destroy operator data and MUST be confirmed before dispatch.
89
+ *
90
+ * Mirrors the agent's own `destructive` flag. The agent deliberately does not
91
+ * prompt inside `run()` — its confirmation lives in the DOM layer, which a remote
92
+ * trigger never reaches — so the "are you sure?" is the CALLER's job. For a
93
+ * remote command that means the app, before it asks the backend to dispatch.
94
+ *
95
+ * `clear-queue` deletes queued work that then never prints. print#235 (agent
96
+ * v2.2.3) stopped it destroying in-flight ACKs, but queued jobs are still lost,
97
+ * and the operator who loses them is not the one who pressed the button.
98
+ */
99
+ export declare const DESTRUCTIVE_AGENT_COMMANDS: readonly AgentCommand[];
100
+ /**
101
+ * `data` payload of the BE → agent `agent_command` frame — nested,
102
+ * `{ action: 'agent_command', data: AgentCommandData }`, like every other
103
+ * server→client frame.
104
+ *
105
+ * Declared here rather than beside `PrintersActiveData` in `print.ts` because it
106
+ * references `AgentCommand`, which is derived from the runtime `AGENT_COMMANDS`
107
+ * array above; re-declaring the vocabulary as an ambient type would give it two
108
+ * sources that drift independently.
109
+ */
110
+ export interface AgentCommandData {
111
+ command: AgentCommand;
112
+ /**
113
+ * The target agent. Advisory for the agent itself — the BE has already scoped
114
+ * delivery to that agent's connections — but carried so the agent can drop a
115
+ * frame that reached it through a mis-scoped broadcast rather than acting on
116
+ * another machine's instruction.
117
+ */
118
+ agentId: string;
119
+ /**
120
+ * Stable per-dispatch id. **Required, never optional.**
121
+ *
122
+ * The wss `$default` route has a bare Lambda integration and no route
123
+ * response, so a frame the agent sends can never fail visibly to it. Delivery
124
+ * therefore has to be idempotent-and-repeatable rather than once-and-hope,
125
+ * and a re-dispatch needs a stable id for the agent to deduplicate on and for
126
+ * the backend to match a late result against.
127
+ */
128
+ commandId: string;
129
+ }
130
+ /**
131
+ * Agent → BE outcome of one `agent_command`.
132
+ *
133
+ * ⚠️ **A report, not an acknowledgement of delivery.** The agent cannot observe
134
+ * whether the backend received this frame (see `commandId`), so "did the command
135
+ * work" is a question the BACKEND answers by reconciling its own dispatch state —
136
+ * never one the agent can settle by sending this.
137
+ *
138
+ * Note that `ok: false` is not necessarily a failure of the action: the agent
139
+ * guards every command with a 3s per-action cooldown and an in-flight lock, and
140
+ * a refusal from either returns `ok: false` with an operator-facing message
141
+ * ("Esperá unos segundos…"). Surface `message`; do not translate `ok: false`
142
+ * into "the agent is broken".
143
+ */
144
+ export interface SocketAgentCommandResultMessage {
145
+ action: 'agent_command_result';
146
+ /** Echoes `AgentCommandData.commandId` — the only way to match a result to its dispatch. */
147
+ commandId: string;
148
+ ok: boolean;
149
+ /** Operator-facing, Spanish voseo, always non-empty. Produced by the agent. */
150
+ message: string;
151
+ [key: string]: unknown;
152
+ }
53
153
  /**
54
154
  * The client-driven actions the WSS `$default` route accepts.
55
155
  *
@@ -68,11 +168,23 @@ export interface SocketMessage<T = unknown> {
68
168
  * shipped in 1.10.x, but the action string was never added to these two arrays —
69
169
  * so `isClientSocketAction`-style checks and exhaustive switches keyed off
70
170
  * `ClientSocketAction` silently excluded a live action. Corrected in 1.10.5.
171
+ *
172
+ * `agent_command_result` is declared but **NOT live** (print#224, 1.10.8): no
173
+ * `$default` union entry or handler exists for it yet, so the api answers it with
174
+ * `400 Invalid message`. It is in this array — the full vocabulary — and out of
175
+ * `LIVE_CLIENT_SOCKET_ACTIONS` below, which is exactly the distinction the two
176
+ * arrays exist to carry. Do not "fix the inconsistency" by adding it there; that
177
+ * is the inverse of the 1.10.5 bug, and it would tell a consumer the frame is
178
+ * accepted when it is rejected.
71
179
  */
72
- export declare const CLIENT_SOCKET_ACTIONS: readonly ["auth", "logs", "heartbeat", "ack", "register_printers", "export_local_rules"];
180
+ export declare const CLIENT_SOCKET_ACTIONS: readonly ["auth", "logs", "heartbeat", "ack", "register_printers", "export_local_rules", "agent_command_result"];
73
181
  /** Union of every client→server action. */
74
182
  export type ClientSocketAction = (typeof CLIENT_SOCKET_ACTIONS)[number];
75
- /** Client→server actions the backend accepts **today**. */
183
+ /**
184
+ * Client→server actions the backend accepts **today** — a strict subset of
185
+ * `CLIENT_SOCKET_ACTIONS`. Anything declared but absent here is published ahead
186
+ * of its handler and will be rejected `400 Invalid message` on the wire.
187
+ */
76
188
  export declare const LIVE_CLIENT_SOCKET_ACTIONS: readonly ["auth", "logs", "heartbeat", "ack", "register_printers", "export_local_rules"];
77
189
  /** Authenticate the connection. Must be the first frame; see `AuthAckFrame`. */
78
190
  export interface SocketAuthMessage {
@@ -167,7 +279,7 @@ export interface SocketExportLocalRulesMessage {
167
279
  [key: string]: unknown;
168
280
  }
169
281
  /** Any client→server JSON frame. */
170
- export type ClientSocketMessage = SocketAuthMessage | SocketLogsMessage | SocketHeartbeatMessage | SocketAckMessage | SocketRegisterPrintersMessage | SocketExportLocalRulesMessage;
282
+ export type ClientSocketMessage = SocketAuthMessage | SocketLogsMessage | SocketHeartbeatMessage | SocketAckMessage | SocketRegisterPrintersMessage | SocketExportLocalRulesMessage | SocketAgentCommandResultMessage;
171
283
  /**
172
284
  * Why the backend refused the handshake (api#977 / app#1353).
173
285
  *
package/dist/socket.js CHANGED
@@ -106,6 +106,18 @@ export const SOCKET_ACTIONS = [
106
106
  // operator's pause toggle (api#2028). Payload is `PrintersActiveData`.
107
107
  // Full replacement, never a delta; both unknowns fail OPEN (see that type).
108
108
  'printers_active',
109
+ // BE → agent, scoped to ONE agent's own connections (like `printers_active`,
110
+ // never fanned to the store): run a local diagnostic/recovery action
111
+ // (print#224). Payload is `AgentCommandData`.
112
+ //
113
+ // ⚠️ NOT understood by any released agent. The agent routes inbound frames
114
+ // through a closed control-frame switch (`request_printers`,
115
+ // `printers_active`) and then a 4-member print-job enum; anything else lands
116
+ // in its "Unhandled frame" warn and is never executed or acknowledged. As of
117
+ // v2.2.2 that includes this action — print#224's agent lane must add the case
118
+ // before a producer can rely on it. Publishing it here first is deliberate, so
119
+ // all three lanes build against one `.d.ts`.
120
+ 'agent_command',
109
121
  // Operations / operator surfaces.
110
122
  'logs',
111
123
  'maintenance',
@@ -117,6 +129,54 @@ export const SOCKET_ACTIONS = [
117
129
  /** Runtime guard — narrows an untrusted string to a known action. */
118
130
  export const isSocketAction = (value) => typeof value === 'string' && SOCKET_ACTIONS.includes(value);
119
131
  /* -------------------------------------------------------------------------- */
132
+ /* Agent diagnostic commands (print#224) */
133
+ /* -------------------------------------------------------------------------- */
134
+ /**
135
+ * The remote-triggerable diagnostic actions, in the agent's OWN spelling.
136
+ *
137
+ * **Kebab-case, verbatim from the agent's `DiagnosticActionId`** (print#223,
138
+ * shipped v2.2.2) — not the snake_case the rest of this file's ACTION names use.
139
+ * That is a deliberate trade. `sinfactura/print` does not depend on this package,
140
+ * so any renaming here has to be re-implemented as a mapping table on the agent
141
+ * side, where a drift is invisible to both ends until an operator reports that a
142
+ * button does nothing. Passing the agent's own ids through untouched removes the
143
+ * mapping layer entirely: the BE forwards a string the agent already keys on.
144
+ * The frame's ACTION (`agent_command`) stays snake_case like its siblings; this
145
+ * is the payload vocabulary, a different namespace.
146
+ *
147
+ * A command the agent does not recognise is safe by construction — its
148
+ * `runDiagnosticAction` returns a structured "unknown command" result instead of
149
+ * throwing — so an older agent degrades to a visible failure, not a crash.
150
+ *
151
+ * Two members of the originally-proposed set are absent on purpose:
152
+ *
153
+ * - **`view-logs`** exists on the agent but is not remotely meaningful. It opens
154
+ * the log FOLDER on the agent's own machine and resolves `ok: true`, so a
155
+ * remote operator would read "logs delivered" while a window opened on an
156
+ * unattended PC. Shipping logs BACK to the backend is a genuinely different
157
+ * capability that the agent does not have; it needs its own contract.
158
+ * - **`test_print`** is not an agent-local action at all. The backend dispatches
159
+ * a real job to an explicit printer (api#2041, shipped), so routing it through
160
+ * here would be a second way to do one thing — and the agent would reject it as
161
+ * an unknown command.
162
+ */
163
+ export const AGENT_COMMANDS = ['redetect-printers', 'reconnect-socket', 'flush-acks', 'clear-queue'];
164
+ /** Runtime guard — narrows an untrusted string to a known command. */
165
+ export const isAgentCommand = (value) => typeof value === 'string' && AGENT_COMMANDS.includes(value);
166
+ /**
167
+ * Commands that destroy operator data and MUST be confirmed before dispatch.
168
+ *
169
+ * Mirrors the agent's own `destructive` flag. The agent deliberately does not
170
+ * prompt inside `run()` — its confirmation lives in the DOM layer, which a remote
171
+ * trigger never reaches — so the "are you sure?" is the CALLER's job. For a
172
+ * remote command that means the app, before it asks the backend to dispatch.
173
+ *
174
+ * `clear-queue` deletes queued work that then never prints. print#235 (agent
175
+ * v2.2.3) stopped it destroying in-flight ACKs, but queued jobs are still lost,
176
+ * and the operator who loses them is not the one who pressed the button.
177
+ */
178
+ export const DESTRUCTIVE_AGENT_COMMANDS = ['clear-queue'];
179
+ /* -------------------------------------------------------------------------- */
120
180
  /* Client → server frames */
121
181
  /* -------------------------------------------------------------------------- */
122
182
  /**
@@ -137,6 +197,14 @@ export const isSocketAction = (value) => typeof value === 'string' && SOCKET_ACT
137
197
  * shipped in 1.10.x, but the action string was never added to these two arrays —
138
198
  * so `isClientSocketAction`-style checks and exhaustive switches keyed off
139
199
  * `ClientSocketAction` silently excluded a live action. Corrected in 1.10.5.
200
+ *
201
+ * `agent_command_result` is declared but **NOT live** (print#224, 1.10.8): no
202
+ * `$default` union entry or handler exists for it yet, so the api answers it with
203
+ * `400 Invalid message`. It is in this array — the full vocabulary — and out of
204
+ * `LIVE_CLIENT_SOCKET_ACTIONS` below, which is exactly the distinction the two
205
+ * arrays exist to carry. Do not "fix the inconsistency" by adding it there; that
206
+ * is the inverse of the 1.10.5 bug, and it would tell a consumer the frame is
207
+ * accepted when it is rejected.
140
208
  */
141
209
  export const CLIENT_SOCKET_ACTIONS = [
142
210
  'auth',
@@ -145,8 +213,13 @@ export const CLIENT_SOCKET_ACTIONS = [
145
213
  'ack',
146
214
  'register_printers',
147
215
  'export_local_rules',
216
+ 'agent_command_result',
148
217
  ];
149
- /** Client→server actions the backend accepts **today**. */
218
+ /**
219
+ * Client→server actions the backend accepts **today** — a strict subset of
220
+ * `CLIENT_SOCKET_ACTIONS`. Anything declared but absent here is published ahead
221
+ * of its handler and will be rejected `400 Invalid message` on the wire.
222
+ */
150
223
  export const LIVE_CLIENT_SOCKET_ACTIONS = [
151
224
  'auth',
152
225
  'logs',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sinfactura-types",
3
- "version": "1.10.7",
3
+ "version": "1.10.8",
4
4
  "main": "dist/index.js",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",