@opengeni/react 0.52.1 → 0.57.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 +48 -0
- package/dist/artifacts.d.ts +1 -1
- package/dist/artifacts.js +43 -14
- package/dist/artifacts.js.map +1 -1
- package/dist/{chunk-ROIRTM37.js → chunk-5UQPNLIO.js} +6 -6
- package/dist/{chunk-JR3QFY6B.js → chunk-6KLX2RH5.js} +49 -2
- package/dist/chunk-6KLX2RH5.js.map +1 -0
- package/dist/{chunk-FWS3KC3Z.js → chunk-B6WPZSTT.js} +104 -104
- package/dist/{chunk-FWS3KC3Z.js.map → chunk-B6WPZSTT.js.map} +1 -1
- package/dist/{chunk-HDBG7XGO.js → chunk-C5ZPSSS5.js} +50 -1
- package/dist/chunk-C5ZPSSS5.js.map +1 -0
- package/dist/{chunk-X4S56Y2H.js → chunk-HA46CYQK.js} +135 -11
- package/dist/chunk-HA46CYQK.js.map +1 -0
- package/dist/{chunk-724BW4TW.js → chunk-IGC6A2ZI.js} +2 -2
- package/dist/chunk-LLOLUHVG.js +5937 -0
- package/dist/chunk-LLOLUHVG.js.map +1 -0
- package/dist/{chunk-WYA65Y3F.js → chunk-MKPIAGVL.js} +13 -3
- package/dist/chunk-MKPIAGVL.js.map +1 -0
- package/dist/chunk-R3AZOAB5.js +933 -0
- package/dist/chunk-R3AZOAB5.js.map +1 -0
- package/dist/{chunk-23EJ676W.js → chunk-XBZWOM2K.js} +9 -4
- package/dist/chunk-XBZWOM2K.js.map +1 -0
- package/dist/{chunk-JAGDUCZQ.js → chunk-Y7OEDUJ5.js} +17 -13
- package/dist/chunk-Y7OEDUJ5.js.map +1 -0
- package/dist/client.d.ts +6 -4
- package/dist/components/artifacts/index.d.ts +1 -0
- package/dist/components/artifacts/published-html-artifact-frame.d.ts +14 -0
- package/dist/components/browser-input.d.ts +7 -0
- package/dist/components/browser-viewer.d.ts +5 -4
- package/dist/components/computer-viewer.d.ts +2 -1
- package/dist/components/desktop-viewer.d.ts +26 -2
- package/dist/components/interaction-intervention-banner.d.ts +11 -0
- package/dist/components/sandbox-terminal.d.ts +4 -1
- package/dist/components/sandbox-workspace.d.ts +3 -0
- package/dist/composer.js +6 -6
- package/dist/hooks/use-auth-runs.d.ts +32 -0
- package/dist/hooks/use-browser-downloads.d.ts +25 -0
- package/dist/hooks/use-browser-identities.d.ts +5 -1
- package/dist/hooks/use-browser-session.d.ts +2 -1
- package/dist/hooks/use-browser-sessions.d.ts +1 -1
- package/dist/hooks/use-channels.d.ts +26 -0
- package/dist/hooks/use-computer-frame-stream.d.ts +1 -1
- package/dist/hooks/use-computer-session.d.ts +2 -1
- package/dist/hooks/use-computer-sessions.d.ts +1 -1
- package/dist/hooks/use-desktop-stream.d.ts +4 -0
- package/dist/hooks/use-interaction-interventions.d.ts +27 -0
- package/dist/hooks/use-interaction-invalidation.d.ts +19 -0
- package/dist/hooks/use-network-routes.d.ts +25 -0
- package/dist/hooks/use-site-auth-connections.d.ts +25 -0
- package/dist/hooks/use-supergrok-accounts.d.ts +26 -0
- package/dist/hooks/use-terminal-stream.d.ts +3 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +1220 -1545
- package/dist/index.js.map +1 -1
- package/dist/interaction.d.ts +13 -3
- package/dist/interaction.js +520 -6
- package/dist/interaction.js.map +1 -1
- package/dist/interactive-workbench-viewers-TOHBXJEE.js +15 -0
- package/dist/lib/host-platform.d.ts +5 -0
- package/dist/lib/relay-wire.d.ts +20 -1
- package/dist/machines.js +3 -3
- package/dist/model-policy.d.ts +1 -1
- package/dist/model-policy.js +1 -1
- package/dist/provider.d.ts +3 -2
- package/dist/realtime/realtime-control.d.ts +2 -2
- package/dist/realtime.js +36 -9
- package/dist/realtime.js.map +1 -1
- package/dist/session-context.d.ts +10 -2
- package/dist/session-ui.js +3 -3
- package/dist/session.js +11 -11
- package/dist/timeline/types.d.ts +5 -0
- package/package.json +2 -2
- package/src/artifacts.ts +3 -0
- package/src/client.ts +73 -3
- package/src/components/artifacts/index.ts +5 -0
- package/src/components/artifacts/published-html-artifact-frame.tsx +38 -0
- package/src/components/browser-input.ts +65 -0
- package/src/components/browser-viewer.tsx +1287 -177
- package/src/components/computer-viewer.tsx +611 -114
- package/src/components/desktop-viewer.tsx +114 -1
- package/src/components/interaction-intervention-banner.tsx +99 -0
- package/src/components/machines/machine-detail.tsx +1 -1
- package/src/components/model-policy-picker.tsx +15 -0
- package/src/components/sandbox-terminal.tsx +24 -7
- package/src/components/sandbox-workspace.tsx +42 -4
- package/src/components/workspace-dock.tsx +59 -5
- package/src/hooks/use-attached-browsers.ts +13 -11
- package/src/hooks/use-auth-runs.ts +278 -0
- package/src/hooks/use-browser-downloads.ts +218 -0
- package/src/hooks/use-browser-frame-stream.ts +168 -59
- package/src/hooks/use-browser-identities.ts +47 -6
- package/src/hooks/use-browser-session.ts +94 -27
- package/src/hooks/use-browser-sessions.ts +19 -14
- package/src/hooks/use-channels.ts +99 -0
- package/src/hooks/use-computer-frame-stream.ts +184 -61
- package/src/hooks/use-computer-session.ts +22 -2
- package/src/hooks/use-computer-sessions.ts +19 -13
- package/src/hooks/use-desktop-stream.ts +39 -7
- package/src/hooks/use-interaction-interventions.ts +242 -0
- package/src/hooks/use-interaction-invalidation.ts +66 -0
- package/src/hooks/use-machine-chip.ts +13 -3
- package/src/hooks/use-network-routes.ts +214 -0
- package/src/hooks/use-site-auth-connections.ts +226 -0
- package/src/hooks/use-supergrok-accounts.ts +81 -0
- package/src/hooks/use-terminal-stream.ts +288 -55
- package/src/index.ts +8 -0
- package/src/interaction.ts +30 -0
- package/src/lib/host-platform.ts +10 -0
- package/src/lib/relay-wire.ts +43 -4
- package/src/lib/terminal-capability.ts +1 -1
- package/src/model-policy.ts +19 -4
- package/src/provider.tsx +133 -1
- package/src/realtime/realtime-control.tsx +34 -6
- package/src/session-context.ts +66 -1
- package/src/timeline/activity-rail.tsx +6 -0
- package/src/timeline/projection.ts +58 -0
- package/src/timeline/types.ts +2 -0
- package/styles/compiled.css +64 -6
- package/dist/chunk-23EJ676W.js.map +0 -1
- package/dist/chunk-6BBWWOOU.js +0 -82
- package/dist/chunk-6BBWWOOU.js.map +0 -1
- package/dist/chunk-7I2KB3OK.js +0 -3895
- package/dist/chunk-7I2KB3OK.js.map +0 -1
- package/dist/chunk-HDBG7XGO.js.map +0 -1
- package/dist/chunk-JAGDUCZQ.js.map +0 -1
- package/dist/chunk-JR3QFY6B.js.map +0 -1
- package/dist/chunk-WYA65Y3F.js.map +0 -1
- package/dist/chunk-X4S56Y2H.js.map +0 -1
- package/dist/interactive-workbench-viewers-KHNOHWQE.js +0 -13
- /package/dist/{chunk-ROIRTM37.js.map → chunk-5UQPNLIO.js.map} +0 -0
- /package/dist/{chunk-724BW4TW.js.map → chunk-IGC6A2ZI.js.map} +0 -0
- /package/dist/{interactive-workbench-viewers-KHNOHWQE.js.map → interactive-workbench-viewers-TOHBXJEE.js.map} +0 -0
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
BrowserAction,
|
|
3
3
|
BrowserActionBatch,
|
|
4
4
|
BrowserActionReceipt,
|
|
5
|
+
BrowserClipboard,
|
|
5
6
|
BrowserDiagnosticBatch,
|
|
6
7
|
BrowserDiagnosticsOptions,
|
|
7
8
|
BrowserFrame,
|
|
@@ -9,6 +10,7 @@ import type {
|
|
|
9
10
|
BrowserSession,
|
|
10
11
|
BrowserTarget,
|
|
11
12
|
} from "@opengeni/sdk/interaction";
|
|
13
|
+
import { OpenGeniApiError } from "@opengeni/sdk";
|
|
12
14
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
13
15
|
import {
|
|
14
16
|
type EmbeddedBrowserInteractionClientOverride,
|
|
@@ -45,6 +47,7 @@ export type UseBrowserSessionResult = {
|
|
|
45
47
|
frame: BrowserFrame,
|
|
46
48
|
operationId?: string,
|
|
47
49
|
) => Promise<BrowserActionReceipt>;
|
|
50
|
+
readClipboard: () => Promise<BrowserClipboard>;
|
|
48
51
|
diagnostics: (options?: BrowserDiagnosticsOptions) => Promise<BrowserDiagnosticBatch>;
|
|
49
52
|
};
|
|
50
53
|
|
|
@@ -118,14 +121,8 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
118
121
|
]);
|
|
119
122
|
if (!mountedRef.current || requestRef.current.id !== id) return;
|
|
120
123
|
const selected = chooseTarget(targetResponse.targets, selectedTargetIdRef.current);
|
|
121
|
-
const observation = selected
|
|
122
|
-
? await client.observeBrowserTarget(workspaceId, browserSessionId, selected.id, {
|
|
123
|
-
signal: controller.signal,
|
|
124
|
-
})
|
|
125
|
-
: null;
|
|
126
|
-
if (!mountedRef.current || requestRef.current.id !== id) return;
|
|
127
124
|
selectedTargetIdRef.current = selected?.id ?? null;
|
|
128
|
-
observationRef.current = { browserSessionId, observation };
|
|
125
|
+
observationRef.current = { browserSessionId, observation: null };
|
|
129
126
|
setState((current) =>
|
|
130
127
|
current.browserSessionId === browserSessionId
|
|
131
128
|
? {
|
|
@@ -133,12 +130,36 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
133
130
|
session,
|
|
134
131
|
targets: targetResponse.targets,
|
|
135
132
|
selectedTargetId: selected?.id ?? null,
|
|
136
|
-
observation,
|
|
133
|
+
observation: null,
|
|
137
134
|
loading: false,
|
|
138
135
|
error: null,
|
|
139
136
|
}
|
|
140
137
|
: current,
|
|
141
138
|
);
|
|
139
|
+
if (!selected) return;
|
|
140
|
+
try {
|
|
141
|
+
const observation = await client.observeBrowserTarget(
|
|
142
|
+
workspaceId,
|
|
143
|
+
browserSessionId,
|
|
144
|
+
selected.id,
|
|
145
|
+
{ signal: controller.signal },
|
|
146
|
+
);
|
|
147
|
+
if (!mountedRef.current || requestRef.current.id !== id) return;
|
|
148
|
+
observationRef.current = { browserSessionId, observation };
|
|
149
|
+
setState((current) =>
|
|
150
|
+
current.browserSessionId === browserSessionId &&
|
|
151
|
+
current.selectedTargetId === observation.target.id
|
|
152
|
+
? { ...current, observation }
|
|
153
|
+
: current,
|
|
154
|
+
);
|
|
155
|
+
} catch (cause) {
|
|
156
|
+
if (controller.signal.aborted || !mountedRef.current || requestRef.current.id !== id)
|
|
157
|
+
return;
|
|
158
|
+
// Target inventory and the media plane remain authoritative for the
|
|
159
|
+
// human-facing browser. A failed semantic snapshot must not blank or
|
|
160
|
+
// disable an otherwise live visual browser.
|
|
161
|
+
void cause;
|
|
162
|
+
}
|
|
142
163
|
} catch (cause) {
|
|
143
164
|
if (controller.signal.aborted || !mountedRef.current || requestRef.current.id !== id) return;
|
|
144
165
|
setState((current) =>
|
|
@@ -239,19 +260,50 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
239
260
|
async (targetId: string): Promise<BrowserTarget> => {
|
|
240
261
|
if (!browserSessionId) throw new Error("No BrowserSession is selected.");
|
|
241
262
|
return await runMutation(browserSessionId, async () => {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
263
|
+
let targets = visible.targets;
|
|
264
|
+
let target: BrowserTarget;
|
|
265
|
+
let observation: BrowserObservation;
|
|
266
|
+
try {
|
|
267
|
+
observation = await client.selectBrowserTarget(workspaceId, browserSessionId, targetId);
|
|
268
|
+
target = observation.target;
|
|
269
|
+
} catch (cause) {
|
|
270
|
+
if (!isMissingBrowserTarget(cause)) throw cause;
|
|
271
|
+
// A physical/attached tab may disappear between inventory and click.
|
|
272
|
+
// Reconcile once from the authoritative controller inventory and move
|
|
273
|
+
// to its live selected/first page instead of leaving a dead tab ID in
|
|
274
|
+
// React state. Session-level 404s are deliberately not swallowed.
|
|
275
|
+
const response = await client.listBrowserTargets(workspaceId, browserSessionId);
|
|
276
|
+
targets = response.targets;
|
|
277
|
+
const fallback = chooseTarget(targets, null);
|
|
278
|
+
if (!fallback) {
|
|
279
|
+
selectedTargetIdRef.current = null;
|
|
280
|
+
observationRef.current = { browserSessionId, observation: null };
|
|
281
|
+
setState((current) =>
|
|
282
|
+
current.browserSessionId === browserSessionId
|
|
283
|
+
? {
|
|
284
|
+
...current,
|
|
285
|
+
targets,
|
|
286
|
+
selectedTargetId: null,
|
|
287
|
+
observation: null,
|
|
288
|
+
}
|
|
289
|
+
: current,
|
|
290
|
+
);
|
|
291
|
+
throw cause;
|
|
292
|
+
}
|
|
293
|
+
observation = await client.selectBrowserTarget(
|
|
294
|
+
workspaceId,
|
|
295
|
+
browserSessionId,
|
|
296
|
+
fallback.id,
|
|
297
|
+
);
|
|
298
|
+
target = observation.target;
|
|
299
|
+
}
|
|
248
300
|
selectedTargetIdRef.current = target.id;
|
|
249
301
|
observationRef.current = { browserSessionId, observation };
|
|
250
302
|
setState((current) =>
|
|
251
303
|
current.browserSessionId === browserSessionId
|
|
252
304
|
? {
|
|
253
305
|
...current,
|
|
254
|
-
targets:
|
|
306
|
+
targets: targets.map((candidate) => ({
|
|
255
307
|
...candidate,
|
|
256
308
|
selected: candidate.id === target.id,
|
|
257
309
|
})),
|
|
@@ -263,23 +315,19 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
263
315
|
return target;
|
|
264
316
|
});
|
|
265
317
|
},
|
|
266
|
-
[browserSessionId, client, runMutation, workspaceId],
|
|
318
|
+
[browserSessionId, client, runMutation, visible.targets, workspaceId],
|
|
267
319
|
);
|
|
268
320
|
|
|
269
321
|
const openTarget = useCallback(
|
|
270
322
|
async (url?: string): Promise<BrowserTarget> => {
|
|
271
323
|
if (!browserSessionId) throw new Error("No BrowserSession is selected.");
|
|
272
324
|
return await runMutation(browserSessionId, async () => {
|
|
273
|
-
const
|
|
325
|
+
const observation = await client.openBrowserTarget(
|
|
274
326
|
workspaceId,
|
|
275
327
|
browserSessionId,
|
|
276
328
|
url === undefined ? {} : { url },
|
|
277
329
|
);
|
|
278
|
-
const
|
|
279
|
-
workspaceId,
|
|
280
|
-
browserSessionId,
|
|
281
|
-
target.id,
|
|
282
|
-
);
|
|
330
|
+
const target = observation.target;
|
|
283
331
|
selectedTargetIdRef.current = target.id;
|
|
284
332
|
observationRef.current = { browserSessionId, observation };
|
|
285
333
|
setState((current) =>
|
|
@@ -362,10 +410,11 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
362
410
|
if (!fence) {
|
|
363
411
|
throw new Error("The browser page is not ready for input.");
|
|
364
412
|
}
|
|
365
|
-
|
|
413
|
+
try {
|
|
366
414
|
const receipt = await client.actInBrowser(workspaceId, browserSessionId, {
|
|
367
415
|
operationId,
|
|
368
416
|
...fence,
|
|
417
|
+
observationMode: "none",
|
|
369
418
|
action,
|
|
370
419
|
});
|
|
371
420
|
if (receipt.observation) {
|
|
@@ -382,13 +431,17 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
382
431
|
}
|
|
383
432
|
: current,
|
|
384
433
|
);
|
|
385
|
-
} else {
|
|
386
|
-
void refresh();
|
|
387
434
|
}
|
|
388
435
|
return receipt;
|
|
389
|
-
})
|
|
436
|
+
} catch (cause) {
|
|
437
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
438
|
+
setState((current) =>
|
|
439
|
+
current.browserSessionId === browserSessionId ? { ...current, error } : current,
|
|
440
|
+
);
|
|
441
|
+
throw error;
|
|
442
|
+
}
|
|
390
443
|
},
|
|
391
|
-
[browserSessionId, client,
|
|
444
|
+
[browserSessionId, client, workspaceId],
|
|
392
445
|
);
|
|
393
446
|
|
|
394
447
|
const act = useCallback(
|
|
@@ -424,6 +477,15 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
424
477
|
[browserSessionId, client, workspaceId],
|
|
425
478
|
);
|
|
426
479
|
|
|
480
|
+
const readClipboard = useCallback(async (): Promise<BrowserClipboard> => {
|
|
481
|
+
if (!browserSessionId) throw new Error("No BrowserSession is selected.");
|
|
482
|
+
const clipboard = await client.readBrowserClipboard(workspaceId, browserSessionId);
|
|
483
|
+
if (clipboard.browserSessionId !== browserSessionId) {
|
|
484
|
+
throw new Error("Browser clipboard belongs to another BrowserSession.");
|
|
485
|
+
}
|
|
486
|
+
return clipboard;
|
|
487
|
+
}, [browserSessionId, client, workspaceId]);
|
|
488
|
+
|
|
427
489
|
return {
|
|
428
490
|
session: visible.session,
|
|
429
491
|
targets: visible.targets,
|
|
@@ -439,6 +501,7 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
439
501
|
closeTarget,
|
|
440
502
|
act,
|
|
441
503
|
actFromFrame,
|
|
504
|
+
readClipboard,
|
|
442
505
|
diagnostics,
|
|
443
506
|
};
|
|
444
507
|
}
|
|
@@ -474,3 +537,7 @@ function replaceTarget(targets: readonly BrowserTarget[], target: BrowserTarget)
|
|
|
474
537
|
next.push(target);
|
|
475
538
|
return next.sort((left, right) => left.createdAt.localeCompare(right.createdAt));
|
|
476
539
|
}
|
|
540
|
+
|
|
541
|
+
function isMissingBrowserTarget(error: unknown): boolean {
|
|
542
|
+
return error instanceof OpenGeniApiError && error.code === "target_not_found";
|
|
543
|
+
}
|
|
@@ -9,13 +9,13 @@ import {
|
|
|
9
9
|
type EmbeddedBrowserInteractionClientOverride,
|
|
10
10
|
useEmbeddedBrowserInteraction,
|
|
11
11
|
} from "../session-context";
|
|
12
|
-
import {
|
|
12
|
+
import { useInteractionInvalidation } from "./use-interaction-invalidation";
|
|
13
13
|
|
|
14
14
|
export type UseBrowserSessionsOptions = EmbeddedBrowserInteractionClientOverride & {
|
|
15
15
|
/** Current agent/session relevance. Workspace peers remain in `sessions`. */
|
|
16
16
|
sessionId?: string | undefined;
|
|
17
17
|
enabled?: boolean | undefined;
|
|
18
|
-
/**
|
|
18
|
+
/** Polling fallback used only while the workspace invalidation stream is unavailable. */
|
|
19
19
|
pollIntervalMs?: number | undefined;
|
|
20
20
|
};
|
|
21
21
|
|
|
@@ -50,10 +50,14 @@ export type UseBrowserSessionsResult = {
|
|
|
50
50
|
export function useBrowserSessions(
|
|
51
51
|
options: UseBrowserSessionsOptions = {},
|
|
52
52
|
): UseBrowserSessionsResult {
|
|
53
|
-
const {
|
|
53
|
+
const {
|
|
54
|
+
client,
|
|
55
|
+
workspaceId,
|
|
56
|
+
registerSessionReconciler,
|
|
57
|
+
workspaceInteractionEvent,
|
|
58
|
+
workspaceInteractionConnectionState,
|
|
59
|
+
} = useEmbeddedBrowserInteraction(options);
|
|
54
60
|
const enabled = options.enabled ?? true;
|
|
55
|
-
const pageLive = usePageLiveActivity();
|
|
56
|
-
const pollIntervalMs = Math.max(1_000, options.pollIntervalMs ?? 3_000);
|
|
57
61
|
const [state, setState] = useState<{
|
|
58
62
|
workspaceId: string;
|
|
59
63
|
revision: number;
|
|
@@ -137,15 +141,16 @@ export function useBrowserSessions(
|
|
|
137
141
|
};
|
|
138
142
|
}, [cancelLoad, enabled, load, workspaceId]);
|
|
139
143
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
144
|
+
useInteractionInvalidation({
|
|
145
|
+
workspaceId,
|
|
146
|
+
key: workspaceId,
|
|
147
|
+
enabled,
|
|
148
|
+
event: workspaceInteractionEvent,
|
|
149
|
+
connectionState: workspaceInteractionConnectionState,
|
|
150
|
+
knownRevision: visibleState.revision,
|
|
151
|
+
refresh,
|
|
152
|
+
fallbackPollIntervalMs: options.pollIntervalMs,
|
|
153
|
+
});
|
|
149
154
|
|
|
150
155
|
useEffect(() => {
|
|
151
156
|
if (!enabled || !options.sessionId) return;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { Channel, CreateChannelRequest, Session, UpdateChannelRequest } from "@opengeni/sdk";
|
|
2
|
+
import { useCallback } from "react";
|
|
3
|
+
import { useOpenGeni, type ClientOverride } from "../provider";
|
|
4
|
+
import { useMutationRunner, usePolledValue } from "./internal";
|
|
5
|
+
|
|
6
|
+
export type UseChannelsOptions = ClientOverride & {
|
|
7
|
+
pollIntervalMs?: number | undefined;
|
|
8
|
+
enabled?: boolean | undefined;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type UseChannelsResult = {
|
|
12
|
+
channels: Channel[];
|
|
13
|
+
loading: boolean;
|
|
14
|
+
error: Error | null;
|
|
15
|
+
refresh: () => Promise<void>;
|
|
16
|
+
create: (request: CreateChannelRequest) => Promise<Channel | null>;
|
|
17
|
+
update: (channelId: string, request: UpdateChannelRequest) => Promise<Channel | null>;
|
|
18
|
+
remove: (channelId: string) => Promise<boolean>;
|
|
19
|
+
/** Re-file a session into a channel; null moves it back to the inbox. */
|
|
20
|
+
moveSession: (sessionId: string, channelId: string | null) => Promise<Session | null>;
|
|
21
|
+
mutating: boolean;
|
|
22
|
+
mutationError: Error | null;
|
|
23
|
+
clearMutationError: () => void;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Workspace-shared channels: rail organization for root sessions
|
|
28
|
+
* ("workstreams") by work type. Pure metadata — filing a session into a
|
|
29
|
+
* channel never affects execution.
|
|
30
|
+
*/
|
|
31
|
+
export function useChannels(options: UseChannelsOptions = {}): UseChannelsResult {
|
|
32
|
+
const { client, workspaceId } = useOpenGeni(options);
|
|
33
|
+
const load = useCallback(
|
|
34
|
+
async () => await client.listChannels(workspaceId),
|
|
35
|
+
[client, workspaceId],
|
|
36
|
+
);
|
|
37
|
+
const { data, loading, error, refresh } = usePolledValue(load, {
|
|
38
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
39
|
+
enabled: options.enabled,
|
|
40
|
+
});
|
|
41
|
+
const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
|
|
42
|
+
|
|
43
|
+
const create = useCallback(
|
|
44
|
+
async (request: CreateChannelRequest): Promise<Channel | null> => {
|
|
45
|
+
const result = await run(() => client.createChannel(workspaceId, request));
|
|
46
|
+
if (result) {
|
|
47
|
+
await refresh();
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
},
|
|
51
|
+
[client, workspaceId, run, refresh],
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const update = useCallback(
|
|
55
|
+
async (channelId: string, request: UpdateChannelRequest): Promise<Channel | null> => {
|
|
56
|
+
const result = await run(() => client.updateChannel(workspaceId, channelId, request));
|
|
57
|
+
if (result) {
|
|
58
|
+
await refresh();
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
},
|
|
62
|
+
[client, workspaceId, run, refresh],
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const remove = useCallback(
|
|
66
|
+
async (channelId: string): Promise<boolean> => {
|
|
67
|
+
const result = await run(async () => {
|
|
68
|
+
await client.deleteChannel(workspaceId, channelId);
|
|
69
|
+
return true;
|
|
70
|
+
});
|
|
71
|
+
if (result) {
|
|
72
|
+
await refresh();
|
|
73
|
+
}
|
|
74
|
+
return result === true;
|
|
75
|
+
},
|
|
76
|
+
[client, workspaceId, run, refresh],
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const moveSession = useCallback(
|
|
80
|
+
async (sessionId: string, channelId: string | null): Promise<Session | null> => {
|
|
81
|
+
return await run(() => client.updateSessionChannel(workspaceId, sessionId, { channelId }));
|
|
82
|
+
},
|
|
83
|
+
[client, workspaceId, run],
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
channels: data ?? [],
|
|
88
|
+
loading,
|
|
89
|
+
error,
|
|
90
|
+
refresh,
|
|
91
|
+
create,
|
|
92
|
+
update,
|
|
93
|
+
remove,
|
|
94
|
+
moveSession,
|
|
95
|
+
mutating,
|
|
96
|
+
mutationError,
|
|
97
|
+
clearMutationError,
|
|
98
|
+
};
|
|
99
|
+
}
|