salt-agent-sdk 0.1.0 → 0.18.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 (103) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +941 -30
  3. package/dist/actions.d.ts +21 -5
  4. package/dist/actions.d.ts.map +1 -1
  5. package/dist/actions.js +673 -54
  6. package/dist/actions.js.map +1 -1
  7. package/dist/ask.d.ts +83 -0
  8. package/dist/ask.d.ts.map +1 -0
  9. package/dist/ask.js +215 -0
  10. package/dist/ask.js.map +1 -0
  11. package/dist/chatPrivacy.d.ts +34 -0
  12. package/dist/chatPrivacy.d.ts.map +1 -0
  13. package/dist/chatPrivacy.js +60 -0
  14. package/dist/chatPrivacy.js.map +1 -0
  15. package/dist/client.d.ts +2783 -35
  16. package/dist/client.d.ts.map +1 -1
  17. package/dist/client.js +1200 -143
  18. package/dist/client.js.map +1 -1
  19. package/dist/config.d.ts +16 -6
  20. package/dist/config.d.ts.map +1 -1
  21. package/dist/config.js +16 -5
  22. package/dist/config.js.map +1 -1
  23. package/dist/crypto.d.ts +62 -0
  24. package/dist/crypto.d.ts.map +1 -1
  25. package/dist/crypto.js +121 -0
  26. package/dist/crypto.js.map +1 -1
  27. package/dist/delegations.d.ts +25 -8
  28. package/dist/delegations.d.ts.map +1 -1
  29. package/dist/delegations.js +67 -11
  30. package/dist/delegations.js.map +1 -1
  31. package/dist/device/client.d.ts +161 -0
  32. package/dist/device/client.d.ts.map +1 -0
  33. package/dist/device/client.js +601 -0
  34. package/dist/device/client.js.map +1 -0
  35. package/dist/device/host.d.ts +222 -0
  36. package/dist/device/host.d.ts.map +1 -0
  37. package/dist/device/host.js +891 -0
  38. package/dist/device/host.js.map +1 -0
  39. package/dist/device/http.d.ts +12 -0
  40. package/dist/device/http.d.ts.map +1 -0
  41. package/dist/device/http.js +124 -0
  42. package/dist/device/http.js.map +1 -0
  43. package/dist/device/index.d.ts +5 -0
  44. package/dist/device/index.d.ts.map +1 -0
  45. package/dist/device/index.js +27 -0
  46. package/dist/device/index.js.map +1 -0
  47. package/dist/device/protocol.d.ts +312 -0
  48. package/dist/device/protocol.d.ts.map +1 -0
  49. package/dist/device/protocol.js +435 -0
  50. package/dist/device/protocol.js.map +1 -0
  51. package/dist/identities.d.ts +9 -2
  52. package/dist/identities.d.ts.map +1 -1
  53. package/dist/identities.js +33 -3
  54. package/dist/identities.js.map +1 -1
  55. package/dist/identity.d.ts +155 -0
  56. package/dist/identity.d.ts.map +1 -0
  57. package/dist/identity.js +196 -0
  58. package/dist/identity.js.map +1 -0
  59. package/dist/identityShare.d.ts +129 -0
  60. package/dist/identityShare.d.ts.map +1 -0
  61. package/dist/identityShare.js +308 -0
  62. package/dist/identityShare.js.map +1 -0
  63. package/dist/ids.d.ts +11 -0
  64. package/dist/ids.d.ts.map +1 -0
  65. package/dist/ids.js +17 -0
  66. package/dist/ids.js.map +1 -0
  67. package/dist/index.d.ts +13 -0
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +13 -0
  70. package/dist/index.js.map +1 -1
  71. package/dist/reconcile.d.ts +25 -0
  72. package/dist/reconcile.d.ts.map +1 -0
  73. package/dist/reconcile.js +60 -0
  74. package/dist/reconcile.js.map +1 -0
  75. package/dist/recovery.d.ts +51 -0
  76. package/dist/recovery.d.ts.map +1 -0
  77. package/dist/recovery.js +59 -0
  78. package/dist/recovery.js.map +1 -0
  79. package/dist/register.d.ts +32 -0
  80. package/dist/register.d.ts.map +1 -0
  81. package/dist/register.js +65 -0
  82. package/dist/register.js.map +1 -0
  83. package/dist/scriptJob.d.ts +92 -0
  84. package/dist/scriptJob.d.ts.map +1 -0
  85. package/dist/scriptJob.js +270 -0
  86. package/dist/scriptJob.js.map +1 -0
  87. package/dist/sessions.d.ts +131 -0
  88. package/dist/sessions.d.ts.map +1 -0
  89. package/dist/sessions.js +240 -0
  90. package/dist/sessions.js.map +1 -0
  91. package/dist/socket.d.ts +115 -0
  92. package/dist/socket.d.ts.map +1 -0
  93. package/dist/socket.js +1038 -0
  94. package/dist/socket.js.map +1 -0
  95. package/dist/webhook.d.ts +613 -24
  96. package/dist/webhook.d.ts.map +1 -1
  97. package/dist/webhook.js +1384 -83
  98. package/dist/webhook.js.map +1 -1
  99. package/dist/work.d.ts +57 -0
  100. package/dist/work.d.ts.map +1 -0
  101. package/dist/work.js +213 -0
  102. package/dist/work.js.map +1 -0
  103. package/package.json +26 -4
