@opengeni/react 0.11.0 → 0.13.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/README.md +1 -1
- package/dist/{chunk-5C7RGAWA.js → chunk-NFYVQWIB.js} +170 -64
- package/dist/chunk-NFYVQWIB.js.map +1 -0
- package/dist/index.d.ts +791 -145
- package/dist/index.js +5447 -2213
- package/dist/index.js.map +1 -1
- package/dist/{machines-BeZ3bD3t.d.ts → machines-CnlMb7E-.d.ts} +5 -5
- package/dist/machines.d.ts +1 -1
- package/dist/machines.js +1 -1
- package/package.json +17 -12
- package/src/approvals.ts +16 -4
- package/src/client.ts +32 -5
- package/src/commands/index.ts +1 -7
- package/src/commands/registry.ts +46 -12
- package/src/components/chat-composer.tsx +135 -39
- package/src/components/code-editor.tsx +28 -36
- package/src/components/command-palette.tsx +26 -5
- package/src/components/desktop-viewer.tsx +29 -16
- package/src/components/diff-view.tsx +27 -189
- package/src/components/enrollment-consent.tsx +28 -10
- package/src/components/enrollment-device-flow.tsx +8 -5
- package/src/components/file-browser.tsx +190 -56
- package/src/components/fleet-tile.tsx +13 -4
- package/src/components/machine-card.tsx +16 -4
- package/src/components/machine-dock-bar.tsx +20 -8
- package/src/components/machine-metrics.tsx +31 -8
- package/src/components/machine-status-pill.tsx +26 -5
- package/src/components/machines-dashboard.tsx +8 -2
- package/src/components/markdown.tsx +39 -9
- package/src/components/message-timeline.tsx +633 -109
- package/src/components/pierre-diff.tsx +83 -15
- package/src/components/pierre-file.tsx +10 -9
- package/src/components/sandbox-files.tsx +110 -227
- package/src/components/sandbox-terminal.tsx +256 -88
- package/src/components/sandbox-workspace.tsx +823 -0
- package/src/components/session-status.tsx +28 -2
- package/src/components/workbench-changes.tsx +541 -0
- package/src/components/workspace-dock.tsx +85 -22
- package/src/hooks/internal.ts +5 -2
- package/src/hooks/use-available-models.ts +7 -2
- package/src/hooks/use-billing-usage.ts +4 -1
- package/src/hooks/use-codex-accounts.ts +74 -44
- package/src/hooks/use-composer.ts +57 -36
- package/src/hooks/use-environments.ts +92 -47
- package/src/hooks/use-file-attachments.ts +101 -55
- package/src/hooks/use-goal.ts +52 -16
- package/src/hooks/use-machine-chip.ts +134 -0
- package/src/hooks/use-machines.ts +34 -16
- package/src/hooks/use-packs.ts +24 -19
- package/src/hooks/use-relay-frame-stream.ts +5 -2
- package/src/hooks/use-rigs.ts +335 -0
- package/src/hooks/use-sandbox-files.ts +213 -39
- package/src/hooks/use-sandbox-git.ts +188 -11
- package/src/hooks/use-sandbox-terminal.ts +18 -14
- package/src/hooks/use-scheduled-tasks.ts +10 -2
- package/src/hooks/use-session-capabilities.ts +77 -20
- package/src/hooks/use-session-control.ts +49 -21
- package/src/hooks/use-session-events.ts +48 -20
- package/src/hooks/use-session-lineage.ts +119 -0
- package/src/hooks/use-session.ts +43 -28
- package/src/hooks/use-slash-commands.ts +6 -4
- package/src/hooks/use-turn-queue.ts +74 -147
- package/src/hooks/use-windowed-sections.ts +204 -0
- package/src/hooks/use-workspace-capture.ts +233 -0
- package/src/hooks/use-workspace-edit.ts +231 -0
- package/src/hooks/use-workspace-sessions.ts +48 -5
- package/src/hooks/use-workspaces.ts +18 -15
- package/src/index.ts +128 -23
- package/src/lib/format.ts +44 -6
- package/src/lib/xterm-renderer.ts +65 -0
- package/src/lib/xterm-theme.ts +224 -18
- package/src/machines.ts +13 -3
- package/src/provider.tsx +3 -1
- package/src/timeline/activity-rail.tsx +213 -54
- package/src/timeline/disclosure-context.tsx +12 -2
- package/src/timeline/index.ts +21 -2
- package/src/timeline/parsers.ts +44 -10
- package/src/timeline/projection.ts +435 -84
- package/src/timeline/shared.tsx +111 -24
- package/src/timeline/tool-diff.tsx +24 -20
- package/src/timeline/tool-renderers.tsx +98 -21
- package/src/timeline/turn-summary.tsx +76 -23
- package/src/timeline/types.ts +96 -12
- package/styles/tokens.css +2 -2
- package/dist/chunk-5C7RGAWA.js.map +0 -1
|
@@ -21,7 +21,10 @@ import type { MachinesResponse, MachineView, MetricSample } from "../types/machi
|
|
|
21
21
|
*/
|
|
22
22
|
export type MachinesClientLike = {
|
|
23
23
|
/** GET /v1/workspaces/:ws/machines — the dashboard list + active pointer. */
|
|
24
|
-
listMachines: (
|
|
24
|
+
listMachines: (
|
|
25
|
+
workspaceId: string,
|
|
26
|
+
options?: { sessionId?: string },
|
|
27
|
+
) => Promise<MachinesResponse>;
|
|
25
28
|
/** GET .../machines/:enrollmentId/metrics/series — the downsampled history. */
|
|
26
29
|
machineMetricsSeries?: (
|
|
27
30
|
workspaceId: string,
|
|
@@ -69,7 +72,10 @@ export type UseMachinesResult = {
|
|
|
69
72
|
/** Whether the host wired an attach/swap path (drives the card affordance). */
|
|
70
73
|
canAttach: boolean;
|
|
71
74
|
/** Fetch a downsampled metric series for one enrolled machine. */
|
|
72
|
-
fetchSeries: (
|
|
75
|
+
fetchSeries: (
|
|
76
|
+
enrollmentId: string,
|
|
77
|
+
window?: "15m" | "1h" | "6h" | "24h",
|
|
78
|
+
) => Promise<MetricSample[]>;
|
|
73
79
|
attaching: boolean;
|
|
74
80
|
/** The sandbox id of the in-flight attach (for per-card spinner gating). */
|
|
75
81
|
attachingSandboxId: string | null;
|
|
@@ -89,19 +95,28 @@ const EMPTY: MachinesResponse = { activeSandboxId: null, activeEpoch: 0, machine
|
|
|
89
95
|
*/
|
|
90
96
|
export function useMachines(options: UseMachinesOptions = {}): UseMachinesResult {
|
|
91
97
|
const { client, workspaceId } = useOpenGeni(options);
|
|
92
|
-
const machinesClient = (options.machinesClient ??
|
|
98
|
+
const machinesClient = (options.machinesClient ??
|
|
99
|
+
(client as unknown as MachinesClientLike)) satisfies MachinesClientLike;
|
|
93
100
|
const sessionId = options.sessionId;
|
|
94
101
|
|
|
95
102
|
const load = useCallback(async () => {
|
|
96
103
|
return await machinesClient.listMachines(workspaceId, sessionId ? { sessionId } : undefined);
|
|
97
104
|
}, [machinesClient, workspaceId, sessionId]);
|
|
98
105
|
|
|
99
|
-
const
|
|
100
|
-
|
|
106
|
+
const {
|
|
107
|
+
data: loadedData,
|
|
108
|
+
loading,
|
|
109
|
+
error,
|
|
110
|
+
refresh,
|
|
111
|
+
} = usePolledValue(load, {
|
|
112
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
113
|
+
enabled: options.enabled,
|
|
114
|
+
});
|
|
115
|
+
const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
|
|
101
116
|
// The sandbox id of the in-flight attach (drives the per-card spinner).
|
|
102
117
|
const [attachingSandboxId, setAttachingSandboxId] = useState<string | null>(null);
|
|
103
118
|
|
|
104
|
-
const data =
|
|
119
|
+
const data = loadedData ?? EMPTY;
|
|
105
120
|
// The swap is session-scoped: a host adapter (`attachMachine`) wins; otherwise
|
|
106
121
|
// the default `swapActiveSandbox` path is wired whenever a sessionId is in
|
|
107
122
|
// scope. Either way attach needs a sessionId to point at.
|
|
@@ -120,19 +135,22 @@ export function useMachines(options: UseMachinesOptions = {}): UseMachinesResult
|
|
|
120
135
|
: null;
|
|
121
136
|
if (!runSwap) return false;
|
|
122
137
|
setAttachingSandboxId(sandboxId);
|
|
123
|
-
const result = await
|
|
138
|
+
const result = await run(async () => {
|
|
124
139
|
await runSwap();
|
|
125
140
|
return true;
|
|
126
141
|
});
|
|
127
142
|
setAttachingSandboxId(null);
|
|
128
|
-
if (result) await
|
|
143
|
+
if (result) await refresh();
|
|
129
144
|
return result === true;
|
|
130
145
|
},
|
|
131
|
-
[machinesClient, workspaceId, sessionId,
|
|
146
|
+
[machinesClient, workspaceId, sessionId, run, refresh],
|
|
132
147
|
);
|
|
133
148
|
|
|
134
149
|
const fetchSeries = useCallback(
|
|
135
|
-
async (
|
|
150
|
+
async (
|
|
151
|
+
enrollmentId: string,
|
|
152
|
+
window: "15m" | "1h" | "6h" | "24h" = "1h",
|
|
153
|
+
): Promise<MetricSample[]> => {
|
|
136
154
|
if (!machinesClient.machineMetricsSeries) return [];
|
|
137
155
|
return await machinesClient.machineMetricsSeries(workspaceId, enrollmentId, { window });
|
|
138
156
|
},
|
|
@@ -143,15 +161,15 @@ export function useMachines(options: UseMachinesOptions = {}): UseMachinesResult
|
|
|
143
161
|
machines: data.machines,
|
|
144
162
|
activeSandboxId: data.activeSandboxId,
|
|
145
163
|
activeEpoch: data.activeEpoch,
|
|
146
|
-
loading
|
|
147
|
-
error
|
|
148
|
-
refresh
|
|
164
|
+
loading,
|
|
165
|
+
error,
|
|
166
|
+
refresh,
|
|
149
167
|
attach,
|
|
150
168
|
canAttach,
|
|
151
169
|
fetchSeries,
|
|
152
|
-
attaching:
|
|
170
|
+
attaching: mutating,
|
|
153
171
|
attachingSandboxId,
|
|
154
|
-
mutationError
|
|
155
|
-
clearMutationError
|
|
172
|
+
mutationError,
|
|
173
|
+
clearMutationError,
|
|
156
174
|
};
|
|
157
175
|
}
|
package/src/hooks/use-packs.ts
CHANGED
|
@@ -9,6 +9,8 @@ import { useCallback } from "react";
|
|
|
9
9
|
import { useOpenGeni, type ClientOverride } from "../provider";
|
|
10
10
|
import { useMutationRunner, usePolledValue } from "./internal";
|
|
11
11
|
|
|
12
|
+
const EMPTY_INSTALLATIONS: PackInstallation[] = [];
|
|
13
|
+
|
|
12
14
|
export type UsePacksOptions = ClientOverride & {
|
|
13
15
|
pollIntervalMs?: number | undefined;
|
|
14
16
|
enabled?: boolean | undefined;
|
|
@@ -38,46 +40,49 @@ export type UsePacksResult = {
|
|
|
38
40
|
export function usePacks(options: UsePacksOptions = {}): UsePacksResult {
|
|
39
41
|
const { client, workspaceId } = useOpenGeni(options);
|
|
40
42
|
const load = useCallback(async () => await client.listPacks(workspaceId), [client, workspaceId]);
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
+
const { data, loading, error, refresh } = usePolledValue(load, {
|
|
44
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
45
|
+
enabled: options.enabled,
|
|
46
|
+
});
|
|
47
|
+
const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
|
|
43
48
|
|
|
44
49
|
const register = useCallback(
|
|
45
50
|
async (manifest: RegisterCapabilityPackRequest): Promise<WorkspaceRegisteredPack | null> => {
|
|
46
|
-
const result = await
|
|
51
|
+
const result = await run(() => client.registerPack(workspaceId, manifest));
|
|
47
52
|
if (result) {
|
|
48
|
-
await
|
|
53
|
+
await refresh();
|
|
49
54
|
}
|
|
50
55
|
return result;
|
|
51
56
|
},
|
|
52
|
-
[client, workspaceId,
|
|
57
|
+
[client, workspaceId, run, refresh],
|
|
53
58
|
);
|
|
54
59
|
|
|
55
60
|
const enable = useCallback(
|
|
56
61
|
async (packId: string, request: EnablePackRequest = {}): Promise<PackInstallation | null> => {
|
|
57
|
-
const result = await
|
|
62
|
+
const result = await run(() => client.enablePack(workspaceId, packId, request));
|
|
58
63
|
if (result) {
|
|
59
|
-
await
|
|
64
|
+
await refresh();
|
|
60
65
|
}
|
|
61
66
|
return result;
|
|
62
67
|
},
|
|
63
|
-
[client, workspaceId,
|
|
68
|
+
[client, workspaceId, run, refresh],
|
|
64
69
|
);
|
|
65
70
|
|
|
66
71
|
const remove = useCallback(
|
|
67
72
|
async (packId: string): Promise<boolean> => {
|
|
68
|
-
const result = await
|
|
73
|
+
const result = await run(async () => {
|
|
69
74
|
await client.deletePack(workspaceId, packId);
|
|
70
75
|
return true;
|
|
71
76
|
});
|
|
72
77
|
if (result) {
|
|
73
|
-
await
|
|
78
|
+
await refresh();
|
|
74
79
|
}
|
|
75
80
|
return result === true;
|
|
76
81
|
},
|
|
77
|
-
[client, workspaceId,
|
|
82
|
+
[client, workspaceId, run, refresh],
|
|
78
83
|
);
|
|
79
84
|
|
|
80
|
-
const installations =
|
|
85
|
+
const installations = data?.installations ?? EMPTY_INSTALLATIONS;
|
|
81
86
|
const installationFor = useCallback(
|
|
82
87
|
(packId: string): PackInstallation | null =>
|
|
83
88
|
installations.find((installation) => installation.packId === packId) ?? null,
|
|
@@ -85,17 +90,17 @@ export function usePacks(options: UsePacksOptions = {}): UsePacksResult {
|
|
|
85
90
|
);
|
|
86
91
|
|
|
87
92
|
return {
|
|
88
|
-
packs:
|
|
93
|
+
packs: data?.packs ?? [],
|
|
89
94
|
installations,
|
|
90
95
|
installationFor,
|
|
91
|
-
loading
|
|
92
|
-
error
|
|
93
|
-
refresh
|
|
96
|
+
loading,
|
|
97
|
+
error,
|
|
98
|
+
refresh,
|
|
94
99
|
register,
|
|
95
100
|
enable,
|
|
96
101
|
remove,
|
|
97
|
-
mutating
|
|
98
|
-
mutationError
|
|
99
|
-
clearMutationError
|
|
102
|
+
mutating,
|
|
103
|
+
mutationError,
|
|
104
|
+
clearMutationError,
|
|
100
105
|
};
|
|
101
106
|
}
|
|
@@ -85,7 +85,9 @@ function defaultWebSocketFactory(url: string): DesktopWebSocketLike {
|
|
|
85
85
|
* decoded at a time; a frame arriving mid-decode replaces the pending one
|
|
86
86
|
* (latest-wins), so a slow decode can never build an unbounded queue.
|
|
87
87
|
*/
|
|
88
|
-
export function useRelayFrameStream(
|
|
88
|
+
export function useRelayFrameStream(
|
|
89
|
+
options: UseRelayFrameStreamOptions,
|
|
90
|
+
): UseRelayFrameStreamResult {
|
|
89
91
|
const { capability, containerRef, webSocketFactory } = options;
|
|
90
92
|
const [state, setState] = useState<DesktopConnectionState>("idle");
|
|
91
93
|
const [error, setError] = useState<Error | null>(null);
|
|
@@ -183,7 +185,8 @@ export function useRelayFrameStream(options: UseRelayFrameStreamOptions): UseRel
|
|
|
183
185
|
if (decoding) return;
|
|
184
186
|
decoding = true;
|
|
185
187
|
try {
|
|
186
|
-
while (
|
|
188
|
+
while (pending) {
|
|
189
|
+
if (disposed) break;
|
|
187
190
|
const data = pending;
|
|
188
191
|
pending = null;
|
|
189
192
|
let bmp: ImageBitmap;
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CreateRigRequest,
|
|
3
|
+
ProposeRigChangeRequest,
|
|
4
|
+
Rig,
|
|
5
|
+
RigChange,
|
|
6
|
+
RigVersion,
|
|
7
|
+
UpdateRigRequest,
|
|
8
|
+
} from "@opengeni/sdk";
|
|
9
|
+
import { useCallback } from "react";
|
|
10
|
+
import { useOpenGeni, type ClientOverride } from "../provider";
|
|
11
|
+
import { useMutationRunner, usePolledValue } from "./internal";
|
|
12
|
+
|
|
13
|
+
export type UseRigsOptions = ClientOverride & {
|
|
14
|
+
pollIntervalMs?: number | undefined;
|
|
15
|
+
enabled?: boolean | undefined;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type UseRigsResult = {
|
|
19
|
+
rigs: Rig[];
|
|
20
|
+
loading: boolean;
|
|
21
|
+
error: Error | null;
|
|
22
|
+
refresh: () => Promise<void>;
|
|
23
|
+
create: (request: CreateRigRequest) => Promise<Rig | null>;
|
|
24
|
+
update: (rigId: string, request: UpdateRigRequest) => Promise<Rig | null>;
|
|
25
|
+
remove: (rigId: string) => Promise<boolean>;
|
|
26
|
+
listVersions: (rigId: string) => Promise<RigVersion[] | null>;
|
|
27
|
+
activateVersion: (rigId: string, versionId: string) => Promise<RigVersion | null>;
|
|
28
|
+
listChanges: (rigId: string) => Promise<RigChange[] | null>;
|
|
29
|
+
proposeChange: (rigId: string, request: ProposeRigChangeRequest) => Promise<RigChange | null>;
|
|
30
|
+
mutating: boolean;
|
|
31
|
+
mutationError: Error | null;
|
|
32
|
+
clearMutationError: () => void;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Rigs (workspace-scoped, versioned sandbox machine definitions). The list
|
|
37
|
+
* polls; version/change reads are on-demand (they are per-rig detail, not part
|
|
38
|
+
* of the list surface).
|
|
39
|
+
*/
|
|
40
|
+
export function useRigs(options: UseRigsOptions = {}): UseRigsResult {
|
|
41
|
+
const { client, workspaceId } = useOpenGeni(options);
|
|
42
|
+
const load = useCallback(async () => await client.listRigs(workspaceId), [client, workspaceId]);
|
|
43
|
+
const { data, loading, error, refresh } = usePolledValue(load, {
|
|
44
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
45
|
+
enabled: options.enabled,
|
|
46
|
+
});
|
|
47
|
+
const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
|
|
48
|
+
|
|
49
|
+
const create = useCallback(
|
|
50
|
+
async (request: CreateRigRequest): Promise<Rig | null> => {
|
|
51
|
+
const result = await run(() => client.createRig(workspaceId, request));
|
|
52
|
+
if (result) {
|
|
53
|
+
await refresh();
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
},
|
|
57
|
+
[client, workspaceId, run, refresh],
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const update = useCallback(
|
|
61
|
+
async (rigId: string, request: UpdateRigRequest): Promise<Rig | null> => {
|
|
62
|
+
const result = await run(() => client.updateRig(workspaceId, rigId, request));
|
|
63
|
+
if (result) {
|
|
64
|
+
await refresh();
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
},
|
|
68
|
+
[client, workspaceId, run, refresh],
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const remove = useCallback(
|
|
72
|
+
async (rigId: string): Promise<boolean> => {
|
|
73
|
+
const result = await run(async () => {
|
|
74
|
+
await client.deleteRig(workspaceId, rigId);
|
|
75
|
+
return true;
|
|
76
|
+
});
|
|
77
|
+
if (result) {
|
|
78
|
+
await refresh();
|
|
79
|
+
}
|
|
80
|
+
return result === true;
|
|
81
|
+
},
|
|
82
|
+
[client, workspaceId, run, refresh],
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const listVersions = useCallback(
|
|
86
|
+
async (rigId: string): Promise<RigVersion[] | null> => {
|
|
87
|
+
return await run(() => client.listRigVersions(workspaceId, rigId));
|
|
88
|
+
},
|
|
89
|
+
[client, workspaceId, run],
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const activateVersion = useCallback(
|
|
93
|
+
async (rigId: string, versionId: string): Promise<RigVersion | null> => {
|
|
94
|
+
const result = await run(() => client.activateRigVersion(workspaceId, rigId, versionId));
|
|
95
|
+
if (result) {
|
|
96
|
+
await refresh();
|
|
97
|
+
}
|
|
98
|
+
return result;
|
|
99
|
+
},
|
|
100
|
+
[client, workspaceId, run, refresh],
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const listChanges = useCallback(
|
|
104
|
+
async (rigId: string): Promise<RigChange[] | null> => {
|
|
105
|
+
return await run(() => client.listRigChanges(workspaceId, rigId));
|
|
106
|
+
},
|
|
107
|
+
[client, workspaceId, run],
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const proposeChange = useCallback(
|
|
111
|
+
async (rigId: string, request: ProposeRigChangeRequest): Promise<RigChange | null> => {
|
|
112
|
+
const result = await run(() => client.proposeRigChange(workspaceId, rigId, request));
|
|
113
|
+
if (result) {
|
|
114
|
+
await refresh();
|
|
115
|
+
}
|
|
116
|
+
return result;
|
|
117
|
+
},
|
|
118
|
+
[client, workspaceId, run, refresh],
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
rigs: data ?? [],
|
|
123
|
+
loading,
|
|
124
|
+
error,
|
|
125
|
+
refresh,
|
|
126
|
+
create,
|
|
127
|
+
update,
|
|
128
|
+
remove,
|
|
129
|
+
listVersions,
|
|
130
|
+
activateVersion,
|
|
131
|
+
listChanges,
|
|
132
|
+
proposeChange,
|
|
133
|
+
mutating,
|
|
134
|
+
mutationError,
|
|
135
|
+
clearMutationError,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export type UseRigOptions = ClientOverride & {
|
|
140
|
+
pollIntervalMs?: number | undefined;
|
|
141
|
+
enabled?: boolean | undefined;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export type UseRigResult = {
|
|
145
|
+
rig: Rig | null;
|
|
146
|
+
loading: boolean;
|
|
147
|
+
error: Error | null;
|
|
148
|
+
refresh: () => Promise<void>;
|
|
149
|
+
update: (request: UpdateRigRequest) => Promise<Rig | null>;
|
|
150
|
+
remove: () => Promise<boolean>;
|
|
151
|
+
activateVersion: (versionId: string) => Promise<RigVersion | null>;
|
|
152
|
+
proposeChange: (request: ProposeRigChangeRequest) => Promise<RigChange | null>;
|
|
153
|
+
/** Re-run verification for a change (asynchronous — poll for the outcome). */
|
|
154
|
+
verifyChange: (changeId: string) => Promise<RigChange | null>;
|
|
155
|
+
/** Promote a verified definition_edit into a new active version (rigs:manage). */
|
|
156
|
+
promoteChange: (changeId: string) => Promise<RigVersion | null>;
|
|
157
|
+
/** Re-verify the active version's checks in a clean throwaway sandbox. */
|
|
158
|
+
verify: () => Promise<{ ok: boolean; versionId: string } | null>;
|
|
159
|
+
mutating: boolean;
|
|
160
|
+
mutationError: Error | null;
|
|
161
|
+
clearMutationError: () => void;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* A single rig (its active version + counts), polled so verification and
|
|
166
|
+
* promotion state stay live. Owns the rig's write actions; the versions and
|
|
167
|
+
* changes lists are separate polled reads (`useRigVersions`/`useRigChanges`).
|
|
168
|
+
*/
|
|
169
|
+
export function useRig(rigId: string, options: UseRigOptions = {}): UseRigResult {
|
|
170
|
+
const { client, workspaceId } = useOpenGeni(options);
|
|
171
|
+
const load = useCallback(
|
|
172
|
+
async () => await client.getRig(workspaceId, rigId),
|
|
173
|
+
[client, workspaceId, rigId],
|
|
174
|
+
);
|
|
175
|
+
const { data, loading, error, refresh } = usePolledValue(load, {
|
|
176
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
177
|
+
enabled: options.enabled,
|
|
178
|
+
});
|
|
179
|
+
const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
|
|
180
|
+
|
|
181
|
+
const update = useCallback(
|
|
182
|
+
async (request: UpdateRigRequest): Promise<Rig | null> => {
|
|
183
|
+
const result = await run(() => client.updateRig(workspaceId, rigId, request));
|
|
184
|
+
if (result) {
|
|
185
|
+
await refresh();
|
|
186
|
+
}
|
|
187
|
+
return result;
|
|
188
|
+
},
|
|
189
|
+
[client, workspaceId, rigId, run, refresh],
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
const remove = useCallback(async (): Promise<boolean> => {
|
|
193
|
+
const result = await run(async () => {
|
|
194
|
+
await client.deleteRig(workspaceId, rigId);
|
|
195
|
+
return true;
|
|
196
|
+
});
|
|
197
|
+
return result === true;
|
|
198
|
+
}, [client, workspaceId, rigId, run]);
|
|
199
|
+
|
|
200
|
+
const activateVersion = useCallback(
|
|
201
|
+
async (versionId: string): Promise<RigVersion | null> => {
|
|
202
|
+
const result = await run(() => client.activateRigVersion(workspaceId, rigId, versionId));
|
|
203
|
+
if (result) {
|
|
204
|
+
await refresh();
|
|
205
|
+
}
|
|
206
|
+
return result;
|
|
207
|
+
},
|
|
208
|
+
[client, workspaceId, rigId, run, refresh],
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
const proposeChange = useCallback(
|
|
212
|
+
async (request: ProposeRigChangeRequest): Promise<RigChange | null> => {
|
|
213
|
+
const result = await run(() => client.proposeRigChange(workspaceId, rigId, request));
|
|
214
|
+
if (result) {
|
|
215
|
+
await refresh();
|
|
216
|
+
}
|
|
217
|
+
return result;
|
|
218
|
+
},
|
|
219
|
+
[client, workspaceId, rigId, run, refresh],
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
const verifyChange = useCallback(
|
|
223
|
+
async (changeId: string): Promise<RigChange | null> => {
|
|
224
|
+
const result = await run(() => client.verifyRigChange(workspaceId, rigId, changeId));
|
|
225
|
+
if (result) {
|
|
226
|
+
await refresh();
|
|
227
|
+
}
|
|
228
|
+
return result;
|
|
229
|
+
},
|
|
230
|
+
[client, workspaceId, rigId, run, refresh],
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
const promoteChange = useCallback(
|
|
234
|
+
async (changeId: string): Promise<RigVersion | null> => {
|
|
235
|
+
const result = await run(() => client.promoteRigChange(workspaceId, rigId, changeId));
|
|
236
|
+
if (result) {
|
|
237
|
+
await refresh();
|
|
238
|
+
}
|
|
239
|
+
return result;
|
|
240
|
+
},
|
|
241
|
+
[client, workspaceId, rigId, run, refresh],
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
const verify = useCallback(async (): Promise<{ ok: boolean; versionId: string } | null> => {
|
|
245
|
+
return await run(() => client.verifyRig(workspaceId, rigId));
|
|
246
|
+
}, [client, workspaceId, rigId, run]);
|
|
247
|
+
|
|
248
|
+
return {
|
|
249
|
+
rig: data,
|
|
250
|
+
loading,
|
|
251
|
+
error,
|
|
252
|
+
refresh,
|
|
253
|
+
update,
|
|
254
|
+
remove,
|
|
255
|
+
activateVersion,
|
|
256
|
+
proposeChange,
|
|
257
|
+
verifyChange,
|
|
258
|
+
promoteChange,
|
|
259
|
+
verify,
|
|
260
|
+
mutating,
|
|
261
|
+
mutationError,
|
|
262
|
+
clearMutationError,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export type UseRigVersionsOptions = ClientOverride & {
|
|
267
|
+
pollIntervalMs?: number | undefined;
|
|
268
|
+
enabled?: boolean | undefined;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
export type UseRigVersionsResult = {
|
|
272
|
+
versions: RigVersion[];
|
|
273
|
+
loading: boolean;
|
|
274
|
+
error: Error | null;
|
|
275
|
+
refresh: () => Promise<void>;
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
/** A rig's append-only version history, newest-first (polled). */
|
|
279
|
+
export function useRigVersions(
|
|
280
|
+
rigId: string,
|
|
281
|
+
options: UseRigVersionsOptions = {},
|
|
282
|
+
): UseRigVersionsResult {
|
|
283
|
+
const { client, workspaceId } = useOpenGeni(options);
|
|
284
|
+
const load = useCallback(
|
|
285
|
+
async () => await client.listRigVersions(workspaceId, rigId),
|
|
286
|
+
[client, workspaceId, rigId],
|
|
287
|
+
);
|
|
288
|
+
const state = usePolledValue(load, {
|
|
289
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
290
|
+
enabled: options.enabled,
|
|
291
|
+
});
|
|
292
|
+
return {
|
|
293
|
+
versions: state.data ?? [],
|
|
294
|
+
loading: state.loading,
|
|
295
|
+
error: state.error,
|
|
296
|
+
refresh: state.refresh,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export type UseRigChangesOptions = ClientOverride & {
|
|
301
|
+
pollIntervalMs?: number | undefined;
|
|
302
|
+
enabled?: boolean | undefined;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
export type UseRigChangesResult = {
|
|
306
|
+
changes: RigChange[];
|
|
307
|
+
loading: boolean;
|
|
308
|
+
error: Error | null;
|
|
309
|
+
refresh: () => Promise<void>;
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* A rig's change queue (polled). The default poll cadence lets a change move
|
|
314
|
+
* through verifying → merged/rejected without a manual refresh.
|
|
315
|
+
*/
|
|
316
|
+
export function useRigChanges(
|
|
317
|
+
rigId: string,
|
|
318
|
+
options: UseRigChangesOptions = {},
|
|
319
|
+
): UseRigChangesResult {
|
|
320
|
+
const { client, workspaceId } = useOpenGeni(options);
|
|
321
|
+
const load = useCallback(
|
|
322
|
+
async () => await client.listRigChanges(workspaceId, rigId),
|
|
323
|
+
[client, workspaceId, rigId],
|
|
324
|
+
);
|
|
325
|
+
const state = usePolledValue(load, {
|
|
326
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
327
|
+
enabled: options.enabled,
|
|
328
|
+
});
|
|
329
|
+
return {
|
|
330
|
+
changes: state.data ?? [],
|
|
331
|
+
loading: state.loading,
|
|
332
|
+
error: state.error,
|
|
333
|
+
refresh: state.refresh,
|
|
334
|
+
};
|
|
335
|
+
}
|