@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,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;
|