@deveye/types 0.15.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 (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +23 -0
  3. package/package.json +68 -0
  4. package/src/domain/audience.ts +549 -0
  5. package/src/domain/backup.ts +355 -0
  6. package/src/domain/credential.ts +55 -0
  7. package/src/domain/database.ts +467 -0
  8. package/src/domain/deploy.ts +231 -0
  9. package/src/domain/device.ts +172 -0
  10. package/src/domain/deviceFiles.ts +84 -0
  11. package/src/domain/deviceLogs.ts +82 -0
  12. package/src/domain/featureRegistry.ts +392 -0
  13. package/src/domain/finance.ts +477 -0
  14. package/src/domain/git.ts +419 -0
  15. package/src/domain/home.ts +314 -0
  16. package/src/domain/live.ts +272 -0
  17. package/src/domain/logs.ts +117 -0
  18. package/src/domain/mail.ts +394 -0
  19. package/src/domain/metrics.ts +127 -0
  20. package/src/domain/note.ts +202 -0
  21. package/src/domain/notifications.ts +268 -0
  22. package/src/domain/packages.ts +35 -0
  23. package/src/domain/password.ts +36 -0
  24. package/src/domain/presence.ts +21 -0
  25. package/src/domain/project.ts +168 -0
  26. package/src/domain/projectBoard.ts +130 -0
  27. package/src/domain/projectChat.ts +46 -0
  28. package/src/domain/projectHistory.ts +82 -0
  29. package/src/domain/projectLink.ts +87 -0
  30. package/src/domain/projectPlan.ts +68 -0
  31. package/src/domain/report.ts +492 -0
  32. package/src/domain/role.ts +8 -0
  33. package/src/domain/secrecy.ts +66 -0
  34. package/src/domain/sentinel.ts +623 -0
  35. package/src/domain/sharing.ts +186 -0
  36. package/src/domain/syncProtocol.ts +116 -0
  37. package/src/domain/twoFactor.ts +40 -0
  38. package/src/domain/uptime.ts +216 -0
  39. package/src/domain/user.ts +141 -0
  40. package/src/domain/workspace.ts +56 -0
  41. package/src/domain/workspaceRole.ts +251 -0
  42. package/src/features/admin.ts +112 -0
  43. package/src/features/audience.ts +275 -0
  44. package/src/features/backup.ts +230 -0
  45. package/src/features/database.ts +461 -0
  46. package/src/features/deploy.ts +245 -0
  47. package/src/features/device.ts +292 -0
  48. package/src/features/deviceFiles.ts +83 -0
  49. package/src/features/deviceLogs.ts +36 -0
  50. package/src/features/deviceTerminal.ts +57 -0
  51. package/src/features/finance.ts +360 -0
  52. package/src/features/git.ts +368 -0
  53. package/src/features/home.ts +32 -0
  54. package/src/features/live.ts +113 -0
  55. package/src/features/logs.ts +86 -0
  56. package/src/features/mail.ts +374 -0
  57. package/src/features/metrics.ts +185 -0
  58. package/src/features/note.ts +189 -0
  59. package/src/features/notify.ts +164 -0
  60. package/src/features/password.ts +67 -0
  61. package/src/features/project.ts +709 -0
  62. package/src/features/registry.ts +103 -0
  63. package/src/features/secrecy.ts +120 -0
  64. package/src/features/sentinel.ts +233 -0
  65. package/src/features/sharing.ts +79 -0
  66. package/src/features/twoFactor.ts +47 -0
  67. package/src/features/uptime.ts +186 -0
  68. package/src/features/user.ts +91 -0
  69. package/src/features/workspace.ts +200 -0
  70. package/src/http/auth.ts +94 -0
  71. package/src/http/device.ts +222 -0
  72. package/src/http/status.ts +45 -0
  73. package/src/index.ts +1700 -0
  74. package/src/protocol/agent.ts +1171 -0
  75. package/src/protocol/envelope.ts +46 -0
  76. package/src/protocol/error.ts +27 -0
  77. package/src/protocol/result.ts +17 -0
  78. package/src/protocol/version.ts +6 -0
  79. package/src/sdk/client-ambient.d.ts +238 -0
  80. package/src/sdk/client.ts +66 -0
  81. package/src/sdk/ids.ts +25 -0
  82. package/src/sdk/index.ts +11 -0
  83. package/src/sdk/manifest.ts +326 -0
  84. package/src/sdk/providers.ts +48 -0
  85. package/src/sdk/server.ts +378 -0
  86. package/src/sdk/testing.ts +179 -0
  87. package/src/utils/version.ts +28 -0
@@ -0,0 +1,1171 @@
1
+ import { z } from 'zod';
2
+ import {
3
+ cloudSyncProgressSchema,
4
+ cloudSyncShareStateSchema,
5
+ sha256HexSchema,
6
+ SYNC_CHUNK_MAX,
7
+ SYNC_INDEX_BATCH_MAX,
8
+ SYNC_PATTERN_MAX,
9
+ SYNC_REL_PATH_MAX,
10
+ SYNC_STORAGE_PATH_MAX,
11
+ syncEntryKindSchema,
12
+ syncExclusionKindSchema,
13
+ syncIndexFingerprintSchema,
14
+ syncScanModeSchema,
15
+ syncShareStatusSchema
16
+ } from '../domain/syncProtocol';
17
+ import {
18
+ fileListingSchema,
19
+ fileMatchSchema,
20
+ fileMutateOpSchema,
21
+ fileSearchFilterSchema,
22
+ fileUsageEntrySchema
23
+ } from '../domain/deviceFiles';
24
+ import {
25
+ deviceLogFilterSchema,
26
+ deviceLogLineSchema,
27
+ deviceLogSourceSchema
28
+ } from '../domain/deviceLogs';
29
+ import { metricsBatchSchema, metricSnapshotSchema } from '../domain/metrics';
30
+ import { packageManagerIdSchema, packageManagerSchema } from '../domain/packages';
31
+ import {
32
+ authWindowSchema,
33
+ deviceReportSchema,
34
+ integrityReportSchema,
35
+ processCaptureSchema
36
+ } from '../domain/report';
37
+ import { agentTargetSchema } from '../http/device';
38
+ import { ProtocolErrorSchema } from './error';
39
+
40
+ /**
41
+ * Agent <-> Server wire protocol (distinct from the user feature protocol).
42
+ *
43
+ * The agent authenticates with its device token, then streams metric batches.
44
+ * The server acknowledges and may push commands (reserved for later).
45
+ */
46
+
47
+ /** Command names the agent may send to the server. */
48
+ export const AGENT_METRICS_BATCH = 'metrics.batch' as const;
49
+ export const AGENT_HELLO = 'agent.hello' as const;
50
+ export const AGENT_REPORT = 'agent.report' as const;
51
+ /** Agent's reply to `agent.destroy`: whether it managed to wipe itself. */
52
+ export const AGENT_DESTROYED = 'agent.destroyed' as const;
53
+ /** Agent's reply to `agent.update`: outcome of a self-update attempt. */
54
+ export const AGENT_UPDATED = 'agent.updated' as const;
55
+
56
+ export const agentUpdatedMessagePayloadSchema = z.object({
57
+ deviceId: z.uuid(),
58
+ /** True when the new binary was verified and swapped in (a restart follows). */
59
+ ok: z.boolean(),
60
+ /** Version the agent updated to, when `ok`. */
61
+ version: z.string().optional(),
62
+ /** Why the update was refused/aborted, when `!ok` (binary left untouched). */
63
+ error: z.string().max(255).optional()
64
+ });
65
+
66
+ /**
67
+ * Persistence/privilege action the server can ask the agent to perform:
68
+ * - `install-user` install a per-user autostart (no privilege needed),
69
+ * - `uninstall-user` remove it,
70
+ * - `elevate` (try to) become a root/system service — see the hybrid flow,
71
+ * - `drop` go back from system/root to a per-user service.
72
+ */
73
+ export const agentServiceActionSchema = z.enum([
74
+ 'install-user',
75
+ 'uninstall-user',
76
+ 'elevate',
77
+ 'drop'
78
+ ]);
79
+ export type AgentServiceAction = z.infer<typeof agentServiceActionSchema>;
80
+
81
+ /**
82
+ * System power action the server can ask the agent to perform on its host.
83
+ * Best-effort per platform; the agent reports the outcome (`agent.powerResult`).
84
+ * - `shutdown` power the machine off,
85
+ * - `reboot` restart it,
86
+ * - `suspend` sleep (suspend to RAM),
87
+ * - `hibernate` deep sleep (suspend to disk),
88
+ * - `lock` lock the screen/session (the machine keeps running).
89
+ */
90
+ export const agentPowerActionSchema = z.enum([
91
+ 'shutdown',
92
+ 'reboot',
93
+ 'suspend',
94
+ 'hibernate',
95
+ 'lock'
96
+ ]);
97
+ export type AgentPowerAction = z.infer<typeof agentPowerActionSchema>;
98
+
99
+ /** Agent's reply to `pkg.list`: the package managers present + their pending counts. */
100
+ export const AGENT_PKG_LIST_RESULT = 'pkg.listResult' as const;
101
+
102
+ export const agentPkgListResultPayloadSchema = z.object({
103
+ deviceId: z.uuid(),
104
+ managers: z.array(packageManagerSchema)
105
+ });
106
+
107
+ /** Live line of an in-progress `pkg.upgrade`. */
108
+ export const AGENT_PKG_PROGRESS = 'pkg.progress' as const;
109
+
110
+ export const agentPkgProgressPayloadSchema = z.object({
111
+ deviceId: z.uuid(),
112
+ manager: packageManagerIdSchema,
113
+ /** 0–100 when the tool emits it, else null (synthesise i/N or show the line). */
114
+ percent: z.number().min(0).max(100).nullable().optional(),
115
+ /** Coarse phase label (e.g. `download`, `install`) when derivable. */
116
+ phase: z.string().max(40).optional(),
117
+ /** The raw output line (already trimmed) to surface in the live log. */
118
+ line: z.string().max(2000)
119
+ });
120
+
121
+ /** Final outcome of a `pkg.upgrade`. */
122
+ export const AGENT_PKG_DONE = 'pkg.done' as const;
123
+
124
+ export const agentPkgDonePayloadSchema = z.object({
125
+ deviceId: z.uuid(),
126
+ manager: packageManagerIdSchema,
127
+ ok: z.boolean(),
128
+ rebootRequired: z.boolean().optional(),
129
+ error: z.string().max(500).optional()
130
+ });
131
+
132
+ /** Agent's reply to `agent.service`: outcome of a persistence/privilege change. */
133
+ export const AGENT_SERVICE_RESULT = 'agent.serviceResult' as const;
134
+
135
+ export const agentServiceResultPayloadSchema = z.object({
136
+ deviceId: z.uuid(),
137
+ action: agentServiceActionSchema,
138
+ ok: z.boolean(),
139
+ /**
140
+ * For `elevate`/`drop`: the agent had no interactive session to pop an OS auth
141
+ * prompt, so the user must run the elevated command on the device manually
142
+ * (the UI already shows it). The action itself was not performed.
143
+ */
144
+ needsManualCommand: z.boolean().optional(),
145
+ error: z.string().max(255).optional()
146
+ });
147
+
148
+ /** Agent's reply to `agent.power`: outcome of a system power action. */
149
+ export const AGENT_POWER_RESULT = 'agent.powerResult' as const;
150
+
151
+ export const agentPowerResultPayloadSchema = z.object({
152
+ deviceId: z.uuid(),
153
+ action: agentPowerActionSchema,
154
+ ok: z.boolean(),
155
+ /** Why the action could not be carried out, when `!ok`. */
156
+ error: z.string().max(255).optional()
157
+ });
158
+
159
+ /** Agent's reply to `log.sources`: the log sources discovered on the device. */
160
+ export const AGENT_LOG_SOURCES_RESULT = 'log.sourcesResult' as const;
161
+
162
+ export const agentLogSourcesResultPayloadSchema = z.object({
163
+ deviceId: z.uuid(),
164
+ sources: z.array(deviceLogSourceSchema)
165
+ });
166
+
167
+ /** Agent's reply to `log.query`: a chunk of matched lines (last one `done: true`). */
168
+ export const AGENT_LOG_LINES = 'log.lines' as const;
169
+
170
+ export const agentLogLinesPayloadSchema = z.object({
171
+ deviceId: z.uuid(),
172
+ /** Correlates back to the originating `log.query` (echoed verbatim). */
173
+ queryId: z.string().max(64),
174
+ lines: z.array(deviceLogLineSchema),
175
+ /** True on the final chunk of this query. */
176
+ done: z.boolean(),
177
+ /** Set on the final chunk when the query failed. */
178
+ error: z.string().max(500).optional()
179
+ });
180
+
181
+ /** Base64-encoded terminal payload (raw PTY bytes), capped per frame (~1.5 MB). */
182
+ const terminalDataSchema = z.string().max(2_000_000);
183
+
184
+ /** Agent → server: a chunk of PTY output for one terminal session. */
185
+ export const AGENT_TERM_OUTPUT = 'term.output' as const;
186
+
187
+ export const agentTermOutputPayloadSchema = z.object({
188
+ deviceId: z.uuid(),
189
+ /** Client-generated session id (echoed from `term.open`). */
190
+ sessionId: z.string().max(64),
191
+ /** Base64 of the raw PTY output bytes. */
192
+ data: terminalDataSchema
193
+ });
194
+
195
+ /** Agent → server: a terminal session ended (shell exited, killed, or open failed). */
196
+ export const AGENT_TERM_EXIT = 'term.exit' as const;
197
+
198
+ export const agentTermExitPayloadSchema = z.object({
199
+ deviceId: z.uuid(),
200
+ sessionId: z.string().max(64),
201
+ /** Process exit code when known. */
202
+ code: z.number().int().nullable().optional(),
203
+ /** Reason when the session ended on an error (e.g. the shell couldn't spawn). */
204
+ error: z.string().max(255).optional()
205
+ });
206
+
207
+ /** Agent → server: a directory listing (reply to `files.list`). */
208
+ export const AGENT_FILES_LISTING = 'files.listing' as const;
209
+ export const agentFilesListingPayloadSchema = z.object({
210
+ deviceId: z.uuid(),
211
+ opId: z.string().max(64),
212
+ listing: fileListingSchema.nullable(),
213
+ /** Set when the directory couldn't be read. */
214
+ error: z.string().max(500).optional()
215
+ });
216
+
217
+ /** Agent → server: recursive usage of a directory's children (reply to `files.analyze`). */
218
+ export const AGENT_FILES_USAGE = 'files.usage' as const;
219
+ export const agentFilesUsagePayloadSchema = z.object({
220
+ deviceId: z.uuid(),
221
+ opId: z.string().max(64),
222
+ entries: z.array(fileUsageEntrySchema),
223
+ error: z.string().max(500).optional()
224
+ });
225
+
226
+ /** Agent → server: search hits (reply to `files.search`). */
227
+ export const AGENT_FILES_MATCHES = 'files.matches' as const;
228
+ export const agentFilesMatchesPayloadSchema = z.object({
229
+ deviceId: z.uuid(),
230
+ opId: z.string().max(64),
231
+ matches: z.array(fileMatchSchema),
232
+ /** True when the result was capped (more hits exist). */
233
+ truncated: z.boolean(),
234
+ error: z.string().max(500).optional()
235
+ });
236
+
237
+ /** Agent → server: outcome of a `files.mutate` (delete/mkdir/rename) or an upload. */
238
+ export const AGENT_FILES_OP_RESULT = 'files.opResult' as const;
239
+ export const agentFilesOpResultPayloadSchema = z.object({
240
+ deviceId: z.uuid(),
241
+ opId: z.string().max(64),
242
+ /** The operation label (a mutate op, or `upload`). */
243
+ op: z.string().max(32),
244
+ ok: z.boolean(),
245
+ error: z.string().max(500).optional()
246
+ });
247
+
248
+ /** Agent → server: one chunk of a downloaded file (`data` base64; last has `done`). */
249
+ export const AGENT_FILES_CHUNK = 'files.chunk' as const;
250
+ export const agentFilesChunkPayloadSchema = z.object({
251
+ deviceId: z.uuid(),
252
+ opId: z.string().max(64),
253
+ /** Base64 of the raw file bytes for this chunk (empty on the terminal frame). */
254
+ data: z.string().max(1_400_000),
255
+ /** True on the final chunk. */
256
+ done: z.boolean(),
257
+ /** Set on the final chunk when the download failed. */
258
+ error: z.string().max(500).optional()
259
+ });
260
+
261
+ const syncOpId = z.string().min(1).max(64);
262
+ const syncRelPath = z.string().min(1).max(SYNC_REL_PATH_MAX);
263
+
264
+ /**
265
+ * CloudSync, agent → serveur. La correction est toujours basée scan : l'agent
266
+ * ne pousse jamais de contenu spontanément, il signale (`sync.changed`), scanne
267
+ * sur ordre (`sync.index`), et transfère sur ordre (`sync.chunk`).
268
+ */
269
+
270
+ /** Le watcher local (débouncé) a vu bouger le dossier d'un partage. */
271
+ export const AGENT_SYNC_CHANGED = 'sync.changed' as const;
272
+ export const agentSyncChangedPayloadSchema = z.object({
273
+ deviceId: z.uuid(),
274
+ shareId: z.number().int().positive()
275
+ });
276
+ export type AgentSyncChangedPayload = z.infer<typeof agentSyncChangedPayloadSchema>;
277
+
278
+ /**
279
+ * Une entrée du scan local (exclusions déjà appliquées). Fichiers réguliers,
280
+ * plus les dossiers VIDES : un dossier non vide est implicite (ses fichiers le
281
+ * recréent), et l'indexer coûterait une ligne par dossier pour rien.
282
+ */
283
+ export const syncIndexEntrySchema = z.object({
284
+ relPath: syncRelPath,
285
+ kind: syncEntryKindSchema.default('file'),
286
+ hash: sha256HexSchema,
287
+ size: z.number().int().nonnegative(),
288
+ /** Millisecondes unix. */
289
+ mtime: z.number().int().nonnegative(),
290
+ /** Bits de permission Unix (`& 0o777`) ; `null` depuis un agent Windows. */
291
+ mode: z.number().int().min(0).max(0o777).nullable().default(null)
292
+ });
293
+ export type SyncIndexEntry = z.infer<typeof syncIndexEntrySchema>;
294
+
295
+ /** Un lot d'index du scan (réponse à `sync.scan`, dernier lot `done: true`). */
296
+ export const AGENT_SYNC_INDEX = 'sync.index' as const;
297
+ export const agentSyncIndexPayloadSchema = z.object({
298
+ deviceId: z.uuid(),
299
+ sessionId: syncOpId,
300
+ shareId: z.number().int().positive(),
301
+ entries: z.array(syncIndexEntrySchema).max(SYNC_INDEX_BATCH_MAX),
302
+ done: z.boolean(),
303
+ /**
304
+ * L'agent a-t-il RÉELLEMENT parcouru le disque ?
305
+ *
306
+ * `false` répond à un `sync.scan` en mode `auto` sur un partage que le
307
+ * watcher sait intact depuis le dernier scan : aucune entrée n'est envoyée,
308
+ * seule `fingerprint` est renseignée, et c'est au serveur de vérifier qu'elle
309
+ * correspond à la baseline qu'il détient. Il ne SUPPOSE donc jamais que
310
+ * l'appareil est à jour, il le VÉRIFIE — une vérification fausse coûte un
311
+ * scan complet, jamais une divergence.
312
+ *
313
+ * Le défaut `true` est ce qui rend un vieil agent inoffensif : sans le champ,
314
+ * le serveur retombe sur le chemin d'aujourd'hui.
315
+ */
316
+ scanned: z.boolean().default(true),
317
+ /** Empreinte de l'index détenu par l'agent, quand il sait la calculer. */
318
+ fingerprint: syncIndexFingerprintSchema.nullable().default(null),
319
+ /** Posé sur le lot final quand le scan a échoué (la session est abandonnée). */
320
+ error: z.string().max(500).optional()
321
+ });
322
+ export type AgentSyncIndexPayload = z.infer<typeof agentSyncIndexPayloadSchema>;
323
+
324
+ /**
325
+ * Un chunk d'upload (réponse à `sync.push`). La frame finale porte le hash,
326
+ * la taille et le mtime constatés — s'ils diffèrent de l'annonce du scan, le
327
+ * fichier a bougé entre-temps et le serveur jette le transfert.
328
+ */
329
+ export const AGENT_SYNC_CHUNK = 'sync.chunk' as const;
330
+ export const agentSyncChunkPayloadSchema = z.object({
331
+ deviceId: z.uuid(),
332
+ opId: syncOpId,
333
+ /** Base64 des octets du chunk (vide autorisé sur la frame terminale). */
334
+ data: z.string().max(SYNC_CHUNK_MAX),
335
+ done: z.boolean(),
336
+ hash: sha256HexSchema.optional(),
337
+ size: z.number().int().nonnegative().optional(),
338
+ mtime: z.number().int().nonnegative().optional(),
339
+ error: z.string().max(500).optional()
340
+ });
341
+ export type AgentSyncChunkPayload = z.infer<typeof agentSyncChunkPayloadSchema>;
342
+
343
+ /** Crédit de flux : l'agent a écrit le chunk `seq` d'un `sync.applyChunk`. */
344
+ export const AGENT_SYNC_ACK = 'sync.ack' as const;
345
+ export const agentSyncAckPayloadSchema = z.object({
346
+ deviceId: z.uuid(),
347
+ opId: syncOpId,
348
+ seq: z.number().int().nonnegative()
349
+ });
350
+ export type AgentSyncAckPayload = z.infer<typeof agentSyncAckPayloadSchema>;
351
+
352
+ /**
353
+ * Issue d'une op locale : install atomique (`apply`), création de dossier vide
354
+ * (`applyDir`), copie locale d'un contenu déjà présent (`applyLocal`), amorce
355
+ * de download avec offset de reprise (`applyReady`), corbeille (`delete`),
356
+ * upload (`push`).
357
+ */
358
+ export const AGENT_SYNC_OP_RESULT = 'sync.opResult' as const;
359
+ export const agentSyncOpResultPayloadSchema = z.object({
360
+ deviceId: z.uuid(),
361
+ opId: syncOpId,
362
+ op: z.enum(['apply', 'applyDir', 'applyLocal', 'applyReady', 'delete', 'move', 'push']),
363
+ ok: z.boolean(),
364
+ /** `applyReady` seulement : octets de clair déjà détenus pour ce hash. */
365
+ resumeFrom: z.number().int().nonnegative().optional(),
366
+ error: z.string().max(500).optional()
367
+ });
368
+ export type AgentSyncOpResultPayload = z.infer<typeof agentSyncOpResultPayloadSchema>;
369
+
370
+ export const agentReportMessagePayloadSchema = z.object({
371
+ deviceId: z.uuid(),
372
+ report: deviceReportSchema
373
+ });
374
+
375
+ /** Manifeste des surfaces de persistance (Sentinelle). */
376
+ export const AGENT_INTEGRITY = 'agent.integrity' as const;
377
+
378
+ export const agentIntegrityMessagePayloadSchema = z.object({
379
+ deviceId: z.uuid(),
380
+ integrity: integrityReportSchema
381
+ });
382
+
383
+ /** Fenêtre d'issues d'authentification (Sentinelle). */
384
+ export const AGENT_AUTH_EVENTS = 'agent.authEvents' as const;
385
+
386
+ export const agentAuthEventsMessagePayloadSchema = z.object({
387
+ deviceId: z.uuid(),
388
+ auth: authWindowSchema
389
+ });
390
+
391
+ export const agentDestroyedMessagePayloadSchema = z.object({
392
+ deviceId: z.uuid(),
393
+ /** True when the agent successfully wiped its local config (and binary). */
394
+ ok: z.boolean(),
395
+ /** Failure reason when `ok` is false (deletion is then aborted server-side). */
396
+ error: z.string().max(255).optional()
397
+ });
398
+
399
+ export const agentClientMessageSchema = z.discriminatedUnion('command', [
400
+ z.object({
401
+ command: z.literal(AGENT_HELLO),
402
+ payload: z.object({
403
+ agentVersion: z.string().min(1),
404
+ /**
405
+ * Build target the running agent was compiled for (e.g. `linux-x86_64`).
406
+ * Lets the server resolve which binary to push for a self-update.
407
+ * Optional: agents predating self-update don't send it.
408
+ */
409
+ target: agentTargetSchema.optional()
410
+ })
411
+ }),
412
+ z.object({
413
+ command: z.literal(AGENT_METRICS_BATCH),
414
+ payload: metricsBatchSchema
415
+ }),
416
+ z.object({
417
+ command: z.literal(AGENT_REPORT),
418
+ payload: agentReportMessagePayloadSchema
419
+ }),
420
+ z.object({
421
+ command: z.literal(AGENT_INTEGRITY),
422
+ payload: agentIntegrityMessagePayloadSchema
423
+ }),
424
+ z.object({
425
+ command: z.literal(AGENT_AUTH_EVENTS),
426
+ payload: agentAuthEventsMessagePayloadSchema
427
+ }),
428
+ z.object({
429
+ command: z.literal(AGENT_DESTROYED),
430
+ payload: agentDestroyedMessagePayloadSchema
431
+ }),
432
+ z.object({
433
+ command: z.literal(AGENT_UPDATED),
434
+ payload: agentUpdatedMessagePayloadSchema
435
+ }),
436
+ z.object({
437
+ command: z.literal(AGENT_SERVICE_RESULT),
438
+ payload: agentServiceResultPayloadSchema
439
+ }),
440
+ z.object({
441
+ command: z.literal(AGENT_PKG_LIST_RESULT),
442
+ payload: agentPkgListResultPayloadSchema
443
+ }),
444
+ z.object({
445
+ command: z.literal(AGENT_PKG_PROGRESS),
446
+ payload: agentPkgProgressPayloadSchema
447
+ }),
448
+ z.object({
449
+ command: z.literal(AGENT_PKG_DONE),
450
+ payload: agentPkgDonePayloadSchema
451
+ }),
452
+ z.object({
453
+ command: z.literal(AGENT_POWER_RESULT),
454
+ payload: agentPowerResultPayloadSchema
455
+ }),
456
+ z.object({
457
+ command: z.literal(AGENT_LOG_SOURCES_RESULT),
458
+ payload: agentLogSourcesResultPayloadSchema
459
+ }),
460
+ z.object({
461
+ command: z.literal(AGENT_LOG_LINES),
462
+ payload: agentLogLinesPayloadSchema
463
+ }),
464
+ z.object({
465
+ command: z.literal(AGENT_TERM_OUTPUT),
466
+ payload: agentTermOutputPayloadSchema
467
+ }),
468
+ z.object({
469
+ command: z.literal(AGENT_TERM_EXIT),
470
+ payload: agentTermExitPayloadSchema
471
+ }),
472
+ z.object({
473
+ command: z.literal(AGENT_FILES_LISTING),
474
+ payload: agentFilesListingPayloadSchema
475
+ }),
476
+ z.object({
477
+ command: z.literal(AGENT_FILES_USAGE),
478
+ payload: agentFilesUsagePayloadSchema
479
+ }),
480
+ z.object({
481
+ command: z.literal(AGENT_FILES_MATCHES),
482
+ payload: agentFilesMatchesPayloadSchema
483
+ }),
484
+ z.object({
485
+ command: z.literal(AGENT_FILES_OP_RESULT),
486
+ payload: agentFilesOpResultPayloadSchema
487
+ }),
488
+ z.object({
489
+ command: z.literal(AGENT_FILES_CHUNK),
490
+ payload: agentFilesChunkPayloadSchema
491
+ }),
492
+ z.object({
493
+ command: z.literal(AGENT_SYNC_CHANGED),
494
+ payload: agentSyncChangedPayloadSchema
495
+ }),
496
+ z.object({
497
+ command: z.literal(AGENT_SYNC_INDEX),
498
+ payload: agentSyncIndexPayloadSchema
499
+ }),
500
+ z.object({
501
+ command: z.literal(AGENT_SYNC_CHUNK),
502
+ payload: agentSyncChunkPayloadSchema
503
+ }),
504
+ z.object({
505
+ command: z.literal(AGENT_SYNC_ACK),
506
+ payload: agentSyncAckPayloadSchema
507
+ }),
508
+ z.object({
509
+ command: z.literal(AGENT_SYNC_OP_RESULT),
510
+ payload: agentSyncOpResultPayloadSchema
511
+ })
512
+ ]);
513
+
514
+ export type AgentClientMessage = z.infer<typeof agentClientMessageSchema>;
515
+
516
+ /** Command names the server may send to the agent. */
517
+ export const AGENT_ACK = 'agent.ack' as const;
518
+ export const AGENT_ERROR = 'agent.error' as const;
519
+ /** Ask the agent to collect and push a fresh sample + report immediately. */
520
+ export const AGENT_COLLECT = 'agent.collect' as const;
521
+ /**
522
+ * Demande un relevé Sentinelle immédiat (persistance + authentification).
523
+ *
524
+ * Distinct d'`agent.collect` exprès : celui-ci coûte quelques millisecondes et
525
+ * peut être déclenché à volonté, tandis qu'un relevé de persistance empreinte
526
+ * des centaines de fichiers. Les confondre reviendrait à faire payer ce prix à
527
+ * chaque bouton « rafraîchir » de la page Monitoring.
528
+ */
529
+ export const AGENT_SCAN = 'agent.scan' as const;
530
+ /** Push the per-device collection config (cadences + capture mode) to the agent. */
531
+ export const AGENT_CONFIG = 'agent.config' as const;
532
+ /** Tell the agent to self-destruct (wipe its local config + binary) and exit. */
533
+ export const AGENT_DESTROY = 'agent.destroy' as const;
534
+ /** Tell the agent to download, verify and swap in a newer signed binary. */
535
+ export const AGENT_UPDATE = 'agent.update' as const;
536
+
537
+ /**
538
+ * Self-update order. The agent downloads the binary for `targetId` from
539
+ * `/api/agent/self-update/:target` (device-token auth), then refuses to replace
540
+ * itself unless BOTH the sha256 matches AND the ed25519 `signature` (over the
541
+ * sha256 bytes) verifies against its embedded public key.
542
+ */
543
+ export const agentUpdatePayloadSchema = z.object({
544
+ targetId: agentTargetSchema,
545
+ version: z.string().min(1),
546
+ sha256: z.string().regex(/^[a-f0-9]{64}$/),
547
+ /** Base64 ed25519 signature over the 32 raw bytes of `sha256`. */
548
+ signature: z.string().min(1)
549
+ });
550
+
551
+ export type AgentUpdatePayload = z.infer<typeof agentUpdatePayloadSchema>;
552
+
553
+ /**
554
+ * Ask the agent to stop or cleanly restart its own *process* (not the machine):
555
+ * - `stop` exit now. Supervised (autostart service) → the manager relaunches
556
+ * it; standalone → the device stays offline until a manual relaunch.
557
+ * - `restart` exit and come back: the manager relaunches a supervised agent, a
558
+ * standalone one respawns itself first.
559
+ * Fire-and-forget: no reply frame (the process exits) — the outcome is observed
560
+ * through presence (offline, then online again for a restart).
561
+ */
562
+ export const AGENT_LIFECYCLE = 'agent.lifecycle' as const;
563
+
564
+ export const agentLifecycleActionSchema = z.enum(['stop', 'restart']);
565
+ export type AgentLifecycleAction = z.infer<typeof agentLifecycleActionSchema>;
566
+
567
+ export const agentLifecyclePayloadSchema = z.object({ action: agentLifecycleActionSchema });
568
+ export type AgentLifecyclePayload = z.infer<typeof agentLifecyclePayloadSchema>;
569
+
570
+ /** Tell the agent to change its persistence/privilege install (see `AgentServiceAction`). */
571
+ export const AGENT_SERVICE = 'agent.service' as const;
572
+
573
+ export const agentServicePayloadSchema = z.object({ action: agentServiceActionSchema });
574
+ export type AgentServicePayload = z.infer<typeof agentServicePayloadSchema>;
575
+
576
+ /** Ask the agent to enumerate its package managers + pending updates (`pkg.listResult`). */
577
+ export const AGENT_PKG_LIST = 'pkg.list' as const;
578
+
579
+ /** Ask the agent to apply all updates of one manager, streaming `pkg.progress`. */
580
+ export const AGENT_PKG_UPGRADE = 'pkg.upgrade' as const;
581
+
582
+ export const agentPkgUpgradePayloadSchema = z.object({ manager: packageManagerIdSchema });
583
+ export type AgentPkgUpgradePayload = z.infer<typeof agentPkgUpgradePayloadSchema>;
584
+
585
+ /** Ask the agent to perform a system power action (`agent.powerResult` reports the outcome). */
586
+ export const AGENT_POWER = 'agent.power' as const;
587
+
588
+ export const agentPowerPayloadSchema = z.object({ action: agentPowerActionSchema });
589
+ export type AgentPowerPayload = z.infer<typeof agentPowerPayloadSchema>;
590
+
591
+ /** Ask the agent to enumerate its log sources (`log.sourcesResult` carries them). */
592
+ export const AGENT_LOG_SOURCES = 'log.sources' as const;
593
+
594
+ /** Ask the agent to run one log query; results stream as `log.lines`. */
595
+ export const AGENT_LOG_QUERY = 'log.query' as const;
596
+
597
+ export const agentLogQueryPayloadSchema = z.object({
598
+ queryId: z.string().max(64),
599
+ sourceId: z.string().min(1).max(512),
600
+ filter: deviceLogFilterSchema.optional(),
601
+ limit: z.number().int().positive().optional()
602
+ });
603
+ export type AgentLogQueryPayload = z.infer<typeof agentLogQueryPayloadSchema>;
604
+
605
+ const terminalCols = z.number().int().min(1).max(2000);
606
+ const terminalRows = z.number().int().min(1).max(2000);
607
+
608
+ /** Open an interactive PTY session. `user`, when set, runs the shell under that
609
+ * account (`su -l`); omitted → the account the agent runs as. */
610
+ export const AGENT_TERM_OPEN = 'term.open' as const;
611
+ export const agentTermOpenPayloadSchema = z.object({
612
+ sessionId: z.string().max(64),
613
+ cols: terminalCols,
614
+ rows: terminalRows,
615
+ user: z.string().max(32).optional()
616
+ });
617
+ export type AgentTermOpenPayload = z.infer<typeof agentTermOpenPayloadSchema>;
618
+
619
+ /** Write input (keystrokes) to a session's PTY. `data` is base64 of raw bytes. */
620
+ export const AGENT_TERM_INPUT = 'term.input' as const;
621
+ export const agentTermInputPayloadSchema = z.object({
622
+ sessionId: z.string().max(64),
623
+ data: terminalDataSchema
624
+ });
625
+ export type AgentTermInputPayload = z.infer<typeof agentTermInputPayloadSchema>;
626
+
627
+ /** Resize a session's PTY to match the client terminal. */
628
+ export const AGENT_TERM_RESIZE = 'term.resize' as const;
629
+ export const agentTermResizePayloadSchema = z.object({
630
+ sessionId: z.string().max(64),
631
+ cols: terminalCols,
632
+ rows: terminalRows
633
+ });
634
+ export type AgentTermResizePayload = z.infer<typeof agentTermResizePayloadSchema>;
635
+
636
+ /** Close a session: kill the shell and free the PTY. */
637
+ export const AGENT_TERM_CLOSE = 'term.close' as const;
638
+ export const agentTermClosePayloadSchema = z.object({ sessionId: z.string().max(64) });
639
+ export type AgentTermClosePayload = z.infer<typeof agentTermClosePayloadSchema>;
640
+
641
+ const filesOpId = z.string().max(64);
642
+ const filesPath = z.string().min(1).max(4096);
643
+
644
+ /** List a directory (replies `files.listing`). */
645
+ export const AGENT_FILES_LIST = 'files.list' as const;
646
+ export const agentFilesListPayloadSchema = z.object({ opId: filesOpId, path: filesPath });
647
+ export type AgentFilesListPayload = z.infer<typeof agentFilesListPayloadSchema>;
648
+
649
+ /** Analyse recursive disk usage of a directory's children (replies `files.usage`). */
650
+ export const AGENT_FILES_ANALYZE = 'files.analyze' as const;
651
+ export const agentFilesAnalyzePayloadSchema = z.object({ opId: filesOpId, path: filesPath });
652
+ export type AgentFilesAnalyzePayload = z.infer<typeof agentFilesAnalyzePayloadSchema>;
653
+
654
+ /** Recursively search a directory (replies `files.matches`). */
655
+ export const AGENT_FILES_SEARCH = 'files.search' as const;
656
+ export const agentFilesSearchPayloadSchema = z.object({
657
+ opId: filesOpId,
658
+ path: filesPath,
659
+ filter: fileSearchFilterSchema
660
+ });
661
+ export type AgentFilesSearchPayload = z.infer<typeof agentFilesSearchPayloadSchema>;
662
+
663
+ /** Mutate the filesystem: delete / mkdir / rename (replies `files.opResult`). */
664
+ export const AGENT_FILES_MUTATE = 'files.mutate' as const;
665
+ export const agentFilesMutatePayloadSchema = z.object({
666
+ opId: filesOpId,
667
+ op: fileMutateOpSchema,
668
+ path: filesPath,
669
+ dest: filesPath.optional()
670
+ });
671
+ export type AgentFilesMutatePayload = z.infer<typeof agentFilesMutatePayloadSchema>;
672
+
673
+ /** Download a file (streams `files.chunk`). */
674
+ export const AGENT_FILES_DOWNLOAD = 'files.download' as const;
675
+ export const agentFilesDownloadPayloadSchema = z.object({ opId: filesOpId, path: filesPath });
676
+ export type AgentFilesDownloadPayload = z.infer<typeof agentFilesDownloadPayloadSchema>;
677
+
678
+ /** Upload one chunk of a file at `offset` (replies `files.opResult` when `done`). */
679
+ export const AGENT_FILES_UPLOAD = 'files.upload' as const;
680
+ export const agentFilesUploadPayloadSchema = z.object({
681
+ opId: filesOpId,
682
+ path: filesPath,
683
+ /** Byte offset of this chunk (0 truncates/creates the file). */
684
+ offset: z.number().int().nonnegative(),
685
+ /** Base64 of this chunk's bytes. */
686
+ data: z.string().max(1_400_000),
687
+ /** True on the final chunk (the agent then confirms via `files.opResult`). */
688
+ done: z.boolean()
689
+ });
690
+ export type AgentFilesUploadPayload = z.infer<typeof agentFilesUploadPayloadSchema>;
691
+
692
+ /**
693
+ * CloudSync, serveur → agent. Le serveur orchestre tout : l'agent reçoit ses
694
+ * assignations (`sync.config`), scanne sur ordre, transfère sur ordre.
695
+ */
696
+
697
+ /** L'assignation d'un partage à cet appareil (dossier local + exclusions). */
698
+ export const syncShareAssignmentSchema = z.object({
699
+ shareId: z.number().int().positive(),
700
+ localPath: z.string().min(1).max(SYNC_STORAGE_PATH_MAX),
701
+ /** `paused` = watcher coupé, scans refusés (pause partage OU appareil). */
702
+ status: syncShareStatusSchema,
703
+ exclusions: z.array(
704
+ z.object({
705
+ kind: syncExclusionKindSchema,
706
+ pattern: z.string().min(1).max(SYNC_PATTERN_MAX)
707
+ })
708
+ ),
709
+ /**
710
+ * Plafond de débit des MONTÉES, en octets/s ; `null` = illimité. Le
711
+ * limiteur vit chez l'émetteur : l'agent pour les montées, le serveur pour
712
+ * les descentes. C'est la seule façon de brider sans laisser gonfler des
713
+ * tampons intermédiaires.
714
+ */
715
+ rateUpBps: z.number().int().positive().nullable().default(null),
716
+ /** Rétention de `.deveye-trash/`, en jours. */
717
+ trashKeepDays: z.number().int().positive().max(3650).default(30)
718
+ });
719
+ export type SyncShareAssignment = z.infer<typeof syncShareAssignmentSchema>;
720
+
721
+ /** Pousse la liste complète des assignations (à la connexion + à chaque changement). */
722
+ export const AGENT_SYNC_CONFIG = 'sync.config' as const;
723
+ export const agentSyncConfigPayloadSchema = z.object({
724
+ shares: z.array(syncShareAssignmentSchema)
725
+ });
726
+ export type AgentSyncConfigPayload = z.infer<typeof agentSyncConfigPayloadSchema>;
727
+
728
+ /** Demande un scan du dossier local (répond en lots `sync.index`). */
729
+ export const AGENT_SYNC_SCAN = 'sync.scan' as const;
730
+ export const agentSyncScanPayloadSchema = z.object({
731
+ sessionId: syncOpId,
732
+ shareId: z.number().int().positive(),
733
+ /**
734
+ * Le défaut `full` est ce qui rend un vieux SERVEUR inoffensif : sans le
735
+ * champ, un agent récent parcourt le disque comme il l'a toujours fait. Les
736
+ * deux sens de la dissymétrie de version dégradent donc vers « scan
737
+ * complet », jamais vers « saut » — sauter exige une empreinte que seul un
738
+ * agent récent produit et que seul un serveur récent exploite.
739
+ */
740
+ mode: syncScanModeSchema.default('full')
741
+ });
742
+ export type AgentSyncScanPayload = z.infer<typeof agentSyncScanPayloadSchema>;
743
+
744
+ /**
745
+ * Demande l'upload d'un fichier local (répond en chunks `sync.chunk`).
746
+ *
747
+ * `startOffset` reprend un transfert interrompu : le serveur a conservé un
748
+ * partiel vérifiable et ne redemande que ce qui manque. Le hash final reste le
749
+ * juge de paix — une reprise incohérente est jetée, jamais installée.
750
+ */
751
+ export const AGENT_SYNC_PUSH = 'sync.push' as const;
752
+ export const agentSyncPushPayloadSchema = z.object({
753
+ opId: syncOpId,
754
+ shareId: z.number().int().positive(),
755
+ relPath: syncRelPath,
756
+ startOffset: z.number().int().nonnegative().default(0)
757
+ });
758
+ export type AgentSyncPushPayload = z.infer<typeof agentSyncPushPayloadSchema>;
759
+
760
+ /**
761
+ * Un chunk de download à installer. hash/size/mtime sont répétés sur chaque
762
+ * frame (sans état) ; l'agent écrit dans un fichier temporaire, vérifie le
763
+ * hash sur `done`, installe par rename atomique puis répond `sync.opResult`.
764
+ * Chaque frame est acquittée (`sync.ack`) — le serveur borne les frames en vol.
765
+ */
766
+ export const AGENT_SYNC_APPLY_CHUNK = 'sync.applyChunk' as const;
767
+ export const agentSyncApplyChunkPayloadSchema = z.object({
768
+ opId: syncOpId,
769
+ shareId: z.number().int().positive(),
770
+ relPath: syncRelPath,
771
+ seq: z.number().int().nonnegative(),
772
+ /** Base64 des octets du chunk. */
773
+ data: z.string().max(SYNC_CHUNK_MAX),
774
+ done: z.boolean(),
775
+ hash: sha256HexSchema,
776
+ size: z.number().int().nonnegative(),
777
+ /** Millisecondes unix, appliqué au fichier installé. */
778
+ mtime: z.number().int().nonnegative(),
779
+ /** Permissions Unix à réappliquer ; ignoré sous Windows, `null` = ne pas toucher. */
780
+ mode: z.number().int().min(0).max(0o777).nullable().default(null),
781
+ /**
782
+ * Offset de clair à partir duquel CE transfert reprend. Décidé par le
783
+ * serveur (à partir du `resumeFrom` annoncé par l'agent) et répété sur
784
+ * chaque frame : l'agent tronque son temporaire à cette valeur avant
785
+ * d'écrire. C'est ce qui empêche les deux côtés de diverger — l'agent ne
786
+ * doit jamais présumer de son propre point de reprise.
787
+ */
788
+ resumeFrom: z.number().int().nonnegative().default(0)
789
+ });
790
+ export type AgentSyncApplyChunkPayload = z.infer<typeof agentSyncApplyChunkPayloadSchema>;
791
+
792
+ /**
793
+ * Amorce un download. L'agent répond `sync.opResult` avec `op: 'applyReady'` et
794
+ * un `resumeFrom` : le nombre d'octets de clair qu'il détient DÉJÀ dans son
795
+ * temporaire pour ce hash exact. Le serveur ne renvoie alors que ce qui manque.
796
+ *
797
+ * Le temporaire est nommé par hash et non par `opId`, ce qui rend la reprise
798
+ * auto-corrective : un fichier modifié entre-temps a un autre hash, donc un
799
+ * autre temporaire, donc aucune reprise possible sur des octets périmés.
800
+ */
801
+ export const AGENT_SYNC_APPLY_START = 'sync.applyStart' as const;
802
+ export const agentSyncApplyStartPayloadSchema = z.object({
803
+ opId: syncOpId,
804
+ shareId: z.number().int().positive(),
805
+ relPath: syncRelPath,
806
+ hash: sha256HexSchema,
807
+ size: z.number().int().nonnegative(),
808
+ mtime: z.number().int().nonnegative(),
809
+ mode: z.number().int().min(0).max(0o777).nullable().default(null)
810
+ });
811
+ export type AgentSyncApplyStartPayload = z.infer<typeof agentSyncApplyStartPayloadSchema>;
812
+
813
+ /**
814
+ * Pose les métadonnées d'un chemin, sans qu'un octet ne transite. Deux usages :
815
+ * créer un dossier VIDE de l'index, et appliquer un `chmod` seul sur un chemin
816
+ * déjà en place (fichier comme dossier). L'agent ne crée le chemin que s'il
817
+ * manque ; sinon il ne touche QUE le mode.
818
+ */
819
+ export const AGENT_SYNC_APPLY_DIR = 'sync.applyDir' as const;
820
+ export const agentSyncApplyDirPayloadSchema = z.object({
821
+ opId: syncOpId,
822
+ shareId: z.number().int().positive(),
823
+ relPath: syncRelPath,
824
+ /**
825
+ * `dir` autorise la CRÉATION du chemin ; `file` interdit de le créer et se
826
+ * contente d'ajuster le mode s'il existe. Sans cette distinction, un `chmod`
827
+ * sur un fichier momentanément absent ferait naître un DOSSIER à sa place —
828
+ * que le planner écarterait ensuite pour toujours en « conflit de nature ».
829
+ */
830
+ kind: syncEntryKindSchema.default('dir'),
831
+ mode: z.number().int().min(0).max(0o777).nullable().default(null)
832
+ });
833
+ export type AgentSyncApplyDirPayload = z.infer<typeof agentSyncApplyDirPayloadSchema>;
834
+
835
+ /**
836
+ * Installe un contenu que l'appareil possède DÉJÀ ailleurs dans le partage
837
+ * (renommage, déplacement, copie) : l'agent vérifie le hash de `sourceRelPath`
838
+ * puis copie en local, sans qu'un octet ne transite par le réseau. En cas
839
+ * d'échec il répond `opResult !ok` et le serveur retombe sur `sync.applyChunk`.
840
+ */
841
+ export const AGENT_SYNC_APPLY_LOCAL = 'sync.applyLocal' as const;
842
+ export const agentSyncApplyLocalPayloadSchema = z.object({
843
+ opId: syncOpId,
844
+ shareId: z.number().int().positive(),
845
+ relPath: syncRelPath,
846
+ /** Chemin, dans le même partage, dont le contenu est déjà le bon. */
847
+ sourceRelPath: syncRelPath,
848
+ hash: sha256HexSchema,
849
+ size: z.number().int().nonnegative(),
850
+ mtime: z.number().int().nonnegative(),
851
+ mode: z.number().int().min(0).max(0o777).nullable().default(null)
852
+ });
853
+ export type AgentSyncApplyLocalPayload = z.infer<typeof agentSyncApplyLocalPayloadSchema>;
854
+
855
+ /**
856
+ * Propage un DÉPLACEMENT : l'agent renomme sur place, sans transfert ni
857
+ * corbeille. Le contenu ne bouge pas d'un octet, seul son chemin change — le
858
+ * traiter comme « supprime ici, télécharge là » coûtait une copie intégrale en
859
+ * corbeille, pour une opération qui ne détruit rien.
860
+ *
861
+ * L'agent vérifie que la source porte bien le contenu attendu avant de bouger.
862
+ * En cas d'échec il répond `opResult !ok`, et le serveur retombe sur le chemin
863
+ * ordinaire (téléchargement puis suppression), qui reste sûr.
864
+ */
865
+ export const AGENT_SYNC_MOVE = 'sync.move' as const;
866
+ export const agentSyncMovePayloadSchema = z.object({
867
+ opId: syncOpId,
868
+ shareId: z.number().int().positive(),
869
+ /** Chemin actuel sur l'appareil. */
870
+ fromRelPath: syncRelPath,
871
+ /** Chemin cible. */
872
+ relPath: syncRelPath,
873
+ hash: sha256HexSchema,
874
+ size: z.number().int().nonnegative(),
875
+ mtime: z.number().int().nonnegative(),
876
+ mode: z.number().int().min(0).max(0o777).nullable().default(null)
877
+ });
878
+ export type AgentSyncMovePayload = z.infer<typeof agentSyncMovePayloadSchema>;
879
+
880
+ /**
881
+ * Propage une suppression : l'agent déplace le fichier vers sa corbeille locale
882
+ * (`.deveye-trash/`) puis répond `sync.opResult`. N'est émis qu'une fois la
883
+ * version archivée côté serveur — jamais de destruction sans sauvegarde.
884
+ */
885
+ export const AGENT_SYNC_DELETE = 'sync.delete' as const;
886
+ export const agentSyncDeletePayloadSchema = z.object({
887
+ opId: syncOpId,
888
+ shareId: z.number().int().positive(),
889
+ relPath: syncRelPath
890
+ });
891
+ export type AgentSyncDeletePayload = z.infer<typeof agentSyncDeletePayloadSchema>;
892
+
893
+ /** Collection config the server pushes to an agent (on connect + on change). */
894
+ export const agentConfigPayloadSchema = z.object({
895
+ /**
896
+ * The agent's single collection interval, in ms. One tick produces one
897
+ * instant: graph signals *and* the process list, under one timestamp.
898
+ */
899
+ metricIntervalMs: z.number().int().positive(),
900
+ /** How much of the process list to carry on each tick (`off`/`top`/`all`). */
901
+ processCapture: processCaptureSchema,
902
+ /**
903
+ * Sentinelle est-elle active sur cet appareil ? Éteinte, l'agent ne relève ni
904
+ * persistance ni authentification — ces deux sondes ne coûtent rien à qui ne
905
+ * les demande pas, et une machine qui n'est pas surveillée ne doit pas voir
906
+ * ses journaux lus « au cas où ».
907
+ *
908
+ * Facultatif : un serveur antérieur à Sentinelle n'envoie pas le champ, et
909
+ * l'agent se comporte alors comme avant.
910
+ */
911
+ sentinelEnabled: z.boolean().optional(),
912
+ /** Cadence du manifeste de persistance, en ms. */
913
+ integrityIntervalMs: z.number().int().positive().optional(),
914
+ /** Relever les issues d'authentification. Réglable à part : c'est la sonde la plus sensible. */
915
+ authEventsEnabled: z.boolean().optional()
916
+ });
917
+
918
+ export type AgentConfigPayload = z.infer<typeof agentConfigPayloadSchema>;
919
+
920
+ export const agentServerMessageSchema = z.discriminatedUnion('command', [
921
+ z.object({
922
+ command: z.literal(AGENT_ACK),
923
+ payload: z.object({ received: z.number().int().nonnegative() })
924
+ }),
925
+ z.object({
926
+ command: z.literal(AGENT_ERROR),
927
+ payload: ProtocolErrorSchema
928
+ }),
929
+ z.object({
930
+ command: z.literal(AGENT_COLLECT),
931
+ payload: z.object({})
932
+ }),
933
+ z.object({
934
+ command: z.literal(AGENT_SCAN),
935
+ payload: z.object({})
936
+ }),
937
+ z.object({
938
+ command: z.literal(AGENT_CONFIG),
939
+ payload: agentConfigPayloadSchema
940
+ }),
941
+ z.object({
942
+ command: z.literal(AGENT_DESTROY),
943
+ payload: z.object({})
944
+ }),
945
+ z.object({
946
+ command: z.literal(AGENT_UPDATE),
947
+ payload: agentUpdatePayloadSchema
948
+ }),
949
+ z.object({
950
+ command: z.literal(AGENT_LIFECYCLE),
951
+ payload: agentLifecyclePayloadSchema
952
+ }),
953
+ z.object({
954
+ command: z.literal(AGENT_SERVICE),
955
+ payload: agentServicePayloadSchema
956
+ }),
957
+ z.object({
958
+ command: z.literal(AGENT_PKG_LIST),
959
+ payload: z.object({})
960
+ }),
961
+ z.object({
962
+ command: z.literal(AGENT_PKG_UPGRADE),
963
+ payload: agentPkgUpgradePayloadSchema
964
+ }),
965
+ z.object({
966
+ command: z.literal(AGENT_POWER),
967
+ payload: agentPowerPayloadSchema
968
+ }),
969
+ z.object({
970
+ command: z.literal(AGENT_LOG_SOURCES),
971
+ payload: z.object({})
972
+ }),
973
+ z.object({
974
+ command: z.literal(AGENT_LOG_QUERY),
975
+ payload: agentLogQueryPayloadSchema
976
+ }),
977
+ z.object({
978
+ command: z.literal(AGENT_TERM_OPEN),
979
+ payload: agentTermOpenPayloadSchema
980
+ }),
981
+ z.object({
982
+ command: z.literal(AGENT_TERM_INPUT),
983
+ payload: agentTermInputPayloadSchema
984
+ }),
985
+ z.object({
986
+ command: z.literal(AGENT_TERM_RESIZE),
987
+ payload: agentTermResizePayloadSchema
988
+ }),
989
+ z.object({
990
+ command: z.literal(AGENT_TERM_CLOSE),
991
+ payload: agentTermClosePayloadSchema
992
+ }),
993
+ z.object({
994
+ command: z.literal(AGENT_FILES_LIST),
995
+ payload: agentFilesListPayloadSchema
996
+ }),
997
+ z.object({
998
+ command: z.literal(AGENT_FILES_ANALYZE),
999
+ payload: agentFilesAnalyzePayloadSchema
1000
+ }),
1001
+ z.object({
1002
+ command: z.literal(AGENT_FILES_SEARCH),
1003
+ payload: agentFilesSearchPayloadSchema
1004
+ }),
1005
+ z.object({
1006
+ command: z.literal(AGENT_FILES_MUTATE),
1007
+ payload: agentFilesMutatePayloadSchema
1008
+ }),
1009
+ z.object({
1010
+ command: z.literal(AGENT_FILES_DOWNLOAD),
1011
+ payload: agentFilesDownloadPayloadSchema
1012
+ }),
1013
+ z.object({
1014
+ command: z.literal(AGENT_FILES_UPLOAD),
1015
+ payload: agentFilesUploadPayloadSchema
1016
+ }),
1017
+ z.object({
1018
+ command: z.literal(AGENT_SYNC_CONFIG),
1019
+ payload: agentSyncConfigPayloadSchema
1020
+ }),
1021
+ z.object({
1022
+ command: z.literal(AGENT_SYNC_SCAN),
1023
+ payload: agentSyncScanPayloadSchema
1024
+ }),
1025
+ z.object({
1026
+ command: z.literal(AGENT_SYNC_PUSH),
1027
+ payload: agentSyncPushPayloadSchema
1028
+ }),
1029
+ z.object({
1030
+ command: z.literal(AGENT_SYNC_APPLY_CHUNK),
1031
+ payload: agentSyncApplyChunkPayloadSchema
1032
+ }),
1033
+ z.object({
1034
+ command: z.literal(AGENT_SYNC_DELETE),
1035
+ payload: agentSyncDeletePayloadSchema
1036
+ })
1037
+ ]);
1038
+
1039
+ export type AgentServerMessage = z.infer<typeof agentServerMessageSchema>;
1040
+
1041
+ /**
1042
+ * Server -> web client push events (no requestId), carried in the standard
1043
+ * ServerMessage envelope. These constants name those unsolicited events.
1044
+ */
1045
+ export const METRICS_PUSH_EVENT = 'metrics.push' as const;
1046
+ export const DEVICE_PRESENCE_EVENT = 'device.presence' as const;
1047
+ export const DEVICE_REPORT_EVENT = 'device.report' as const;
1048
+ /** Package-manager inventory, live upgrade progress, and completion (Appareils panel). */
1049
+ export const PACKAGE_LIST_EVENT = 'package.list' as const;
1050
+ /**
1051
+ * Une mise à jour vient d'être **acceptée** pour ce gestionnaire.
1052
+ *
1053
+ * Émis par le serveur, qui seul sait qu'un verrou vient d'être pris — et avant
1054
+ * la première ligne de sortie de l'outil, qui peut se faire attendre. C'est ce
1055
+ * qui permet à tous les écrans ouverts, y compris ceux d'autres personnes, de
1056
+ * griser le bouton au même instant.
1057
+ */
1058
+ export const PACKAGE_STARTED_EVENT = 'package.started' as const;
1059
+ export const PACKAGE_PROGRESS_EVENT = 'package.progress' as const;
1060
+ export const PACKAGE_DONE_EVENT = 'package.done' as const;
1061
+ /** Outcome of a system power action (shutdown/reboot/suspend…), fanned to subscribers. */
1062
+ export const DEVICE_POWER_EVENT = 'device.powerResult' as const;
1063
+ /**
1064
+ * Outcome of a persistence/privilege change (autostart, elevate, drop), fanned to
1065
+ * subscribers exactly like the power result.
1066
+ *
1067
+ * Sans lui, la seule trace de l'échec d'une installation de service était une
1068
+ * ligne de journal d'audit : l'interface attendait quelques secondes, relisait
1069
+ * l'appareil, et n'affichait rien — ni la réussite, ni la raison de l'échec.
1070
+ */
1071
+ export const DEVICE_SERVICE_EVENT = 'device.serviceResult' as const;
1072
+ /** Device log sources inventory + queried log lines, fanned to subscribers. */
1073
+ export const DEVICE_LOG_SOURCES_EVENT = 'device.logSources' as const;
1074
+ export const DEVICE_LOG_LINES_EVENT = 'device.logLines' as const;
1075
+ /** Terminal session PTY output + session-end, fanned to subscribers. */
1076
+ export const DEVICE_TERM_OUTPUT_EVENT = 'device.termOutput' as const;
1077
+ export const DEVICE_TERM_EXIT_EVENT = 'device.termExit' as const;
1078
+ /** File explorer results (listing, usage, search hits, mutation outcome). */
1079
+ export const DEVICE_FILES_LISTING_EVENT = 'device.filesListing' as const;
1080
+ export const DEVICE_FILES_USAGE_EVENT = 'device.filesUsage' as const;
1081
+ export const DEVICE_FILES_MATCHES_EVENT = 'device.filesMatches' as const;
1082
+ export const DEVICE_FILES_OP_EVENT = 'device.filesOp' as const;
1083
+ export const DEVICE_FILES_CHUNK_EVENT = 'device.filesChunk' as const;
1084
+ /** CloudSync : progression de session, état agrégé d'un partage, et chunks de téléchargement. */
1085
+ export const CLOUD_SYNC_PROGRESS_EVENT = 'cloudSync.progress' as const;
1086
+ export const CLOUD_SYNC_STATE_EVENT = 'cloudSync.state' as const;
1087
+ export const CLOUD_SYNC_CHUNK_EVENT = 'cloudSync.chunk' as const;
1088
+
1089
+ /** Push payloads reuse the agent reply shapes (already carry `deviceId`). */
1090
+ /**
1091
+ * L'inventaire des gestionnaires, **enrichi** par le serveur de ce que l'agent
1092
+ * ne peut pas savoir : quelles mises à jour tournent déjà.
1093
+ *
1094
+ * Sans ce champ, un écran ouvert pendant qu'une mise à jour est en cours —
1095
+ * après avoir refermé la fenêtre, ou chez quelqu'un d'autre — repartait d'un
1096
+ * bouton actif et permettait de relancer la même commande.
1097
+ */
1098
+ export const packageListPushSchema = agentPkgListResultPayloadSchema.extend({
1099
+ running: z.array(packageManagerIdSchema).default([])
1100
+ });
1101
+
1102
+ export const packageStartedPushSchema = z.object({
1103
+ deviceId: z.uuid(),
1104
+ manager: packageManagerIdSchema
1105
+ });
1106
+ export const packageProgressPushSchema = agentPkgProgressPayloadSchema;
1107
+ export const packageDonePushSchema = agentPkgDonePayloadSchema;
1108
+ export const devicePowerPushSchema = agentPowerResultPayloadSchema;
1109
+ export const deviceServicePushSchema = agentServiceResultPayloadSchema;
1110
+ export const deviceLogSourcesPushSchema = agentLogSourcesResultPayloadSchema;
1111
+ export const deviceLogLinesPushSchema = agentLogLinesPayloadSchema;
1112
+ export const deviceTermOutputPushSchema = agentTermOutputPayloadSchema;
1113
+ export const deviceTermExitPushSchema = agentTermExitPayloadSchema;
1114
+ export const deviceFilesListingPushSchema = agentFilesListingPayloadSchema;
1115
+ export const deviceFilesUsagePushSchema = agentFilesUsagePayloadSchema;
1116
+ export const deviceFilesMatchesPushSchema = agentFilesMatchesPayloadSchema;
1117
+ export const deviceFilesOpPushSchema = agentFilesOpResultPayloadSchema;
1118
+ export const deviceFilesChunkPushSchema = agentFilesChunkPayloadSchema;
1119
+
1120
+ /** Push CloudSync : abonnement par partage (pas par appareil). */
1121
+ export const cloudSyncProgressPushSchema = cloudSyncProgressSchema;
1122
+ export const cloudSyncStatePushSchema = cloudSyncShareStateSchema;
1123
+ export const cloudSyncChunkPushSchema = z.object({
1124
+ opId: z.string().max(64),
1125
+ /** Base64 des octets du chunk (vide autorisé sur la frame terminale). */
1126
+ data: z.string().max(SYNC_CHUNK_MAX),
1127
+ done: z.boolean(),
1128
+ error: z.string().max(500).optional()
1129
+ });
1130
+
1131
+ export type CloudSyncProgressPush = z.infer<typeof cloudSyncProgressPushSchema>;
1132
+ export type CloudSyncStatePush = z.infer<typeof cloudSyncStatePushSchema>;
1133
+ export type CloudSyncChunkPush = z.infer<typeof cloudSyncChunkPushSchema>;
1134
+
1135
+ export type PackageListPush = z.infer<typeof packageListPushSchema>;
1136
+ export type PackageStartedPush = z.infer<typeof packageStartedPushSchema>;
1137
+ export type PackageProgressPush = z.infer<typeof packageProgressPushSchema>;
1138
+ export type PackageDonePush = z.infer<typeof packageDonePushSchema>;
1139
+ export type DevicePowerPush = z.infer<typeof devicePowerPushSchema>;
1140
+ export type DeviceServicePush = z.infer<typeof deviceServicePushSchema>;
1141
+ export type DeviceLogSourcesPush = z.infer<typeof deviceLogSourcesPushSchema>;
1142
+ export type DeviceLogLinesPush = z.infer<typeof deviceLogLinesPushSchema>;
1143
+ export type DeviceTermOutputPush = z.infer<typeof deviceTermOutputPushSchema>;
1144
+ export type DeviceTermExitPush = z.infer<typeof deviceTermExitPushSchema>;
1145
+ export type DeviceFilesListingPush = z.infer<typeof deviceFilesListingPushSchema>;
1146
+ export type DeviceFilesUsagePush = z.infer<typeof deviceFilesUsagePushSchema>;
1147
+ export type DeviceFilesMatchesPush = z.infer<typeof deviceFilesMatchesPushSchema>;
1148
+ export type DeviceFilesOpPush = z.infer<typeof deviceFilesOpPushSchema>;
1149
+ export type DeviceFilesChunkPush = z.infer<typeof deviceFilesChunkPushSchema>;
1150
+
1151
+ export const metricsPushSchema = z.object({
1152
+ deviceId: z.uuid(),
1153
+ snapshot: metricSnapshotSchema
1154
+ });
1155
+
1156
+ export type MetricsPush = z.infer<typeof metricsPushSchema>;
1157
+
1158
+ export const deviceReportPushSchema = z.object({
1159
+ deviceId: z.uuid(),
1160
+ report: deviceReportSchema
1161
+ });
1162
+
1163
+ export type DeviceReportPush = z.infer<typeof deviceReportPushSchema>;
1164
+
1165
+ export const devicePresenceSchema = z.object({
1166
+ deviceId: z.uuid(),
1167
+ online: z.boolean(),
1168
+ lastSeen: z.number().int().nonnegative().nullable()
1169
+ });
1170
+
1171
+ export type DevicePresence = z.infer<typeof devicePresenceSchema>;