@@ -0,0 +1,435 @@
1
+ "use strict";
2
+ // The `[[SALT-DEVICE]]` wire grammar (salt-api docs/DEVICE_PROTOCOL.md).
3
+ //
4
+ // A device command or result is the PLAINTEXT of an ordinary end-to-end
5
+ // encrypted lane message. Salt relays the ciphertext and never parses it;
6
+ // everything in this file runs INSIDE the agent and INSIDE the device, on
7
+ // plaintext that has already been decrypted (device client) or is about to be
8
+ // encrypted (device host). Nothing here touches a screen, a keyboard, or a
9
+ // network -- it only turns a command into a line of text and back.
10
+ //
11
+ // This module is pure: no I/O, no crypto, no transport. encode/decode/verify
12
+ // only. client.ts (agent side) and host.ts (device side) wrap it with the
13
+ // injected transports that do the encrypting, posting and dispatching.
14
+ //
15
+ // It is the single source of truth for the grammar on the TypeScript side; a
16
+ // change to the grammar is made in DEVICE_PROTOCOL.md first, then mirrored
17
+ // here, then announced -- never the other way round.
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.DeviceProtocolError = exports.TASK_STEP_KINDS = exports.TASK_STATUSES = exports.TASK_CLIENT_GRACE_MS = exports.TASK_DEVICE_GRACE_MS = exports.PROGRESS_MIN_INTERVAL_MS = exports.TASK_ID_MAX = exports.TASK_CHECKED_WHAT_MAX = exports.TASK_RESULT_STEPS_MAX = exports.TASK_STEP_LINE_MAX = exports.TASK_SUMMARY_MAX = exports.PROGRESS_LINE_MAX = exports.TASK_TIMEOUT_S_DEFAULT = exports.TASK_TIMEOUT_S_MAX = exports.TASK_TIMEOUT_S_MIN = exports.TASK_MAX_STEPS_DEFAULT = exports.TASK_MAX_STEPS_MAX = exports.TASK_MAX_STEPS_MIN = exports.TASK_DONE_WHEN_MAX = exports.TASK_GOAL_MAX = exports.DEVICE_COUNT_CLASSES = exports.CAPABILITY_FOR_OP = exports.DEVICE_ERROR_CODES = exports.DEVICE_RESULT_OPS = exports.AGENT_OPS = exports.MUTATING_OPS = exports.RESULT_CACHE_SIZE = exports.COMMAND_TTL_MS = exports.MAX_OBSERVE_PER_SECOND = exports.MAX_COMMANDS_PER_SECOND = exports.MAX_DEVICE_PLAINTEXT_BYTES = exports.DEVICE_PROTOCOL_VERSION = void 0;
20
+ exports.countClassForOp = countClassForOp;
21
+ exports.parseTaskArgs = parseTaskArgs;
22
+ exports.taskCommandBody = taskCommandBody;
23
+ exports.normalizeProgress = normalizeProgress;
24
+ exports.newTaskId = newTaskId;
25
+ exports.normalizeTaskResult = normalizeTaskResult;
26
+ exports.encodeDeviceMessage = encodeDeviceMessage;
27
+ exports.decodeDeviceMessage = decodeDeviceMessage;
28
+ exports.deviceMessageId = deviceMessageId;
29
+ /** Grammar version. An incoming message with any other `v` is answered
30
+ * `error { code: "unsupported" }`, never guessed at. */
31
+ exports.DEVICE_PROTOCOL_VERSION = 1;
32
+ /** A single lane message carries at most this much PLAINTEXT. Larger files or
33
+ * images are refused with `too_large` (a later phase may chunk). Mirrors
34
+ * salt-api's own per-message plaintext cap. */
35
+ exports.MAX_DEVICE_PLAINTEXT_BYTES = 4 * 1024 * 1024;
36
+ /** Per-session rate ceilings the device enforces. Over either, the device
37
+ * answers `busy` and does not run the command. */
38
+ exports.MAX_COMMANDS_PER_SECOND = 5;
39
+ exports.MAX_OBSERVE_PER_SECOND = 1;
40
+ // ---- one command in flight (device-locking contract §5) ---------------------
41
+ // Every constant the client and host must agree on lives here.
42
+ /** A command is valid for this long after the client built it. The client sets
43
+ * `exp = now + COMMAND_TTL_MS` (epoch MILLISECONDS); the host refuses a
44
+ * command whose `exp` has passed with `expired`, without executing it. */
45
+ exports.COMMAND_TTL_MS = 30_000;
46
+ /** The host remembers this many results per session, by command `id`; a
47
+ * repeated `id` returns the stored result without executing. */
48
+ exports.RESULT_CACHE_SIZE = 64;
49
+ /** Ops that change what is on the machine's screen/input state. The host
50
+ * serialises these device-wide (across every session on the machine) behind
51
+ * one mutex. `observe`, `list_apps` and the file ops do not take it. */
52
+ exports.MUTATING_OPS = ["click", "type", "key", "scroll", "submit", "focus_app", "task"];
53
+ exports.AGENT_OPS = [
54
+ "observe",
55
+ "click",
56
+ "type",
57
+ "key",
58
+ "scroll",
59
+ "submit",
60
+ "focus_app",
61
+ "list_apps",
62
+ "read_file",
63
+ "write_file",
64
+ "task",
65
+ "stop",
66
+ "cancel",
67
+ "shell",
68
+ ];
69
+ exports.DEVICE_RESULT_OPS = [
70
+ "ack",
71
+ "observation",
72
+ "apps",
73
+ "file",
74
+ "ask",
75
+ "error",
76
+ "ended",
77
+ "progress",
78
+ "task_result",
79
+ ];
80
+ exports.DEVICE_ERROR_CODES = [
81
+ "forbidden",
82
+ "out_of_scope",
83
+ "needs_approval",
84
+ "unsupported",
85
+ "out_of_order",
86
+ "bad_args",
87
+ "too_large",
88
+ "busy",
89
+ "expired",
90
+ "clock_skew",
91
+ "paused",
92
+ "failed",
93
+ ];
94
+ /** The `device.*` mandate capability each agent op requires. `stop` needs
95
+ * none. `shell` maps to the reserved `device.shell`, which the server refuses
96
+ * in P0, so a host answers it `forbidden` without consulting scope. */
97
+ exports.CAPABILITY_FOR_OP = {
98
+ observe: "device.observe",
99
+ click: "device.act",
100
+ type: "device.act",
101
+ key: "device.act",
102
+ scroll: "device.act",
103
+ submit: "device.act",
104
+ focus_app: "device.apps",
105
+ list_apps: "device.apps",
106
+ read_file: "device.files.read",
107
+ write_file: "device.files.write",
108
+ task: "device.task",
109
+ stop: null,
110
+ cancel: null,
111
+ shell: "device.shell",
112
+ };
113
+ exports.DEVICE_COUNT_CLASSES = [
114
+ "observe",
115
+ "click",
116
+ "type",
117
+ "key",
118
+ "scroll",
119
+ "file_read",
120
+ "file_write",
121
+ "app_focus",
122
+ "task",
123
+ "command",
124
+ "look",
125
+ "error",
126
+ ];
127
+ /** Map a successfully-run op to the metadata class it increments. `list_apps`
128
+ * and `focus_app` both count as `app_focus`; file ops split read/write;
129
+ * `stop`/`shell` have no count. Returns null when nothing should be counted. */
130
+ function countClassForOp(op) {
131
+ switch (op) {
132
+ case "observe":
133
+ return "observe";
134
+ case "click":
135
+ return "click";
136
+ case "type":
137
+ return "type";
138
+ case "key":
139
+ return "key";
140
+ case "scroll":
141
+ return "scroll";
142
+ case "submit": // a confirm is a press of a control: counted with clicks
143
+ return "click";
144
+ case "read_file":
145
+ return "file_read";
146
+ case "write_file":
147
+ return "file_write";
148
+ case "focus_app":
149
+ case "list_apps":
150
+ return "app_focus";
151
+ case "task":
152
+ return "task";
153
+ case "stop":
154
+ case "cancel":
155
+ case "shell":
156
+ return null;
157
+ }
158
+ }
159
+ // ---- a whole task (op `task`, capability `device.task`) ---------------------
160
+ // The agent hands the computer ONE job in plain words; a local agent on the
161
+ // device does it (commands, screenshots, the screen reader, input), checks it,
162
+ // and answers with a checked receipt. Command output and screenshots never
163
+ // leave the computer: a TaskResult carries words and numbers only.
164
+ /** Caps on what a task may say. The agent's side checks the same ones on what it
165
+ * receives, so neither end trusts the other's limits. */
166
+ exports.TASK_GOAL_MAX = 2000;
167
+ exports.TASK_DONE_WHEN_MAX = 500;
168
+ exports.TASK_MAX_STEPS_MIN = 1;
169
+ exports.TASK_MAX_STEPS_MAX = 60;
170
+ exports.TASK_MAX_STEPS_DEFAULT = 30;
171
+ exports.TASK_TIMEOUT_S_MIN = 10;
172
+ exports.TASK_TIMEOUT_S_MAX = 600;
173
+ exports.TASK_TIMEOUT_S_DEFAULT = 180;
174
+ exports.PROGRESS_LINE_MAX = 140;
175
+ exports.TASK_SUMMARY_MAX = 500;
176
+ exports.TASK_STEP_LINE_MAX = 140;
177
+ exports.TASK_RESULT_STEPS_MAX = 60;
178
+ exports.TASK_CHECKED_WHAT_MAX = 200;
179
+ exports.TASK_ID_MAX = 64;
180
+ /** The device sends `progress` at most this often (the host throttles). */
181
+ exports.PROGRESS_MIN_INTERVAL_MS = 2_000;
182
+ /** A task's command deadline on the device is `timeout_s` + this, not COMMAND_TTL_MS. */
183
+ exports.TASK_DEVICE_GRACE_MS = 15_000;
184
+ /** The agent waits `timeout_s` + this for the result before it cancels. */
185
+ exports.TASK_CLIENT_GRACE_MS = 30_000;
186
+ exports.TASK_STATUSES = ["done", "failed", "stopped", "denied", "timeout", "paused_out"];
187
+ exports.TASK_STEP_KINDS = ["command", "look", "observe", "act", "apps", "ask", "check"];
188
+ // A cap counts characters as people do (Unicode code points), not UTF-16 units, so an emoji
189
+ // is never cut in half and the Python SDK (which counts code points) reads the same limits.
190
+ function charCount(s) {
191
+ let n = 0;
192
+ for (const _ of s)
193
+ n += 1;
194
+ return n;
195
+ }
196
+ function clip(s, max) {
197
+ if (s.length <= max)
198
+ return s; // code points <= UTF-16 units: certainly within the cap
199
+ const chars = Array.from(s);
200
+ return chars.length > max ? chars.slice(0, max).join("") : s;
201
+ }
202
+ function isRecord(x) {
203
+ return typeof x === "object" && x !== null && !Array.isArray(x);
204
+ }
205
+ function wholeNumber(x) {
206
+ return typeof x === "number" && Number.isInteger(x) ? x : null;
207
+ }
208
+ /** The task arguments, checked and with defaults filled in; unknown keys are dropped.
209
+ * A goal that is missing, empty or over 2000 characters, or a `done_when` over 500, is
210
+ * `bad_args` (the sender fixes the words; nothing is cut off silently). `max_steps` and
211
+ * `timeout_s` must be whole numbers and are clamped into their range. Throws
212
+ * DeviceProtocolError("bad_args"). Used by the agent before it sends and by the device
213
+ * before it runs. */
214
+ function parseTaskArgs(body) {
215
+ if (!isRecord(body))
216
+ throw new DeviceProtocolError("bad_args", "task needs a goal");
217
+ const { goal, done_when, max_steps, timeout_s } = body;
218
+ if (typeof goal !== "string" || goal.trim().length === 0)
219
+ throw new DeviceProtocolError("bad_args", "goal must be a non-empty string");
220
+ if (charCount(goal) > exports.TASK_GOAL_MAX)
221
+ throw new DeviceProtocolError("bad_args", `goal is over ${exports.TASK_GOAL_MAX} characters`);
222
+ let doneWhen;
223
+ if (done_when !== undefined && done_when !== null) {
224
+ if (typeof done_when !== "string")
225
+ throw new DeviceProtocolError("bad_args", "done_when must be a string");
226
+ if (charCount(done_when) > exports.TASK_DONE_WHEN_MAX)
227
+ throw new DeviceProtocolError("bad_args", `done_when is over ${exports.TASK_DONE_WHEN_MAX} characters`);
228
+ doneWhen = done_when;
229
+ }
230
+ let steps = exports.TASK_MAX_STEPS_DEFAULT;
231
+ if (max_steps !== undefined && max_steps !== null) {
232
+ const n = wholeNumber(max_steps);
233
+ if (n === null)
234
+ throw new DeviceProtocolError("bad_args", "max_steps must be a whole number");
235
+ steps = Math.min(exports.TASK_MAX_STEPS_MAX, Math.max(exports.TASK_MAX_STEPS_MIN, n));
236
+ }
237
+ let timeout = exports.TASK_TIMEOUT_S_DEFAULT;
238
+ if (timeout_s !== undefined && timeout_s !== null) {
239
+ const n = wholeNumber(timeout_s);
240
+ if (n === null)
241
+ throw new DeviceProtocolError("bad_args", "timeout_s must be a whole number");
242
+ timeout = Math.min(exports.TASK_TIMEOUT_S_MAX, Math.max(exports.TASK_TIMEOUT_S_MIN, n));
243
+ }
244
+ const out = { goal, max_steps: steps, timeout_s: timeout };
245
+ if (doneWhen !== undefined && doneWhen !== "")
246
+ out.done_when = doneWhen;
247
+ return out;
248
+ }
249
+ /** The wire body of a `task` command: only the fields that were given. */
250
+ function taskCommandBody(goal, opts = {}) {
251
+ const raw = { goal };
252
+ if (opts.doneWhen !== undefined)
253
+ raw.done_when = opts.doneWhen;
254
+ if (opts.maxSteps !== undefined)
255
+ raw.max_steps = opts.maxSteps;
256
+ if (opts.timeoutS !== undefined)
257
+ raw.timeout_s = opts.timeoutS;
258
+ const p = parseTaskArgs(raw); // refuses what the device would refuse
259
+ const out = { goal: p.goal };
260
+ if (p.done_when !== undefined)
261
+ out.done_when = p.done_when;
262
+ if (opts.maxSteps !== undefined)
263
+ out.max_steps = p.max_steps;
264
+ if (opts.timeoutS !== undefined)
265
+ out.timeout_s = p.timeout_s;
266
+ return out;
267
+ }
268
+ /** A `progress` body, allow-listed with the caps applied; null when it is not one (no
269
+ * usable `step` or `line`). Nothing but `step`, `of`, `line` and `waiting` gets through. */
270
+ function normalizeProgress(raw) {
271
+ if (!isRecord(raw))
272
+ return null;
273
+ const step = wholeNumber(raw.step);
274
+ if (step === null || step < 0 || typeof raw.line !== "string")
275
+ return null;
276
+ const out = { step, line: clip(raw.line, exports.PROGRESS_LINE_MAX) };
277
+ const of = wholeNumber(raw.of);
278
+ if (of !== null && of >= 1)
279
+ out.of = of;
280
+ if (typeof raw.waiting === "boolean")
281
+ out.waiting = raw.waiting;
282
+ return out;
283
+ }
284
+ /** A fresh local task id, `t_<hex>`. */
285
+ function newTaskId(rand = Math.random) {
286
+ let hex = "";
287
+ for (let i = 0; i < 12; i++)
288
+ hex += Math.floor(rand() * 16).toString(16);
289
+ return `t_${hex}`;
290
+ }
291
+ /** A `task_result` body, allow-listed field by field with type checks and caps. Used by the
292
+ * device on what its handler returned and by the agent on what the device sent, so extra
293
+ * fields never travel and a malformed one becomes a safe default rather than a throw:
294
+ * an unknown `status` reads `failed`, `steps` keeps the first 60 valid rows, strings are cut
295
+ * to their caps, numbers must be whole and non-negative. `fallback` fills what is missing. */
296
+ function normalizeTaskResult(raw, fallback = {}) {
297
+ const r = isRecord(raw) ? raw : {};
298
+ const task_id = typeof r.task_id === "string" && r.task_id.length > 0 ? clip(r.task_id, exports.TASK_ID_MAX) : (fallback.task_id ?? newTaskId());
299
+ const status = exports.TASK_STATUSES.includes(r.status) ? r.status : "failed";
300
+ const summary = typeof r.summary === "string" ? clip(r.summary, exports.TASK_SUMMARY_MAX) : "";
301
+ let checked = null;
302
+ if (isRecord(r.checked) && typeof r.checked.ok === "boolean") {
303
+ checked = { ok: r.checked.ok, what: typeof r.checked.what === "string" ? clip(r.checked.what, exports.TASK_CHECKED_WHAT_MAX) : "" };
304
+ }
305
+ const steps = [];
306
+ if (Array.isArray(r.steps)) {
307
+ for (const s of r.steps) {
308
+ if (steps.length >= exports.TASK_RESULT_STEPS_MAX)
309
+ break;
310
+ if (!isRecord(s) || !exports.TASK_STEP_KINDS.includes(s.kind) || typeof s.line !== "string")
311
+ continue;
312
+ const n = wholeNumber(s.n);
313
+ steps.push({
314
+ n: n !== null && n >= 0 ? n : steps.length + 1,
315
+ kind: s.kind,
316
+ line: clip(s.line, exports.TASK_STEP_LINE_MAX),
317
+ ok: s.ok === true,
318
+ });
319
+ }
320
+ }
321
+ const commands = wholeNumber(r.commands);
322
+ const duration = wholeNumber(r.duration_ms);
323
+ return {
324
+ task_id,
325
+ status,
326
+ summary,
327
+ checked,
328
+ steps,
329
+ commands: commands !== null && commands >= 0 ? commands : 0,
330
+ duration_ms: duration !== null && duration >= 0 ? duration : (fallback.duration_ms ?? 0),
331
+ };
332
+ }
333
+ const MARKER_RE = /^\[\[SALT-DEVICE ((?:[a-z_]+=[^\s\]]+ ?)+)\]\]\s*([\s\S]*)$/;
334
+ /** Turn a header + optional body into the plaintext of one lane message. The
335
+ * caller encrypts the returned string for the lane readers and posts it. */
336
+ function encodeDeviceMessage(header, body) {
337
+ const parts = [
338
+ `v=${header.v}`,
339
+ `id=${header.id}`,
340
+ `seq=${header.seq}`,
341
+ `op=${header.op}`,
342
+ `session=${header.session}`,
343
+ ];
344
+ if (header.re !== undefined)
345
+ parts.push(`re=${header.re}`);
346
+ if (header.exp !== undefined)
347
+ parts.push(`exp=${header.exp}`);
348
+ const marker = `[[SALT-DEVICE ${parts.join(" ")}]]`;
349
+ if (body === undefined)
350
+ return marker;
351
+ return `${marker}\n${JSON.stringify(body)}`;
352
+ }
353
+ /** Parse the decrypted plaintext of a lane message. Returns the header + body,
354
+ * or `null` when the text is not a device message at all (it may be a plain
355
+ * chat line, which the caller ignores). Throws only on a message that LOOKS
356
+ * like one but is structurally broken, so the caller can answer `bad_args`. */
357
+ function decodeDeviceMessage(plaintext) {
358
+ const trimmed = plaintext.trimStart();
359
+ if (!trimmed.startsWith("[[SALT-DEVICE"))
360
+ return null;
361
+ const m = MARKER_RE.exec(trimmed);
362
+ if (!m)
363
+ throw new DeviceProtocolError("bad_args", "malformed SALT-DEVICE marker");
364
+ const fields = {};
365
+ for (const pair of m[1].trim().split(/\s+/)) {
366
+ const eq = pair.indexOf("=");
367
+ if (eq === -1)
368
+ throw new DeviceProtocolError("bad_args", `bad marker field: ${pair}`);
369
+ fields[pair.slice(0, eq)] = pair.slice(eq + 1);
370
+ }
371
+ for (const required of ["v", "id", "seq", "op", "session"]) {
372
+ if (fields[required] === undefined) {
373
+ throw new DeviceProtocolError("bad_args", `marker missing ${required}`);
374
+ }
375
+ }
376
+ const v = Number(fields.v);
377
+ const seq = Number(fields.seq);
378
+ if (!Number.isInteger(v) || !Number.isInteger(seq) || seq < 1) {
379
+ throw new DeviceProtocolError("bad_args", "v and seq must be positive integers");
380
+ }
381
+ const header = {
382
+ v,
383
+ id: fields.id,
384
+ seq,
385
+ op: fields.op,
386
+ session: fields.session,
387
+ };
388
+ if (fields.re !== undefined)
389
+ header.re = fields.re;
390
+ if (fields.exp !== undefined) {
391
+ const exp = Number(fields.exp);
392
+ if (!Number.isFinite(exp))
393
+ throw new DeviceProtocolError("bad_args", "exp must be a number");
394
+ header.exp = exp;
395
+ }
396
+ const rest = m[2];
397
+ let body;
398
+ if (rest.trim().length > 0) {
399
+ try {
400
+ body = JSON.parse(rest);
401
+ }
402
+ catch {
403
+ throw new DeviceProtocolError("bad_args", "SALT-DEVICE body is not JSON");
404
+ }
405
+ }
406
+ return { header, body };
407
+ }
408
+ class DeviceProtocolError extends Error {
409
+ code;
410
+ constructor(code, message) {
411
+ super(message ?? code);
412
+ this.name = "DeviceProtocolError";
413
+ this.code = code;
414
+ }
415
+ }
416
+ exports.DeviceProtocolError = DeviceProtocolError;
417
+ // ---- a tiny ULID (time-sortable, opaque to the server) ----------------------
418
+ const CROCKFORD = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
419
+ /** A ULID-shaped id for a command. The server treats it as an opaque token;
420
+ * the only property code here relies on is uniqueness. Sortable by time is a
421
+ * convenience for logs and nothing more (ordering is enforced by `seq`). */
422
+ function deviceMessageId(now = Date.now(), rand = Math.random) {
423
+ let time = "";
424
+ let t = now;
425
+ for (let i = 0; i < 10; i++) {
426
+ time = CROCKFORD[t % 32] + time;
427
+ t = Math.floor(t / 32);
428
+ }
429
+ let randPart = "";
430
+ for (let i = 0; i < 16; i++) {
431
+ randPart += CROCKFORD[Math.floor(rand() * 32)];
432
+ }
433
+ return time + randPart;
434
+ }
435
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/device/protocol.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
+ import { type SaltId } from "./ids.js";
1
2
  export interface AgentIdentity {
2
- saltAppId: number;
3
+ saltAppId: SaltId;
3
4
  username: string;
4
5
  displayName?: string;
5
6
  apiKey: string;
@@ -10,8 +11,14 @@ export interface AgentIdentity {
10
11
  }
11
12
  export interface IdentityStore {
12
13
  register(identity: AgentIdentity): void;
13
- get(saltAppId: number): AgentIdentity | undefined;
14
+ get(saltAppId: SaltId): AgentIdentity | undefined;
14
15
  all(): AgentIdentity[];
16
+ /**
17
+ * Move an identity from the id it was registered under to the id salt-api
18
+ * now uses for it (see reconcile.ts). Returns whatever is registered under
19
+ * `to` afterwards, or undefined when nothing was registered under `from`.
20
+ */
21
+ reassignId(from: SaltId, to: SaltId): AgentIdentity | undefined;
15
22
  }
16
23
  /**
17
24
  * Creates a JSON-file-backed identity store. Loads synchronously at
@@ -1 +1 @@
1
- {"version":3,"file":"identities.d.ts","sourceRoot":"","sources":["../src/identities.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,yGAAyG;IACzG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IACxC,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAAC;IAClD,GAAG,IAAI,aAAa,EAAE,CAAC;CACxB;AAID;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,GAAE,MAA2B,GAAG,aAAa,CA6CzF"}
1
+ {"version":3,"file":"identities.d.ts","sourceRoot":"","sources":["../src/identities.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAU,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AAE/C,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,yGAAyG;IACzG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IACxC,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAAC;IAClD,GAAG,IAAI,aAAa,EAAE,CAAC;IACvB;;;;OAIG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAAC;CACjE;AAID;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,GAAE,MAA2B,GAAG,aAAa,CAwEzF"}
@@ -50,6 +50,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
50
50
  exports.createIdentityStore = createIdentityStore;
51
51
  const fs = __importStar(require("node:fs"));
52
52
  const path = __importStar(require("node:path"));
53
+ const ids_js_1 = require("./ids.js");
53
54
  const DEFAULT_STORE_PATH = path.join(process.cwd(), "data", "identities.json");
54
55
  /**
55
56
  * Creates a JSON-file-backed identity store. Loads synchronously at
@@ -60,7 +61,12 @@ const DEFAULT_STORE_PATH = path.join(process.cwd(), "data", "identities.json");
60
61
  * them).
61
62
  */
62
63
  function createIdentityStore(storePath = DEFAULT_STORE_PATH) {
64
+ // Keyed by the lowercased id. Ids arrive from webhook bodies, REST
65
+ // responses and env vars, and a uuid that differs only in case would
66
+ // otherwise register as a second, separate identity -- the process would
67
+ // hold two copies of one agent and route to whichever it looked up first.
63
68
  const identities = new Map();
69
+ const mapKey = (id) => String(id).toLowerCase();
64
70
  function load() {
65
71
  let raw;
66
72
  try {
@@ -74,7 +80,7 @@ function createIdentityStore(storePath = DEFAULT_STORE_PATH) {
74
80
  }
75
81
  try {
76
82
  for (const identity of JSON.parse(raw)) {
77
- identities.set(identity.saltAppId, identity);
83
+ identities.set(mapKey(identity.saltAppId), identity);
78
84
  }
79
85
  }
80
86
  catch (err) {
@@ -93,15 +99,39 @@ function createIdentityStore(storePath = DEFAULT_STORE_PATH) {
93
99
  load();
94
100
  return {
95
101
  register(identity) {
96
- identities.set(identity.saltAppId, identity);
102
+ identities.set(mapKey(identity.saltAppId), identity);
97
103
  persist();
98
104
  },
99
105
  get(saltAppId) {
100
- return identities.get(saltAppId);
106
+ return identities.get(mapKey(saltAppId));
101
107
  },
102
108
  all() {
103
109
  return Array.from(identities.values());
104
110
  },
111
+ reassignId(from, to) {
112
+ const moving = identities.get(mapKey(from));
113
+ if (!moving)
114
+ return undefined;
115
+ if ((0, ids_js_1.sameId)(from, to))
116
+ return moving;
117
+ // Both entries name one agent -- that is what the API said when it
118
+ // answered `to` for `from`'s api key. It happens when the env var was
119
+ // corrected before the store was: boot registered the identity afresh
120
+ // under the new id while the old entry stayed on disk. Keep the entry
121
+ // already under `to` (it is what the process routes by, and the
122
+ // freshest config) and drop the stale duplicate rather than hold two
123
+ // copies of one agent.
124
+ const existing = identities.get(mapKey(to));
125
+ identities.delete(mapKey(from));
126
+ // Mutated in place rather than copied so a handler holding this
127
+ // identity mid-request sees the new id too.
128
+ if (!existing)
129
+ moving.saltAppId = to;
130
+ const survivor = existing ?? moving;
131
+ identities.set(mapKey(to), survivor);
132
+ persist();
133
+ return survivor;
134
+ },
105
135
  };
106
136
  }
107
137
  //# sourceMappingURL=identities.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"identities.js","sourceRoot":"","sources":["../src/identities.ts"],"names":[],"mappings":";AAAA,wEAAwE;AACxE,0EAA0E;AAC1E,sEAAsE;AACtE,yEAAyE;AACzE,yEAAyE;AACzE,sCAAsC;AACtC,EAAE;AACF,uEAAuE;AACvE,0EAA0E;AAC1E,2EAA2E;AAC3E,kEAAkE;AAClE,0EAA0E;AAC1E,0EAA0E;AAC1E,mEAAmE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCnE,kDA6CC;AA3ED,4CAA8B;AAC9B,gDAAkC;AAmBlC,MAAM,kBAAkB,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,iBAAiB,CAAC,CAAC;AAE/E;;;;;;;GAOG;AACH,SAAgB,mBAAmB,CAAC,YAAoB,kBAAkB;IACxE,MAAM,UAAU,GAAG,IAAI,GAAG,EAAyB,CAAC;IAEpD,SAAS,IAAI;QACX,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACH,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAC3C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACrD,OAAO,CAAC,KAAK,CAAC,oCAAoC,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC;YAC9E,CAAC;YACD,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAoB,EAAE,CAAC;gBAC1D,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YAC/C,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;IAED,SAAS,OAAO;QACd,IAAI,CAAC;YACH,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC3D,EAAE,CAAC,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACxF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC;QACjF,CAAC;IACH,CAAC;IAED,IAAI,EAAE,CAAC;IAEP,OAAO;QACL,QAAQ,CAAC,QAAuB;YAC9B,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YAC7C,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,GAAG,CAAC,SAAiB;YACnB,OAAO,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACnC,CAAC;QACD,GAAG;YACD,OAAO,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;QACzC,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"identities.js","sourceRoot":"","sources":["../src/identities.ts"],"names":[],"mappings":";AAAA,wEAAwE;AACxE,0EAA0E;AAC1E,sEAAsE;AACtE,yEAAyE;AACzE,yEAAyE;AACzE,sCAAsC;AACtC,EAAE;AACF,uEAAuE;AACvE,0EAA0E;AAC1E,2EAA2E;AAC3E,kEAAkE;AAClE,0EAA0E;AAC1E,0EAA0E;AAC1E,mEAAmE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCnE,kDAwEC;AA9GD,4CAA8B;AAC9B,gDAAkC;AAElC,qCAA+C;AAyB/C,MAAM,kBAAkB,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,iBAAiB,CAAC,CAAC;AAE/E;;;;;;;GAOG;AACH,SAAgB,mBAAmB,CAAC,YAAoB,kBAAkB;IACxE,mEAAmE;IACnE,qEAAqE;IACrE,yEAAyE;IACzE,0EAA0E;IAC1E,MAAM,UAAU,GAAG,IAAI,GAAG,EAAyB,CAAC;IACpD,MAAM,MAAM,GAAG,CAAC,EAAU,EAAU,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAEhE,SAAS,IAAI;QACX,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACH,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAC3C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACrD,OAAO,CAAC,KAAK,CAAC,oCAAoC,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC;YAC9E,CAAC;YACD,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAoB,EAAE,CAAC;gBAC1D,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,CAAC;YACvD,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;IAED,SAAS,OAAO;QACd,IAAI,CAAC;YACH,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC3D,EAAE,CAAC,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACxF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC;QACjF,CAAC;IACH,CAAC;IAED,IAAI,EAAE,CAAC;IAEP,OAAO;QACL,QAAQ,CAAC,QAAuB;YAC9B,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,CAAC;YACrD,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,GAAG,CAAC,SAAiB;YACnB,OAAO,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;QAC3C,CAAC;QACD,GAAG;YACD,OAAO,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;QACzC,CAAC;QACD,UAAU,CAAC,IAAY,EAAE,EAAU;YACjC,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;YAC5C,IAAI,CAAC,MAAM;gBAAE,OAAO,SAAS,CAAC;YAC9B,IAAI,IAAA,eAAM,EAAC,IAAI,EAAE,EAAE,CAAC;gBAAE,OAAO,MAAM,CAAC;YAEpC,mEAAmE;YACnE,sEAAsE;YACtE,sEAAsE;YACtE,sEAAsE;YACtE,gEAAgE;YAChE,qEAAqE;YACrE,uBAAuB;YACvB,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;YAC5C,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;YAChC,gEAAgE;YAChE,4CAA4C;YAC5C,IAAI,CAAC,QAAQ;gBAAE,MAAM,CAAC,SAAS,GAAG,EAAE,CAAC;YACrC,MAAM,QAAQ,GAAG,QAAQ,IAAI,MAAM,CAAC;YACpC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC;YACrC,OAAO,EAAE,CAAC;YACV,OAAO,QAAQ,CAAC;QAClB,CAAC;KACF,CAAC;AACJ,CAAC"}