@deveye/types 0.15.1 → 0.16.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/package.json +9 -6
- package/src/domain/device.ts +4 -29
- package/src/domain/featureRegistry.ts +40 -108
- package/src/domain/home.ts +40 -101
- package/src/domain/live.ts +36 -87
- package/src/domain/metrics.ts +10 -14
- package/src/domain/notifications.ts +39 -110
- package/src/domain/project.ts +3 -162
- package/src/domain/report.ts +46 -95
- package/src/domain/secrecy.ts +3 -5
- package/src/domain/sharing.ts +33 -70
- package/src/domain/syncProtocol.ts +5 -12
- package/src/domain/user.ts +8 -14
- package/src/domain/workspace.ts +0 -3
- package/src/domain/workspaceRole.ts +34 -82
- package/src/features/agent.ts +306 -0
- package/src/features/live.ts +17 -37
- package/src/features/notify.ts +19 -57
- package/src/features/registry.ts +7 -44
- package/src/features/secrecy.ts +4 -9
- package/src/features/sharing.ts +12 -26
- package/src/features/user.ts +6 -11
- package/src/features/workspace.ts +6 -11
- package/src/http/auth.ts +6 -13
- package/src/http/device.ts +13 -17
- package/src/http/status.ts +6 -10
- package/src/index.ts +29 -932
- package/src/protocol/agent.ts +40 -70
- package/src/protocol/envelope.ts +3 -10
- package/src/sdk/client-ambient.d.ts +244 -22
- package/src/sdk/client.ts +277 -17
- package/src/sdk/devb.ts +120 -0
- package/src/sdk/manifest.test.ts +96 -0
- package/src/sdk/manifest.ts +160 -25
- package/src/sdk/providers.ts +310 -5
- package/src/sdk/server.ts +483 -19
- package/src/sdk/testing.test.ts +62 -0
- package/src/sdk/testing.ts +416 -40
- package/src/utils/version.ts +5 -8
- package/src/domain/audience.ts +0 -549
- package/src/domain/backup.ts +0 -355
- package/src/domain/credential.ts +0 -55
- package/src/domain/database.ts +0 -467
- package/src/domain/deploy.ts +0 -231
- package/src/domain/finance.ts +0 -477
- package/src/domain/git.ts +0 -419
- package/src/domain/mail.ts +0 -394
- package/src/domain/note.ts +0 -202
- package/src/domain/password.ts +0 -36
- package/src/domain/projectBoard.ts +0 -130
- package/src/domain/projectChat.ts +0 -46
- package/src/domain/projectHistory.ts +0 -82
- package/src/domain/projectLink.ts +0 -87
- package/src/domain/projectPlan.ts +0 -68
- package/src/domain/sentinel.ts +0 -623
- package/src/domain/uptime.ts +0 -216
- package/src/features/audience.ts +0 -275
- package/src/features/backup.ts +0 -230
- package/src/features/database.ts +0 -461
- package/src/features/deploy.ts +0 -245
- package/src/features/device.ts +0 -292
- package/src/features/deviceFiles.ts +0 -83
- package/src/features/deviceLogs.ts +0 -36
- package/src/features/deviceTerminal.ts +0 -57
- package/src/features/finance.ts +0 -360
- package/src/features/git.ts +0 -368
- package/src/features/mail.ts +0 -374
- package/src/features/metrics.ts +0 -185
- package/src/features/note.ts +0 -189
- package/src/features/password.ts +0 -67
- package/src/features/project.ts +0 -709
- package/src/features/sentinel.ts +0 -233
- package/src/features/uptime.ts +0 -186
package/src/protocol/agent.ts
CHANGED
|
@@ -39,9 +39,8 @@ import { ProtocolErrorSchema } from './error';
|
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* Agent <-> Server wire protocol (distinct from the user feature protocol).
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* The server acknowledges and may push commands (reserved for later).
|
|
42
|
+
* The agent authenticates with its device token, then streams metric batches,
|
|
43
|
+
* reports and replies; the server acknowledges and pushes orders.
|
|
45
44
|
*/
|
|
46
45
|
|
|
47
46
|
/** Command names the agent may send to the server. */
|
|
@@ -301,17 +300,12 @@ export const agentSyncIndexPayloadSchema = z.object({
|
|
|
301
300
|
entries: z.array(syncIndexEntrySchema).max(SYNC_INDEX_BATCH_MAX),
|
|
302
301
|
done: z.boolean(),
|
|
303
302
|
/**
|
|
304
|
-
* L'agent a-t-il
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
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.
|
|
303
|
+
* L'agent a-t-il réellement parcouru le disque ? `false` répond à un
|
|
304
|
+
* `sync.scan` en mode `auto` sur un partage que le watcher sait intact :
|
|
305
|
+
* aucune entrée, seule `fingerprint` est renseignée, et le serveur VÉRIFIE
|
|
306
|
+
* qu'elle correspond à sa baseline (une vérification fausse coûte un scan
|
|
307
|
+
* complet, jamais une divergence). Le défaut `true` rend un vieil agent
|
|
308
|
+
* inoffensif.
|
|
315
309
|
*/
|
|
316
310
|
scanned: z.boolean().default(true),
|
|
317
311
|
/** Empreinte de l'index détenu par l'agent, quand il sait la calculer. */
|
|
@@ -520,11 +514,8 @@ export const AGENT_ERROR = 'agent.error' as const;
|
|
|
520
514
|
export const AGENT_COLLECT = 'agent.collect' as const;
|
|
521
515
|
/**
|
|
522
516
|
* Demande un relevé Sentinelle immédiat (persistance + authentification).
|
|
523
|
-
*
|
|
524
|
-
*
|
|
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.
|
|
517
|
+
* Distinct d'`agent.collect` : celui-ci coûte quelques millisecondes, un relevé
|
|
518
|
+
* de persistance empreinte des centaines de fichiers.
|
|
528
519
|
*/
|
|
529
520
|
export const AGENT_SCAN = 'agent.scan' as const;
|
|
530
521
|
/** Push the per-device collection config (cadences + capture mode) to the agent. */
|
|
@@ -731,11 +722,9 @@ export const agentSyncScanPayloadSchema = z.object({
|
|
|
731
722
|
sessionId: syncOpId,
|
|
732
723
|
shareId: z.number().int().positive(),
|
|
733
724
|
/**
|
|
734
|
-
* Le défaut `full`
|
|
735
|
-
*
|
|
736
|
-
*
|
|
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.
|
|
725
|
+
* Le défaut `full` rend un vieux serveur inoffensif : les deux sens de la
|
|
726
|
+
* dissymétrie de version dégradent vers « scan complet », jamais vers
|
|
727
|
+
* « saut ».
|
|
739
728
|
*/
|
|
740
729
|
mode: syncScanModeSchema.default('full')
|
|
741
730
|
});
|
|
@@ -780,23 +769,20 @@ export const agentSyncApplyChunkPayloadSchema = z.object({
|
|
|
780
769
|
mode: z.number().int().min(0).max(0o777).nullable().default(null),
|
|
781
770
|
/**
|
|
782
771
|
* Offset de clair à partir duquel CE transfert reprend. Décidé par le
|
|
783
|
-
* serveur (
|
|
784
|
-
*
|
|
785
|
-
*
|
|
786
|
-
* doit jamais présumer de son propre point de reprise.
|
|
772
|
+
* serveur (d'après le `resumeFrom` de l'agent) et répété sur chaque frame :
|
|
773
|
+
* l'agent tronque son temporaire à cette valeur avant d'écrire, et ne
|
|
774
|
+
* présume jamais de son propre point de reprise.
|
|
787
775
|
*/
|
|
788
776
|
resumeFrom: z.number().int().nonnegative().default(0)
|
|
789
777
|
});
|
|
790
778
|
export type AgentSyncApplyChunkPayload = z.infer<typeof agentSyncApplyChunkPayloadSchema>;
|
|
791
779
|
|
|
792
780
|
/**
|
|
793
|
-
* Amorce un download. L'agent répond `sync.opResult` avec `op: 'applyReady'`
|
|
794
|
-
* un `resumeFrom` :
|
|
795
|
-
* temporaire pour ce hash exact. Le
|
|
796
|
-
*
|
|
797
|
-
*
|
|
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.
|
|
781
|
+
* Amorce un download. L'agent répond `sync.opResult` avec `op: 'applyReady'`
|
|
782
|
+
* et un `resumeFrom` : les octets de clair qu'il détient DÉJÀ dans son
|
|
783
|
+
* temporaire pour ce hash exact. Le temporaire est nommé par hash et non par
|
|
784
|
+
* `opId` : un fichier modifié entre-temps a un autre hash, donc aucune reprise
|
|
785
|
+
* sur des octets périmés.
|
|
800
786
|
*/
|
|
801
787
|
export const AGENT_SYNC_APPLY_START = 'sync.applyStart' as const;
|
|
802
788
|
export const agentSyncApplyStartPayloadSchema = z.object({
|
|
@@ -822,10 +808,9 @@ export const agentSyncApplyDirPayloadSchema = z.object({
|
|
|
822
808
|
shareId: z.number().int().positive(),
|
|
823
809
|
relPath: syncRelPath,
|
|
824
810
|
/**
|
|
825
|
-
* `dir` autorise la CRÉATION du chemin ; `file`
|
|
826
|
-
*
|
|
827
|
-
*
|
|
828
|
-
* que le planner écarterait ensuite pour toujours en « conflit de nature ».
|
|
811
|
+
* `dir` autorise la CRÉATION du chemin ; `file` ne fait qu'ajuster le mode
|
|
812
|
+
* s'il existe. Sans cette distinction, un `chmod` sur un fichier
|
|
813
|
+
* momentanément absent ferait naître un DOSSIER à sa place.
|
|
829
814
|
*/
|
|
830
815
|
kind: syncEntryKindSchema.default('dir'),
|
|
831
816
|
mode: z.number().int().min(0).max(0o777).nullable().default(null)
|
|
@@ -854,13 +839,9 @@ export type AgentSyncApplyLocalPayload = z.infer<typeof agentSyncApplyLocalPaylo
|
|
|
854
839
|
|
|
855
840
|
/**
|
|
856
841
|
* Propage un DÉPLACEMENT : l'agent renomme sur place, sans transfert ni
|
|
857
|
-
* corbeille
|
|
858
|
-
*
|
|
859
|
-
*
|
|
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.
|
|
842
|
+
* corbeille, après avoir vérifié que la source porte le contenu attendu. En
|
|
843
|
+
* cas d'échec il répond `opResult !ok` et le serveur retombe sur le chemin
|
|
844
|
+
* ordinaire (téléchargement puis suppression).
|
|
864
845
|
*/
|
|
865
846
|
export const AGENT_SYNC_MOVE = 'sync.move' as const;
|
|
866
847
|
export const agentSyncMovePayloadSchema = z.object({
|
|
@@ -900,13 +881,10 @@ export const agentConfigPayloadSchema = z.object({
|
|
|
900
881
|
/** How much of the process list to carry on each tick (`off`/`top`/`all`). */
|
|
901
882
|
processCapture: processCaptureSchema,
|
|
902
883
|
/**
|
|
903
|
-
* Sentinelle est-elle active sur cet appareil ? Éteinte, l'agent ne relève
|
|
904
|
-
* persistance ni authentification
|
|
905
|
-
*
|
|
906
|
-
*
|
|
907
|
-
*
|
|
908
|
-
* Facultatif : un serveur antérieur à Sentinelle n'envoie pas le champ, et
|
|
909
|
-
* l'agent se comporte alors comme avant.
|
|
884
|
+
* Sentinelle est-elle active sur cet appareil ? Éteinte, l'agent ne relève
|
|
885
|
+
* ni persistance ni authentification : une machine non surveillée ne doit
|
|
886
|
+
* pas voir ses journaux lus « au cas où ». Facultatif : absent, l'agent ne
|
|
887
|
+
* relève rien.
|
|
910
888
|
*/
|
|
911
889
|
sentinelEnabled: z.boolean().optional(),
|
|
912
890
|
/** Cadence du manifeste de persistance, en ms. */
|
|
@@ -1048,12 +1026,9 @@ export const DEVICE_REPORT_EVENT = 'device.report' as const;
|
|
|
1048
1026
|
/** Package-manager inventory, live upgrade progress, and completion (Appareils panel). */
|
|
1049
1027
|
export const PACKAGE_LIST_EVENT = 'package.list' as const;
|
|
1050
1028
|
/**
|
|
1051
|
-
* Une mise à jour vient d'être
|
|
1052
|
-
*
|
|
1053
|
-
*
|
|
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.
|
|
1029
|
+
* Une mise à jour vient d'être acceptée pour ce gestionnaire. Émis par le
|
|
1030
|
+
* serveur avant la première ligne de sortie de l'outil, pour que tous les
|
|
1031
|
+
* écrans ouverts grisent le bouton au même instant.
|
|
1057
1032
|
*/
|
|
1058
1033
|
export const PACKAGE_STARTED_EVENT = 'package.started' as const;
|
|
1059
1034
|
export const PACKAGE_PROGRESS_EVENT = 'package.progress' as const;
|
|
@@ -1063,10 +1038,6 @@ export const DEVICE_POWER_EVENT = 'device.powerResult' as const;
|
|
|
1063
1038
|
/**
|
|
1064
1039
|
* Outcome of a persistence/privilege change (autostart, elevate, drop), fanned to
|
|
1065
1040
|
* 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
1041
|
*/
|
|
1071
1042
|
export const DEVICE_SERVICE_EVENT = 'device.serviceResult' as const;
|
|
1072
1043
|
/** Device log sources inventory + queried log lines, fanned to subscribers. */
|
|
@@ -1086,14 +1057,13 @@ export const CLOUD_SYNC_PROGRESS_EVENT = 'cloudSync.progress' as const;
|
|
|
1086
1057
|
export const CLOUD_SYNC_STATE_EVENT = 'cloudSync.state' as const;
|
|
1087
1058
|
export const CLOUD_SYNC_CHUNK_EVENT = 'cloudSync.chunk' as const;
|
|
1088
1059
|
|
|
1089
|
-
|
|
1060
|
+
// Push payloads reuse the agent reply shapes (they already carry `deviceId`).
|
|
1061
|
+
|
|
1090
1062
|
/**
|
|
1091
|
-
* L'inventaire des gestionnaires,
|
|
1092
|
-
*
|
|
1093
|
-
*
|
|
1094
|
-
*
|
|
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.
|
|
1063
|
+
* L'inventaire des gestionnaires, enrichi par le serveur de ce que l'agent ne
|
|
1064
|
+
* peut pas savoir : quelles mises à jour tournent déjà. Sans ce champ, un
|
|
1065
|
+
* écran ouvert pendant une mise à jour permettrait de relancer la même
|
|
1066
|
+
* commande.
|
|
1097
1067
|
*/
|
|
1098
1068
|
export const packageListPushSchema = agentPkgListResultPayloadSchema.extend({
|
|
1099
1069
|
running: z.array(packageManagerIdSchema).default([])
|
package/src/protocol/envelope.ts
CHANGED
|
@@ -15,16 +15,9 @@ export const clientMessageSchema = z.object({
|
|
|
15
15
|
command: z.string().min(1),
|
|
16
16
|
/**
|
|
17
17
|
* Espace de travail visé par la commande. Absent → l'espace personnel de
|
|
18
|
-
* l'appelant.
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* trois raisons : aucun schéma d'entrée ne porte de `workspaceId`, aucun site
|
|
22
|
-
* d'appel client ne le passe à la main (`ws.send` l'estampille), et le
|
|
23
|
-
* serveur n'a qu'un seul point de résolution et d'autorisation. Le tenir dans
|
|
24
|
-
* un état de session côté serveur serait plus fragile : la socket se
|
|
25
|
-
* reconnecte seule (backoff, retour de focus) et une commande émise avant la
|
|
26
|
-
* ré-activation viserait le mauvais espace — ici chaque message se décrit
|
|
27
|
-
* lui-même.
|
|
18
|
+
* l'appelant. Porté par l'enveloppe et non par chaque `payload` : `ws.send`
|
|
19
|
+
* l'estampille, le serveur n'a qu'un seul point de résolution et
|
|
20
|
+
* d'autorisation, et chaque message se décrit lui-même, reconnexions comprises.
|
|
28
21
|
*/
|
|
29
22
|
workspaceId: z.number().int().positive().optional(),
|
|
30
23
|
payload: z.unknown()
|
|
@@ -1,32 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Ambient declaration of `deveye-sdk-client
|
|
2
|
+
* Ambient declaration of `deveye-sdk-client`, the runtime barrel the APP
|
|
3
3
|
* provides when a feature module is compiled in (resolved by alias inside
|
|
4
|
-
* DevEye). The module itself is app code
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* one tsconfig `include` entry pointing at this file in node_modules;
|
|
10
|
-
* - DevEye itself verifies MECHANICALLY that the real barrel honours this
|
|
11
|
-
* declaration (`client/npm run check:sdk`): a drift breaks the app's CI,
|
|
12
|
-
* never a third-party build.
|
|
13
|
-
*
|
|
14
|
-
* Extend it when the app's stable surface grows — same gesture as before,
|
|
15
|
-
* one file instead of one per module repo.
|
|
4
|
+
* DevEye). The module itself is app code, published nowhere; this file is its
|
|
5
|
+
* typed portrait, so a module repo can `tsc --noEmit` standalone (one tsconfig
|
|
6
|
+
* `include` entry pointing here). DevEye verifies mechanically that the real
|
|
7
|
+
* barrel honours it (`client/npm run check:sdk`): a drift breaks the app's CI,
|
|
8
|
+
* never a third-party build.
|
|
16
9
|
*/
|
|
17
10
|
declare module 'deveye-sdk-client' {
|
|
18
11
|
import type {
|
|
19
12
|
ButtonHTMLAttributes,
|
|
13
|
+
CSSProperties,
|
|
20
14
|
ChangeEvent,
|
|
21
15
|
ComponentType,
|
|
22
|
-
CSSProperties,
|
|
23
16
|
InputHTMLAttributes,
|
|
24
17
|
ReactNode,
|
|
18
|
+
RefObject,
|
|
25
19
|
SelectHTMLAttributes
|
|
26
20
|
} from 'react';
|
|
27
21
|
import type { z, ZodType } from 'zod';
|
|
28
|
-
import type {
|
|
29
|
-
|
|
22
|
+
import type {
|
|
23
|
+
ButtonHTMLAttributes as DialogButtonAttributes,
|
|
24
|
+
PointerEvent as ReactPointerEvent
|
|
25
|
+
} from 'react';
|
|
26
|
+
import type {
|
|
27
|
+
FeatureAccess,
|
|
28
|
+
FeatureId,
|
|
29
|
+
MinimalUser,
|
|
30
|
+
User,
|
|
31
|
+
WorkspaceCapability
|
|
32
|
+
} from '@deveye/types';
|
|
33
|
+
import type { FeatureManifest, ManifestCommand } from '@deveye/types/sdk';
|
|
34
|
+
import type { SdkDeviceSummary } from '@deveye/types/sdk/client';
|
|
30
35
|
|
|
31
36
|
// ── UI kit ─────────────────────────────────────────────────────────────
|
|
32
37
|
export const Button: ComponentType<
|
|
@@ -36,7 +41,7 @@ declare module 'deveye-sdk-client' {
|
|
|
36
41
|
}
|
|
37
42
|
>;
|
|
38
43
|
export const TextInput: ComponentType<
|
|
39
|
-
InputHTMLAttributes<HTMLInputElement> & { enableShowHideButton?: boolean }
|
|
44
|
+
InputHTMLAttributes<HTMLInputElement> & { enableShowHideButton?: boolean; error?: string }
|
|
40
45
|
>;
|
|
41
46
|
export const SelectInput: ComponentType<SelectHTMLAttributes<HTMLSelectElement>>;
|
|
42
47
|
export const Checkbox: ComponentType<{
|
|
@@ -45,6 +50,7 @@ declare module 'deveye-sdk-client' {
|
|
|
45
50
|
onChange: (checked: boolean) => void;
|
|
46
51
|
children?: ReactNode;
|
|
47
52
|
disabled?: boolean;
|
|
53
|
+
className?: string;
|
|
48
54
|
/** Required when there is no visible label. */
|
|
49
55
|
'aria-label'?: string;
|
|
50
56
|
}>;
|
|
@@ -78,6 +84,50 @@ declare module 'deveye-sdk-client' {
|
|
|
78
84
|
fill?: boolean;
|
|
79
85
|
holdSecrecy?: boolean;
|
|
80
86
|
}>;
|
|
87
|
+
/** The "Annuler" button of a Dialog footer: closes through the guarded close. */
|
|
88
|
+
export const DialogCancelButton: ComponentType<
|
|
89
|
+
DialogButtonAttributes<HTMLButtonElement> & {
|
|
90
|
+
variant?: 'primary' | 'secondary' | 'danger' | 'ghost';
|
|
91
|
+
icon?: string;
|
|
92
|
+
}
|
|
93
|
+
>;
|
|
94
|
+
/**
|
|
95
|
+
* The imperative dialog layer over Dialog: mount `<Popup id=...>` once
|
|
96
|
+
* around a form, then drive it with `OpenPopup(id, input)` (resolves with
|
|
97
|
+
* whatever `ClosePopup(id, result)` passes, `null` on dismiss).
|
|
98
|
+
*/
|
|
99
|
+
export function Popup<TInput = unknown>(props: {
|
|
100
|
+
children: ReactNode;
|
|
101
|
+
id: string;
|
|
102
|
+
title?: string;
|
|
103
|
+
width?: number;
|
|
104
|
+
headerAction?: ReactNode;
|
|
105
|
+
onInputChange?: ((input: TInput) => void) | null;
|
|
106
|
+
onClosePopup?: ((id: string) => void) | null;
|
|
107
|
+
onSubmit?: () => void;
|
|
108
|
+
autoFocus?: boolean;
|
|
109
|
+
dirty?: boolean;
|
|
110
|
+
onSave?: () => void;
|
|
111
|
+
tall?: boolean;
|
|
112
|
+
holdSecrecy?: boolean;
|
|
113
|
+
}): ReactNode;
|
|
114
|
+
export function OpenPopup<T = object>(id: string, inputData?: unknown): Promise<T | null>;
|
|
115
|
+
export function ClosePopup(id: string, data?: unknown): void;
|
|
116
|
+
/** The app-wide "i" explainer dialog: a heading and a body, one behaviour everywhere. */
|
|
117
|
+
export function openInfo(input: {
|
|
118
|
+
title: string;
|
|
119
|
+
body: ReactNode;
|
|
120
|
+
width?: number;
|
|
121
|
+
}): Promise<unknown>;
|
|
122
|
+
/** Request the enclosing Dialog's guarded close (the unsaved-changes prompt included). */
|
|
123
|
+
export function useDialogClose(): () => void;
|
|
124
|
+
/** Register `fn` as the enclosing Dialog's primary action (Enter triggers it); `null` clears it. */
|
|
125
|
+
export function useDialogSubmit(fn: (() => void) | null): void;
|
|
126
|
+
/**
|
|
127
|
+
* Register `onEscape` as the topmost dismissible layer while `open`, so
|
|
128
|
+
* Escape closes overlays innermost first. `null` absorbs Escape without closing.
|
|
129
|
+
*/
|
|
130
|
+
export function useDismissLayer(open: boolean, onEscape: (() => void) | null): void;
|
|
81
131
|
export const StatusBadge: ComponentType<{
|
|
82
132
|
tone?: 'online' | 'offline' | 'success' | 'warning' | 'danger' | 'accent' | 'neutral';
|
|
83
133
|
/** Show the leading status dot (default true). */
|
|
@@ -103,12 +153,69 @@ declare module 'deveye-sdk-client' {
|
|
|
103
153
|
export const FeatureSettingsButton: ComponentType<{
|
|
104
154
|
scope:
|
|
105
155
|
| { kind: 'feature'; feature: FeatureId }
|
|
106
|
-
| {
|
|
156
|
+
| {
|
|
157
|
+
kind: 'item';
|
|
158
|
+
feature: FeatureId;
|
|
159
|
+
itemId: number | string;
|
|
160
|
+
itemLabel: string;
|
|
161
|
+
shareable?: boolean;
|
|
162
|
+
};
|
|
107
163
|
variant?: 'primary' | 'secondary' | 'danger' | 'ghost';
|
|
108
164
|
label?: string;
|
|
165
|
+
/** The tab a click opens (one of the manifest's tab ids); the first by default. */
|
|
166
|
+
initialSection?: string;
|
|
167
|
+
/**
|
|
168
|
+
* The shell opened or closed (unmount counts as closed): for the one
|
|
169
|
+
* component that owns the item's presence (`useLiveSegment`) when
|
|
170
|
+
* nothing else announces the item. The shell never declares the level.
|
|
171
|
+
*/
|
|
172
|
+
onOpenChange?: (open: boolean) => void;
|
|
109
173
|
}>;
|
|
110
174
|
/** The canonical settings row classes (channelRow, field, sectionHint...). */
|
|
111
175
|
export const settingsStyles: Readonly<Record<string, string>>;
|
|
176
|
+
/** A plain count on a home card: a big number, a noun, a secondary line. */
|
|
177
|
+
export type CountState = { kind: 'loading' } | { kind: 'ready'; count: number };
|
|
178
|
+
export const CountWidget: ComponentType<{
|
|
179
|
+
state: CountState;
|
|
180
|
+
/** Singular noun, pluralized with a trailing "s" unless `plural` says otherwise. */
|
|
181
|
+
noun: string;
|
|
182
|
+
plural?: string;
|
|
183
|
+
/** Secondary line when there is at least one item. */
|
|
184
|
+
hint: string;
|
|
185
|
+
/** Secondary line when the count is zero. */
|
|
186
|
+
empty: string;
|
|
187
|
+
tone?: 'neutral' | 'danger';
|
|
188
|
+
}>;
|
|
189
|
+
/**
|
|
190
|
+
* A workspace count over one of YOUR `.count` commands (or any `...Count`
|
|
191
|
+
* command answering `{ count }`), re-fetched on reconnect and on
|
|
192
|
+
* invalidation of that key. Never gated by the password-encryption unlock:
|
|
193
|
+
* the command must answer while locked.
|
|
194
|
+
*/
|
|
195
|
+
export function useWorkspaceCount(
|
|
196
|
+
command: `x-${string}.count` | `${string}.${string}Count`
|
|
197
|
+
): CountState;
|
|
198
|
+
/**
|
|
199
|
+
* Reorder a list by drag-and-drop, the app's one gesture for it (Pointer
|
|
200
|
+
* Events, a handle per row, an insertion bar the hook positions itself).
|
|
201
|
+
* The list container must be `position: relative`; rows carry
|
|
202
|
+
* `rowSelector`; the handle wires `onGripPointerDown`.
|
|
203
|
+
*/
|
|
204
|
+
export function useDragReorder<
|
|
205
|
+
L extends HTMLElement = HTMLElement,
|
|
206
|
+
B extends HTMLElement = HTMLElement
|
|
207
|
+
>(options: {
|
|
208
|
+
ids: (string | number)[];
|
|
209
|
+
rowSelector: string;
|
|
210
|
+
onReorder: (ids: (string | number)[]) => void;
|
|
211
|
+
onDragStateChange?: (dragging: boolean) => void;
|
|
212
|
+
layout?: 'rows' | 'grid';
|
|
213
|
+
}): {
|
|
214
|
+
listRef: RefObject<L | null>;
|
|
215
|
+
barRef: RefObject<B | null>;
|
|
216
|
+
onGripPointerDown: (e: ReactPointerEvent, id: string | number) => void;
|
|
217
|
+
draggingId: string | number | null;
|
|
218
|
+
};
|
|
112
219
|
|
|
113
220
|
// ── Server push events ─────────────────────────────────────────────────
|
|
114
221
|
/** Typed push subscription: filters `event`, safeParses, drops mismatches. */
|
|
@@ -141,6 +248,29 @@ declare module 'deveye-sdk-client' {
|
|
|
141
248
|
/** Imperative subscription to one resource key's invalidations. Returns the unsubscribe. */
|
|
142
249
|
export function onResourceChange(key: ExternalResourceKey, cb: () => void): () => void;
|
|
143
250
|
export function humanizeError(error: unknown, fallback: string): string;
|
|
251
|
+
/** The error a command rejects with: the server's code (or the socket's), its message, and the validation details when any. */
|
|
252
|
+
export class WsError extends Error {
|
|
253
|
+
constructor(code: string, message: string, details?: unknown);
|
|
254
|
+
readonly code: string;
|
|
255
|
+
readonly details?: unknown;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* The typed sender of any list of contracts (`featureApi` is it on a
|
|
259
|
+
* manifest's commands). `commandsApi(agentCommands)` is how a module talks
|
|
260
|
+
* to a device: the native agent transport (`agent.*`: files, terminal,
|
|
261
|
+
* logs, packages, power, live metrics subscription), under the active
|
|
262
|
+
* workspace's `devices` right; deferred answers arrive through
|
|
263
|
+
* `onServerEvent`.
|
|
264
|
+
*/
|
|
265
|
+
export function commandsApi<const C extends readonly ManifestCommand[]>(
|
|
266
|
+
commands: C
|
|
267
|
+
): {
|
|
268
|
+
send<N extends C[number]['command']>(
|
|
269
|
+
name: N,
|
|
270
|
+
input: z.input<Extract<C[number], { command: N }>['input'] & ZodType>,
|
|
271
|
+
opts?: { timeoutMs?: number }
|
|
272
|
+
): Promise<z.output<Extract<C[number], { command: N }>['output'] & ZodType>>;
|
|
273
|
+
};
|
|
144
274
|
export function featureApi<const M extends FeatureManifest>(
|
|
145
275
|
manifest: M
|
|
146
276
|
): {
|
|
@@ -158,8 +288,14 @@ declare module 'deveye-sdk-client' {
|
|
|
158
288
|
export interface SecrecyState {
|
|
159
289
|
/** True when password-based encryption is enabled for the account. */
|
|
160
290
|
enabled: boolean;
|
|
161
|
-
/** True while the session holds the unlocked key. */
|
|
291
|
+
/** True while the session holds the unlocked key (no prompt needed). */
|
|
162
292
|
unlocked: boolean;
|
|
293
|
+
/** True while the unlock dialog is open. */
|
|
294
|
+
prompting: boolean;
|
|
295
|
+
/** Epoch ms at which the grace window expires, or null when nothing counts down. */
|
|
296
|
+
unlockedUntil: number | null;
|
|
297
|
+
/** "Validate on every action": the key is never cached, unlocking ahead of time is pointless. */
|
|
298
|
+
alwaysPrompt: boolean;
|
|
163
299
|
}
|
|
164
300
|
/** Live lock state of the session (shared with the topbar widget and the global prompt). */
|
|
165
301
|
export function useSecrecy(): SecrecyState;
|
|
@@ -167,6 +303,14 @@ declare module 'deveye-sdk-client' {
|
|
|
167
303
|
export function ensureSecrecyUnlocked(): Promise<void>;
|
|
168
304
|
/** Runs `run`; on a `locked` error, opens the unlock prompt and retries once. */
|
|
169
305
|
export function withSecrecy<T>(run: () => Promise<T>): Promise<T>;
|
|
306
|
+
/** Keep the unlocked session alive during a long operation (a mailbox sync the user is watching). */
|
|
307
|
+
export function touchSecrecy(): void;
|
|
308
|
+
/**
|
|
309
|
+
* The rejection of `ensureSecrecyUnlocked` / `withSecrecy` when the user
|
|
310
|
+
* dismisses the unlock prompt: a deliberate cancel, not a failure (close a
|
|
311
|
+
* view that has nothing to show, rather than reporting an error).
|
|
312
|
+
*/
|
|
313
|
+
export class UnlockCancelledError extends Error {}
|
|
170
314
|
|
|
171
315
|
// ── Live ───────────────────────────────────────────────────────────────
|
|
172
316
|
export type LiveSegmentKind = 'view' | 'l1' | 'l2' | 'l3' | 'l4';
|
|
@@ -201,13 +345,37 @@ declare module 'deveye-sdk-client' {
|
|
|
201
345
|
/** What the folder is for, said by the caller. */
|
|
202
346
|
description?: string;
|
|
203
347
|
}>;
|
|
204
|
-
/**
|
|
348
|
+
/**
|
|
349
|
+
* The workspace's enrolled devices, live: what the Devices module offers
|
|
350
|
+
* the app (`DEVICES_CLIENT_PROVIDER`), refreshed by the `devices` topic.
|
|
351
|
+
* Empty, loaded and without error when that module is not installed.
|
|
352
|
+
*/
|
|
205
353
|
export function useDevices(): {
|
|
206
|
-
devices:
|
|
354
|
+
devices: readonly SdkDeviceSummary[];
|
|
207
355
|
loading: boolean;
|
|
208
356
|
error: string | null;
|
|
209
|
-
refresh: () => Promise<void>;
|
|
210
357
|
};
|
|
358
|
+
/**
|
|
359
|
+
* Acquires a live metrics subscription to a device and returns its release;
|
|
360
|
+
* consumers of one socket are counted so that none unsubscribes another.
|
|
361
|
+
*/
|
|
362
|
+
export function acquireMetrics(deviceId: string): () => void;
|
|
363
|
+
/** Device paths as the agent reports them: Windows or POSIX, joined accordingly. */
|
|
364
|
+
export function isWinPath(p: string): boolean;
|
|
365
|
+
export function joinPath(base: string, name: string): string;
|
|
366
|
+
/**
|
|
367
|
+
* The DevEye version this interface was built from: what an agent's
|
|
368
|
+
* reported version is compared against to offer a self-update.
|
|
369
|
+
*/
|
|
370
|
+
export const APP_VERSION: string;
|
|
371
|
+
/**
|
|
372
|
+
* A validated GET on one of the app's HTTP routes (the session cookie
|
|
373
|
+
* rides along; an expired access token is renewed and the call replayed
|
|
374
|
+
* once). For the routes that stay HTTP because they serve binaries.
|
|
375
|
+
*/
|
|
376
|
+
export function httpGet<T>(path: string, outputSchema?: ZodType<T>): Promise<T>;
|
|
377
|
+
/** Renews the access cookie before a raw `fetch` that bypasses the client (a download). */
|
|
378
|
+
export function ensureFreshAccess(): Promise<void>;
|
|
211
379
|
export interface LiveOutlineProps {
|
|
212
380
|
'data-live-peer'?: true;
|
|
213
381
|
style?: CSSProperties;
|
|
@@ -232,6 +400,60 @@ declare module 'deveye-sdk-client' {
|
|
|
232
400
|
name: string;
|
|
233
401
|
} | null;
|
|
234
402
|
export function useFeatureLifecycle(hooks: { onUnmount?: () => void }): void;
|
|
403
|
+
/** The members of the active workspace, as the session lists them (empty before it answers). */
|
|
404
|
+
export function useWorkspaceMembers(): readonly MinimalUser[];
|
|
405
|
+
/**
|
|
406
|
+
* A member's identity dot: their avatar, or their initial on their account
|
|
407
|
+
* colour (the same one as the live presence). `user` may be undefined: a
|
|
408
|
+
* deleted account must not break a row.
|
|
409
|
+
*/
|
|
410
|
+
export const Avatar: ComponentType<{
|
|
411
|
+
user: MinimalUser | undefined;
|
|
412
|
+
size?: number;
|
|
413
|
+
title?: string;
|
|
414
|
+
}>;
|
|
415
|
+
/** The CSS variable of an account colour, the one the live presence paints with. */
|
|
416
|
+
export function userColorVar(color: MinimalUser['color']): string;
|
|
417
|
+
/**
|
|
418
|
+
* Two sticky bands, one under the other: measure the top one and hand the
|
|
419
|
+
* ancestor a `--sticky-head` variable the lower band offsets itself by.
|
|
420
|
+
*/
|
|
421
|
+
export interface StickyOffset<T extends HTMLElement> {
|
|
422
|
+
ref: RefObject<T | null>;
|
|
423
|
+
style: CSSProperties;
|
|
424
|
+
}
|
|
425
|
+
export function useStickyOffset<T extends HTMLElement>(): StickyOffset<T>;
|
|
426
|
+
/**
|
|
427
|
+
* The client contract another module offers (`FeatureClient.providers`,
|
|
428
|
+
* keys in `@deveye/types/sdk`): how a module composes another's screens
|
|
429
|
+
* (Projects renders the linked items of Git, Uptime...). `undefined` when
|
|
430
|
+
* that module is not installed: degrade, never assume.
|
|
431
|
+
*/
|
|
432
|
+
export function moduleClientProvider<T>(key: string): T | undefined;
|
|
433
|
+
/** The signed-in user, `null` before the session answers. */
|
|
434
|
+
export function useCurrentUser(): User | null;
|
|
435
|
+
/** Image inputs accepted by `fileToSquareDataUrl`. */
|
|
436
|
+
export const ACCEPTED_TYPES: readonly string[];
|
|
437
|
+
export const MAX_INPUT_BYTES: number;
|
|
438
|
+
/** A picked image, resized to a square data URL under `maxLength` characters; throws a readable message. */
|
|
439
|
+
export function fileToSquareDataUrl(
|
|
440
|
+
file: File,
|
|
441
|
+
opts: { size: number; maxLength: number }
|
|
442
|
+
): Promise<string>;
|
|
443
|
+
/**
|
|
444
|
+
* Open another feature of the active workspace, on one of its items when
|
|
445
|
+
* `itemId` is given (the item's presence segment is its bare id): the
|
|
446
|
+
* host's teleport, the same mechanism as "join someone". The access guard
|
|
447
|
+
* is the host's; a missing item is ignored after a short grace.
|
|
448
|
+
*/
|
|
449
|
+
export function openFeature(feature: string, itemId?: number): void;
|
|
450
|
+
/**
|
|
451
|
+
* Ask the feature popup for a wider frame (px) while the calling
|
|
452
|
+
* component is mounted, `null` to ask for nothing. Several requests
|
|
453
|
+
* coexist; the widest wins. For a view that outgrows the default width
|
|
454
|
+
* (a table explorer in expanded mode).
|
|
455
|
+
*/
|
|
456
|
+
export function useRequestPopupWidth(px: number | null): void;
|
|
235
457
|
|
|
236
458
|
// Change events re-exported for convenience in handlers.
|
|
237
459
|
export type InputChange = ChangeEvent<HTMLInputElement>;
|