@neta-art/cohub 8.22.0 → 8.23.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/dist/board/task.d.ts +2 -3
- package/dist/board/task.js +25 -95
- package/dist/chunks/environment.d.ts +18 -1
- package/dist/chunks/http.d.ts +78 -3
- package/dist/chunks/http.js +12 -0
- package/dist/chunks/websocket.d.ts +1 -1
- package/dist/generation-blocks.js +125 -0
- package/dist/http.d.ts +3 -3
- package/dist/index.d.ts +123 -3
- package/dist/index.js +674 -29
- package/dist/media.d.ts +81 -0
- package/dist/media.js +177 -0
- package/package.json +5 -1
package/dist/index.js
CHANGED
|
@@ -14,15 +14,28 @@ const appAuthorizationTargetSchema = z.discriminatedUnion("kind", [
|
|
|
14
14
|
}),
|
|
15
15
|
z.object({ kind: z.literal("pick-space") })
|
|
16
16
|
]);
|
|
17
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Read-only scopes the Shell may renew without a dialog for any App on the
|
|
19
|
+
* Space it shows. The API still checks the viewer's own permissions.
|
|
20
|
+
*/
|
|
21
|
+
const APP_SILENT_SHELL_SCOPES = [
|
|
18
22
|
"space.view",
|
|
19
23
|
"file.view",
|
|
20
24
|
"file.view.filtered",
|
|
21
25
|
"session.view",
|
|
22
26
|
"taskrun.view",
|
|
23
27
|
"checkpoint.view"
|
|
24
|
-
]
|
|
28
|
+
];
|
|
29
|
+
const APP_SILENT_SHELL_SCOPE_SET = new Set(APP_SILENT_SHELL_SCOPES);
|
|
25
30
|
const isAppSilentShellScope = (scope) => APP_SILENT_SHELL_SCOPE_SET.has(scope);
|
|
31
|
+
/**
|
|
32
|
+
* The API-enforced ceiling for Host consent (`consent: "host"`), given to Apps
|
|
33
|
+
* the Shell Space published or installed. Trust is decided by the Shell, the
|
|
34
|
+
* only holder of the viewer's token.
|
|
35
|
+
*/
|
|
36
|
+
const APP_HOST_CONSENT_SCOPES = [...APP_SILENT_SHELL_SCOPES, "file.edit"];
|
|
37
|
+
const APP_HOST_CONSENT_SCOPE_SET = new Set(APP_HOST_CONSENT_SCOPES);
|
|
38
|
+
const isAppHostConsentScope = (scope) => APP_HOST_CONSENT_SCOPE_SET.has(scope);
|
|
26
39
|
const appAuthorizationRequestSchema = z.object({
|
|
27
40
|
target: appAuthorizationTargetSchema,
|
|
28
41
|
scopes: z.array(z.string().min(1)).min(1).max(128),
|
|
@@ -281,8 +294,8 @@ const BUILTIN_VERSIONS = new Set(BOARD_BUILTIN_CAPABILITIES.map((c) => `${c.kind
|
|
|
281
294
|
function isBuiltinBoardCapability(kind, id, version) {
|
|
282
295
|
return BUILTIN_VERSIONS.has(`${kind}:${id}@${version}`);
|
|
283
296
|
}
|
|
284
|
-
const record = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
285
|
-
const finite = (value) => typeof value === "number" && Number.isFinite(value);
|
|
297
|
+
const record$1 = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
298
|
+
const finite$1 = (value) => typeof value === "number" && Number.isFinite(value);
|
|
286
299
|
function validateBuiltinBoardClip(clip, path = "clip") {
|
|
287
300
|
if (!CLIPS.has(clip.kind)) return [];
|
|
288
301
|
const errors = [];
|
|
@@ -300,13 +313,13 @@ function validateBuiltinBoardClip(clip, path = "clip") {
|
|
|
300
313
|
if (clip.kind === "motion.path") {
|
|
301
314
|
const points = clip.params.points;
|
|
302
315
|
if (!Array.isArray(points) || points.length < 2 || points.length > 1e4) error("motion path must contain 2 to 10000 world-offset points", "params.points", "world-offset");
|
|
303
|
-
else for (const [index, point] of points.entries()) if (!record(point) || !finite(point.x) || !finite(point.y)) error("motion path point must contain finite x and y", `params.points.${index}`, "world-offset");
|
|
316
|
+
else for (const [index, point] of points.entries()) if (!record$1(point) || !finite$1(point.x) || !finite$1(point.y)) error("motion path point must contain finite x and y", `params.points.${index}`, "world-offset");
|
|
304
317
|
}
|
|
305
318
|
if (clip.kind === "effects.particles") {
|
|
306
319
|
const count = clip.params.count;
|
|
307
320
|
if (!Number.isSafeInteger(count) || count < 1 || count > DEFAULT_BOARD_RENDER_LIMITS.particles) error(`particle count must be an integer from 1 to ${DEFAULT_BOARD_RENDER_LIMITS.particles}`, "params.count", void 0, "INVALID_PARTICLE_COUNT");
|
|
308
321
|
const bounds = clip.params.bounds;
|
|
309
|
-
if (!record(bounds) || !finite(bounds.x) || !finite(bounds.y) || !finite(bounds.width) || !finite(bounds.height) || bounds.width <= 0 || bounds.height <= 0) error("particles require positive finite world bounds", "params.bounds", "world", "PARTICLE_BOUNDS_REQUIRED");
|
|
322
|
+
if (!record$1(bounds) || !finite$1(bounds.x) || !finite$1(bounds.y) || !finite$1(bounds.width) || !finite$1(bounds.height) || bounds.width <= 0 || bounds.height <= 0) error("particles require positive finite world bounds", "params.bounds", "world", "PARTICLE_BOUNDS_REQUIRED");
|
|
310
323
|
}
|
|
311
324
|
if (clip.kind === "camera.focus") {
|
|
312
325
|
const parsed = BoardCameraFocusParamsSchema.safeParse(clip.params);
|
|
@@ -1036,14 +1049,14 @@ var GenerationPolicyError = class extends Error {
|
|
|
1036
1049
|
this.name = "GenerationPolicyError";
|
|
1037
1050
|
}
|
|
1038
1051
|
};
|
|
1039
|
-
function isRecord$
|
|
1052
|
+
function isRecord$5(value) {
|
|
1040
1053
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
1041
1054
|
}
|
|
1042
1055
|
function isPrimitiveEnumValue(value) {
|
|
1043
1056
|
return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
1044
1057
|
}
|
|
1045
1058
|
function normalizeConstraint(value) {
|
|
1046
|
-
if (!isRecord$
|
|
1059
|
+
if (!isRecord$5(value) || typeof value.kind !== "string") return null;
|
|
1047
1060
|
if (value.kind === "enum") {
|
|
1048
1061
|
if (!Array.isArray(value.values) || value.values.length === 0 || !value.values.every(isPrimitiveEnumValue)) return null;
|
|
1049
1062
|
return {
|
|
@@ -1073,7 +1086,7 @@ function normalizeConstraint(value) {
|
|
|
1073
1086
|
return null;
|
|
1074
1087
|
}
|
|
1075
1088
|
function normalizeGenerationPolicy(value) {
|
|
1076
|
-
if (!isRecord$
|
|
1089
|
+
if (!isRecord$5(value) || value.version !== 1) return null;
|
|
1077
1090
|
if (value.mode === "auto") return {
|
|
1078
1091
|
version: 1,
|
|
1079
1092
|
mode: "auto"
|
|
@@ -1081,10 +1094,10 @@ function normalizeGenerationPolicy(value) {
|
|
|
1081
1094
|
if (value.mode !== "limited" || !Array.isArray(value.models) || value.models.length === 0) return null;
|
|
1082
1095
|
const models = [];
|
|
1083
1096
|
for (const item of value.models) {
|
|
1084
|
-
if (!isRecord$
|
|
1097
|
+
if (!isRecord$5(item) || typeof item.model !== "string" || !item.model.trim()) return null;
|
|
1085
1098
|
const modelPolicy = { model: item.model.trim() };
|
|
1086
1099
|
if (item.parameters !== void 0) {
|
|
1087
|
-
if (!isRecord$
|
|
1100
|
+
if (!isRecord$5(item.parameters)) return null;
|
|
1088
1101
|
const parameters = {};
|
|
1089
1102
|
for (const [key, rawConstraint] of Object.entries(item.parameters)) {
|
|
1090
1103
|
if (!key.trim()) return null;
|
|
@@ -2378,8 +2391,8 @@ const APP_SURFACE_REQUEST_TIMEOUT_MS = 15e3;
|
|
|
2378
2391
|
const APP_COMPOSER_CHIP_KEY_MAX_LENGTH = 80;
|
|
2379
2392
|
const APP_COMPOSER_CHIP_LABEL_MAX_LENGTH = 120;
|
|
2380
2393
|
const APP_COMPOSER_CHIP_CONTENT_MAX_BYTES = 32768;
|
|
2381
|
-
const isRecord$
|
|
2382
|
-
const isSurfaceEnvelope = (value) => isRecord$
|
|
2394
|
+
const isRecord$4 = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
2395
|
+
const isSurfaceEnvelope = (value) => isRecord$4(value) && value.protocol === "cohub.app.surface" && value.version === 1;
|
|
2383
2396
|
const parseAppSurfaceReady = (value) => {
|
|
2384
2397
|
if (!isSurfaceEnvelope(value) || value.type !== "ready") return null;
|
|
2385
2398
|
const methods = Array.isArray(value.methods) ? value.methods.filter((method) => typeof method === "string" && Boolean(method)) : [];
|
|
@@ -2393,7 +2406,7 @@ const parseAppSurfaceReady = (value) => {
|
|
|
2393
2406
|
const parseAppSurfaceResponse = (value) => {
|
|
2394
2407
|
if (!isSurfaceEnvelope(value) || value.type !== "response") return null;
|
|
2395
2408
|
if (typeof value.requestId !== "string" || !value.requestId) return null;
|
|
2396
|
-
const error = isRecord$
|
|
2409
|
+
const error = isRecord$4(value.error) ? {
|
|
2397
2410
|
code: typeof value.error.code === "string" && value.error.code ? value.error.code : "surface_error",
|
|
2398
2411
|
message: typeof value.error.message === "string" ? value.error.message : "App surface call failed"
|
|
2399
2412
|
} : void 0;
|
|
@@ -2413,7 +2426,7 @@ const parseComposerChipKey = (value) => {
|
|
|
2413
2426
|
return key;
|
|
2414
2427
|
};
|
|
2415
2428
|
const parseAppComposerChipSet = (value) => {
|
|
2416
|
-
if (!isSurfaceEnvelope(value) || value.type !== "composer.chip.set" || !isRecord$
|
|
2429
|
+
if (!isSurfaceEnvelope(value) || value.type !== "composer.chip.set" || !isRecord$4(value.chip)) return null;
|
|
2417
2430
|
const key = parseComposerChipKey(value.chip.key);
|
|
2418
2431
|
if (!key || typeof value.chip.label !== "string" || typeof value.chip.content !== "string") return null;
|
|
2419
2432
|
const label = value.chip.label.trim();
|
|
@@ -2490,6 +2503,7 @@ const buildAppComposerChipClear = (key) => ({
|
|
|
2490
2503
|
//#endregion
|
|
2491
2504
|
//#region ../protocol/dist/app-runtime.js
|
|
2492
2505
|
const APP_RUNTIME_PROTOCOL = "cohub.app.runtime";
|
|
2506
|
+
const isRecord$3 = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
2493
2507
|
const buildAppRuntimeReady = () => ({
|
|
2494
2508
|
protocol: APP_RUNTIME_PROTOCOL,
|
|
2495
2509
|
version: 1,
|
|
@@ -2500,6 +2514,7 @@ const buildAppRuntimeCloseRequest = () => ({
|
|
|
2500
2514
|
version: 1,
|
|
2501
2515
|
type: "close.request"
|
|
2502
2516
|
});
|
|
2517
|
+
const finite = (value) => typeof value === "number" && Number.isFinite(value);
|
|
2503
2518
|
const buildAppRuntimeConfigureRequest = (input) => ({
|
|
2504
2519
|
protocol: APP_RUNTIME_PROTOCOL,
|
|
2505
2520
|
version: 1,
|
|
@@ -2514,6 +2529,154 @@ const buildAppRuntimePointer = (input) => ({
|
|
|
2514
2529
|
y: input.y,
|
|
2515
2530
|
down: input.down
|
|
2516
2531
|
});
|
|
2532
|
+
const isRuntimeMessage = (value, type) => isRecord$3(value) && value.protocol === "cohub.app.runtime" && value.version === 1 && value.type === type;
|
|
2533
|
+
const envelope$1 = (type) => ({
|
|
2534
|
+
protocol: APP_RUNTIME_PROTOCOL,
|
|
2535
|
+
version: 1,
|
|
2536
|
+
type
|
|
2537
|
+
});
|
|
2538
|
+
const boundedText = (value, max) => {
|
|
2539
|
+
if (typeof value !== "string") return null;
|
|
2540
|
+
const text = value.replace(/\s+/g, " ").trim();
|
|
2541
|
+
return text ? text.slice(0, max) : null;
|
|
2542
|
+
};
|
|
2543
|
+
const buildAppRuntimeAnnounce = () => envelope$1("announce");
|
|
2544
|
+
const parseAppRuntimeAnnounce = (value) => isRuntimeMessage(value, "announce") ? buildAppRuntimeAnnounce() : null;
|
|
2545
|
+
const buildAppRuntimeWindowState = (state) => ({
|
|
2546
|
+
...envelope$1("window.state"),
|
|
2547
|
+
title: state.title,
|
|
2548
|
+
status: state.status,
|
|
2549
|
+
dirty: state.dirty
|
|
2550
|
+
});
|
|
2551
|
+
const requestIdOf = (value) => typeof value.requestId === "string" && value.requestId && value.requestId.length <= 128 ? value.requestId : null;
|
|
2552
|
+
const buildAppRuntimeClosePrepare = (requestId) => ({
|
|
2553
|
+
...envelope$1("close.prepare"),
|
|
2554
|
+
requestId
|
|
2555
|
+
});
|
|
2556
|
+
const parseAppRuntimeClosePrepare = (value) => {
|
|
2557
|
+
if (!isRuntimeMessage(value, "close.prepare")) return null;
|
|
2558
|
+
const requestId = requestIdOf(value);
|
|
2559
|
+
return requestId ? buildAppRuntimeClosePrepare(requestId) : null;
|
|
2560
|
+
};
|
|
2561
|
+
const buildAppRuntimeClosePrepared = (input) => ({
|
|
2562
|
+
...envelope$1("close.prepared"),
|
|
2563
|
+
requestId: input.requestId,
|
|
2564
|
+
ok: input.ok
|
|
2565
|
+
});
|
|
2566
|
+
/** Select all, copy, paste, cut, undo, redo: the browser's, and the App's. */
|
|
2567
|
+
const EDITING_CHORD_KEYS = /* @__PURE__ */ new Set([
|
|
2568
|
+
"a",
|
|
2569
|
+
"c",
|
|
2570
|
+
"v",
|
|
2571
|
+
"x",
|
|
2572
|
+
"y",
|
|
2573
|
+
"z"
|
|
2574
|
+
]);
|
|
2575
|
+
/** Whether a Ctrl / Meta chord may reach the host; both sides apply this rule. */
|
|
2576
|
+
const isAppRuntimeHostChord = (input) => (input.ctrlKey || input.metaKey) && !EDITING_CHORD_KEYS.has(input.key.toLowerCase());
|
|
2577
|
+
const buildAppRuntimeKey = (input) => ({
|
|
2578
|
+
...envelope$1("key"),
|
|
2579
|
+
key: input.key,
|
|
2580
|
+
code: input.code,
|
|
2581
|
+
altKey: input.altKey,
|
|
2582
|
+
ctrlKey: input.ctrlKey,
|
|
2583
|
+
metaKey: input.metaKey,
|
|
2584
|
+
shiftKey: input.shiftKey
|
|
2585
|
+
});
|
|
2586
|
+
/** Resource kinds the Cohub host can drop into an App. */
|
|
2587
|
+
const APP_DROP_RESOURCE_TYPES = [
|
|
2588
|
+
"file",
|
|
2589
|
+
"task",
|
|
2590
|
+
"app",
|
|
2591
|
+
"session",
|
|
2592
|
+
"checkpoint"
|
|
2593
|
+
];
|
|
2594
|
+
const isDropResourceType = (value) => APP_DROP_RESOURCE_TYPES.includes(value);
|
|
2595
|
+
const MAX_DROP_RESOURCES = 100;
|
|
2596
|
+
const parseDropResource = (value) => {
|
|
2597
|
+
if (!isRecord$3(value) || !isDropResourceType(value.type)) return null;
|
|
2598
|
+
const ref = boundedText(value.ref, 4096);
|
|
2599
|
+
if (!ref) return null;
|
|
2600
|
+
const resource = {
|
|
2601
|
+
type: value.type,
|
|
2602
|
+
ref
|
|
2603
|
+
};
|
|
2604
|
+
for (const key of [
|
|
2605
|
+
"title",
|
|
2606
|
+
"path",
|
|
2607
|
+
"mimeType",
|
|
2608
|
+
"appId",
|
|
2609
|
+
"href",
|
|
2610
|
+
"taskRunId"
|
|
2611
|
+
]) {
|
|
2612
|
+
const text = boundedText(value[key], 4096);
|
|
2613
|
+
if (text) resource[key] = text;
|
|
2614
|
+
}
|
|
2615
|
+
if (finite(value.size) && value.size >= 0) resource.size = value.size;
|
|
2616
|
+
if (finite(value.mtimeMs)) resource.mtimeMs = value.mtimeMs;
|
|
2617
|
+
if (isRecord$3(value.snapshot)) resource.snapshot = value.snapshot;
|
|
2618
|
+
return resource;
|
|
2619
|
+
};
|
|
2620
|
+
const parseDropTypes = (value) => Array.isArray(value) ? [...new Set(value.filter(isDropResourceType))] : [];
|
|
2621
|
+
const buildAppRuntimeDropConfig = (accept) => ({
|
|
2622
|
+
...envelope$1("drop.config"),
|
|
2623
|
+
accept: [...accept]
|
|
2624
|
+
});
|
|
2625
|
+
const buildAppRuntimeDrag = (input) => ({
|
|
2626
|
+
...envelope$1("drag"),
|
|
2627
|
+
phase: input.phase,
|
|
2628
|
+
x: input.x,
|
|
2629
|
+
y: input.y,
|
|
2630
|
+
types: [...input.types],
|
|
2631
|
+
...input.phase === "drop" ? { resources: input.resources ?? [] } : {}
|
|
2632
|
+
});
|
|
2633
|
+
const parseAppRuntimeDrag = (value) => {
|
|
2634
|
+
if (!isRuntimeMessage(value, "drag")) return null;
|
|
2635
|
+
if (value.phase !== "over" && value.phase !== "leave" && value.phase !== "drop") return null;
|
|
2636
|
+
if (!finite(value.x) || !finite(value.y)) return null;
|
|
2637
|
+
const resources = value.phase === "drop" && Array.isArray(value.resources) ? value.resources.slice(0, MAX_DROP_RESOURCES).map(parseDropResource).filter((resource) => resource !== null) : void 0;
|
|
2638
|
+
return buildAppRuntimeDrag({
|
|
2639
|
+
phase: value.phase,
|
|
2640
|
+
x: value.x,
|
|
2641
|
+
y: value.y,
|
|
2642
|
+
types: parseDropTypes(value.types),
|
|
2643
|
+
...resources ? { resources } : {}
|
|
2644
|
+
});
|
|
2645
|
+
};
|
|
2646
|
+
/** Public design tokens, sent as resolved CSS values. */
|
|
2647
|
+
const APP_APPEARANCE_TOKENS = [
|
|
2648
|
+
"bg-primary",
|
|
2649
|
+
"bg-content",
|
|
2650
|
+
"bg-surface",
|
|
2651
|
+
"bg-elevated",
|
|
2652
|
+
"bg-input",
|
|
2653
|
+
"bg-hover",
|
|
2654
|
+
"bg-active",
|
|
2655
|
+
"text-primary",
|
|
2656
|
+
"text-secondary",
|
|
2657
|
+
"text-tertiary",
|
|
2658
|
+
"text-placeholder",
|
|
2659
|
+
"text-disabled",
|
|
2660
|
+
"border-primary",
|
|
2661
|
+
"border-subtle",
|
|
2662
|
+
"brand",
|
|
2663
|
+
"brand-hover",
|
|
2664
|
+
"brand-soft",
|
|
2665
|
+
"brand-muted",
|
|
2666
|
+
"brand-border",
|
|
2667
|
+
"brand-ring",
|
|
2668
|
+
"brand-contrast-fg",
|
|
2669
|
+
"error-fg",
|
|
2670
|
+
"selection-bg",
|
|
2671
|
+
"overlay-scrim",
|
|
2672
|
+
"shadow-subtle",
|
|
2673
|
+
"shadow-medium",
|
|
2674
|
+
"shadow-strong",
|
|
2675
|
+
"font-sans",
|
|
2676
|
+
"font-mono"
|
|
2677
|
+
];
|
|
2678
|
+
/** CSS custom property an App reads a shared token from, e.g. `--cohub-bg-primary`. */
|
|
2679
|
+
const appAppearanceVar = (token) => `--cohub-${token}`;
|
|
2517
2680
|
//#endregion
|
|
2518
2681
|
//#region ../protocol/dist/app-navigation.js
|
|
2519
2682
|
const APP_NAVIGATION_PROTOCOL = "cohub.app.navigation";
|
|
@@ -3138,6 +3301,8 @@ var AppSurfaceApi = class {
|
|
|
3138
3301
|
listening = false;
|
|
3139
3302
|
allowedOrigins = null;
|
|
3140
3303
|
trustedOrigin;
|
|
3304
|
+
/** The chip on show, so it can be announced again. */
|
|
3305
|
+
chip = null;
|
|
3141
3306
|
allowHostOrigins(origins) {
|
|
3142
3307
|
this.allowedOrigins = origins.map((origin) => origin.trim()).filter(Boolean).map((origin) => {
|
|
3143
3308
|
try {
|
|
@@ -3168,11 +3333,14 @@ var AppSurfaceApi = class {
|
|
|
3168
3333
|
setComposerChip(chip) {
|
|
3169
3334
|
const message = parseAppComposerChipSet(buildAppComposerChipSet(chip));
|
|
3170
3335
|
if (!message) throw new Error("Invalid App composer chip");
|
|
3336
|
+
this.chip = message.chip;
|
|
3337
|
+
this.start();
|
|
3171
3338
|
this.post(message);
|
|
3172
3339
|
}
|
|
3173
3340
|
clearComposerChip(key) {
|
|
3174
3341
|
const message = parseAppComposerChipClear(buildAppComposerChipClear(key));
|
|
3175
3342
|
if (!message) throw new Error("Invalid App composer chip key");
|
|
3343
|
+
if (this.chip?.key === message.key) this.chip = null;
|
|
3176
3344
|
this.post(message);
|
|
3177
3345
|
}
|
|
3178
3346
|
announce() {
|
|
@@ -3197,6 +3365,11 @@ var AppSurfaceApi = class {
|
|
|
3197
3365
|
onMessage = (event) => {
|
|
3198
3366
|
if (typeof window === "undefined" || event.source !== window.parent) return;
|
|
3199
3367
|
if (!this.isTrusted(event.origin)) return;
|
|
3368
|
+
if (parseAppRuntimeAnnounce(event.data)) {
|
|
3369
|
+
if (this.handlers.size > 0) this.announce();
|
|
3370
|
+
if (this.chip) this.post(buildAppComposerChipSet(this.chip));
|
|
3371
|
+
return;
|
|
3372
|
+
}
|
|
3200
3373
|
const request = parseAppSurfaceRequest(event.data);
|
|
3201
3374
|
if (!request) return;
|
|
3202
3375
|
const commandId = request.commandId;
|
|
@@ -3301,6 +3474,171 @@ function attachAppEmbed(frame, options) {
|
|
|
3301
3474
|
};
|
|
3302
3475
|
}
|
|
3303
3476
|
//#endregion
|
|
3477
|
+
//#region src/app-window.ts
|
|
3478
|
+
/** One document listener for all clients, so a chord reaches the host once. */
|
|
3479
|
+
const keyTransports = /* @__PURE__ */ new Set();
|
|
3480
|
+
function forwardKey(event) {
|
|
3481
|
+
if (event.isComposing || !isAppRuntimeHostChord(event)) return;
|
|
3482
|
+
setTimeout(() => {
|
|
3483
|
+
if (event.defaultPrevented) return;
|
|
3484
|
+
[...keyTransports].find((candidate) => candidate.isHostConnected?.())?.notify?.(buildAppRuntimeKey({
|
|
3485
|
+
key: event.key,
|
|
3486
|
+
code: event.code,
|
|
3487
|
+
altKey: event.altKey,
|
|
3488
|
+
ctrlKey: event.ctrlKey,
|
|
3489
|
+
metaKey: event.metaKey,
|
|
3490
|
+
shiftKey: event.shiftKey
|
|
3491
|
+
}));
|
|
3492
|
+
}, 0);
|
|
3493
|
+
}
|
|
3494
|
+
function attachKeys(transport) {
|
|
3495
|
+
if (keyTransports.size === 0) window.addEventListener("keydown", forwardKey);
|
|
3496
|
+
keyTransports.add(transport);
|
|
3497
|
+
}
|
|
3498
|
+
function detachKeys(transport) {
|
|
3499
|
+
if (!keyTransports.delete(transport)) return;
|
|
3500
|
+
if (keyTransports.size === 0) window.removeEventListener("keydown", forwardKey);
|
|
3501
|
+
}
|
|
3502
|
+
/** The App's side of its host window; a no-op outside a Cohub host. */
|
|
3503
|
+
var AppWindowApi = class {
|
|
3504
|
+
transport;
|
|
3505
|
+
runtime;
|
|
3506
|
+
state = {
|
|
3507
|
+
title: null,
|
|
3508
|
+
status: "idle",
|
|
3509
|
+
dirty: false
|
|
3510
|
+
};
|
|
3511
|
+
/** Only a client that reported state re-announces it; others must not reset it. */
|
|
3512
|
+
stateReported = false;
|
|
3513
|
+
beforeClose = null;
|
|
3514
|
+
dropHandler = null;
|
|
3515
|
+
hostUnsubscribe = null;
|
|
3516
|
+
constructor(transport, runtime) {
|
|
3517
|
+
this.transport = transport;
|
|
3518
|
+
this.runtime = runtime;
|
|
3519
|
+
if (typeof window === "undefined" || !transport.notify) return;
|
|
3520
|
+
if (window.parent === window || typeof window.addEventListener !== "function") return;
|
|
3521
|
+
attachKeys(transport);
|
|
3522
|
+
}
|
|
3523
|
+
/** Report the tab title, save status, and whether work is still unsaved. */
|
|
3524
|
+
setState(patch) {
|
|
3525
|
+
this.state = {
|
|
3526
|
+
...this.state,
|
|
3527
|
+
...patch
|
|
3528
|
+
};
|
|
3529
|
+
this.stateReported = true;
|
|
3530
|
+
this.listen();
|
|
3531
|
+
this.transport.notify?.(buildAppRuntimeWindowState(this.state));
|
|
3532
|
+
}
|
|
3533
|
+
/** Persist work before a `dirty` window closes; `false` or throw keeps it open. */
|
|
3534
|
+
onBeforeClose(handler) {
|
|
3535
|
+
this.beforeClose = handler;
|
|
3536
|
+
this.listen();
|
|
3537
|
+
return () => {
|
|
3538
|
+
if (this.beforeClose === handler) this.beforeClose = null;
|
|
3539
|
+
};
|
|
3540
|
+
}
|
|
3541
|
+
/** Accept Cohub resources dragged from the host onto this App. */
|
|
3542
|
+
onDrop(handler) {
|
|
3543
|
+
this.dropHandler = handler;
|
|
3544
|
+
this.listen();
|
|
3545
|
+
this.transport.notify?.(buildAppRuntimeDropConfig(handler.accept));
|
|
3546
|
+
return () => {
|
|
3547
|
+
if (this.dropHandler !== handler) return;
|
|
3548
|
+
this.dropHandler = null;
|
|
3549
|
+
this.transport.notify?.(buildAppRuntimeDropConfig([]));
|
|
3550
|
+
};
|
|
3551
|
+
}
|
|
3552
|
+
/** The window's file: on open, on reopen, and after a rename or move. */
|
|
3553
|
+
onLaunch(listener) {
|
|
3554
|
+
let last = null;
|
|
3555
|
+
const handle = (context) => {
|
|
3556
|
+
const invocation = context?.invocation;
|
|
3557
|
+
if (!invocation?.file || !invocation.spaceId) return;
|
|
3558
|
+
const key = `${invocation.id ?? ""}:${invocation.spaceId}:${invocation.file.path}`;
|
|
3559
|
+
if (key === last) return;
|
|
3560
|
+
last = key;
|
|
3561
|
+
listener({ file: {
|
|
3562
|
+
spaceId: invocation.spaceId,
|
|
3563
|
+
path: invocation.file.path
|
|
3564
|
+
} });
|
|
3565
|
+
};
|
|
3566
|
+
const unsubscribe = this.runtime.onContextChanged(handle);
|
|
3567
|
+
this.runtime.context().then(handle, () => {});
|
|
3568
|
+
return unsubscribe;
|
|
3569
|
+
}
|
|
3570
|
+
dispose() {
|
|
3571
|
+
this.hostUnsubscribe?.();
|
|
3572
|
+
this.hostUnsubscribe = null;
|
|
3573
|
+
detachKeys(this.transport);
|
|
3574
|
+
}
|
|
3575
|
+
listen() {
|
|
3576
|
+
if (this.hostUnsubscribe || !this.transport.subscribeHostMessages) return;
|
|
3577
|
+
this.hostUnsubscribe = this.transport.subscribeHostMessages((data) => {
|
|
3578
|
+
if (parseAppRuntimeAnnounce(data)) {
|
|
3579
|
+
this.announce();
|
|
3580
|
+
return;
|
|
3581
|
+
}
|
|
3582
|
+
if (!this.transport.isHostConnected?.()) return;
|
|
3583
|
+
const prepare = parseAppRuntimeClosePrepare(data);
|
|
3584
|
+
if (prepare) {
|
|
3585
|
+
this.prepareClose(prepare.requestId);
|
|
3586
|
+
return;
|
|
3587
|
+
}
|
|
3588
|
+
const drag = parseAppRuntimeDrag(data);
|
|
3589
|
+
if (drag) this.dispatchDrag(drag);
|
|
3590
|
+
});
|
|
3591
|
+
this.runtime.context().catch(() => {});
|
|
3592
|
+
}
|
|
3593
|
+
/** A reloaded frame forgot the tab state and drops; report them again. */
|
|
3594
|
+
announce() {
|
|
3595
|
+
if (this.stateReported) this.transport.notify?.(buildAppRuntimeWindowState(this.state));
|
|
3596
|
+
if (this.dropHandler) this.transport.notify?.(buildAppRuntimeDropConfig(this.dropHandler.accept));
|
|
3597
|
+
}
|
|
3598
|
+
async prepareClose(requestId) {
|
|
3599
|
+
let ok;
|
|
3600
|
+
try {
|
|
3601
|
+
ok = this.beforeClose ? await this.beforeClose() !== false : !this.state.dirty;
|
|
3602
|
+
} catch {
|
|
3603
|
+
ok = false;
|
|
3604
|
+
}
|
|
3605
|
+
this.transport.notify?.(buildAppRuntimeClosePrepared({
|
|
3606
|
+
requestId,
|
|
3607
|
+
ok
|
|
3608
|
+
}));
|
|
3609
|
+
}
|
|
3610
|
+
dispatchDrag(drag) {
|
|
3611
|
+
const handler = this.dropHandler;
|
|
3612
|
+
if (!handler) return;
|
|
3613
|
+
const point = {
|
|
3614
|
+
x: drag.x,
|
|
3615
|
+
y: drag.y,
|
|
3616
|
+
types: drag.types
|
|
3617
|
+
};
|
|
3618
|
+
if (drag.phase === "over") handler.over?.(point);
|
|
3619
|
+
else if (drag.phase === "leave") handler.leave?.();
|
|
3620
|
+
else handler.drop({
|
|
3621
|
+
...point,
|
|
3622
|
+
resources: drag.resources ?? []
|
|
3623
|
+
});
|
|
3624
|
+
}
|
|
3625
|
+
};
|
|
3626
|
+
/** Writes host tokens (`--cohub-*`), color scheme, and `lang` onto `root`. */
|
|
3627
|
+
function applyAppAppearance(root, context) {
|
|
3628
|
+
const appearance = context?.appearance;
|
|
3629
|
+
for (const token of APP_APPEARANCE_TOKENS) {
|
|
3630
|
+
const value = appearance?.tokens[token];
|
|
3631
|
+
if (value) root.style.setProperty(appAppearanceVar(token), value);
|
|
3632
|
+
else root.style.removeProperty(appAppearanceVar(token));
|
|
3633
|
+
}
|
|
3634
|
+
if (appearance) {
|
|
3635
|
+
root.style.colorScheme = appearance.colorScheme;
|
|
3636
|
+
root.dataset.cohubColorScheme = appearance.colorScheme;
|
|
3637
|
+
root.dataset.cohubReducedMotion = String(appearance.reducedMotion);
|
|
3638
|
+
}
|
|
3639
|
+
if (context?.locale) root.lang = context.locale;
|
|
3640
|
+
}
|
|
3641
|
+
//#endregion
|
|
3304
3642
|
//#region src/app-runtime.ts
|
|
3305
3643
|
var AppRuntimeError = class extends Error {
|
|
3306
3644
|
code;
|
|
@@ -3340,6 +3678,36 @@ var ParentBridgeTransport = class {
|
|
|
3340
3678
|
contextListener = null;
|
|
3341
3679
|
diagnosticListeners = /* @__PURE__ */ new Set();
|
|
3342
3680
|
diagnosticListener = null;
|
|
3681
|
+
hostListeners = /* @__PURE__ */ new Set();
|
|
3682
|
+
hostListener = null;
|
|
3683
|
+
announceListener = null;
|
|
3684
|
+
isHostConnected() {
|
|
3685
|
+
return this.trustedParentOrigin !== null;
|
|
3686
|
+
}
|
|
3687
|
+
isFromParent(event) {
|
|
3688
|
+
if (event.source !== window.parent) return false;
|
|
3689
|
+
const parentOrigin = this.trustedParentOrigin ?? getParentOrigin();
|
|
3690
|
+
return !parentOrigin || event.origin === parentOrigin;
|
|
3691
|
+
}
|
|
3692
|
+
subscribeHostMessages(listener) {
|
|
3693
|
+
if (!hasParent() || typeof window.addEventListener !== "function") return () => {};
|
|
3694
|
+
this.hostListeners.add(listener);
|
|
3695
|
+
this.announceReady();
|
|
3696
|
+
if (!this.hostListener) {
|
|
3697
|
+
this.hostListener = (event) => {
|
|
3698
|
+
if (!this.isFromParent(event)) return;
|
|
3699
|
+
for (const current of this.hostListeners) current(event.data);
|
|
3700
|
+
};
|
|
3701
|
+
window.addEventListener("message", this.hostListener);
|
|
3702
|
+
}
|
|
3703
|
+
return () => {
|
|
3704
|
+
this.hostListeners.delete(listener);
|
|
3705
|
+
if (this.hostListeners.size === 0 && this.hostListener) {
|
|
3706
|
+
window.removeEventListener("message", this.hostListener);
|
|
3707
|
+
this.hostListener = null;
|
|
3708
|
+
}
|
|
3709
|
+
};
|
|
3710
|
+
}
|
|
3343
3711
|
/**
|
|
3344
3712
|
* Tells the host the runtime is ready once, before any subscription. Hosts
|
|
3345
3713
|
* gate unsolicited messages (context pushes, diagnostics) on this handshake,
|
|
@@ -3353,8 +3721,19 @@ var ParentBridgeTransport = class {
|
|
|
3353
3721
|
try {
|
|
3354
3722
|
window.parent.postMessage(buildAppRuntimeReady(), parentOrigin);
|
|
3355
3723
|
this.readyAnnounced = true;
|
|
3724
|
+
this.answerAnnounceRequests();
|
|
3356
3725
|
} catch {}
|
|
3357
3726
|
}
|
|
3727
|
+
/** Says ready again when a reloaded host asks. */
|
|
3728
|
+
answerAnnounceRequests() {
|
|
3729
|
+
if (this.announceListener || typeof window.addEventListener !== "function") return;
|
|
3730
|
+
this.announceListener = (event) => {
|
|
3731
|
+
if (!this.isFromParent(event) || !parseAppRuntimeAnnounce(event.data)) return;
|
|
3732
|
+
this.readyAnnounced = false;
|
|
3733
|
+
this.announceReady();
|
|
3734
|
+
};
|
|
3735
|
+
window.addEventListener("message", this.announceListener);
|
|
3736
|
+
}
|
|
3358
3737
|
subscribeDiagnostics(listener) {
|
|
3359
3738
|
if (!hasParent() || typeof window.addEventListener !== "function") return () => {};
|
|
3360
3739
|
this.diagnosticListeners.add(listener);
|
|
@@ -4333,6 +4712,7 @@ var CohubClient = class {
|
|
|
4333
4712
|
transport;
|
|
4334
4713
|
websocketClient;
|
|
4335
4714
|
appRuntime;
|
|
4715
|
+
appWindow;
|
|
4336
4716
|
constructor(options = {}) {
|
|
4337
4717
|
const apiBaseUrl = resolveApiBaseUrl(options);
|
|
4338
4718
|
const appRuntime = options.app ?? options.work;
|
|
@@ -4348,6 +4728,10 @@ var CohubClient = class {
|
|
|
4348
4728
|
fetch: options.fetch
|
|
4349
4729
|
}), resolveWebBaseUrl({ env: options.env }));
|
|
4350
4730
|
this.appRuntime = createAppRuntime(appTransport, appRuntime?.appId, appIdResolver);
|
|
4731
|
+
this.appWindow = new AppWindowApi(appTransport, {
|
|
4732
|
+
context: () => this.appRuntime.context(),
|
|
4733
|
+
onContextChanged: (listener) => this.appRuntime.onContextChanged(listener)
|
|
4734
|
+
});
|
|
4351
4735
|
const executionToken = resolveExecutionToken();
|
|
4352
4736
|
const getAccessToken = options.getAccessToken ?? (executionToken ? () => executionToken : (tokenOptions) => this.appRuntime.getAccessToken(tokenOptions));
|
|
4353
4737
|
const resolvedOptions = {
|
|
@@ -4403,6 +4787,7 @@ var CohubClient = class {
|
|
|
4403
4787
|
/** Releases listeners this client registered. Safe to call more than once. */
|
|
4404
4788
|
dispose() {
|
|
4405
4789
|
this.appRuntime.dispose();
|
|
4790
|
+
this.appWindow.dispose();
|
|
4406
4791
|
}
|
|
4407
4792
|
auth = {
|
|
4408
4793
|
authorize: (input) => this.appRuntime.authorize(input),
|
|
@@ -4424,6 +4809,26 @@ var CohubClient = class {
|
|
|
4424
4809
|
/** Expose callable methods from inside a published app. */
|
|
4425
4810
|
surface: new AppSurfaceApi(),
|
|
4426
4811
|
onContextChanged: (listener) => this.appRuntime.onContextChanged(listener),
|
|
4812
|
+
/** Files the host opens with this App — the first one and every later open. */
|
|
4813
|
+
onLaunch: (listener) => this.appWindow.onLaunch(listener),
|
|
4814
|
+
/** Accept Cohub resources the viewer drags from the host onto this App. */
|
|
4815
|
+
onDrop: (handler) => this.appWindow.onDrop(handler),
|
|
4816
|
+
window: {
|
|
4817
|
+
/** Report the tab title, save status, and whether work is still unsaved. */
|
|
4818
|
+
setState: (patch) => this.appWindow.setState(patch),
|
|
4819
|
+
/** Persist pending work before the host closes a `dirty` window. */
|
|
4820
|
+
onBeforeClose: (handler) => this.appWindow.onBeforeClose(handler)
|
|
4821
|
+
},
|
|
4822
|
+
appearance: {
|
|
4823
|
+
/** Keep `root` (default `<html>`) in sync with the host theme and locale. */
|
|
4824
|
+
sync: (root) => {
|
|
4825
|
+
const target = root ?? (typeof document === "undefined" ? null : document.documentElement);
|
|
4826
|
+
if (!target) return () => {};
|
|
4827
|
+
const apply = (context) => applyAppAppearance(target, context);
|
|
4828
|
+
const stop = this.appRuntime.onContextChanged(apply);
|
|
4829
|
+
this.appRuntime.context().then(apply, () => {});
|
|
4830
|
+
return stop;
|
|
4831
|
+
} },
|
|
4427
4832
|
/** Ask the host to close this App's surface. */
|
|
4428
4833
|
requestClose: () => this.appRuntime.requestClose(),
|
|
4429
4834
|
/**
|
|
@@ -4793,7 +5198,17 @@ function cloneInvocation(invocation) {
|
|
|
4793
5198
|
...invocation.embedder ? { embedder: {
|
|
4794
5199
|
appId: invocation.embedder.appId,
|
|
4795
5200
|
slug: invocation.embedder.slug
|
|
4796
|
-
} } : {}
|
|
5201
|
+
} } : {},
|
|
5202
|
+
...invocation.file ? { file: { path: invocation.file.path } } : {}
|
|
5203
|
+
};
|
|
5204
|
+
}
|
|
5205
|
+
/** Host appearance can be reactive state too; copy it into plain data. */
|
|
5206
|
+
function cloneAppearance(appearance) {
|
|
5207
|
+
return {
|
|
5208
|
+
colorScheme: appearance.colorScheme,
|
|
5209
|
+
theme: appearance.theme,
|
|
5210
|
+
tokens: { ...appearance.tokens },
|
|
5211
|
+
reducedMotion: appearance.reducedMotion
|
|
4797
5212
|
};
|
|
4798
5213
|
}
|
|
4799
5214
|
/**
|
|
@@ -5046,6 +5461,9 @@ function createAppBridgeCore(config) {
|
|
|
5046
5461
|
async function getContext() {
|
|
5047
5462
|
const invocation = activeInvocation !== void 0 ? activeInvocation : config.getInvocation?.() ?? config.invocation;
|
|
5048
5463
|
const shell = config.getShell?.() ?? config.shell;
|
|
5464
|
+
const locale = config.getLocale?.();
|
|
5465
|
+
const appearance = config.getAppearance?.();
|
|
5466
|
+
const windowState = config.getWindow?.();
|
|
5049
5467
|
const appScopes = clonePermissionScopes(app.appScopes);
|
|
5050
5468
|
const viewerUuid = await getViewerUuid();
|
|
5051
5469
|
synchronizeViewer(viewerUuid);
|
|
@@ -5074,6 +5492,9 @@ function createAppBridgeCore(config) {
|
|
|
5074
5492
|
session: shell.session ? { ...shell.session } : null,
|
|
5075
5493
|
turn: shell.turn ? { ...shell.turn } : null
|
|
5076
5494
|
} } : {},
|
|
5495
|
+
...locale ? { locale } : {},
|
|
5496
|
+
...appearance ? { appearance: cloneAppearance(appearance) } : {},
|
|
5497
|
+
...windowState ? { window: { visible: windowState.visible } } : {},
|
|
5077
5498
|
permissions: {
|
|
5078
5499
|
scopes: normalizePermissionScopes([...appScopes, ...viewerGrants.flatMap((grant) => grant.scopes)]),
|
|
5079
5500
|
appScopes,
|
|
@@ -5251,8 +5672,7 @@ function createAppBridgeCore(config) {
|
|
|
5251
5672
|
/**
|
|
5252
5673
|
* Calls the authorize endpoint. `silent` marks a background refresh of a
|
|
5253
5674
|
* previous consent: the server then only renews a live grant and never
|
|
5254
|
-
* creates or revives one
|
|
5255
|
-
* fresh dialog.
|
|
5675
|
+
* creates or revives one. `consent: "host"` may also create or widen one.
|
|
5256
5676
|
*/
|
|
5257
5677
|
async function authorize(scopes, spaceId, options) {
|
|
5258
5678
|
const incremental = Boolean(state.pendingAuth && authorizationRequests.has(state.pendingAuth.requestId));
|
|
@@ -5276,6 +5696,7 @@ function createAppBridgeCore(config) {
|
|
|
5276
5696
|
scopes,
|
|
5277
5697
|
...spaceId ? { spaceId } : {},
|
|
5278
5698
|
...options?.silent ? { silent: true } : {},
|
|
5699
|
+
...options?.consent ? { consent: options.consent } : {},
|
|
5279
5700
|
...incremental ? { scopeMode: "extend" } : {}
|
|
5280
5701
|
})
|
|
5281
5702
|
});
|
|
@@ -5322,17 +5743,15 @@ function createAppBridgeCore(config) {
|
|
|
5322
5743
|
}
|
|
5323
5744
|
}
|
|
5324
5745
|
/**
|
|
5325
|
-
*
|
|
5746
|
+
* Grants scopes on the current Shell Space without opening the dialog.
|
|
5326
5747
|
*
|
|
5327
5748
|
* Deciding that a request needs no dialog is the Host's job: it compares the
|
|
5328
|
-
* target
|
|
5329
|
-
* *whether to ask*; the server still decides *what may be granted* through
|
|
5330
|
-
* the silent path, so a revoked consent can never come back on its own.
|
|
5749
|
+
* target and trust; the server still caps what may be granted.
|
|
5331
5750
|
*/
|
|
5332
5751
|
const shellAuthorizationInFlight = /* @__PURE__ */ new Map();
|
|
5333
5752
|
const shellAuthorizationCache = /* @__PURE__ */ new Map();
|
|
5334
5753
|
const SHELL_AUTH_CACHE_MS = 3e4;
|
|
5335
|
-
async function authorizeShellSilently(scopes, spaceId) {
|
|
5754
|
+
async function authorizeShellSilently(scopes, spaceId, consent) {
|
|
5336
5755
|
const shellContext = config.getShell?.() ?? config.shell;
|
|
5337
5756
|
const shell = shellContext?.space ?? null;
|
|
5338
5757
|
if (!shellContext || authorizationContext.surface === "broker" || !shell || shell.id !== spaceId) throw new AppAuthorizationError("Shell authorization is unavailable for this surface.", 403, "consent_required");
|
|
@@ -5341,13 +5760,16 @@ function createAppBridgeCore(config) {
|
|
|
5341
5760
|
await startSignIn();
|
|
5342
5761
|
throw new AppLoginRedirect();
|
|
5343
5762
|
}
|
|
5344
|
-
const cacheKey = `${viewerUuid}:${app.id}:${spaceId}:${[...scopes].sort().join(",")}`;
|
|
5763
|
+
const cacheKey = `${viewerUuid}:${app.id}:${spaceId}:${consent}:${[...scopes].sort().join(",")}`;
|
|
5345
5764
|
const cached = shellAuthorizationCache.get(cacheKey);
|
|
5346
5765
|
if (cached && cached.expiresAt > Date.now()) return cached.result;
|
|
5347
5766
|
if (cached) shellAuthorizationCache.delete(cacheKey);
|
|
5348
5767
|
const pending = shellAuthorizationInFlight.get(cacheKey);
|
|
5349
5768
|
if (pending) return pending;
|
|
5350
|
-
const request = authorize(scopes, spaceId,
|
|
5769
|
+
const request = authorize(scopes, spaceId, consent === "host" ? {
|
|
5770
|
+
silent: true,
|
|
5771
|
+
consent: "host"
|
|
5772
|
+
} : { silent: true });
|
|
5351
5773
|
shellAuthorizationInFlight.set(cacheKey, request);
|
|
5352
5774
|
try {
|
|
5353
5775
|
const result = await request;
|
|
@@ -5360,6 +5782,20 @@ function createAppBridgeCore(config) {
|
|
|
5360
5782
|
shellAuthorizationInFlight.delete(cacheKey);
|
|
5361
5783
|
}
|
|
5362
5784
|
}
|
|
5785
|
+
/** Trusted Apps get Host consent; others may only renew read-only grants. */
|
|
5786
|
+
async function shellConsentFor(scopes, spaceId) {
|
|
5787
|
+
if (!scopes.every(isAppHostConsentScope)) return null;
|
|
5788
|
+
if (await isTrustedIn(spaceId)) return "host";
|
|
5789
|
+
return scopes.every(isAppSilentShellScope) ? "renew" : null;
|
|
5790
|
+
}
|
|
5791
|
+
async function isTrustedIn(spaceId) {
|
|
5792
|
+
if (app.spaceId === spaceId) return true;
|
|
5793
|
+
try {
|
|
5794
|
+
return await config.isInstalledIn?.(spaceId) === true;
|
|
5795
|
+
} catch {
|
|
5796
|
+
return false;
|
|
5797
|
+
}
|
|
5798
|
+
}
|
|
5363
5799
|
/**
|
|
5364
5800
|
* Reads the Shell Space supplied by the current Host. Invocation context is
|
|
5365
5801
|
* deliberately excluded: an App may be opened from one Space while the Shell
|
|
@@ -5823,9 +6259,11 @@ function createAppBridgeCore(config) {
|
|
|
5823
6259
|
return;
|
|
5824
6260
|
}
|
|
5825
6261
|
const shellSpace = resolveShellSpace();
|
|
5826
|
-
const shellTargetId = spaceId
|
|
5827
|
-
|
|
5828
|
-
|
|
6262
|
+
const shellTargetId = !alwaysAsk && !selectSpace && shellSpace && (!spaceId || spaceId === shellSpace.id) ? shellSpace.id : null;
|
|
6263
|
+
const shellConsent = shellTargetId ? await shellConsentFor(scopes, shellTargetId) : null;
|
|
6264
|
+
if (state.pendingAuth !== reserved) return;
|
|
6265
|
+
if (shellConsent && shellTargetId) try {
|
|
6266
|
+
const result = await authorizeShellSilently(scopes, shellTargetId, shellConsent);
|
|
5829
6267
|
if (state.pendingAuth !== reserved) return;
|
|
5830
6268
|
setGrantedAppScopes(await getViewerUuid(), app.id, result.scopes, result.spaceId);
|
|
5831
6269
|
replyPendingAuth(reserved, authorizeResult(result.token, result.spaceId, shellSpace?.name ?? null));
|
|
@@ -6078,6 +6516,213 @@ function createAppBridgeCore(config) {
|
|
|
6078
6516
|
};
|
|
6079
6517
|
}
|
|
6080
6518
|
//#endregion
|
|
6519
|
+
//#region src/generation-blocks.ts
|
|
6520
|
+
/** Tolerant readers for generation payloads, shared by every task projection. */
|
|
6521
|
+
const EXCERPT_LIMIT = 240;
|
|
6522
|
+
function record(value) {
|
|
6523
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
6524
|
+
}
|
|
6525
|
+
function cleanExcerpt(value, limit = EXCERPT_LIMIT) {
|
|
6526
|
+
if (typeof value !== "string") return void 0;
|
|
6527
|
+
const clean = value.replace(/\s+/g, " ").trim();
|
|
6528
|
+
if (!clean) return void 0;
|
|
6529
|
+
return clean.length > limit ? `${clean.slice(0, limit - 3).trimEnd()}...` : clean;
|
|
6530
|
+
}
|
|
6531
|
+
function blockText(block) {
|
|
6532
|
+
return cleanExcerpt(block.text ?? block.content ?? block.value);
|
|
6533
|
+
}
|
|
6534
|
+
function blockUrl(block) {
|
|
6535
|
+
const source = record(block.source);
|
|
6536
|
+
return normalizeBoardRemoteUrl(source?.url ?? source?.src ?? block.url ?? block.src);
|
|
6537
|
+
}
|
|
6538
|
+
function blockMimeType(block) {
|
|
6539
|
+
const source = record(block.source);
|
|
6540
|
+
const value = source?.mediaType ?? source?.media_type ?? source?.mimeType ?? block.mediaType ?? block.media_type ?? block.mimeType;
|
|
6541
|
+
return typeof value === "string" ? value : void 0;
|
|
6542
|
+
}
|
|
6543
|
+
function positiveNumber(value) {
|
|
6544
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
|
|
6545
|
+
}
|
|
6546
|
+
function blockNaturalSize(block) {
|
|
6547
|
+
const source = record(block.source);
|
|
6548
|
+
const naturalWidth = positiveNumber(source?.width ?? source?.naturalWidth ?? block.width ?? block.naturalWidth);
|
|
6549
|
+
const naturalHeight = positiveNumber(source?.height ?? source?.naturalHeight ?? block.height ?? block.naturalHeight);
|
|
6550
|
+
return {
|
|
6551
|
+
...naturalWidth ? { naturalWidth } : {},
|
|
6552
|
+
...naturalHeight ? { naturalHeight } : {}
|
|
6553
|
+
};
|
|
6554
|
+
}
|
|
6555
|
+
function contentBlocks(value) {
|
|
6556
|
+
return Array.isArray(value) ? value.filter((item) => record(item)) : [];
|
|
6557
|
+
}
|
|
6558
|
+
function taskData(run) {
|
|
6559
|
+
const payload = record(run.payload);
|
|
6560
|
+
return record(payload?.data) ?? payload;
|
|
6561
|
+
}
|
|
6562
|
+
function generationOutput(run) {
|
|
6563
|
+
return contentBlocks(record(run.result)?.output);
|
|
6564
|
+
}
|
|
6565
|
+
function generationPrompt(run) {
|
|
6566
|
+
for (const block of contentBlocks(taskData(run)?.content)) {
|
|
6567
|
+
const text = blockText(block);
|
|
6568
|
+
if (text) return text;
|
|
6569
|
+
}
|
|
6570
|
+
}
|
|
6571
|
+
function blockMeta(block) {
|
|
6572
|
+
return record(block.meta);
|
|
6573
|
+
}
|
|
6574
|
+
function blockIdentity(block) {
|
|
6575
|
+
const meta = blockMeta(block);
|
|
6576
|
+
const value = meta?.id ?? meta?.clip_id ?? meta?.clipId ?? block.id ?? block.clip_id ?? block.clipId;
|
|
6577
|
+
if (typeof value !== "string" && typeof value !== "number") return void 0;
|
|
6578
|
+
return cleanExcerpt(String(value), 220);
|
|
6579
|
+
}
|
|
6580
|
+
function blockTitle(block) {
|
|
6581
|
+
return cleanExcerpt(blockMeta(block)?.title ?? block.title ?? block.name, 240);
|
|
6582
|
+
}
|
|
6583
|
+
function blockDurationMs(block) {
|
|
6584
|
+
const source = record(block.source);
|
|
6585
|
+
const meta = blockMeta(block);
|
|
6586
|
+
const milliseconds = positiveNumber(source?.durationMs ?? meta?.durationMs ?? block.durationMs);
|
|
6587
|
+
if (milliseconds) return Math.round(milliseconds);
|
|
6588
|
+
const seconds = positiveNumber(source?.duration ?? meta?.duration ?? block.duration);
|
|
6589
|
+
return seconds ? Math.round(seconds * 1e3) : void 0;
|
|
6590
|
+
}
|
|
6591
|
+
function blockPreviewUrl(block) {
|
|
6592
|
+
const source = record(block.source);
|
|
6593
|
+
return normalizeBoardRemoteUrl(source?.poster ?? source?.thumbnail ?? source?.previewUrl ?? block.poster ?? block.thumbnail ?? block.previewUrl);
|
|
6594
|
+
}
|
|
6595
|
+
/**
|
|
6596
|
+
* Image roles that depict playable media, by the media types they may cover;
|
|
6597
|
+
* `last_frame` stays a result of its own.
|
|
6598
|
+
*/
|
|
6599
|
+
const COVER_ROLES = /* @__PURE__ */ new Map([
|
|
6600
|
+
["first_frame", /* @__PURE__ */ new Set(["video"])],
|
|
6601
|
+
["cover", /* @__PURE__ */ new Set(["video", "audio"])],
|
|
6602
|
+
["poster", /* @__PURE__ */ new Set(["video", "audio"])],
|
|
6603
|
+
["thumbnail", /* @__PURE__ */ new Set(["video", "audio"])]
|
|
6604
|
+
]);
|
|
6605
|
+
function isPlayable(block) {
|
|
6606
|
+
return block.type === "video" || block.type === "audio";
|
|
6607
|
+
}
|
|
6608
|
+
/**
|
|
6609
|
+
* Pair playable media with its cover image, returning media index → cover
|
|
6610
|
+
* index. An image sharing the provider id wins (music tracks and their art,
|
|
6611
|
+
* shared by every media of that id); otherwise cover-role images pair in
|
|
6612
|
+
* order with remaining media their role fits (a video and its `first_frame`).
|
|
6613
|
+
* Covers need a remote URL, so an inline image is never folded away.
|
|
6614
|
+
*/
|
|
6615
|
+
function generationCovers(blocks) {
|
|
6616
|
+
const covers = /* @__PURE__ */ new Map();
|
|
6617
|
+
const isCover = (block) => block.type === "image" && blockUrl(block) !== void 0 && blockMeta(block)?.role !== "last_frame";
|
|
6618
|
+
blocks.forEach((block, index) => {
|
|
6619
|
+
const id = blockIdentity(block);
|
|
6620
|
+
if (!isPlayable(block) || id === void 0) return;
|
|
6621
|
+
const cover = blocks.findIndex((candidate) => isCover(candidate) && blockIdentity(candidate) === id);
|
|
6622
|
+
if (cover >= 0) covers.set(index, cover);
|
|
6623
|
+
});
|
|
6624
|
+
const paired = new Set(covers.values());
|
|
6625
|
+
const roleCovers = blocks.flatMap((block, index) => {
|
|
6626
|
+
const role = blockMeta(block)?.role;
|
|
6627
|
+
const types = typeof role === "string" ? COVER_ROLES.get(role) : void 0;
|
|
6628
|
+
return isCover(block) && !paired.has(index) && types ? [{
|
|
6629
|
+
index,
|
|
6630
|
+
types
|
|
6631
|
+
}] : [];
|
|
6632
|
+
});
|
|
6633
|
+
blocks.forEach((block, index) => {
|
|
6634
|
+
if (!isPlayable(block) || covers.has(index)) return;
|
|
6635
|
+
const at = roleCovers.findIndex(({ types }) => types.has(String(block.type)));
|
|
6636
|
+
const [cover] = at < 0 ? [] : roleCovers.splice(at, 1);
|
|
6637
|
+
if (cover) covers.set(index, cover.index);
|
|
6638
|
+
});
|
|
6639
|
+
return covers;
|
|
6640
|
+
}
|
|
6641
|
+
//#endregion
|
|
6642
|
+
//#region src/generation-task.ts
|
|
6643
|
+
const INLINE_KEYS = [
|
|
6644
|
+
"data",
|
|
6645
|
+
"base64",
|
|
6646
|
+
"contentBase64"
|
|
6647
|
+
];
|
|
6648
|
+
function isDeferred(block) {
|
|
6649
|
+
return block.deferredBase64 === true || record(block.source)?.deferredBase64 === true;
|
|
6650
|
+
}
|
|
6651
|
+
const MEDIA_TYPE_PATTERN = /^(image|video|audio)\/[\w.+-]+$/;
|
|
6652
|
+
const DEFAULT_MEDIA_TYPES = {
|
|
6653
|
+
image: "image/png",
|
|
6654
|
+
video: "video/mp4",
|
|
6655
|
+
audio: "audio/mpeg"
|
|
6656
|
+
};
|
|
6657
|
+
function outputType(value) {
|
|
6658
|
+
return value === "image" || value === "video" || value === "audio" || value === "text" ? value : null;
|
|
6659
|
+
}
|
|
6660
|
+
/** Covers fold into the media they depict instead of listing as images. */
|
|
6661
|
+
function projectOutputs(run) {
|
|
6662
|
+
const blocks = generationOutput(run);
|
|
6663
|
+
const covers = generationCovers(blocks);
|
|
6664
|
+
const coverIndexes = new Set(covers.values());
|
|
6665
|
+
const coverUrl = (index) => {
|
|
6666
|
+
const cover = covers.get(index);
|
|
6667
|
+
return cover === void 0 ? void 0 : blockUrl(blocks[cover] ?? {});
|
|
6668
|
+
};
|
|
6669
|
+
return blocks.flatMap((block, index) => {
|
|
6670
|
+
const type = outputType(block.type);
|
|
6671
|
+
if (!type || coverIndexes.has(index)) return [];
|
|
6672
|
+
const size = blockNaturalSize(block);
|
|
6673
|
+
const text = type === "text" ? blockText(block) ?? null : null;
|
|
6674
|
+
if (type === "text" && !text) return [];
|
|
6675
|
+
return [{
|
|
6676
|
+
index,
|
|
6677
|
+
type,
|
|
6678
|
+
url: type === "text" ? null : blockUrl(block) ?? null,
|
|
6679
|
+
previewUrl: blockPreviewUrl(block) ?? coverUrl(index) ?? null,
|
|
6680
|
+
title: blockTitle(block) ?? null,
|
|
6681
|
+
mimeType: blockMimeType(block) ?? null,
|
|
6682
|
+
text,
|
|
6683
|
+
width: size.naturalWidth ?? null,
|
|
6684
|
+
height: size.naturalHeight ?? null,
|
|
6685
|
+
durationMs: blockDurationMs(block) ?? null,
|
|
6686
|
+
deferred: type !== "text" && isDeferred(block)
|
|
6687
|
+
}];
|
|
6688
|
+
});
|
|
6689
|
+
}
|
|
6690
|
+
function toGenerationTaskView(run) {
|
|
6691
|
+
const data = taskData(run);
|
|
6692
|
+
const result = record(run.result);
|
|
6693
|
+
const model = data?.model ?? result?.model;
|
|
6694
|
+
return {
|
|
6695
|
+
id: run.id,
|
|
6696
|
+
status: run.status,
|
|
6697
|
+
spaceId: run.spaceId,
|
|
6698
|
+
sessionId: run.sessionId,
|
|
6699
|
+
turnId: run.turnId,
|
|
6700
|
+
model: typeof model === "string" && model ? model : null,
|
|
6701
|
+
prompt: generationPrompt(run) ?? null,
|
|
6702
|
+
errorMessage: run.errorMessage,
|
|
6703
|
+
outputs: run.status === "completed" ? projectOutputs(run) : [],
|
|
6704
|
+
createdAt: run.createdAt,
|
|
6705
|
+
startedAt: run.startedAt,
|
|
6706
|
+
finishedAt: run.finishedAt,
|
|
6707
|
+
updatedAt: run.updatedAt
|
|
6708
|
+
};
|
|
6709
|
+
}
|
|
6710
|
+
function isActiveGenerationTask(task) {
|
|
6711
|
+
return task.status === "pending" || task.status === "running";
|
|
6712
|
+
}
|
|
6713
|
+
/** Playable source of an output: its URL, or a data URL for inline payloads. */
|
|
6714
|
+
function generationOutputSource(result, outputIndex) {
|
|
6715
|
+
const block = contentBlocks(record(result)?.output)[outputIndex];
|
|
6716
|
+
if (!block) return null;
|
|
6717
|
+
const url = blockUrl(block);
|
|
6718
|
+
if (url) return url;
|
|
6719
|
+
const source = record(block.source);
|
|
6720
|
+
const data = INLINE_KEYS.map((key) => source?.[key] ?? block[key]).find((value) => typeof value === "string" && value !== "");
|
|
6721
|
+
if (!data) return null;
|
|
6722
|
+
const declared = blockMimeType(block);
|
|
6723
|
+
return `data:${declared && MEDIA_TYPE_PATTERN.test(declared) ? declared : DEFAULT_MEDIA_TYPES[String(block.type)] ?? "application/octet-stream"};base64,${data}`;
|
|
6724
|
+
}
|
|
6725
|
+
//#endregion
|
|
6081
6726
|
//#region src/board/animation.ts
|
|
6082
6727
|
function track(input) {
|
|
6083
6728
|
return BoardTrackSchema.parse(input);
|
|
@@ -6323,4 +6968,4 @@ function createBoardExtensionRegistry() {
|
|
|
6323
6968
|
}
|
|
6324
6969
|
const BOARD_CHANNELS = BOARD_ANIMATION_CHANNELS;
|
|
6325
6970
|
//#endregion
|
|
6326
|
-
export { APP_COMPOSER_CHIP_CONTENT_MAX_BYTES, APP_COMPOSER_CHIP_KEY_MAX_LENGTH, APP_COMPOSER_CHIP_LABEL_MAX_LENGTH, APP_SURFACE_READY_TIMEOUT_MS, APP_SURFACE_REQUEST_TIMEOUT_MS, AppCommerceApi, AppRealtimeApi, AppRefParseError, AppRoom, AppRuntimeApi, AppRuntimeApi as WorkRuntimeApi, AppRuntimeError, AppSurfaceApi, AppsApi, BILLING_ACCESS_BLOCKED_ERROR_CODE, BOARD_ANIMATION_CHANNEL_CAPABILITIES, BOARD_CHANNELS, BOARD_COLOR_IDS, BOARD_GEO_KINDS, BillingApi, BoardAnimationSpecSchema, BoardAuthoringItemSchema, BoardClient, BoardCompositionInputSchema, BoardCompositionSchema, BoardDealParamsSchema, BoardEffectInputSchema, BoardEffectSchema, BoardExtensionRegistry, BoardItemPatchSchema, BoardPlaybackPolicySchema, BoardSemanticCommandSchema, COHUB_ENVIRONMENTS, COHUB_SOURCE_HEADER, COHUB_SOURCE_HEADER_NAMES, CohubClient, CohubHttpClient, DEFAULT_BOARD_LIMITS, DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS, DESKTOP_COMMAND_MAX_TIMEOUT_MS, DESKTOP_COMMAND_PAYLOAD_MAX_BYTES, DESKTOP_COMMAND_PENDING_TTL_SECONDS, DESKTOP_COMMAND_SETTLEMENT_GRACE_SECONDS, DESKTOP_COMMAND_TERMINAL_TTL_SECONDS, DESKTOP_COMMAND_VERSION, DesktopCommandsApi, FEATURE_NOT_ENTITLED_ERROR_CODE, GenerationPolicyError, HttpError, NATIVE_INGEST_MAX_TURNS, NATIVE_SYNC_SOURCE, OriginBrokerTransport, PERMISSIONS, ParentBridgeTransport, PopupBrokerTransport, REQUEST_SOURCE_VIA_MAX_LENGTH, RUNTIME_ARCHIVE_SEGMENT_BYTES, RUNTIME_MAX_FRAME_BYTES, RUNTIME_PROTOCOL_VERSION, RUNTIME_RECOVERY_BATCH_SIZE, ReferencesApi, ReferralsApi, SETTLED_TURN_STATUSES, SessionGenerationStreamClient, SessionPatchReducer, SpacePublicFilesApi, SpaceWebhooksApi, USER_LEVEL_PERMISSIONS, UiCommandsApi, UsersApi, VoiceApi, VoiceInputClient, WebsocketClient, WorkCommerceApi, WorkRealtimeApi, WorkRefParseError, WorkRoom, WorkSurfaceApi, assertGenerationRequestAllowedByPolicy, attachAppEmbed, buildAppSurfaceRequest, buildSpaceInvitePath, buildSpacePath, clearGrantedAppScopes, compileComposition, composition, contextToPiMessages, createAppBridgeCore, createAppBridgeCore as createWorkBridgeCore, createAppRuntime, createAppRuntime as createWorkRuntime, createBoardExtensionRegistry, createCohubClient, createHttpClient, createOriginAppResolver, createSessionGenerationStreamClient, createSessionPatchReducer, createSlugAppIdResolver, createSlugAppIdResolver as createSlugWorkIdResolver, createVoiceInputClient, createWebsocketClient, decodeGenerationPolicy, encodeGenerationPolicy, extractBillingPayload, filterDiscoverableGenerationModels, filterGenerationDeclarationsByPolicy, filterSpacePickerItems, findGenerationModelPolicy, fingerprintProjectionTurns, formatAppRef, formatWorkRef, getAllowedGenerationModelIds, getCohubContext, harnessArchiveIndexSchema, hasGrantedAppScopes, hasRequestSourceIdentity, isAppId, isBillingAccessBlockedCode, isBillingAccessBlockedError, isDesktopCallMethod, isFeatureNotEntitledError, isGenerationModelHidden, isHttpErrorCode, isLocalHarness, isNativeClientTurn, isProjectionCompaction, isRequestSourceClientId, isRequestSourceEmpty, isRequestSourceUuid, isTerminalDesktopCommandStatus, isUserLevelPermission, isUuid, isWorkId, joinApiUrl, matchesUnauthorizedErrorToken, mergeRequestSourceIntoMeta, nativeRuntimeEventSchema, nativeTurnCompleteSchema, nativeTurnProgressSchema, normalizeBaseUrl, normalizeGenerationPolicy, normalizeRequestSource, normalizeSpacePickerQuery, normalizeVoiceInputWebsocketUrl, normalizeWebsocketUrl, orderSpacePickerItems, parseAppRef, parseAppSurfaceReady, parseAppSurfaceResponse, parseAssistantMessageCommit, parseBoardCompositionInput, parseBoardEffectInput, parseBoardPlaybackPolicy, parseDesktopCommand, parseGenerationPolicyFromEnv, parseRequestSourceFromHeaders, parseWorkRef, proceduralClip, projectNativeMessageMeta, projectNativeSession, readRequestSourceFromEnv, requestSourceToHeaders, resolveApiBaseUrl, resolveAppTransport, resolveAppTransport as resolveWorkTransport, resolveCohubEnvironment, resolveExecutionAppId, resolveExecutionToken, resolveHarness, resolveOpenSurface, resolveRequestSourceChannel, resolveVoiceInputWebsocketUrl, resolveWebBaseUrl, resolveWebsocketUrl, runtimeCommandSchema, runtimeEventSchema, runtimeNativeStopSchema, runtimeReadySchema, sampleCompositionTracks, sampleEasing, sampleTrack, sanitizeAccessToken, scopeListHasPermission, selectRuntimeContextMessages, selectSpacePickerItems, serializeProjection, serializeProjectionRecords, setGrantedAppScopes, track, trimProjectionTurnsToCompaction, validateArchiveBoundary };
|
|
6971
|
+
export { APP_APPEARANCE_TOKENS, APP_COMPOSER_CHIP_CONTENT_MAX_BYTES, APP_COMPOSER_CHIP_KEY_MAX_LENGTH, APP_COMPOSER_CHIP_LABEL_MAX_LENGTH, APP_SURFACE_READY_TIMEOUT_MS, APP_SURFACE_REQUEST_TIMEOUT_MS, AppCommerceApi, AppRealtimeApi, AppRefParseError, AppRoom, AppRuntimeApi, AppRuntimeApi as WorkRuntimeApi, AppRuntimeError, AppSurfaceApi, AppWindowApi, AppsApi, BILLING_ACCESS_BLOCKED_ERROR_CODE, BOARD_ANIMATION_CHANNEL_CAPABILITIES, BOARD_CHANNELS, BOARD_COLOR_IDS, BOARD_GEO_KINDS, BillingApi, BoardAnimationSpecSchema, BoardAuthoringItemSchema, BoardClient, BoardCompositionInputSchema, BoardCompositionSchema, BoardDealParamsSchema, BoardEffectInputSchema, BoardEffectSchema, BoardExtensionRegistry, BoardItemPatchSchema, BoardPlaybackPolicySchema, BoardSemanticCommandSchema, COHUB_ENVIRONMENTS, COHUB_SOURCE_HEADER, COHUB_SOURCE_HEADER_NAMES, CohubClient, CohubHttpClient, DEFAULT_BOARD_LIMITS, DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS, DESKTOP_COMMAND_MAX_TIMEOUT_MS, DESKTOP_COMMAND_PAYLOAD_MAX_BYTES, DESKTOP_COMMAND_PENDING_TTL_SECONDS, DESKTOP_COMMAND_SETTLEMENT_GRACE_SECONDS, DESKTOP_COMMAND_TERMINAL_TTL_SECONDS, DESKTOP_COMMAND_VERSION, DesktopCommandsApi, FEATURE_NOT_ENTITLED_ERROR_CODE, GenerationPolicyError, HttpError, NATIVE_INGEST_MAX_TURNS, NATIVE_SYNC_SOURCE, OriginBrokerTransport, PERMISSIONS, ParentBridgeTransport, PopupBrokerTransport, REQUEST_SOURCE_VIA_MAX_LENGTH, RUNTIME_ARCHIVE_SEGMENT_BYTES, RUNTIME_MAX_FRAME_BYTES, RUNTIME_PROTOCOL_VERSION, RUNTIME_RECOVERY_BATCH_SIZE, ReferencesApi, ReferralsApi, SETTLED_TURN_STATUSES, SessionGenerationStreamClient, SessionPatchReducer, SpacePublicFilesApi, SpaceWebhooksApi, USER_LEVEL_PERMISSIONS, UiCommandsApi, UsersApi, VoiceApi, VoiceInputClient, WebsocketClient, WorkCommerceApi, WorkRealtimeApi, WorkRefParseError, WorkRoom, WorkSurfaceApi, appAppearanceVar, applyAppAppearance, assertGenerationRequestAllowedByPolicy, attachAppEmbed, buildAppSurfaceRequest, buildSpaceInvitePath, buildSpacePath, clearGrantedAppScopes, compileComposition, composition, contextToPiMessages, createAppBridgeCore, createAppBridgeCore as createWorkBridgeCore, createAppRuntime, createAppRuntime as createWorkRuntime, createBoardExtensionRegistry, createCohubClient, createHttpClient, createOriginAppResolver, createSessionGenerationStreamClient, createSessionPatchReducer, createSlugAppIdResolver, createSlugAppIdResolver as createSlugWorkIdResolver, createVoiceInputClient, createWebsocketClient, decodeGenerationPolicy, encodeGenerationPolicy, extractBillingPayload, filterDiscoverableGenerationModels, filterGenerationDeclarationsByPolicy, filterSpacePickerItems, findGenerationModelPolicy, fingerprintProjectionTurns, formatAppRef, formatWorkRef, generationOutputSource, getAllowedGenerationModelIds, getCohubContext, harnessArchiveIndexSchema, hasGrantedAppScopes, hasRequestSourceIdentity, isActiveGenerationTask, isAppId, isBillingAccessBlockedCode, isBillingAccessBlockedError, isDesktopCallMethod, isFeatureNotEntitledError, isGenerationModelHidden, isHttpErrorCode, isLocalHarness, isNativeClientTurn, isProjectionCompaction, isRequestSourceClientId, isRequestSourceEmpty, isRequestSourceUuid, isTerminalDesktopCommandStatus, isUserLevelPermission, isUuid, isWorkId, joinApiUrl, matchesUnauthorizedErrorToken, mergeRequestSourceIntoMeta, nativeIngestTurnSchema, nativeRuntimeEventSchema, nativeTurnCompleteSchema, nativeTurnProgressSchema, normalizeBaseUrl, normalizeGenerationPolicy, normalizeRequestSource, normalizeSpacePickerQuery, normalizeVoiceInputWebsocketUrl, normalizeWebsocketUrl, orderSpacePickerItems, parseAppRef, parseAppSurfaceReady, parseAppSurfaceResponse, parseAssistantMessageCommit, parseBoardCompositionInput, parseBoardEffectInput, parseBoardPlaybackPolicy, parseDesktopCommand, parseGenerationPolicyFromEnv, parseRequestSourceFromHeaders, parseWorkRef, proceduralClip, projectNativeMessageMeta, projectNativeSession, readRequestSourceFromEnv, requestSourceToHeaders, resolveApiBaseUrl, resolveAppTransport, resolveAppTransport as resolveWorkTransport, resolveCohubEnvironment, resolveExecutionAppId, resolveExecutionToken, resolveHarness, resolveOpenSurface, resolveRequestSourceChannel, resolveVoiceInputWebsocketUrl, resolveWebBaseUrl, resolveWebsocketUrl, runtimeCommandSchema, runtimeEventSchema, runtimeNativeStopSchema, runtimeReadySchema, sampleCompositionTracks, sampleEasing, sampleTrack, sanitizeAccessToken, scopeListHasPermission, selectRuntimeContextMessages, selectSpacePickerItems, serializeProjection, serializeProjectionRecords, setGrantedAppScopes, toGenerationTaskView, track, trimProjectionTurnsToCompaction, validateArchiveBoundary };
|