@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,103 @@
1
+ import { z } from 'zod';
2
+ import { audienceCommands } from './audience';
3
+ import { backupCommands } from './backup';
4
+ import { deviceCommands } from './device';
5
+ import { deviceFilesCommands } from './deviceFiles';
6
+ import { deviceLogCommands } from './deviceLogs';
7
+ import { deviceTerminalCommands } from './deviceTerminal';
8
+ import { databaseCommands } from './database';
9
+ import { deployCommands } from './deploy';
10
+ import { financeCommands } from './finance';
11
+ import { gitCommands } from './git';
12
+ import { homeCommands } from './home';
13
+ import { liveCommands } from './live';
14
+ import { logsCommands } from './logs';
15
+ import { mailCommands } from './mail';
16
+ import { metricsCommands } from './metrics';
17
+ import { noteCommands } from './note';
18
+ import { notifyCommands } from './notify';
19
+ import { sharingCommands } from './sharing';
20
+ import { passwordCommands } from './password';
21
+ import { projectCommands } from './project';
22
+ import { secrecyCommands } from './secrecy';
23
+ import { sentinelCommands } from './sentinel';
24
+ import { twoFactorCommands } from './twoFactor';
25
+ import { uptimeCommands } from './uptime';
26
+ import { userCommands } from './user';
27
+ import { adminCommands } from './admin';
28
+ import { workspaceCommands } from './workspace';
29
+
30
+ export interface FeatureCommandDescriptor<C extends string = string> {
31
+ command: C;
32
+ input: z.ZodTypeAny;
33
+ output: z.ZodTypeAny;
34
+ }
35
+
36
+ export const featureCommands = [
37
+ ...workspaceCommands,
38
+ ...adminCommands,
39
+ ...userCommands,
40
+ ...passwordCommands,
41
+ ...noteCommands,
42
+ ...projectCommands,
43
+ ...gitCommands,
44
+ ...deployCommands,
45
+ ...backupCommands,
46
+ ...databaseCommands,
47
+ ...financeCommands,
48
+ ...audienceCommands,
49
+ ...deviceCommands,
50
+ ...deviceLogCommands,
51
+ ...deviceTerminalCommands,
52
+ ...deviceFilesCommands,
53
+ ...metricsCommands,
54
+ ...sentinelCommands,
55
+ ...uptimeCommands,
56
+ ...twoFactorCommands,
57
+ ...secrecyCommands,
58
+ ...logsCommands,
59
+ ...homeCommands,
60
+ ...mailCommands,
61
+ ...notifyCommands,
62
+ ...sharingCommands,
63
+ ...liveCommands
64
+ ] as const;
65
+
66
+ export const featureCommandRegistry: Record<string, FeatureCommandDescriptor> = Object.fromEntries(
67
+ featureCommands.map((c) => [c.command, c])
68
+ );
69
+
70
+ /**
71
+ * Verse les contrats d'un module installé dans le registre des commandes,
72
+ * celui que le `ws.send` du client consulte avant tout envoi. Sans cet
73
+ * enregistrement, chaque commande d'un module serait refusée côté client
74
+ * (« Unknown command ») avant même d'atteindre la socket, et son interface
75
+ * resterait en chargement pour toujours.
76
+ *
77
+ * Une native rapatriée déclare les MÊMES objets que le registre publié : la
78
+ * réinscription à l'identique est un no-op. Deux contrats différents sous le
79
+ * même nom, en revanche, sont une collision de config, et le chargement doit
80
+ * le dire plutôt que d'en servir un des deux au hasard.
81
+ */
82
+ export function registerFeatureCommands(commands: readonly FeatureCommandDescriptor[]): void {
83
+ for (const c of commands) {
84
+ const existing = featureCommandRegistry[c.command];
85
+ if (existing === c) continue;
86
+ if (existing) {
87
+ throw new Error(
88
+ `registerFeatureCommands: « ${c.command} » est déjà enregistrée par un autre module`
89
+ );
90
+ }
91
+ featureCommandRegistry[c.command] = c;
92
+ }
93
+ }
94
+
95
+ export type FeatureCommandName = (typeof featureCommands)[number]['command'];
96
+
97
+ export type CommandInput<N extends FeatureCommandName> = z.infer<
98
+ Extract<(typeof featureCommands)[number], { command: N }>['input']
99
+ >;
100
+
101
+ export type CommandOutput<N extends FeatureCommandName> = z.infer<
102
+ Extract<(typeof featureCommands)[number], { command: N }>['output']
103
+ >;
@@ -0,0 +1,120 @@
1
+ import { z } from 'zod';
2
+ import { secrecyStatusSchema } from '../domain/secrecy';
3
+
4
+ /** Current state of password-based encryption for the caller. */
5
+ export const secrecyStatus = {
6
+ command: 'secrecy.status' as const,
7
+ input: z.object({}),
8
+ output: z.object({ status: secrecyStatusSchema })
9
+ };
10
+
11
+ /**
12
+ * Unlock the DEK for this session by supplying the account password. Required
13
+ * before reading/writing encrypted data while the feature is enabled.
14
+ */
15
+ export const secrecyUnlock = {
16
+ command: 'secrecy.unlock' as const,
17
+ input: z.object({ password: z.string().min(1) }),
18
+ output: z.object({ status: secrecyStatusSchema })
19
+ };
20
+
21
+ /**
22
+ * Enable password-based encryption: re-wrap the DEK with a password-derived
23
+ * key. When `recovery` is true a one-time recovery code is generated and
24
+ * returned exactly once (it also wraps the DEK as a safety net).
25
+ */
26
+ export const secrecyEnable = {
27
+ command: 'secrecy.enable' as const,
28
+ input: z.object({
29
+ password: z.string().min(1),
30
+ recovery: z.boolean()
31
+ }),
32
+ output: z.object({
33
+ status: secrecyStatusSchema,
34
+ /** Present only when `recovery` was requested. Shown once. */
35
+ recoveryCode: z.string().min(1).optional()
36
+ })
37
+ };
38
+
39
+ /** Disable the feature: re-wrap the DEK with the server key. */
40
+ export const secrecyDisable = {
41
+ command: 'secrecy.disable' as const,
42
+ input: z.object({ password: z.string().min(1) }),
43
+ output: z.object({ status: secrecyStatusSchema })
44
+ };
45
+
46
+ /**
47
+ * Set how long the password stays validated after entry (the "sudo-like" grace
48
+ * window), in seconds. `0` disables it: the password is then required for every
49
+ * encrypted action. Bounded to a day to keep the window meaningful.
50
+ */
51
+ export const secrecySetReauth = {
52
+ command: 'secrecy.setReauth' as const,
53
+ input: z.object({ seconds: z.number().int().min(0).max(86400) }),
54
+ output: z.object({ status: secrecyStatusSchema })
55
+ };
56
+
57
+ /**
58
+ * Hold (or release) the cached DEK for the duration of an open action popup.
59
+ *
60
+ * `active: true` is a heartbeat: it pins the DEK so the sliding grace window
61
+ * cannot flush it while the user composes a long action, and must be re-sent
62
+ * periodically — the server only honours the hold for a short lease, so if the
63
+ * popup vanishes for *any* reason (close, navigation, crash, disconnect) the
64
+ * heartbeats stop and the DEK reverts to a normal countdown. `active: false`
65
+ * releases the hold and restarts a fresh grace window. The DEK can therefore
66
+ * never linger indefinitely: only a genuinely-still-open popup keeps it alive.
67
+ */
68
+ export const secrecyHold = {
69
+ command: 'secrecy.hold' as const,
70
+ input: z.object({ active: z.boolean() }),
71
+ output: z.object({ status: secrecyStatusSchema })
72
+ };
73
+
74
+ /**
75
+ * Slide the grace window forward by one full interval, as if an encrypted action
76
+ * had just happened — used by the topbar timer widget to let the user manually
77
+ * postpone the password flush. No-op when locked or when the feature is off.
78
+ */
79
+ export const secrecyTouch = {
80
+ command: 'secrecy.touch' as const,
81
+ input: z.object({}),
82
+ output: z.object({ status: secrecyStatusSchema })
83
+ };
84
+
85
+ /**
86
+ * Immediately flush the cached DEK for this session, re-locking the vault so the
87
+ * next encrypted action re-prompts — the manual counterpart to {@link secrecyTouch},
88
+ * letting the user force a re-lock ahead of the grace window. No-op when already
89
+ * locked or when the feature is off.
90
+ */
91
+ export const secrecyLock = {
92
+ command: 'secrecy.lock' as const,
93
+ input: z.object({}),
94
+ output: z.object({ status: secrecyStatusSchema })
95
+ };
96
+
97
+ /**
98
+ * Recover access after a forgotten password using the recovery code, choosing
99
+ * a new password to re-wrap the DEK with.
100
+ */
101
+ export const secrecyRecover = {
102
+ command: 'secrecy.recover' as const,
103
+ input: z.object({
104
+ recoveryCode: z.string().min(1),
105
+ newPassword: z.string().min(1)
106
+ }),
107
+ output: z.object({ status: secrecyStatusSchema })
108
+ };
109
+
110
+ export const secrecyCommands = [
111
+ secrecyStatus,
112
+ secrecyUnlock,
113
+ secrecyEnable,
114
+ secrecyDisable,
115
+ secrecySetReauth,
116
+ secrecyHold,
117
+ secrecyTouch,
118
+ secrecyLock,
119
+ secrecyRecover
120
+ ] as const;
@@ -0,0 +1,233 @@
1
+ import { z } from 'zod';
2
+ import {
3
+ allowEntrySchema,
4
+ allowScopeSchema,
5
+ baselineEntrySchema,
6
+ baselineKindSchema,
7
+ devicePostureSchema,
8
+ deviceSentinelStateSchema,
9
+ findingSchema,
10
+ findingSeveritySchema,
11
+ findingStateSchema,
12
+ SENTINEL_INTEGRITY_MINUTES_MAX,
13
+ SENTINEL_INTEGRITY_MINUTES_MIN,
14
+ SENTINEL_LEARNING_DAYS_MAX,
15
+ SENTINEL_LEARNING_DAYS_MIN,
16
+ sentinelRuleIdSchema,
17
+ severityCountsSchema
18
+ } from '../domain/sentinel';
19
+
20
+ const deviceId = z.uuid();
21
+ const findingId = z.number().int().positive();
22
+
23
+ /** Plafond d'une page de constats. Au-delà, on filtre plutôt qu'on déroule. */
24
+ export const SENTINEL_PAGE_MAX = 200;
25
+
26
+ /**
27
+ * L'état de la flotte en une réponse : de quoi peindre la carte d'accueil et
28
+ * l'en-tête de la vue plein écran sans second aller-retour.
29
+ */
30
+ export const sentinelOverview = {
31
+ command: 'sentinel.overview' as const,
32
+ input: z.object({}),
33
+ output: z.object({
34
+ /** Constats ouverts de tout l'espace, par gravité. */
35
+ open: severityCountsSchema,
36
+ /**
37
+ * Score de posture de la flotte : moyenne des scores concluants. `null`
38
+ * quand aucune machine n'a encore de posture mesurable — et non `0`, qui
39
+ * se lirait comme une flotte en ruine.
40
+ */
41
+ fleetScore: z.number().int().min(0).max(100).nullable(),
42
+ devices: z.array(deviceSentinelStateSchema).max(500)
43
+ })
44
+ };
45
+
46
+ /**
47
+ * Le décompte seul, pour la carte de grille et la pastille de Monitoring.
48
+ *
49
+ * Séparé d'`overview` parce qu'il se rafraîchit à chaque changement et qu'il
50
+ * n'a besoin d'aucune jointure : deux `COUNT` indexés contre un balayage de
51
+ * toute la flotte.
52
+ */
53
+ export const sentinelCount = {
54
+ command: 'sentinel.count' as const,
55
+ input: z.object({}),
56
+ output: z.object({
57
+ open: severityCountsSchema,
58
+ /** Appareils sur lesquels Sentinelle est active. */
59
+ watched: z.number().int().nonnegative()
60
+ })
61
+ };
62
+
63
+ /** Les constats, filtrables. Tri imposé : gravité décroissante puis fraîcheur. */
64
+ export const sentinelFindings = {
65
+ command: 'sentinel.findings' as const,
66
+ input: z.object({
67
+ deviceId: deviceId.nullable().default(null),
68
+ state: findingStateSchema.nullable().default(null),
69
+ minSeverity: findingSeveritySchema.nullable().default(null),
70
+ rule: sentinelRuleIdSchema.nullable().default(null),
71
+ limit: z.number().int().min(1).max(SENTINEL_PAGE_MAX).default(50),
72
+ offset: z.number().int().nonnegative().default(0)
73
+ }),
74
+ output: z.object({
75
+ findings: z.array(findingSchema).max(SENTINEL_PAGE_MAX),
76
+ /** Total correspondant au filtre, pour paginer sans deviner. */
77
+ total: z.number().int().nonnegative()
78
+ })
79
+ };
80
+
81
+ /** L'inventaire appris d'une machine, par nature. */
82
+ export const sentinelBaseline = {
83
+ command: 'sentinel.baseline' as const,
84
+ input: z.object({
85
+ deviceId,
86
+ kind: baselineKindSchema.nullable().default(null),
87
+ limit: z.number().int().min(1).max(1000).default(500)
88
+ }),
89
+ output: z.object({
90
+ deviceId,
91
+ entries: z.array(baselineEntrySchema).max(1000),
92
+ total: z.number().int().nonnegative()
93
+ })
94
+ };
95
+
96
+ /** Le détail de posture d'une machine, contrôle par contrôle. */
97
+ export const sentinelPosture = {
98
+ command: 'sentinel.posture' as const,
99
+ input: z.object({ deviceId }),
100
+ output: z.object({ posture: devicePostureSchema })
101
+ };
102
+
103
+ /**
104
+ * « Ceci est légitime. »
105
+ *
106
+ * Écrit une autorisation **et** résout le constat, dans cet ordre : si l'écriture
107
+ * échoue, le constat reste ouvert plutôt que de disparaître sans qu'aucune règle
108
+ * ne l'en empêche de revenir. En portée `fleet`, l'autorisation vaut pour tout
109
+ * l'espace, y compris les machines qui le rejoindront ensuite.
110
+ */
111
+ export const sentinelAcknowledge = {
112
+ command: 'sentinel.acknowledge' as const,
113
+ input: z.object({
114
+ findingId,
115
+ scope: allowScopeSchema.default('device'),
116
+ reason: z.string().max(255).nullable().default(null)
117
+ }),
118
+ output: z.object({
119
+ finding: findingSchema,
120
+ allow: allowEntrySchema
121
+ })
122
+ };
123
+
124
+ /**
125
+ * « C'est réglé. »
126
+ *
127
+ * Ferme le constat **sans** écrire d'autorisation : la situation a cessé, elle
128
+ * n'a pas été jugée normale. La distinction n'est pas cosmétique — un constat
129
+ * acquitté ne rouvrira plus jamais, un constat réglé rouvre au premier relevé
130
+ * qui le revoit, et c'est précisément ce qu'on veut d'une correction.
131
+ *
132
+ * Le moteur résout tout seul ce qu'il sait rejouer (les règles d'instant). Restent
133
+ * les constats d'**événement** — une authentification, une entrée de persistance :
134
+ * ils décrivent quelque chose qui a eu lieu, donc rien ne cessera de les
135
+ * déclencher, et sans cette commande la seule sortie était de les déclarer
136
+ * légitimes. C'est-à-dire de mentir pour faire le ménage.
137
+ */
138
+ export const sentinelResolve = {
139
+ command: 'sentinel.resolve' as const,
140
+ input: z.object({ findingId }),
141
+ output: z.object({ finding: findingSchema })
142
+ };
143
+
144
+ /** Annule un acquittement ou une résolution : retire l'autorisation et rouvre le constat. */
145
+ export const sentinelReopen = {
146
+ command: 'sentinel.reopen' as const,
147
+ input: z.object({ findingId }),
148
+ output: z.object({ finding: findingSchema })
149
+ };
150
+
151
+ /** Les décisions humaines en vigueur dans l'espace. */
152
+ export const sentinelAllowlist = {
153
+ command: 'sentinel.allowlist' as const,
154
+ input: z.object({ deviceId: deviceId.nullable().default(null) }),
155
+ output: z.object({ entries: z.array(allowEntrySchema).max(1000) })
156
+ };
157
+
158
+ /** Retire une autorisation. Le constat correspondant pourra rouvrir. */
159
+ export const sentinelRemoveAllow = {
160
+ command: 'sentinel.removeAllow' as const,
161
+ input: z.object({ allowId: z.number().int().positive() }),
162
+ output: z.object({ removed: z.boolean() })
163
+ };
164
+
165
+ /**
166
+ * Active Sentinelle sur une machine et règle ses cadences.
167
+ *
168
+ * Activer (re)part une fenêtre d'apprentissage de `learningDays` : sans elle, le
169
+ * premier jour produirait des centaines de constats « nouveau programme » et la
170
+ * liste deviendrait illisible avant d'avoir servi.
171
+ */
172
+ export const sentinelSetConfig = {
173
+ command: 'sentinel.setConfig' as const,
174
+ input: z.object({
175
+ deviceId,
176
+ enabled: z.boolean(),
177
+ learningDays: z
178
+ .number()
179
+ .int()
180
+ .min(SENTINEL_LEARNING_DAYS_MIN)
181
+ .max(SENTINEL_LEARNING_DAYS_MAX)
182
+ .nullable()
183
+ .default(null),
184
+ integrityMinutes: z
185
+ .number()
186
+ .int()
187
+ .min(SENTINEL_INTEGRITY_MINUTES_MIN)
188
+ .max(SENTINEL_INTEGRITY_MINUTES_MAX)
189
+ .nullable()
190
+ .default(null),
191
+ authEvents: z.boolean().nullable().default(null)
192
+ }),
193
+ output: z.object({ device: deviceSentinelStateSchema })
194
+ };
195
+
196
+ /** Demande un relevé de persistance + authentification immédiat. */
197
+ export const sentinelScanNow = {
198
+ command: 'sentinel.scanNow' as const,
199
+ input: z.object({ deviceId }),
200
+ /** `requested` est faux quand l'agent n'est pas connecté. */
201
+ output: z.object({ deviceId, requested: z.boolean() })
202
+ };
203
+
204
+ /**
205
+ * Efface la ligne de base d'une machine et relance l'apprentissage.
206
+ *
207
+ * Le geste à faire après une montée de version d'agent qui change ce qui est
208
+ * observé — typiquement l'arrivée des chemins d'exécutables, qui redéfinit la
209
+ * clé d'un programme. **Les autorisations survivent** : ce sont des décisions,
210
+ * pas des observations, et les réclamer une seconde fois serait une punition
211
+ * pour avoir mis l'agent à jour.
212
+ */
213
+ export const sentinelResetBaseline = {
214
+ command: 'sentinel.resetBaseline' as const,
215
+ input: z.object({ deviceId }),
216
+ output: z.object({ deviceId, cleared: z.number().int().nonnegative() })
217
+ };
218
+
219
+ export const sentinelCommands = [
220
+ sentinelOverview,
221
+ sentinelCount,
222
+ sentinelFindings,
223
+ sentinelBaseline,
224
+ sentinelPosture,
225
+ sentinelAcknowledge,
226
+ sentinelResolve,
227
+ sentinelReopen,
228
+ sentinelAllowlist,
229
+ sentinelRemoveAllow,
230
+ sentinelSetConfig,
231
+ sentinelScanNow,
232
+ sentinelResetBaseline
233
+ ] as const;
@@ -0,0 +1,79 @@
1
+ import { z } from 'zod';
2
+
3
+ import {
4
+ itemAccessSchema,
5
+ itemGrantStateSchema,
6
+ itemRefSchema,
7
+ itemShareStateSchema
8
+ } from '../domain/sharing';
9
+
10
+ /**
11
+ * Le partage d'un élément et ses restrictions par rôle.
12
+ *
13
+ * Un module transversal plutôt qu'un jeu de commandes par fonctionnalité : ce
14
+ * que ces commandes prennent est toujours le même couple `(feature, itemId)`, et
15
+ * les recopier par émetteur aurait reproduit exactement ce que l'unification des
16
+ * notifications vient de défaire.
17
+ *
18
+ * L'autorisation ne peut pas être déclarative — la fonctionnalité visée est une
19
+ * **donnée d'entrée**. Elle est vérifiée en tête de handler, comme pour
20
+ * `notify.route*` et `device.setConfig`.
21
+ */
22
+
23
+ /** Où cet élément est visible, et pourquoi il ne pourrait pas l'être. */
24
+ export const shareGet = {
25
+ command: 'share.get' as const,
26
+ input: itemRefSchema,
27
+ output: itemShareStateSchema
28
+ };
29
+
30
+ /**
31
+ * Projette (ou retire) l'élément dans un espace.
32
+ *
33
+ * Un seul espace par appel : la case de l'écran est la commande, ce qui rend
34
+ * l'échec lisible — cocher une case qui échoue ne laisse pas les autres dans un
35
+ * état incertain.
36
+ */
37
+ export const shareSet = {
38
+ command: 'share.set' as const,
39
+ input: itemRefSchema.extend({
40
+ workspaceId: z.number().int().positive(),
41
+ shared: z.boolean()
42
+ }),
43
+ output: itemShareStateSchema
44
+ };
45
+
46
+ /**
47
+ * Ce que chaque rôle d'un espace voit de cet élément — l'hérité et l'exception.
48
+ *
49
+ * `workspaceId` absent = l'espace actif. Renseigné, il vise n'importe quel
50
+ * espace où l'élément est visible : c'est ce qui permet de régler, depuis
51
+ * l'onglet Partage du domicile, ce que chaque fenêtre montre — sans changer
52
+ * d'espace. L'appelant doit être membre de l'espace visé ; y **écrire** exige
53
+ * d'y tenir `workspace.roles`.
54
+ */
55
+ export const itemGrantList = {
56
+ command: 'share.grantList' as const,
57
+ input: itemRefSchema.extend({ workspaceId: z.number().int().positive().optional() }),
58
+ output: itemGrantStateSchema
59
+ };
60
+
61
+ /**
62
+ * Abaisse (ou rétablit) ce qu'un rôle peut faire sur cet élément, dans l'espace
63
+ * visé (`workspaceId` absent = l'actif).
64
+ *
65
+ * `access: null` **retire** la restriction : le rôle reprend ce que la
66
+ * fonctionnalité lui donne. C'est l'absence de ligne qui exprime « rien de
67
+ * particulier », pas une valeur.
68
+ */
69
+ export const itemGrantSet = {
70
+ command: 'share.grantSet' as const,
71
+ input: itemRefSchema.extend({
72
+ workspaceId: z.number().int().positive().optional(),
73
+ roleId: z.number().int().positive(),
74
+ access: itemAccessSchema.nullable()
75
+ }),
76
+ output: itemGrantStateSchema
77
+ };
78
+
79
+ export const sharingCommands = [shareGet, shareSet, itemGrantList, itemGrantSet] as const;
@@ -0,0 +1,47 @@
1
+ import { z } from 'zod';
2
+ import { twoFactorSetupSchema, twoFactorStatusSchema } from '../domain/twoFactor';
3
+
4
+ export const twoFactorGetStatus = {
5
+ command: 'twofa.status' as const,
6
+ input: z.object({}),
7
+ output: z.object({ status: twoFactorStatusSchema })
8
+ };
9
+
10
+ /** Begin enrollment: returns a fresh secret, otpauth URL and backup codes. */
11
+ export const twoFactorSetup = {
12
+ command: 'twofa.setup' as const,
13
+ input: z.object({}),
14
+ output: z.object({ setup: twoFactorSetupSchema })
15
+ };
16
+
17
+ /** Confirm enrollment by verifying the first TOTP code. */
18
+ export const twoFactorEnable = {
19
+ command: 'twofa.enable' as const,
20
+ input: z.object({ code: z.string().min(6).max(8) }),
21
+ output: z.object({ status: twoFactorStatusSchema })
22
+ };
23
+
24
+ /** Disable 2FA (requires a valid current code or backup code). */
25
+ export const twoFactorDisable = {
26
+ command: 'twofa.disable' as const,
27
+ input: z.object({ code: z.string().min(6).max(16) }),
28
+ output: z.object({ status: twoFactorStatusSchema })
29
+ };
30
+
31
+ /**
32
+ * Regenerate recovery codes (invalidates previous ones). Authenticated by the
33
+ * live session — no fresh TOTP code is required, only that 2FA be enabled.
34
+ */
35
+ export const twoFactorRegenBackup = {
36
+ command: 'twofa.regenBackup' as const,
37
+ input: z.object({}),
38
+ output: z.object({ backupCodes: z.array(z.string().min(1)) })
39
+ };
40
+
41
+ export const twoFactorCommands = [
42
+ twoFactorGetStatus,
43
+ twoFactorSetup,
44
+ twoFactorEnable,
45
+ twoFactorDisable,
46
+ twoFactorRegenBackup
47
+ ] as const;