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.
- package/LICENSE +21 -0
- package/README.md +941 -30
- package/dist/actions.d.ts +21 -5
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +673 -54
- package/dist/actions.js.map +1 -1
- package/dist/ask.d.ts +83 -0
- package/dist/ask.d.ts.map +1 -0
- package/dist/ask.js +215 -0
- package/dist/ask.js.map +1 -0
- package/dist/chatPrivacy.d.ts +34 -0
- package/dist/chatPrivacy.d.ts.map +1 -0
- package/dist/chatPrivacy.js +60 -0
- package/dist/chatPrivacy.js.map +1 -0
- package/dist/client.d.ts +2783 -35
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +1200 -143
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +16 -6
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +16 -5
- package/dist/config.js.map +1 -1
- package/dist/crypto.d.ts +62 -0
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +121 -0
- package/dist/crypto.js.map +1 -1
- package/dist/delegations.d.ts +25 -8
- package/dist/delegations.d.ts.map +1 -1
- package/dist/delegations.js +67 -11
- package/dist/delegations.js.map +1 -1
- package/dist/device/client.d.ts +161 -0
- package/dist/device/client.d.ts.map +1 -0
- package/dist/device/client.js +601 -0
- package/dist/device/client.js.map +1 -0
- package/dist/device/host.d.ts +222 -0
- package/dist/device/host.d.ts.map +1 -0
- package/dist/device/host.js +891 -0
- package/dist/device/host.js.map +1 -0
- package/dist/device/http.d.ts +12 -0
- package/dist/device/http.d.ts.map +1 -0
- package/dist/device/http.js +124 -0
- package/dist/device/http.js.map +1 -0
- package/dist/device/index.d.ts +5 -0
- package/dist/device/index.d.ts.map +1 -0
- package/dist/device/index.js +27 -0
- package/dist/device/index.js.map +1 -0
- package/dist/device/protocol.d.ts +312 -0
- package/dist/device/protocol.d.ts.map +1 -0
- package/dist/device/protocol.js +435 -0
- package/dist/device/protocol.js.map +1 -0
- package/dist/identities.d.ts +9 -2
- package/dist/identities.d.ts.map +1 -1
- package/dist/identities.js +33 -3
- package/dist/identities.js.map +1 -1
- package/dist/identity.d.ts +155 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +196 -0
- package/dist/identity.js.map +1 -0
- package/dist/identityShare.d.ts +129 -0
- package/dist/identityShare.d.ts.map +1 -0
- package/dist/identityShare.js +308 -0
- package/dist/identityShare.js.map +1 -0
- package/dist/ids.d.ts +11 -0
- package/dist/ids.d.ts.map +1 -0
- package/dist/ids.js +17 -0
- package/dist/ids.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -1
- package/dist/reconcile.d.ts +25 -0
- package/dist/reconcile.d.ts.map +1 -0
- package/dist/reconcile.js +60 -0
- package/dist/reconcile.js.map +1 -0
- package/dist/recovery.d.ts +51 -0
- package/dist/recovery.d.ts.map +1 -0
- package/dist/recovery.js +59 -0
- package/dist/recovery.js.map +1 -0
- package/dist/register.d.ts +32 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +65 -0
- package/dist/register.js.map +1 -0
- package/dist/scriptJob.d.ts +92 -0
- package/dist/scriptJob.d.ts.map +1 -0
- package/dist/scriptJob.js +270 -0
- package/dist/scriptJob.js.map +1 -0
- package/dist/sessions.d.ts +131 -0
- package/dist/sessions.d.ts.map +1 -0
- package/dist/sessions.js +240 -0
- package/dist/sessions.js.map +1 -0
- package/dist/socket.d.ts +115 -0
- package/dist/socket.d.ts.map +1 -0
- package/dist/socket.js +1038 -0
- package/dist/socket.js.map +1 -0
- package/dist/webhook.d.ts +613 -24
- package/dist/webhook.d.ts.map +1 -1
- package/dist/webhook.js +1384 -83
- package/dist/webhook.js.map +1 -1
- package/dist/work.d.ts +57 -0
- package/dist/work.d.ts.map +1 -0
- package/dist/work.js +213 -0
- package/dist/work.js.map +1 -0
- 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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"}
|
package/dist/identities.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { type SaltId } from "./ids.js";
|
|
1
2
|
export interface AgentIdentity {
|
|
2
|
-
saltAppId:
|
|
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:
|
|
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
|
package/dist/identities.d.ts.map
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/identities.js
CHANGED
|
@@ -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
|
package/dist/identities.js.map
CHANGED
|
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"identities.js","sourceRoot":"","sources":["../src/identities.ts"],"names":[],"mappings":"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"}
|