@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.
- package/LICENSE +21 -0
- package/README.md +23 -0
- package/package.json +68 -0
- package/src/domain/audience.ts +549 -0
- package/src/domain/backup.ts +355 -0
- package/src/domain/credential.ts +55 -0
- package/src/domain/database.ts +467 -0
- package/src/domain/deploy.ts +231 -0
- package/src/domain/device.ts +172 -0
- package/src/domain/deviceFiles.ts +84 -0
- package/src/domain/deviceLogs.ts +82 -0
- package/src/domain/featureRegistry.ts +392 -0
- package/src/domain/finance.ts +477 -0
- package/src/domain/git.ts +419 -0
- package/src/domain/home.ts +314 -0
- package/src/domain/live.ts +272 -0
- package/src/domain/logs.ts +117 -0
- package/src/domain/mail.ts +394 -0
- package/src/domain/metrics.ts +127 -0
- package/src/domain/note.ts +202 -0
- package/src/domain/notifications.ts +268 -0
- package/src/domain/packages.ts +35 -0
- package/src/domain/password.ts +36 -0
- package/src/domain/presence.ts +21 -0
- package/src/domain/project.ts +168 -0
- package/src/domain/projectBoard.ts +130 -0
- package/src/domain/projectChat.ts +46 -0
- package/src/domain/projectHistory.ts +82 -0
- package/src/domain/projectLink.ts +87 -0
- package/src/domain/projectPlan.ts +68 -0
- package/src/domain/report.ts +492 -0
- package/src/domain/role.ts +8 -0
- package/src/domain/secrecy.ts +66 -0
- package/src/domain/sentinel.ts +623 -0
- package/src/domain/sharing.ts +186 -0
- package/src/domain/syncProtocol.ts +116 -0
- package/src/domain/twoFactor.ts +40 -0
- package/src/domain/uptime.ts +216 -0
- package/src/domain/user.ts +141 -0
- package/src/domain/workspace.ts +56 -0
- package/src/domain/workspaceRole.ts +251 -0
- package/src/features/admin.ts +112 -0
- package/src/features/audience.ts +275 -0
- package/src/features/backup.ts +230 -0
- package/src/features/database.ts +461 -0
- package/src/features/deploy.ts +245 -0
- package/src/features/device.ts +292 -0
- package/src/features/deviceFiles.ts +83 -0
- package/src/features/deviceLogs.ts +36 -0
- package/src/features/deviceTerminal.ts +57 -0
- package/src/features/finance.ts +360 -0
- package/src/features/git.ts +368 -0
- package/src/features/home.ts +32 -0
- package/src/features/live.ts +113 -0
- package/src/features/logs.ts +86 -0
- package/src/features/mail.ts +374 -0
- package/src/features/metrics.ts +185 -0
- package/src/features/note.ts +189 -0
- package/src/features/notify.ts +164 -0
- package/src/features/password.ts +67 -0
- package/src/features/project.ts +709 -0
- package/src/features/registry.ts +103 -0
- package/src/features/secrecy.ts +120 -0
- package/src/features/sentinel.ts +233 -0
- package/src/features/sharing.ts +79 -0
- package/src/features/twoFactor.ts +47 -0
- package/src/features/uptime.ts +186 -0
- package/src/features/user.ts +91 -0
- package/src/features/workspace.ts +200 -0
- package/src/http/auth.ts +94 -0
- package/src/http/device.ts +222 -0
- package/src/http/status.ts +45 -0
- package/src/index.ts +1700 -0
- package/src/protocol/agent.ts +1171 -0
- package/src/protocol/envelope.ts +46 -0
- package/src/protocol/error.ts +27 -0
- package/src/protocol/result.ts +17 -0
- package/src/protocol/version.ts +6 -0
- package/src/sdk/client-ambient.d.ts +238 -0
- package/src/sdk/client.ts +66 -0
- package/src/sdk/ids.ts +25 -0
- package/src/sdk/index.ts +11 -0
- package/src/sdk/manifest.ts +326 -0
- package/src/sdk/providers.ts +48 -0
- package/src/sdk/server.ts +378 -0
- package/src/sdk/testing.ts +179 -0
- package/src/utils/version.ts +28 -0
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import {
|
|
3
|
+
MAIL_DISPLAY_NAME_MAX_LENGTH,
|
|
4
|
+
MAIL_SEARCH_QUERY_MAX_LENGTH,
|
|
5
|
+
MAIL_SUBJECT_MAX_LENGTH,
|
|
6
|
+
MAIL_SYNC_INTERVAL_MAX_MINUTES,
|
|
7
|
+
MAIL_SYNC_INTERVAL_MIN_MINUTES,
|
|
8
|
+
mailAccountDraftSchema,
|
|
9
|
+
mailAccountEditSchema,
|
|
10
|
+
mailAccountSchema,
|
|
11
|
+
mailAddressSchema,
|
|
12
|
+
mailFolderSchema,
|
|
13
|
+
mailMessageSchema,
|
|
14
|
+
mailMessageCursorSchema,
|
|
15
|
+
mailMessageSummarySchema,
|
|
16
|
+
mailOAuthProviderSchema,
|
|
17
|
+
mailProxySchema,
|
|
18
|
+
mailSecurityTierSchema,
|
|
19
|
+
mailSettingsSchema
|
|
20
|
+
} from '../domain/mail';
|
|
21
|
+
|
|
22
|
+
const accountId = z.number().int().positive();
|
|
23
|
+
const folderId = z.number().int().positive();
|
|
24
|
+
const messageId = z.number().int().positive();
|
|
25
|
+
|
|
26
|
+
export const mailAccountList = {
|
|
27
|
+
command: 'mail.accountList' as const,
|
|
28
|
+
input: z.object({}),
|
|
29
|
+
output: z.object({ accounts: z.array(mailAccountSchema) })
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Clear metadata only (row count across the user's accounts), so — like
|
|
34
|
+
* `password.count`/`uptime.count` — it never requires any account's session to
|
|
35
|
+
* be unlocked and a summary widget always renders a number.
|
|
36
|
+
*/
|
|
37
|
+
export const mailAccountCount = {
|
|
38
|
+
command: 'mail.accountCount' as const,
|
|
39
|
+
input: z.object({}),
|
|
40
|
+
output: z.object({ count: z.number().int().nonnegative() })
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/** Password-auth only — OAuth accounts are created via `mail.oauthStart`. */
|
|
44
|
+
export const mailAccountAdd = {
|
|
45
|
+
command: 'mail.accountAdd' as const,
|
|
46
|
+
input: z.object({ draft: mailAccountDraftSchema }),
|
|
47
|
+
output: z.object({ account: mailAccountSchema })
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Replaces a password-auth account's server configuration. Blank credentials
|
|
52
|
+
* and an omitted proxy keep whatever is stored — see `mailAccountEditSchema`.
|
|
53
|
+
*/
|
|
54
|
+
export const mailAccountUpdate = {
|
|
55
|
+
command: 'mail.accountUpdate' as const,
|
|
56
|
+
input: z.object({ id: accountId, draft: mailAccountEditSchema }),
|
|
57
|
+
output: z.object({ account: mailAccountSchema })
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The part of an account that exists whatever its auth method: label, storage
|
|
62
|
+
* tier, optional proxy. Separate from `mailAccountUpdate` because that one is
|
|
63
|
+
* inseparable from a full set of server credentials — which an OAuth mailbox
|
|
64
|
+
* simply doesn't have to give, its servers and secrets being the provider's.
|
|
65
|
+
* This is therefore the only edit path for an OAuth account, and a perfectly
|
|
66
|
+
* good one for a password account whose servers aren't changing.
|
|
67
|
+
*
|
|
68
|
+
* `proxy` omitted means "leave it as it is" — the account DTO deliberately
|
|
69
|
+
* never echoes proxy credentials back, so the client has nothing to resubmit
|
|
70
|
+
* and an absent field must not be read as "remove it". `null` removes it.
|
|
71
|
+
*/
|
|
72
|
+
export const mailAccountSetProfile = {
|
|
73
|
+
command: 'mail.accountSetProfile' as const,
|
|
74
|
+
input: z.object({
|
|
75
|
+
id: accountId,
|
|
76
|
+
displayName: z.string().min(1).max(MAIL_DISPLAY_NAME_MAX_LENGTH),
|
|
77
|
+
securityTier: mailSecurityTierSchema,
|
|
78
|
+
syncIntervalMinutes: z
|
|
79
|
+
.number()
|
|
80
|
+
.int()
|
|
81
|
+
.min(MAIL_SYNC_INTERVAL_MIN_MINUTES)
|
|
82
|
+
.max(MAIL_SYNC_INTERVAL_MAX_MINUTES),
|
|
83
|
+
proxy: mailProxySchema.nullable().optional()
|
|
84
|
+
}),
|
|
85
|
+
output: z.object({ account: mailAccountSchema })
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export const mailAccountDelete = {
|
|
89
|
+
command: 'mail.accountDelete' as const,
|
|
90
|
+
input: z.object({ id: accountId }),
|
|
91
|
+
output: z.object({ id: accountId })
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/** `ids` is the complete, final order — identical convention to `uptime.reorder`/`note.reorder`. */
|
|
95
|
+
export const mailAccountReorder = {
|
|
96
|
+
command: 'mail.accountReorder' as const,
|
|
97
|
+
input: z.object({ ids: z.array(accountId).min(1) }),
|
|
98
|
+
output: z.object({ ids: z.array(accountId) })
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/** Pause/resume background sync without deleting the account. */
|
|
102
|
+
export const mailAccountSetEnabled = {
|
|
103
|
+
command: 'mail.accountSetEnabled' as const,
|
|
104
|
+
input: z.object({ id: accountId, enabled: z.boolean() }),
|
|
105
|
+
output: z.object({ account: mailAccountSchema })
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Test IMAP/SMTP reachability either for an already-saved account (`id`) or a
|
|
110
|
+
* not-yet-submitted draft (`draft`) — exactly one must be given, so the "Test
|
|
111
|
+
* connection" button in the add form works before the account exists.
|
|
112
|
+
*/
|
|
113
|
+
export const mailAccountTestConnection = {
|
|
114
|
+
command: 'mail.accountTestConnection' as const,
|
|
115
|
+
input: z
|
|
116
|
+
.object({ id: accountId.optional(), draft: mailAccountDraftSchema.optional() })
|
|
117
|
+
.refine((v) => Boolean(v.id) !== Boolean(v.draft), {
|
|
118
|
+
message: 'Fournir soit id, soit draft, jamais les deux'
|
|
119
|
+
}),
|
|
120
|
+
output: z.object({ imapOk: z.boolean(), smtpOk: z.boolean(), error: z.string().nullable() })
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Authorization URL to send the browser to for a Google/Microsoft OAuth
|
|
125
|
+
* mailbox. The account row is created server-side once the provider redirects
|
|
126
|
+
* back to the callback HTTP route (never over this WS command) — the callback
|
|
127
|
+
* page closes itself via `window.opener.postMessage`, which the client uses to
|
|
128
|
+
* detect completion and refresh `mail.accountList`. No WS push event needed.
|
|
129
|
+
*/
|
|
130
|
+
export const mailOAuthStart = {
|
|
131
|
+
command: 'mail.oauthStart' as const,
|
|
132
|
+
input: z.object({ provider: mailOAuthProviderSchema, securityTier: mailSecurityTierSchema }),
|
|
133
|
+
output: z.object({ authUrl: z.string().url() })
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export const mailFolderList = {
|
|
137
|
+
command: 'mail.folderList' as const,
|
|
138
|
+
input: z.object({ accountId }),
|
|
139
|
+
output: z.object({ folders: z.array(mailFolderSchema) })
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export const mailFolderReorder = {
|
|
143
|
+
command: 'mail.folderReorder' as const,
|
|
144
|
+
input: z.object({ accountId, ids: z.array(folderId).min(1) }),
|
|
145
|
+
output: z.object({ ids: z.array(folderId) })
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Force a refresh now instead of waiting for the next background tick (or for
|
|
150
|
+
* guarded accounts, which are never background-synced). Incrémentale : elle
|
|
151
|
+
* rapatrie les arrivées et réconcilie la fenêtre récente, sans rien jeter —
|
|
152
|
+
* contrairement à `mail.folderReset`, qui reconstruit tout.
|
|
153
|
+
*/
|
|
154
|
+
export const mailFolderSync = {
|
|
155
|
+
command: 'mail.folderSync' as const,
|
|
156
|
+
input: z.object({ folderId }),
|
|
157
|
+
output: z.object({
|
|
158
|
+
ok: z.boolean(),
|
|
159
|
+
newCount: z.number().int().nonnegative(),
|
|
160
|
+
/** Drapeaux corrigés par la passe de réconciliation. */
|
|
161
|
+
changedCount: z.number().int().nonnegative(),
|
|
162
|
+
/** Lignes retirées du cache parce que le serveur ne les a plus. */
|
|
163
|
+
removedCount: z.number().int().nonnegative()
|
|
164
|
+
})
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Fetches one older batch (`limit`, newest-first among the older ones) for a
|
|
169
|
+
* folder — the "force refetch" path, since the regular sync only ever moves
|
|
170
|
+
* forward from `last_seen_uid` and can never backfill history that fell
|
|
171
|
+
* outside a folder's initial sync window. Call repeatedly (client-driven, not
|
|
172
|
+
* a server-side loop) until `reachedStart`.
|
|
173
|
+
*/
|
|
174
|
+
export const mailFolderBackfill = {
|
|
175
|
+
command: 'mail.folderBackfill' as const,
|
|
176
|
+
input: z.object({ folderId, limit: z.number().int().min(1).max(500) }),
|
|
177
|
+
output: z.object({ addedCount: z.number().int().nonnegative(), reachedStart: z.boolean() })
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Drops a folder's whole cache and re-syncs it from scratch — the "really
|
|
182
|
+
* refresh" path, next to `mail.folderSync` (which only pulls what arrived
|
|
183
|
+
* since) and `mail.folderBackfill` (which only extends downwards). Destructive
|
|
184
|
+
* only in appearance: `mail_messages` is a metadata cache, rebuilt from IMAP,
|
|
185
|
+
* and nothing on the mail server is touched. Use it when the cache and the
|
|
186
|
+
* mailbox have drifted apart — a partial first sync, a folder rebuilt server
|
|
187
|
+
* side, anything where reconciling is less trustworthy than starting over.
|
|
188
|
+
*/
|
|
189
|
+
export const mailFolderReset = {
|
|
190
|
+
command: 'mail.folderReset' as const,
|
|
191
|
+
input: z.object({ folderId }),
|
|
192
|
+
output: z.object({ count: z.number().int().nonnegative() })
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
export const mailMessageList = {
|
|
196
|
+
command: 'mail.messageList' as const,
|
|
197
|
+
input: z.object({
|
|
198
|
+
folderId,
|
|
199
|
+
/** Opaque page cursor from a previous call; `null` for the first page. */
|
|
200
|
+
cursor: mailMessageCursorSchema.nullable(),
|
|
201
|
+
limit: z.number().int().min(1).max(200)
|
|
202
|
+
}),
|
|
203
|
+
output: z.object({
|
|
204
|
+
messages: z.array(mailMessageSummarySchema),
|
|
205
|
+
nextCursor: mailMessageCursorSchema.nullable()
|
|
206
|
+
})
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Search one folder, on two legs that complement each other:
|
|
211
|
+
*
|
|
212
|
+
* 1. **Local** — decrypts the folder's cached envelopes and filters them on
|
|
213
|
+
* subject, sender and recipients. Necessarily server-side even though the
|
|
214
|
+
* data is already ours: `envelope_enc` is encrypted at rest, so no SQL
|
|
215
|
+
* predicate can see inside it. Capped, with `scanned` reporting how far it
|
|
216
|
+
* reached.
|
|
217
|
+
* 2. **Remote** — an IMAP `UID SEARCH` against the real mailbox, covering
|
|
218
|
+
* message **bodies** and every message that was never synced. Envelopes for
|
|
219
|
+
* hits missing from the cache are fetched and cached, so a result is a
|
|
220
|
+
* normal row: openable, flaggable, deletable like any other.
|
|
221
|
+
*
|
|
222
|
+
* The remote leg is best-effort. If the mailbox is unreachable, the account is
|
|
223
|
+
* paused, or the server rejects the search, the local results still come back
|
|
224
|
+
* with `remote: false` and `remoteError` set — a search never fails outright
|
|
225
|
+
* just because IMAP was.
|
|
226
|
+
*
|
|
227
|
+
* Whitespace splits the query into terms that must *all* match, in any order.
|
|
228
|
+
* The local leg ignores case and accents (`reunion` finds « Réunion »); the
|
|
229
|
+
* remote leg is at the mercy of the server's own matching, which by RFC 3501
|
|
230
|
+
* is case-insensitive but says nothing about accents.
|
|
231
|
+
*/
|
|
232
|
+
export const mailMessageSearch = {
|
|
233
|
+
command: 'mail.messageSearch' as const,
|
|
234
|
+
input: z.object({
|
|
235
|
+
folderId,
|
|
236
|
+
query: z.string().min(1).max(MAIL_SEARCH_QUERY_MAX_LENGTH),
|
|
237
|
+
limit: z.number().int().min(1).max(200)
|
|
238
|
+
}),
|
|
239
|
+
output: z.object({
|
|
240
|
+
messages: z.array(mailMessageSummarySchema),
|
|
241
|
+
/** More rows matched than `limit` allowed through — the UI says so rather than implying completeness. */
|
|
242
|
+
truncated: z.boolean(),
|
|
243
|
+
/** Cached envelopes actually examined, so the UI can be honest about how far the search reached. */
|
|
244
|
+
scanned: z.number().int().nonnegative(),
|
|
245
|
+
/** The IMAP leg ran: results cover the whole mailbox and message bodies, not just the cache. */
|
|
246
|
+
remote: z.boolean(),
|
|
247
|
+
/** Why the IMAP leg didn't run or didn't finish. Non-null implies `remote: false`. */
|
|
248
|
+
remoteError: z.string().nullable()
|
|
249
|
+
})
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Fetches the body live from IMAP — never served from cache — and runs the
|
|
254
|
+
* sanitize/remote-image-block/suspicious-link pipeline server-side.
|
|
255
|
+
* `allowRemoteImages` unblocks images for this single response only; nothing
|
|
256
|
+
* is persisted about the choice.
|
|
257
|
+
*/
|
|
258
|
+
export const mailMessageGet = {
|
|
259
|
+
command: 'mail.messageGet' as const,
|
|
260
|
+
input: z.object({ messageId, allowRemoteImages: z.boolean() }),
|
|
261
|
+
output: z.object({ message: mailMessageSchema })
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
export const mailMessageSetFlags = {
|
|
265
|
+
command: 'mail.messageSetFlags' as const,
|
|
266
|
+
input: z.object({
|
|
267
|
+
messageId,
|
|
268
|
+
flags: z.object({
|
|
269
|
+
seen: z.boolean().optional(),
|
|
270
|
+
flagged: z.boolean().optional(),
|
|
271
|
+
answered: z.boolean().optional()
|
|
272
|
+
})
|
|
273
|
+
}),
|
|
274
|
+
output: z.object({ message: mailMessageSummarySchema })
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
export const mailMessageMove = {
|
|
278
|
+
command: 'mail.messageMove' as const,
|
|
279
|
+
input: z.object({ messageId, toFolderId: folderId }),
|
|
280
|
+
output: z.object({ id: messageId })
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
export const mailMessageDelete = {
|
|
284
|
+
command: 'mail.messageDelete' as const,
|
|
285
|
+
input: z.object({ messageId }),
|
|
286
|
+
output: z.object({ id: messageId })
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
/** Returns a short-lived, cookie-authed download URL — mirrors the agent file-serve pattern rather than chunking over WS. */
|
|
290
|
+
export const mailAttachmentDownload = {
|
|
291
|
+
command: 'mail.attachmentDownload' as const,
|
|
292
|
+
input: z.object({ messageId, attachmentId: z.string() }),
|
|
293
|
+
output: z.object({ downloadUrl: z.string() })
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Opt-in external scan of one attachment. Rejected with `forbidden` unless the
|
|
298
|
+
* account/message has scanning explicitly enabled — never triggered implicitly.
|
|
299
|
+
*/
|
|
300
|
+
export const mailAttachmentScan = {
|
|
301
|
+
command: 'mail.attachmentScan' as const,
|
|
302
|
+
input: z.object({ messageId, attachmentId: z.string() }),
|
|
303
|
+
output: z.object({
|
|
304
|
+
status: z.enum(['clean', 'suspicious', 'malicious', 'unknown']),
|
|
305
|
+
provider: z.string().nullable()
|
|
306
|
+
})
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
export const mailSend = {
|
|
310
|
+
command: 'mail.send' as const,
|
|
311
|
+
input: z.object({
|
|
312
|
+
accountId,
|
|
313
|
+
to: z.array(mailAddressSchema).min(1),
|
|
314
|
+
cc: z.array(mailAddressSchema).optional(),
|
|
315
|
+
bcc: z.array(mailAddressSchema).optional(),
|
|
316
|
+
subject: z.string().max(MAIL_SUBJECT_MAX_LENGTH),
|
|
317
|
+
bodyText: z.string(),
|
|
318
|
+
bodyHtml: z.string().nullable().optional(),
|
|
319
|
+
attachments: z
|
|
320
|
+
.array(
|
|
321
|
+
z.object({ filename: z.string(), mimeType: z.string(), contentBase64: z.string() })
|
|
322
|
+
)
|
|
323
|
+
.optional(),
|
|
324
|
+
inReplyTo: z.string().nullable().optional()
|
|
325
|
+
}),
|
|
326
|
+
output: z.object({ ok: z.boolean(), messageId: z.string().nullable() })
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
export const mailGetSettings = {
|
|
330
|
+
command: 'mail.getSettings' as const,
|
|
331
|
+
input: z.object({}),
|
|
332
|
+
output: z.object({ settings: mailSettingsSchema })
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Input is `mailSettingsSchema` itself (not a hand-duplicated field list) —
|
|
337
|
+
* a settings save always resends the whole object (see `withSettingsDefaults`
|
|
338
|
+
* client-side), and duplicating the shape here is exactly what let this drift
|
|
339
|
+
* out of sync with the domain schema once already.
|
|
340
|
+
*/
|
|
341
|
+
export const mailSetSettings = {
|
|
342
|
+
command: 'mail.setSettings' as const,
|
|
343
|
+
input: mailSettingsSchema,
|
|
344
|
+
output: z.object({ settings: mailSettingsSchema })
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
export const mailCommands = [
|
|
348
|
+
mailAccountList,
|
|
349
|
+
mailAccountCount,
|
|
350
|
+
mailAccountAdd,
|
|
351
|
+
mailAccountUpdate,
|
|
352
|
+
mailAccountSetProfile,
|
|
353
|
+
mailAccountDelete,
|
|
354
|
+
mailAccountReorder,
|
|
355
|
+
mailAccountSetEnabled,
|
|
356
|
+
mailAccountTestConnection,
|
|
357
|
+
mailOAuthStart,
|
|
358
|
+
mailFolderList,
|
|
359
|
+
mailFolderReorder,
|
|
360
|
+
mailFolderSync,
|
|
361
|
+
mailFolderBackfill,
|
|
362
|
+
mailFolderReset,
|
|
363
|
+
mailMessageList,
|
|
364
|
+
mailMessageSearch,
|
|
365
|
+
mailMessageGet,
|
|
366
|
+
mailMessageSetFlags,
|
|
367
|
+
mailMessageMove,
|
|
368
|
+
mailMessageDelete,
|
|
369
|
+
mailAttachmentDownload,
|
|
370
|
+
mailAttachmentScan,
|
|
371
|
+
mailSend,
|
|
372
|
+
mailGetSettings,
|
|
373
|
+
mailSetSettings
|
|
374
|
+
] as const;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { metricSeriesPointSchema, metricsResolutionSchema } from '../domain/metrics';
|
|
3
|
+
import { presenceEventSchema } from '../domain/presence';
|
|
4
|
+
import { processSampleSchema } from '../domain/report';
|
|
5
|
+
|
|
6
|
+
const deviceId = z.uuid();
|
|
7
|
+
|
|
8
|
+
/** Fetch a time-series window for graphs, optionally downsampled. */
|
|
9
|
+
export const metricsQuery = {
|
|
10
|
+
command: 'metrics.query' as const,
|
|
11
|
+
input: z.object({
|
|
12
|
+
deviceId,
|
|
13
|
+
from: z.number().int().nonnegative(),
|
|
14
|
+
to: z.number().int().positive(),
|
|
15
|
+
resolution: metricsResolutionSchema.default('raw')
|
|
16
|
+
}),
|
|
17
|
+
output: z.object({
|
|
18
|
+
deviceId,
|
|
19
|
+
points: z.array(metricSeriesPointSchema)
|
|
20
|
+
})
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/** Subscribe to live metric pushes for one or more devices. */
|
|
24
|
+
export const metricsSubscribe = {
|
|
25
|
+
command: 'metrics.subscribe' as const,
|
|
26
|
+
input: z.object({ deviceIds: z.array(deviceId).min(1).max(50) }),
|
|
27
|
+
output: z.object({ deviceIds: z.array(deviceId) })
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const metricsUnsubscribe = {
|
|
31
|
+
command: 'metrics.unsubscribe' as const,
|
|
32
|
+
input: z.object({ deviceIds: z.array(deviceId).min(1).max(50) }),
|
|
33
|
+
output: z.object({ deviceIds: z.array(deviceId) })
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/** Ask an online device to push a fresh sample + report right now. */
|
|
37
|
+
export const metricsRefresh = {
|
|
38
|
+
command: 'metrics.refresh' as const,
|
|
39
|
+
input: z.object({ deviceId }),
|
|
40
|
+
/** `requested` is false when the device isn't currently connected. */
|
|
41
|
+
output: z.object({ deviceId, requested: z.boolean() })
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/** Agent connectivity over a window, to draw the uptime timeline. */
|
|
45
|
+
export const metricsPresence = {
|
|
46
|
+
command: 'metrics.presence' as const,
|
|
47
|
+
input: z.object({
|
|
48
|
+
deviceId,
|
|
49
|
+
from: z.number().int().nonnegative(),
|
|
50
|
+
to: z.number().int().positive()
|
|
51
|
+
}),
|
|
52
|
+
output: z.object({
|
|
53
|
+
deviceId,
|
|
54
|
+
/** Online state at `from` (carried over from the last prior event). */
|
|
55
|
+
onlineAtStart: z.boolean(),
|
|
56
|
+
events: z.array(presenceEventSchema)
|
|
57
|
+
})
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/** Processes captured nearest to a given instant (null if none in range). */
|
|
61
|
+
export const metricsProcessesAt = {
|
|
62
|
+
command: 'metrics.processesAt' as const,
|
|
63
|
+
input: z.object({ deviceId, at: z.number().int().positive() }),
|
|
64
|
+
output: z.object({ deviceId, sample: processSampleSchema.nullable() })
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/** Distinct local days (YYYY-MM-DD) that have metric data, for the calendar. */
|
|
68
|
+
export const metricsAvailability = {
|
|
69
|
+
command: 'metrics.availability' as const,
|
|
70
|
+
input: z.object({
|
|
71
|
+
deviceId,
|
|
72
|
+
/** Client UTC offset (`Date.getTimezoneOffset()`), to bucket by local day. */
|
|
73
|
+
tzOffsetMinutes: z.number().int().default(0)
|
|
74
|
+
}),
|
|
75
|
+
output: z.object({ deviceId, days: z.array(z.string()) })
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Timestamps of the stored instants in a window, to mark them on the timeline.
|
|
80
|
+
* With a single collection cadence there is one per metric point, so this is
|
|
81
|
+
* dense — the timeline draws continuous bands rather than individual marks above
|
|
82
|
+
* a threshold.
|
|
83
|
+
*
|
|
84
|
+
* `timestamps` are the *metric* instants, not the process samples: process
|
|
85
|
+
* capture is optional (`processCapture: 'off'`), and keying the marks on the
|
|
86
|
+
* process blob made the whole instant navigation — marks, ‹ › stepping, keyboard
|
|
87
|
+
* arrows — silently vanish whenever a device chose not to record processes.
|
|
88
|
+
* `withProcesses` is the subset that additionally carries a process list.
|
|
89
|
+
*/
|
|
90
|
+
export const metricsSnapshots = {
|
|
91
|
+
command: 'metrics.snapshots' as const,
|
|
92
|
+
input: z.object({
|
|
93
|
+
deviceId,
|
|
94
|
+
from: z.number().int().nonnegative(),
|
|
95
|
+
to: z.number().int().positive()
|
|
96
|
+
}),
|
|
97
|
+
output: z.object({
|
|
98
|
+
deviceId,
|
|
99
|
+
timestamps: z.array(z.number().int().positive()),
|
|
100
|
+
/** Subset of `timestamps` that are pinned (kept past retention). */
|
|
101
|
+
pinned: z.array(z.number().int().positive()),
|
|
102
|
+
/** Subset of `timestamps` whose process list was recorded. */
|
|
103
|
+
withProcesses: z.array(z.number().int().positive()),
|
|
104
|
+
/**
|
|
105
|
+
* Le relevé a buté sur son plafond : la fenêtre contenait plus d'instants
|
|
106
|
+
* que ce qui peut être rendu, et seuls les plus **récents** sont là.
|
|
107
|
+
* L'interface le dit plutôt que de laisser croire à un trou de données.
|
|
108
|
+
*/
|
|
109
|
+
truncated: z.boolean().default(false)
|
|
110
|
+
})
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Pin (or unpin) the snapshots within `[from, to]` (inclusive). Pinned snapshots
|
|
115
|
+
* keep their process list *and* their metric point past the device's retention.
|
|
116
|
+
* Unpinning lets them expire again: rows already past their retention deadline are
|
|
117
|
+
* deleted immediately, the rest at the next retention sweep.
|
|
118
|
+
*/
|
|
119
|
+
export const metricsSetSnapshotsPinned = {
|
|
120
|
+
command: 'metrics.setSnapshotsPinned' as const,
|
|
121
|
+
input: z
|
|
122
|
+
.object({
|
|
123
|
+
deviceId,
|
|
124
|
+
from: z.number().int().nonnegative(),
|
|
125
|
+
to: z.number().int().positive(),
|
|
126
|
+
pinned: z.boolean()
|
|
127
|
+
})
|
|
128
|
+
.refine((v) => v.to >= v.from, { message: 'to must be >= from' }),
|
|
129
|
+
output: z.object({
|
|
130
|
+
deviceId,
|
|
131
|
+
/** Snapshot instants whose pin state changed. */
|
|
132
|
+
affected: z.number().int().nonnegative(),
|
|
133
|
+
/** Snapshot instants deleted right away on unpin (already past retention). */
|
|
134
|
+
deletedSnapshots: z.number().int().nonnegative()
|
|
135
|
+
})
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/** Storage footprint of a device's stored process snapshots (count + bytes). */
|
|
139
|
+
export const metricsStorage = {
|
|
140
|
+
command: 'metrics.storage' as const,
|
|
141
|
+
input: z.object({ deviceId }),
|
|
142
|
+
output: z.object({
|
|
143
|
+
deviceId,
|
|
144
|
+
/** Snapshot instants kept for the device (one stored row each). */
|
|
145
|
+
snapshots: z.number().int().nonnegative(),
|
|
146
|
+
/** Total process entries recorded across those instants. */
|
|
147
|
+
processes: z.number().int().nonnegative(),
|
|
148
|
+
/** Bytes the compressed process blobs occupy — measured, not estimated. */
|
|
149
|
+
bytes: z.number().int().nonnegative()
|
|
150
|
+
})
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Delete the process snapshots within `[from, to]` (inclusive). A single snapshot
|
|
155
|
+
* is removed by passing `from === to === ts`; a dragged zone passes its bounds.
|
|
156
|
+
*/
|
|
157
|
+
export const metricsDeleteSnapshots = {
|
|
158
|
+
command: 'metrics.deleteSnapshots' as const,
|
|
159
|
+
input: z
|
|
160
|
+
.object({
|
|
161
|
+
deviceId,
|
|
162
|
+
from: z.number().int().nonnegative(),
|
|
163
|
+
to: z.number().int().positive()
|
|
164
|
+
})
|
|
165
|
+
.refine((v) => v.to >= v.from, { message: 'to must be >= from' }),
|
|
166
|
+
output: z.object({
|
|
167
|
+
deviceId,
|
|
168
|
+
/** Snapshot instants removed. */
|
|
169
|
+
deletedSnapshots: z.number().int().nonnegative()
|
|
170
|
+
})
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
export const metricsCommands = [
|
|
174
|
+
metricsQuery,
|
|
175
|
+
metricsSubscribe,
|
|
176
|
+
metricsUnsubscribe,
|
|
177
|
+
metricsRefresh,
|
|
178
|
+
metricsPresence,
|
|
179
|
+
metricsProcessesAt,
|
|
180
|
+
metricsAvailability,
|
|
181
|
+
metricsSnapshots,
|
|
182
|
+
metricsStorage,
|
|
183
|
+
metricsDeleteSnapshots,
|
|
184
|
+
metricsSetSnapshotsPinned
|
|
185
|
+
] as const;
|