@neta-art/cohub 8.23.0 → 9.0.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/animation.d.ts +57 -181
- package/dist/board/animation.js +374 -220
- package/dist/board/core/arrow-geometry.d.ts +33 -18
- package/dist/board/core/arrow-geometry.js +283 -52
- package/dist/board/core/draw-geometry.d.ts +22 -36
- package/dist/board/core/draw-geometry.js +148 -85
- package/dist/board/core/export-assets.d.ts +3 -6
- package/dist/board/core/export-assets.js +0 -2
- package/dist/board/core/export-plan.d.ts +12 -63
- package/dist/board/core/export-plan.js +55 -94
- package/dist/board/core/palette.d.ts +4 -21
- package/dist/board/core/palette.js +8 -13
- package/dist/board/core/scene.d.ts +33 -0
- package/dist/board/core/scene.js +302 -0
- package/dist/board/core/shape-definition.d.ts +12 -24
- package/dist/board/core/shape-definition.js +0 -19
- package/dist/board/core/shape-types.d.ts +6 -120
- package/dist/board/core/shape-types.js +5 -5
- package/dist/board/core/text-metrics.d.ts +8 -11
- package/dist/board/core/text-metrics.js +5 -34
- package/dist/board/core/tool-styles.d.ts +6 -16
- package/dist/board/core/tool-styles.js +17 -25
- package/dist/board/export/index.d.ts +10 -17
- package/dist/board/export/index.js +22 -21
- package/dist/board/export/scene.d.ts +10 -16
- package/dist/board/export/scene.js +17 -30
- package/dist/board/geometry.d.ts +27 -56
- package/dist/board/geometry.js +7 -67
- package/dist/board/headless/index.d.ts +7 -27
- package/dist/board/headless/index.js +20 -19
- package/dist/board/headless/sketch.d.ts +19 -0
- package/dist/board/headless/sketch.js +146 -0
- package/dist/board/image-key.d.ts +2 -12
- package/dist/board/image-key.js +3 -13
- package/dist/board/index.d.ts +18 -20
- package/dist/board/index.js +17 -17
- package/dist/board/media-playback.d.ts +3 -6
- package/dist/board/media-playback.js +6 -6
- package/dist/board/presets.d.ts +150 -0
- package/dist/board/presets.js +226 -0
- package/dist/board/render/board-background.d.ts +5 -3
- package/dist/board/render/board-background.js +8 -7
- package/dist/board/render/clip.d.ts +14 -0
- package/dist/board/render/clip.js +47 -0
- package/dist/board/render/index.d.ts +8 -5
- package/dist/board/render/index.js +7 -4
- package/dist/board/render/media-interaction.d.ts +4 -5
- package/dist/board/render/media-interaction.js +1 -2
- package/dist/board/render/palette.d.ts +6 -2
- package/dist/board/render/palette.js +7 -1
- package/dist/board/render/renderers/arrow-card-renderer.js +69 -71
- package/dist/board/render/renderers/audio-card-renderer.js +3 -3
- package/dist/board/render/renderers/base-card-renderer.d.ts +6 -17
- package/dist/board/render/renderers/base-card-renderer.js +1 -24
- package/dist/board/render/renderers/board-renderer-registry.d.ts +20 -50
- package/dist/board/render/renderers/board-renderer-registry.js +6 -7
- package/dist/board/render/renderers/draw-card-renderer.js +89 -56
- package/dist/board/render/renderers/effect-card-renderer.d.ts +6 -0
- package/dist/board/render/renderers/effect-card-renderer.js +197 -0
- package/dist/board/render/renderers/far-plate.d.ts +4 -21
- package/dist/board/render/renderers/far-plate.js +15 -34
- package/dist/board/render/renderers/file-card-renderer.d.ts +4 -25
- package/dist/board/render/renderers/file-card-renderer.js +8 -58
- package/dist/board/render/renderers/frame-card-renderer.js +28 -28
- package/dist/board/render/renderers/image-card-renderer.d.ts +4 -3
- package/dist/board/render/renderers/image-card-renderer.js +0 -9
- package/dist/board/render/renderers/shape-card-renderer.d.ts +9 -0
- package/dist/board/render/renderers/shape-card-renderer.js +240 -0
- package/dist/board/render/renderers/sketch-card-renderer.d.ts +5 -0
- package/dist/board/render/renderers/sketch-card-renderer.js +98 -0
- package/dist/board/render/renderers/task-card-renderer.d.ts +0 -1
- package/dist/board/render/renderers/task-card-renderer.js +14 -16
- package/dist/board/render/renderers/text-card-renderer.d.ts +1 -3
- package/dist/board/render/renderers/text-card-renderer.js +56 -46
- package/dist/board/render/renderers/unknown-card-renderer.js +4 -10
- package/dist/board/render/renderers/video-card-renderer.d.ts +4 -2
- package/dist/board/render/renderers/video-card-renderer.js +2 -1
- package/dist/board/render/sketch-host.d.ts +14 -0
- package/dist/board/render/sketch-host.js +216 -0
- package/dist/board/render/stroke.d.ts +12 -0
- package/dist/board/render/stroke.js +90 -0
- package/dist/board/render/text-measurement.d.ts +6 -26
- package/dist/board/render/text-measurement.js +11 -30
- package/dist/board/replay.d.ts +6 -31
- package/dist/board/replay.js +45 -203
- package/dist/board/runtime/board-player.d.ts +24 -0
- package/dist/board/runtime/board-player.js +161 -0
- package/dist/board/task.d.ts +2 -10
- package/dist/board/task.js +1 -10
- package/dist/chunks/environment.d.ts +2296 -3864
- package/dist/chunks/host-bridge.d.ts +253 -0
- package/dist/chunks/http.d.ts +238 -58
- package/dist/chunks/http.js +1061 -1034
- package/dist/chunks/transport.js +113 -59
- package/dist/chunks/websocket.d.ts +1 -1
- package/dist/chunks/websocket.js +1 -1
- package/dist/host-bridge.d.ts +54 -0
- package/dist/host-bridge.js +322 -0
- package/dist/http.d.ts +3 -3
- package/dist/index.d.ts +76 -193
- package/dist/index.js +2037 -1191
- package/dist/protocol/dist/board-constants.d.ts +1 -29
- package/dist/protocol/dist/board-constants.js +2 -115
- package/dist/protocol/dist/board-layout.d.ts +59 -0
- package/dist/protocol/dist/board-layout.js +482 -0
- package/dist/protocol/dist/board-model.d.ts +2052 -0
- package/dist/protocol/dist/board-model.js +592 -0
- package/dist/protocol/dist/board-patch.d.ts +86 -0
- package/dist/protocol/dist/board-patch.js +734 -0
- package/dist/protocol/dist/board-schema.d.ts +33 -0
- package/dist/protocol/dist/board-schema.js +59 -0
- package/dist/protocol/dist/board-upgrade.d.ts +1 -0
- package/dist/protocol/dist/board-url.d.ts +1 -3
- package/dist/protocol/dist/board-url.js +2 -2
- package/dist/protocol/dist/board.d.ts +37 -232
- package/dist/protocol/dist/board.js +62 -160
- package/dist/protocol/dist/display/actions.d.ts +1 -0
- package/dist/protocol/dist/display/index.d.ts +2 -0
- package/dist/protocol/dist/index.d.ts +11 -12
- package/dist/protocol/dist/model/metrics.d.ts +2 -0
- package/dist/protocol/dist/model/session.d.ts +2 -1
- package/dist/protocol/dist/realtime/types.d.ts +2 -2
- package/dist/protocol/dist/runtime/index.d.ts +1 -0
- package/dist/space-picker.d.ts +1 -0
- package/dist/space-picker.js +1 -0
- package/dist/types.d.ts +2 -5
- package/package.json +9 -1
- package/dist/board/core/connections.d.ts +0 -108
- package/dist/board/core/connections.js +0 -455
- package/dist/board/mutation.d.ts +0 -6
- package/dist/board/mutation.js +0 -14
- package/dist/board/render/connection-layer.d.ts +0 -53
- package/dist/board/render/connection-layer.js +0 -375
- package/dist/board/render/renderers/geo-card-renderer.d.ts +0 -5
- package/dist/board/render/renderers/geo-card-renderer.js +0 -127
- package/dist/board/semantic-document.d.ts +0 -37
- package/dist/board/semantic-document.js +0 -288
- package/dist/board/semantic-mutation.d.ts +0 -10
- package/dist/board/semantic-mutation.js +0 -209
- package/dist/protocol/dist/board-animation.js +0 -34
- package/dist/protocol/dist/board-authoring.d.ts +0 -1326
- package/dist/protocol/dist/board-authoring.js +0 -302
- package/dist/protocol/dist/board-capability-registry.d.ts +0 -2
- package/dist/protocol/dist/board-capability-registry.js +0 -123
- package/dist/protocol/dist/board-codec.d.ts +0 -3
- package/dist/protocol/dist/board-codec.js +0 -383
- package/dist/protocol/dist/board-composition.d.ts +0 -383
- package/dist/protocol/dist/board-composition.js +0 -310
- package/dist/protocol/dist/board-connection.d.ts +0 -317
- package/dist/protocol/dist/board-connection.js +0 -219
- package/dist/protocol/dist/board-document.d.ts +0 -1566
- package/dist/protocol/dist/board-document.js +0 -456
- package/dist/protocol/dist/board-effect.d.ts +0 -96
- package/dist/protocol/dist/board-effect.js +0 -54
- package/dist/protocol/dist/board-geometry.d.ts +0 -15
- package/dist/protocol/dist/board-geometry.js +0 -137
- package/dist/protocol/dist/board-node.d.ts +0 -8
- package/dist/protocol/dist/board-node.js +0 -252
- /package/dist/protocol/dist/{board-animation.d.ts → host-bridge.d.ts} +0 -0
package/dist/chunks/http.js
CHANGED
|
@@ -1,894 +1,397 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as isUuid, f as getRealtimeBoardRoom, n as HttpTransport, p as getRealtimeSpaceRoom, t as HttpError } from "./transport.js";
|
|
2
2
|
import { l as resolveExecutionToken, o as resolveApiBaseUrl } from "./environment.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
//#region ../protocol/dist/
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
function
|
|
64
|
-
|
|
65
|
-
case "effects.deal": return { params: {
|
|
66
|
-
lift: { unit: "board" },
|
|
67
|
-
swing: { unit: "board" },
|
|
68
|
-
curve: { unit: "board" },
|
|
69
|
-
tilt: { unit: "degree" },
|
|
70
|
-
scale: { unit: "ratio" },
|
|
71
|
-
duration: { unit: "ms" },
|
|
72
|
-
landing: { unit: "ms" }
|
|
73
|
-
} };
|
|
74
|
-
default: return;
|
|
75
|
-
}
|
|
4
|
+
//#region ../protocol/dist/model/metrics.js
|
|
5
|
+
const count = z.number().finite().nonnegative();
|
|
6
|
+
const usageSchema = z.object({
|
|
7
|
+
input: count.optional(),
|
|
8
|
+
output: count.optional(),
|
|
9
|
+
cacheRead: count.optional(),
|
|
10
|
+
cacheWrite: count.optional(),
|
|
11
|
+
totalTokens: count.optional(),
|
|
12
|
+
cost: z.object({
|
|
13
|
+
input: count.optional(),
|
|
14
|
+
output: count.optional(),
|
|
15
|
+
cacheRead: count.optional(),
|
|
16
|
+
cacheWrite: count.optional(),
|
|
17
|
+
total: count.optional()
|
|
18
|
+
}).nullable().optional()
|
|
19
|
+
});
|
|
20
|
+
/** One provider attempt, independent of whether its response becomes a chat message. */
|
|
21
|
+
const requestMetricSchema = z.object({
|
|
22
|
+
id: z.string(),
|
|
23
|
+
provider: z.string(),
|
|
24
|
+
model: z.string(),
|
|
25
|
+
startedAt: count,
|
|
26
|
+
completedAt: count.optional(),
|
|
27
|
+
durationMs: count.optional(),
|
|
28
|
+
firstTokenMs: count.optional(),
|
|
29
|
+
outputDurationMs: count.optional(),
|
|
30
|
+
status: z.enum([
|
|
31
|
+
"running",
|
|
32
|
+
"completed",
|
|
33
|
+
"failed",
|
|
34
|
+
"interrupted"
|
|
35
|
+
]),
|
|
36
|
+
usage: usageSchema.nullable().optional(),
|
|
37
|
+
omitted: z.boolean().optional(),
|
|
38
|
+
imageToText: z.object({
|
|
39
|
+
calls: count,
|
|
40
|
+
durationMs: count,
|
|
41
|
+
usage: usageSchema.nullable(),
|
|
42
|
+
unknownUsageCalls: count
|
|
43
|
+
}).optional()
|
|
44
|
+
});
|
|
45
|
+
const toolMetricsSchema = z.object({
|
|
46
|
+
calls: count,
|
|
47
|
+
errors: count,
|
|
48
|
+
timedCalls: count,
|
|
49
|
+
activeMs: count.nullable()
|
|
50
|
+
});
|
|
51
|
+
z.object({
|
|
52
|
+
version: z.literal(1),
|
|
53
|
+
executionStartedAt: count.optional(),
|
|
54
|
+
requests: z.record(z.string(), requestMetricSchema).optional(),
|
|
55
|
+
retryWaitMs: count.optional(),
|
|
56
|
+
retryCount: count.optional(),
|
|
57
|
+
tools: toolMetricsSchema.optional(),
|
|
58
|
+
imageToText: z.object({
|
|
59
|
+
durationMs: count.nullable(),
|
|
60
|
+
timedCalls: count
|
|
61
|
+
}).optional()
|
|
62
|
+
});
|
|
63
|
+
function metricsRecord(value) {
|
|
64
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
76
65
|
}
|
|
77
|
-
const
|
|
78
|
-
|
|
66
|
+
const optionalCount = count.nullable();
|
|
67
|
+
/** Additive facts only. Rates are computed from matching samples, never averaged averages. */
|
|
68
|
+
const executionStatsSchema = z.object({
|
|
69
|
+
turns: count,
|
|
70
|
+
generations: count,
|
|
71
|
+
calls: optionalCount,
|
|
72
|
+
failedCalls: optionalCount,
|
|
73
|
+
unknownUsageCalls: count,
|
|
74
|
+
toolCalls: optionalCount,
|
|
75
|
+
toolErrors: optionalCount,
|
|
76
|
+
elapsedMs: optionalCount,
|
|
77
|
+
queueMs: optionalCount,
|
|
78
|
+
modelMs: optionalCount,
|
|
79
|
+
toolMs: optionalCount,
|
|
80
|
+
retryCount: optionalCount,
|
|
81
|
+
retryWaitMs: optionalCount,
|
|
82
|
+
ttftMs: count,
|
|
83
|
+
ttftSamples: count,
|
|
84
|
+
outputMs: count,
|
|
85
|
+
timedOutputTokens: count,
|
|
86
|
+
compactions: count,
|
|
87
|
+
compactionMs: optionalCount,
|
|
88
|
+
compactionUsage: usageSchema.nullable(),
|
|
89
|
+
imageToTextCalls: count,
|
|
90
|
+
imageToTextMs: optionalCount,
|
|
91
|
+
usage: usageSchema.nullable(),
|
|
92
|
+
modelCostUsd: optionalCount,
|
|
93
|
+
generationCostUsd: optionalCount,
|
|
94
|
+
/** Shared statistics never expose actual charges. */
|
|
95
|
+
chargedCostUsd: z.null().default(null),
|
|
96
|
+
partial: z.boolean()
|
|
97
|
+
});
|
|
98
|
+
const sessionStatsSchema = z.object({
|
|
99
|
+
version: z.literal(1),
|
|
100
|
+
updatedAt: z.string(),
|
|
101
|
+
revision: count,
|
|
102
|
+
own: executionStatsSchema,
|
|
103
|
+
inherited: executionStatsSchema,
|
|
104
|
+
auxiliaryUsage: usageSchema.nullable()
|
|
105
|
+
});
|
|
106
|
+
const normalizeExecutionStatsAliases = (value) => {
|
|
107
|
+
const record = metricsRecord(value);
|
|
108
|
+
const modelAlias = !Object.hasOwn(record, "modelCostUsd") && Object.hasOwn(record, "estimatedCostUsd");
|
|
109
|
+
const generationAlias = !Object.hasOwn(record, "generationCostUsd") && Object.hasOwn(record, "providerCostUsd");
|
|
110
|
+
if (!modelAlias && !generationAlias) return value;
|
|
79
111
|
return {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
renderers: ["webgpu", "webgl"],
|
|
84
|
-
...schema ? { schema } : {}
|
|
112
|
+
...record,
|
|
113
|
+
...modelAlias ? { modelCostUsd: record.estimatedCostUsd } : {},
|
|
114
|
+
...generationAlias ? { generationCostUsd: record.providerCostUsd } : {}
|
|
85
115
|
};
|
|
86
|
-
}
|
|
87
|
-
|
|
116
|
+
};
|
|
117
|
+
function sanitizeSessionStatsMeta(meta) {
|
|
118
|
+
const source = metricsRecord(meta);
|
|
119
|
+
if (!source.stats || typeof source.stats !== "object" || Array.isArray(source.stats)) return meta;
|
|
120
|
+
const stats = metricsRecord(source.stats);
|
|
121
|
+
const stripCharge = (value) => {
|
|
122
|
+
const record = metricsRecord(value);
|
|
123
|
+
return Object.hasOwn(record, "chargedCostUsd") && record.chargedCostUsd !== null ? {
|
|
124
|
+
...record,
|
|
125
|
+
chargedCostUsd: null
|
|
126
|
+
} : value;
|
|
127
|
+
};
|
|
128
|
+
const own = stripCharge(stats.own);
|
|
129
|
+
const inherited = stripCharge(stats.inherited);
|
|
130
|
+
if (own === stats.own && inherited === stats.inherited) return meta;
|
|
88
131
|
return {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
132
|
+
...source,
|
|
133
|
+
stats: {
|
|
134
|
+
...stats,
|
|
135
|
+
own,
|
|
136
|
+
inherited
|
|
137
|
+
}
|
|
94
138
|
};
|
|
95
|
-
}
|
|
139
|
+
}
|
|
140
|
+
function readSessionStats(meta) {
|
|
141
|
+
const raw = metricsRecord(sanitizeSessionStatsMeta(meta)).stats;
|
|
142
|
+
const record = metricsRecord(raw);
|
|
143
|
+
const own = normalizeExecutionStatsAliases(record.own);
|
|
144
|
+
const inherited = normalizeExecutionStatsAliases(record.inherited);
|
|
145
|
+
const normalized = own === record.own && inherited === record.inherited ? raw : {
|
|
146
|
+
...record,
|
|
147
|
+
own,
|
|
148
|
+
inherited
|
|
149
|
+
};
|
|
150
|
+
const result = sessionStatsSchema.safeParse(normalized);
|
|
151
|
+
return result.success ? result.data : null;
|
|
152
|
+
}
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region ../protocol/dist/display/index.js
|
|
96
155
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
* - `side` — the user pinned a side; `offset` (0..1) positions it along that edge.
|
|
103
|
-
* - `fixed` — the user pinned an exact normalized point on the frame.
|
|
156
|
+
* Displays: a machine's screens, shared with people over WebRTC and with
|
|
157
|
+
* agents as stills and input. Mirrored in Go by `apps/sandbox/display` and
|
|
158
|
+
* `apps/sandbox/rtc`, and in Kotlin by the Android display provider; the
|
|
159
|
+
* vocabulary and limits below are generated into both
|
|
160
|
+
* (`pnpm --filter @cohub/protocol generate:display`).
|
|
104
161
|
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
* first layout it happened to have.
|
|
162
|
+
* Coordinates are normalized to the display (0..1, origin top-left), so
|
|
163
|
+
* viewers and agents never need its pixel size to act on it.
|
|
108
164
|
*/
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
]
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
165
|
+
const unit = z.number().min(0).max(1);
|
|
166
|
+
const displayIdSchema = z.string().regex(/^[A-Za-z0-9._-]{1,64}$/);
|
|
167
|
+
const DISPLAY_DIMENSION_MAX = 16384;
|
|
168
|
+
const DISPLAY_SYSTEM_ACTIONS = [
|
|
169
|
+
"back",
|
|
170
|
+
"home",
|
|
171
|
+
"recents",
|
|
172
|
+
"notifications",
|
|
173
|
+
"quickSettings",
|
|
174
|
+
"lock"
|
|
175
|
+
];
|
|
176
|
+
const DISPLAY_PERMISSIONS = ["screenRecording", "accessibility"];
|
|
177
|
+
const DISPLAY_POINTER_ACTIONS = [
|
|
178
|
+
"down",
|
|
179
|
+
"move",
|
|
180
|
+
"up",
|
|
181
|
+
"cancel"
|
|
182
|
+
];
|
|
183
|
+
const DISPLAY_KEY_ACTIONS = [
|
|
184
|
+
"down",
|
|
185
|
+
"up",
|
|
186
|
+
"press"
|
|
187
|
+
];
|
|
188
|
+
const DISPLAY_BUTTONS = [
|
|
189
|
+
"primary",
|
|
190
|
+
"secondary",
|
|
191
|
+
"middle"
|
|
192
|
+
];
|
|
193
|
+
/** A list of known names; names a newer machine adds are dropped, so older clients keep working. */
|
|
194
|
+
const knownNames = (names, max) => z.array(z.string().max(32)).max(max).transform((values) => values.filter((value) => names.includes(value)));
|
|
195
|
+
const displayViewerSchema = z.object({
|
|
196
|
+
userId: z.string().min(1).max(64),
|
|
197
|
+
control: z.boolean().optional()
|
|
132
198
|
});
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
199
|
+
const displayInfoSchema = z.object({
|
|
200
|
+
id: displayIdSchema,
|
|
201
|
+
name: z.string().max(200),
|
|
202
|
+
width: z.number().int().min(0).max(DISPLAY_DIMENSION_MAX),
|
|
203
|
+
height: z.number().int().min(0).max(DISPLAY_DIMENSION_MAX),
|
|
204
|
+
stream: z.boolean(),
|
|
205
|
+
capture: z.boolean(),
|
|
206
|
+
input: z.boolean(),
|
|
207
|
+
system: knownNames(DISPLAY_SYSTEM_ACTIONS, 16).optional(),
|
|
208
|
+
desktop: z.boolean().optional(),
|
|
209
|
+
tree: z.boolean().optional(),
|
|
210
|
+
needs: knownNames(DISPLAY_PERMISSIONS, 8).optional(),
|
|
211
|
+
viewers: z.array(displayViewerSchema).max(16).optional()
|
|
212
|
+
});
|
|
213
|
+
z.object({ displays: z.array(displayInfoSchema).max(16) });
|
|
214
|
+
z.object({ size: z.string().regex(/^\d{3,4}x\d{3,4}$/).optional() });
|
|
215
|
+
const DISPLAY_ELEMENT_ROLES = [
|
|
216
|
+
"window",
|
|
217
|
+
"dialog",
|
|
218
|
+
"group",
|
|
219
|
+
"list",
|
|
220
|
+
"listItem",
|
|
221
|
+
"text",
|
|
222
|
+
"heading",
|
|
223
|
+
"image",
|
|
224
|
+
"button",
|
|
225
|
+
"link",
|
|
226
|
+
"textField",
|
|
227
|
+
"checkbox",
|
|
228
|
+
"radio",
|
|
229
|
+
"switch",
|
|
230
|
+
"slider",
|
|
231
|
+
"tab",
|
|
232
|
+
"menu",
|
|
233
|
+
"menuItem",
|
|
234
|
+
"web",
|
|
235
|
+
"other"
|
|
146
236
|
];
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
"
|
|
150
|
-
"
|
|
151
|
-
"
|
|
237
|
+
const DISPLAY_ELEMENT_STATES = [
|
|
238
|
+
"focused",
|
|
239
|
+
"selected",
|
|
240
|
+
"checked",
|
|
241
|
+
"disabled",
|
|
242
|
+
"editable",
|
|
243
|
+
"password",
|
|
244
|
+
"scrollable",
|
|
245
|
+
"expanded"
|
|
152
246
|
];
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
247
|
+
const DISPLAY_ELEMENT_ACTIONS = [
|
|
248
|
+
"click",
|
|
249
|
+
"longPress",
|
|
250
|
+
"focus",
|
|
251
|
+
"setText",
|
|
252
|
+
"scrollForward",
|
|
253
|
+
"scrollBackward"
|
|
254
|
+
];
|
|
255
|
+
const DISPLAY_ELEMENT_REF_PATTERN = /^e\d{1,9}\.\d{1,5}$/;
|
|
256
|
+
const DISPLAY_TREE_MAX_ELEMENTS = 1e3;
|
|
257
|
+
const displayElementSchema = z.object({
|
|
258
|
+
ref: z.string().regex(DISPLAY_ELEMENT_REF_PATTERN),
|
|
259
|
+
depth: z.number().int().min(0).max(64),
|
|
260
|
+
role: z.string().max(32).transform((role) => DISPLAY_ELEMENT_ROLES.includes(role) ? role : "other"),
|
|
261
|
+
name: z.string().max(500).optional(),
|
|
262
|
+
/** The current text of a field; never reported for passwords. */
|
|
263
|
+
value: z.string().max(500).optional(),
|
|
264
|
+
bounds: z.tuple([
|
|
265
|
+
unit,
|
|
266
|
+
unit,
|
|
267
|
+
unit,
|
|
268
|
+
unit
|
|
269
|
+
]),
|
|
270
|
+
states: knownNames(DISPLAY_ELEMENT_STATES, 16).optional(),
|
|
271
|
+
actions: knownNames(DISPLAY_ELEMENT_ACTIONS, 16).optional()
|
|
173
272
|
});
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
bend: z.number().finite().min(-.85).max(.85).default(0),
|
|
178
|
-
waypoints: z.array(BoardConnectionWaypointSchema).max(64).default([])
|
|
273
|
+
z.object({
|
|
274
|
+
maxElements: z.number().int().min(1).max(DISPLAY_TREE_MAX_ELEMENTS).optional(),
|
|
275
|
+
actionable: z.boolean().optional()
|
|
179
276
|
});
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
277
|
+
z.object({
|
|
278
|
+
width: z.number().int().min(0).max(DISPLAY_DIMENSION_MAX),
|
|
279
|
+
height: z.number().int().min(0).max(DISPLAY_DIMENSION_MAX),
|
|
280
|
+
elements: z.array(displayElementSchema).max(DISPLAY_TREE_MAX_ELEMENTS),
|
|
281
|
+
truncated: z.boolean().optional()
|
|
185
282
|
});
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const jsonObjectSchema$2 = z.record(z.string(), z.unknown());
|
|
219
|
-
const BoardEasingSchema = z.enum([
|
|
220
|
-
"linear",
|
|
221
|
-
"ease-in-quad",
|
|
222
|
-
"ease-out-quad",
|
|
223
|
-
"ease-in-out-quad",
|
|
224
|
-
"ease-in-cubic",
|
|
225
|
-
"ease-out-cubic",
|
|
226
|
-
"ease-in-out-cubic",
|
|
227
|
-
"ease-out-quart",
|
|
228
|
-
"ease-out-expo"
|
|
229
|
-
]);
|
|
230
|
-
const BoardAnimationTargetSchema = z.discriminatedUnion("type", [
|
|
283
|
+
const DISPLAY_INPUT_MAX_TEXT = 4096;
|
|
284
|
+
const DISPLAY_INPUT_MAX_SCHEDULE_MS = 6e4;
|
|
285
|
+
const delta = z.number().min(-10).max(10);
|
|
286
|
+
const at = z.number().int().min(0).max(DISPLAY_INPUT_MAX_SCHEDULE_MS).optional();
|
|
287
|
+
const displayInputEventSchema = z.discriminatedUnion("type", [
|
|
288
|
+
z.object({
|
|
289
|
+
type: z.literal("pointer"),
|
|
290
|
+
action: z.enum(DISPLAY_POINTER_ACTIONS),
|
|
291
|
+
x: unit,
|
|
292
|
+
y: unit,
|
|
293
|
+
button: z.enum(DISPLAY_BUTTONS).optional(),
|
|
294
|
+
t: at
|
|
295
|
+
}),
|
|
296
|
+
z.object({
|
|
297
|
+
type: z.literal("scroll"),
|
|
298
|
+
x: unit,
|
|
299
|
+
y: unit,
|
|
300
|
+
dx: delta.optional(),
|
|
301
|
+
dy: delta.optional(),
|
|
302
|
+
t: at
|
|
303
|
+
}),
|
|
304
|
+
z.object({
|
|
305
|
+
type: z.literal("key"),
|
|
306
|
+
action: z.enum(DISPLAY_KEY_ACTIONS),
|
|
307
|
+
key: z.string().min(1).max(64),
|
|
308
|
+
t: at
|
|
309
|
+
}),
|
|
310
|
+
z.object({
|
|
311
|
+
type: z.literal("text"),
|
|
312
|
+
text: z.string().min(1).max(DISPLAY_INPUT_MAX_TEXT),
|
|
313
|
+
t: at
|
|
314
|
+
}),
|
|
231
315
|
z.object({
|
|
232
|
-
type: z.literal("
|
|
233
|
-
|
|
234
|
-
|
|
316
|
+
type: z.literal("system"),
|
|
317
|
+
action: z.enum(DISPLAY_SYSTEM_ACTIONS),
|
|
318
|
+
t: at
|
|
319
|
+
}),
|
|
235
320
|
z.object({
|
|
236
|
-
type: z.literal("
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
321
|
+
type: z.literal("element"),
|
|
322
|
+
ref: z.string().regex(DISPLAY_ELEMENT_REF_PATTERN),
|
|
323
|
+
action: z.enum(DISPLAY_ELEMENT_ACTIONS),
|
|
324
|
+
text: z.string().max(DISPLAY_INPUT_MAX_TEXT).optional(),
|
|
325
|
+
t: at
|
|
326
|
+
})
|
|
241
327
|
]);
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const scaleSchema = z.union([finiteSchema$1.nonnegative(), z.object({
|
|
247
|
-
x: finiteSchema$1.nonnegative(),
|
|
248
|
-
y: finiteSchema$1.nonnegative()
|
|
249
|
-
}).strict()]);
|
|
250
|
-
const BOARD_ANIMATION_CHANNELS = {
|
|
251
|
-
"transform.translation": {
|
|
252
|
-
targets: ["item"],
|
|
253
|
-
value: vector2Schema,
|
|
254
|
-
interpolations: ["linear", "step"],
|
|
255
|
-
coordinateSpace: "world-offset",
|
|
256
|
-
unit: "board"
|
|
257
|
-
},
|
|
258
|
-
"transform.rotation": {
|
|
259
|
-
targets: ["item"],
|
|
260
|
-
value: finiteSchema$1,
|
|
261
|
-
interpolations: ["linear", "step"],
|
|
262
|
-
unit: "radian"
|
|
263
|
-
},
|
|
264
|
-
"transform.scale": {
|
|
265
|
-
targets: ["item"],
|
|
266
|
-
value: scaleSchema,
|
|
267
|
-
interpolations: ["linear", "step"],
|
|
268
|
-
unit: "ratio"
|
|
269
|
-
},
|
|
270
|
-
"style.opacity": {
|
|
271
|
-
targets: ["item"],
|
|
272
|
-
value: finiteSchema$1.min(0).max(1),
|
|
273
|
-
interpolations: ["linear", "step"],
|
|
274
|
-
unit: "ratio"
|
|
275
|
-
}
|
|
276
|
-
};
|
|
277
|
-
const BoardTrackKeyframeSchema = z.object({
|
|
278
|
-
time: finiteSchema$1.nonnegative(),
|
|
279
|
-
value: z.unknown(),
|
|
280
|
-
easing: BoardEasingSchema.optional()
|
|
281
|
-
}).strict();
|
|
282
|
-
const BoardTrackSchema = z.object({
|
|
283
|
-
id: idSchema$2,
|
|
284
|
-
target: BoardAnimationTargetSchema,
|
|
285
|
-
channel: z.string().min(1).max(160),
|
|
286
|
-
channelVersion: z.number().int().positive().default(1),
|
|
287
|
-
interpolation: z.enum(["linear", "step"]).default("linear"),
|
|
288
|
-
fill: z.enum([
|
|
289
|
-
"none",
|
|
290
|
-
"backwards",
|
|
291
|
-
"forwards",
|
|
292
|
-
"both"
|
|
293
|
-
]).default("none"),
|
|
294
|
-
keyframes: z.array(BoardTrackKeyframeSchema).min(1).max(1e5),
|
|
295
|
-
metadata: jsonObjectSchema$2.default({})
|
|
296
|
-
}).strict().superRefine((track, context) => {
|
|
297
|
-
let previous = -1;
|
|
298
|
-
for (const [index, keyframe] of track.keyframes.entries()) {
|
|
299
|
-
if (keyframe.time <= previous) context.addIssue({
|
|
300
|
-
code: "custom",
|
|
301
|
-
message: "keyframe times must be strictly increasing",
|
|
302
|
-
path: [
|
|
303
|
-
"keyframes",
|
|
304
|
-
index,
|
|
305
|
-
"time"
|
|
306
|
-
]
|
|
307
|
-
});
|
|
308
|
-
previous = keyframe.time;
|
|
309
|
-
}
|
|
310
|
-
const channel = BOARD_ANIMATION_CHANNELS[track.channel];
|
|
311
|
-
if (!channel) {
|
|
312
|
-
if (!/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+){2,}$/.test(track.channel)) context.addIssue({
|
|
313
|
-
code: "custom",
|
|
314
|
-
message: `unknown channel: ${track.channel}`,
|
|
315
|
-
path: ["channel"]
|
|
316
|
-
});
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
if (!channel.targets.includes(track.target.type)) context.addIssue({
|
|
320
|
-
code: "custom",
|
|
321
|
-
message: `${track.channel} cannot target ${track.target.type}`,
|
|
322
|
-
path: ["target"]
|
|
323
|
-
});
|
|
324
|
-
if (!channel.interpolations.includes(track.interpolation)) context.addIssue({
|
|
325
|
-
code: "custom",
|
|
326
|
-
message: `${track.channel} does not support ${track.interpolation} interpolation`,
|
|
327
|
-
path: ["interpolation"]
|
|
328
|
-
});
|
|
329
|
-
for (const [index, keyframe] of track.keyframes.entries()) {
|
|
330
|
-
const parsed = channel.value.safeParse(keyframe.value);
|
|
331
|
-
if (!parsed.success) context.addIssue({
|
|
332
|
-
code: "custom",
|
|
333
|
-
message: parsed.error.issues[0]?.message ?? `invalid value for ${track.channel}`,
|
|
334
|
-
path: [
|
|
335
|
-
"keyframes",
|
|
336
|
-
index,
|
|
337
|
-
"value"
|
|
338
|
-
]
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
});
|
|
342
|
-
const extensionKindSchema = z.string().regex(/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)+$/).max(160);
|
|
343
|
-
const BoardProceduralClipSchema = z.object({
|
|
344
|
-
id: idSchema$2,
|
|
345
|
-
kind: extensionKindSchema,
|
|
346
|
-
kindVersion: z.number().int().positive(),
|
|
347
|
-
target: BoardAnimationTargetSchema,
|
|
348
|
-
start: finiteSchema$1.nonnegative(),
|
|
349
|
-
duration: finiteSchema$1.positive(),
|
|
350
|
-
layer: z.enum([
|
|
351
|
-
"behind",
|
|
352
|
-
"content",
|
|
353
|
-
"front",
|
|
354
|
-
"screen"
|
|
355
|
-
]).default("content"),
|
|
356
|
-
fill: z.enum([
|
|
357
|
-
"none",
|
|
358
|
-
"backwards",
|
|
359
|
-
"forwards",
|
|
360
|
-
"both"
|
|
361
|
-
]).default("none"),
|
|
362
|
-
easing: BoardEasingSchema.default("linear"),
|
|
363
|
-
params: jsonObjectSchema$2.default({}),
|
|
364
|
-
assetRefs: z.array(z.object({
|
|
365
|
-
type: z.enum(["space-file", "extension"]),
|
|
366
|
-
ref: z.string().min(1).max(4096),
|
|
367
|
-
digest: z.string().min(16).max(160).optional()
|
|
368
|
-
}).strict()).default([]),
|
|
369
|
-
seed: idSchema$2,
|
|
370
|
-
metadata: jsonObjectSchema$2.default({})
|
|
371
|
-
}).strict();
|
|
372
|
-
const BoardTimelineMarkerSchema = z.object({
|
|
373
|
-
id: idSchema$2,
|
|
374
|
-
time: finiteSchema$1.nonnegative(),
|
|
375
|
-
duration: finiteSchema$1.nonnegative().optional(),
|
|
376
|
-
metadata: jsonObjectSchema$2.default({})
|
|
377
|
-
}).strict();
|
|
378
|
-
const BoardTimelineSchema = z.object({
|
|
379
|
-
duration: finiteSchema$1.nonnegative(),
|
|
380
|
-
tracks: z.array(BoardTrackSchema).max(5e4).default([]),
|
|
381
|
-
clips: z.array(BoardProceduralClipSchema).max(5e4).default([]),
|
|
382
|
-
markers: z.array(BoardTimelineMarkerSchema).max(1e4).default([])
|
|
383
|
-
}).strict().superRefine((timeline, context) => {
|
|
384
|
-
const ids = /* @__PURE__ */ new Set();
|
|
385
|
-
const channels = /* @__PURE__ */ new Set();
|
|
386
|
-
for (const [index, track] of timeline.tracks.entries()) {
|
|
387
|
-
if (ids.has(track.id)) context.addIssue({
|
|
388
|
-
code: "custom",
|
|
389
|
-
message: `duplicate timeline id: ${track.id}`,
|
|
390
|
-
path: [
|
|
391
|
-
"tracks",
|
|
392
|
-
index,
|
|
393
|
-
"id"
|
|
394
|
-
]
|
|
395
|
-
});
|
|
396
|
-
ids.add(track.id);
|
|
397
|
-
const targetId = track.target.type === "item" ? track.target.itemId : track.target.type === "effect" ? track.target.effectId : track.target.type;
|
|
398
|
-
const key = `${track.target.type}:${targetId}:${track.channel}`;
|
|
399
|
-
if (channels.has(key)) context.addIssue({
|
|
328
|
+
z.object({ events: z.array(displayInputEventSchema).min(1).max(512) }).superRefine(({ events }, ctx) => {
|
|
329
|
+
let last = 0;
|
|
330
|
+
events.forEach((event, index) => {
|
|
331
|
+
if (event.type === "scroll" && !event.dx && !event.dy) ctx.addIssue({
|
|
400
332
|
code: "custom",
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
"tracks",
|
|
404
|
-
index,
|
|
405
|
-
"channel"
|
|
406
|
-
]
|
|
333
|
+
path: ["events", index],
|
|
334
|
+
message: "scroll needs dx or dy"
|
|
407
335
|
});
|
|
408
|
-
|
|
409
|
-
for (const [keyframeIndex, keyframe] of track.keyframes.entries()) if (keyframe.time > timeline.duration) context.addIssue({
|
|
336
|
+
if (event.type === "element" && event.action === "setText" !== (event.text !== void 0)) ctx.addIssue({
|
|
410
337
|
code: "custom",
|
|
411
|
-
message: "keyframe exceeds timeline duration",
|
|
412
338
|
path: [
|
|
413
|
-
"
|
|
339
|
+
"events",
|
|
414
340
|
index,
|
|
415
|
-
"
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
]
|
|
341
|
+
"text"
|
|
342
|
+
],
|
|
343
|
+
message: "text goes with setText only"
|
|
419
344
|
});
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
if (ids.has(clip.id)) context.addIssue({
|
|
345
|
+
if (event.t === void 0) return;
|
|
346
|
+
if (event.t < last) ctx.addIssue({
|
|
423
347
|
code: "custom",
|
|
424
|
-
message: `duplicate timeline id: ${clip.id}`,
|
|
425
348
|
path: [
|
|
426
|
-
"
|
|
349
|
+
"events",
|
|
427
350
|
index,
|
|
428
|
-
"
|
|
429
|
-
]
|
|
351
|
+
"t"
|
|
352
|
+
],
|
|
353
|
+
message: "t must ascend"
|
|
430
354
|
});
|
|
431
|
-
|
|
432
|
-
if (clip.start + clip.duration > timeline.duration) context.addIssue({
|
|
433
|
-
code: "custom",
|
|
434
|
-
message: "clip exceeds timeline duration",
|
|
435
|
-
path: [
|
|
436
|
-
"clips",
|
|
437
|
-
index,
|
|
438
|
-
"duration"
|
|
439
|
-
]
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
for (const [index, marker] of timeline.markers.entries()) {
|
|
443
|
-
if (ids.has(marker.id)) context.addIssue({
|
|
444
|
-
code: "custom",
|
|
445
|
-
message: `duplicate timeline id: ${marker.id}`,
|
|
446
|
-
path: [
|
|
447
|
-
"markers",
|
|
448
|
-
index,
|
|
449
|
-
"id"
|
|
450
|
-
]
|
|
451
|
-
});
|
|
452
|
-
ids.add(marker.id);
|
|
453
|
-
if (marker.time + (marker.duration ?? 0) > timeline.duration) context.addIssue({
|
|
454
|
-
code: "custom",
|
|
455
|
-
message: "marker exceeds timeline duration",
|
|
456
|
-
path: ["markers", index]
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
});
|
|
460
|
-
const BoardCompositionPlaybackSchema = z.object({
|
|
461
|
-
loop: z.boolean().default(false),
|
|
462
|
-
endBehavior: z.enum(["hold", "reset"]).default("hold"),
|
|
463
|
-
reducedMotion: z.discriminatedUnion("mode", [
|
|
464
|
-
z.object({ mode: z.literal("base") }).strict(),
|
|
465
|
-
z.object({
|
|
466
|
-
mode: z.literal("time"),
|
|
467
|
-
time: finiteSchema$1.nonnegative()
|
|
468
|
-
}).strict(),
|
|
469
|
-
z.object({
|
|
470
|
-
mode: z.literal("marker"),
|
|
471
|
-
markerId: idSchema$2
|
|
472
|
-
}).strict()
|
|
473
|
-
]).default({ mode: "base" })
|
|
474
|
-
}).strict();
|
|
475
|
-
const compositionFields = {
|
|
476
|
-
id: idSchema$2,
|
|
477
|
-
name: z.string().min(1).max(255),
|
|
478
|
-
timeline: BoardTimelineSchema,
|
|
479
|
-
playback: BoardCompositionPlaybackSchema.default({
|
|
480
|
-
loop: false,
|
|
481
|
-
endBehavior: "hold",
|
|
482
|
-
reducedMotion: { mode: "base" }
|
|
483
|
-
}),
|
|
484
|
-
metadata: jsonObjectSchema$2.default({})
|
|
485
|
-
};
|
|
486
|
-
function validateCompositionFallback(composition, context) {
|
|
487
|
-
const fallback = composition.playback.reducedMotion;
|
|
488
|
-
if (fallback.mode === "time" && fallback.time > composition.timeline.duration) context.addIssue({
|
|
489
|
-
code: "custom",
|
|
490
|
-
message: "reduced-motion time exceeds timeline duration",
|
|
491
|
-
path: [
|
|
492
|
-
"playback",
|
|
493
|
-
"reducedMotion",
|
|
494
|
-
"time"
|
|
495
|
-
]
|
|
496
|
-
});
|
|
497
|
-
if (fallback.mode === "marker" && !composition.timeline.markers.some((marker) => marker.id === fallback.markerId)) context.addIssue({
|
|
498
|
-
code: "custom",
|
|
499
|
-
message: `marker does not exist: ${fallback.markerId}`,
|
|
500
|
-
path: [
|
|
501
|
-
"playback",
|
|
502
|
-
"reducedMotion",
|
|
503
|
-
"markerId"
|
|
504
|
-
]
|
|
355
|
+
last = event.t;
|
|
505
356
|
});
|
|
357
|
+
});
|
|
358
|
+
z.object({
|
|
359
|
+
format: z.enum(["jpeg", "png"]).optional(),
|
|
360
|
+
quality: z.number().int().min(1).max(100).optional(),
|
|
361
|
+
maxSize: z.number().int().min(1).max(DISPLAY_DIMENSION_MAX).optional()
|
|
362
|
+
});
|
|
363
|
+
z.object({
|
|
364
|
+
urls: z.union([z.string().min(1).max(512), z.array(z.string().min(1).max(512)).min(1).max(16)]),
|
|
365
|
+
username: z.string().max(512).optional(),
|
|
366
|
+
credential: z.string().max(512).optional()
|
|
367
|
+
});
|
|
368
|
+
z.object({
|
|
369
|
+
offer: z.string().min(1).max(65536),
|
|
370
|
+
control: z.boolean().optional()
|
|
371
|
+
});
|
|
372
|
+
const DISPLAY_CONTROL_CHANNEL = "control";
|
|
373
|
+
const DISPLAY_INPUT_CHANNEL = "input";
|
|
374
|
+
const DISPLAY_PING_INTERVAL_MS = 5e3;
|
|
375
|
+
function normalizeDisplayPoint(x, y, width, height) {
|
|
376
|
+
const unit = (value, extent) => Math.min(1, Math.max(0, extent > 1 ? value / (extent - 1) : 0));
|
|
377
|
+
return {
|
|
378
|
+
x: unit(x, width),
|
|
379
|
+
y: unit(y, height)
|
|
380
|
+
};
|
|
506
381
|
}
|
|
507
|
-
|
|
508
|
-
const
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
* `revision`. Anything else that looks server-owned (or simply unknown) fails
|
|
515
|
-
* with a pointer at the offending key, so a get → edit → apply round-trip that
|
|
516
|
-
* accidentally carries runtime fields names the culprit instead of a bare
|
|
517
|
-
* "Unrecognized key".
|
|
518
|
-
*/
|
|
519
|
-
function parseBoardCompositionInput(value) {
|
|
520
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
521
|
-
const { revision: _revision, ...input } = value;
|
|
522
|
-
const result = BoardCompositionInputSchema.safeParse(input);
|
|
523
|
-
if (!result.success) {
|
|
524
|
-
const issue = result.error.issues.find((item) => item.code === "unrecognized_keys");
|
|
525
|
-
if (issue) throw new Error(`unknown field in composition input: ${issue.message}. Server-owned fields such as revision are stripped automatically; remove other fields not part of the composition schema`);
|
|
526
|
-
throw result.error;
|
|
382
|
+
function splitDisplayText(text, max = DISPLAY_INPUT_MAX_TEXT) {
|
|
383
|
+
const pieces = [];
|
|
384
|
+
let piece = "";
|
|
385
|
+
for (const char of text) {
|
|
386
|
+
if (piece.length + char.length > max) {
|
|
387
|
+
pieces.push(piece);
|
|
388
|
+
piece = "";
|
|
527
389
|
}
|
|
528
|
-
|
|
390
|
+
piece += char;
|
|
529
391
|
}
|
|
530
|
-
|
|
392
|
+
if (piece) pieces.push(piece);
|
|
393
|
+
return pieces;
|
|
531
394
|
}
|
|
532
|
-
const BOARD_ANIMATION_CHANNEL_CAPABILITIES = Object.entries(BOARD_ANIMATION_CHANNELS).map(([id, definition]) => ({
|
|
533
|
-
id,
|
|
534
|
-
version: 1,
|
|
535
|
-
targets: definition.targets,
|
|
536
|
-
interpolations: definition.interpolations,
|
|
537
|
-
..."coordinateSpace" in definition ? { coordinateSpace: definition.coordinateSpace } : {},
|
|
538
|
-
..."unit" in definition ? { unit: definition.unit } : {},
|
|
539
|
-
valueSchema: z.toJSONSchema(definition.value)
|
|
540
|
-
}));
|
|
541
|
-
//#endregion
|
|
542
|
-
//#region ../protocol/dist/board-effect.js
|
|
543
|
-
/**
|
|
544
|
-
* Board effect schema, isolated so `board-authoring.ts` can reuse the exact
|
|
545
|
-
* definition without a runtime cycle through `board.ts`. One definition, one
|
|
546
|
-
* strictness level — semantic mutations and Board create must not drift apart.
|
|
547
|
-
*/
|
|
548
|
-
const idSchema$1 = z.string().min(1).max(160);
|
|
549
|
-
const extensionIdSchema = z.string().regex(/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)+$/).max(160);
|
|
550
|
-
const jsonObjectSchema$1 = z.record(z.string(), z.unknown());
|
|
551
|
-
const BoardAssetRefSchema = z.object({
|
|
552
|
-
type: z.enum(["space-file", "extension"]),
|
|
553
|
-
ref: z.string().min(1).max(4096),
|
|
554
|
-
digest: z.string().min(16).max(160).optional()
|
|
555
|
-
}).strict();
|
|
556
|
-
const BoardEffectSchema = z.object({
|
|
557
|
-
id: idSchema$1,
|
|
558
|
-
boardId: z.string().uuid(),
|
|
559
|
-
target: z.discriminatedUnion("type", [z.object({
|
|
560
|
-
type: z.literal("item"),
|
|
561
|
-
itemId: idSchema$1
|
|
562
|
-
}).strict(), z.object({ type: z.literal("board") }).strict()]),
|
|
563
|
-
kind: extensionIdSchema,
|
|
564
|
-
kindVersion: z.number().int().positive().default(1),
|
|
565
|
-
enabled: z.boolean().default(true),
|
|
566
|
-
lifecycle: z.enum([
|
|
567
|
-
"persistent",
|
|
568
|
-
"when-visible",
|
|
569
|
-
"manual",
|
|
570
|
-
"on-enter"
|
|
571
|
-
]),
|
|
572
|
-
timeOrigin: z.enum([
|
|
573
|
-
"board",
|
|
574
|
-
"visible",
|
|
575
|
-
"activation"
|
|
576
|
-
]),
|
|
577
|
-
layer: z.enum([
|
|
578
|
-
"behind",
|
|
579
|
-
"front",
|
|
580
|
-
"screen"
|
|
581
|
-
]).default("front"),
|
|
582
|
-
seed: z.string().min(1).max(160),
|
|
583
|
-
params: jsonObjectSchema$1.default({}),
|
|
584
|
-
assetRefs: z.array(BoardAssetRefSchema).default([]),
|
|
585
|
-
metadata: jsonObjectSchema$1.default({}),
|
|
586
|
-
revision: z.number().int().nonnegative()
|
|
587
|
-
}).strict();
|
|
588
|
-
/** The client-authored effect form: no server-owned `boardId`/`revision`. */
|
|
589
|
-
const BoardEffectInputSchema = BoardEffectSchema.omit({
|
|
590
|
-
boardId: true,
|
|
591
|
-
revision: true
|
|
592
|
-
});
|
|
593
|
-
/** Accept a read projection in a write command while stripping server fields. */
|
|
594
|
-
function parseBoardEffectInput(value) {
|
|
595
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
596
|
-
const { boardId: _boardId, revision: _revision, ...input } = value;
|
|
597
|
-
return BoardEffectInputSchema.parse(input);
|
|
598
|
-
}
|
|
599
|
-
return BoardEffectInputSchema.parse(value);
|
|
600
|
-
}
|
|
601
|
-
//#endregion
|
|
602
|
-
//#region ../protocol/dist/board-authoring.js
|
|
603
|
-
const BOARD_AUTHORING_COLOR_IDS = [
|
|
604
|
-
"brand",
|
|
605
|
-
"neutral",
|
|
606
|
-
"black",
|
|
607
|
-
"white",
|
|
608
|
-
"blue",
|
|
609
|
-
"green",
|
|
610
|
-
"amber",
|
|
611
|
-
"violet",
|
|
612
|
-
"rose"
|
|
613
|
-
];
|
|
614
|
-
const BOARD_AUTHORING_GEO_KINDS = [
|
|
615
|
-
"rectangle",
|
|
616
|
-
"rounded",
|
|
617
|
-
"ellipse",
|
|
618
|
-
"diamond",
|
|
619
|
-
"triangle"
|
|
620
|
-
];
|
|
621
|
-
const idSchema = z.string().min(1).max(160);
|
|
622
|
-
const jsonObjectSchema = z.record(z.string(), z.unknown());
|
|
623
|
-
const finiteSchema = z.number().finite();
|
|
624
|
-
const extensionTypeSchema = z.string().regex(/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)+$/).max(160);
|
|
625
|
-
const BoardAuthoringPositionSchema = z.object({
|
|
626
|
-
x: finiteSchema,
|
|
627
|
-
y: finiteSchema
|
|
628
|
-
}).strict();
|
|
629
|
-
const BoardAuthoringSizeSchema = z.object({
|
|
630
|
-
width: finiteSchema.positive(),
|
|
631
|
-
height: finiteSchema.positive()
|
|
632
|
-
}).strict();
|
|
633
|
-
const BoardAuthoringPositionPatchSchema = z.object({
|
|
634
|
-
x: finiteSchema.optional(),
|
|
635
|
-
y: finiteSchema.optional()
|
|
636
|
-
}).strict();
|
|
637
|
-
const pointSchema = z.object({
|
|
638
|
-
x: finiteSchema,
|
|
639
|
-
y: finiteSchema,
|
|
640
|
-
p: finiteSchema.min(0).max(1).default(.5)
|
|
641
|
-
}).strict();
|
|
642
|
-
const worldPointSchema = z.object({
|
|
643
|
-
x: finiteSchema,
|
|
644
|
-
y: finiteSchema
|
|
645
|
-
}).strict();
|
|
646
|
-
const cropSchema = z.object({
|
|
647
|
-
x: finiteSchema.min(0).max(1),
|
|
648
|
-
y: finiteSchema.min(0).max(1),
|
|
649
|
-
w: finiteSchema.min(0).max(1),
|
|
650
|
-
h: finiteSchema.min(0).max(1)
|
|
651
|
-
}).strict();
|
|
652
|
-
const BoardItemStyleSchema = z.object({
|
|
653
|
-
color: z.enum(BOARD_AUTHORING_COLOR_IDS).optional(),
|
|
654
|
-
strokeWidth: finiteSchema.min(1).max(64).optional(),
|
|
655
|
-
fillOpacity: finiteSchema.min(0).max(1).optional()
|
|
656
|
-
}).strict();
|
|
657
|
-
const sourceSnapshotSchema = z.record(z.string(), z.unknown());
|
|
658
|
-
function isSafeBoardSourcePath(value) {
|
|
659
|
-
return value.length > 0 && value.length <= 4096 && !value.startsWith("/") && !value.startsWith("\\") && !value.includes("\\") && value.split("/").every((part) => part.length > 0 && part !== "." && part !== "..");
|
|
660
|
-
}
|
|
661
|
-
const BoardItemSourceSchema = z.object({
|
|
662
|
-
kind: z.literal("space-file"),
|
|
663
|
-
path: z.string().refine(isSafeBoardSourcePath, "source path must be a safe relative Board file path"),
|
|
664
|
-
snapshot: sourceSnapshotSchema.optional()
|
|
665
|
-
}).strict();
|
|
666
|
-
const sizedBaseFields = {
|
|
667
|
-
id: idSchema,
|
|
668
|
-
position: BoardAuthoringPositionSchema.default({
|
|
669
|
-
x: 0,
|
|
670
|
-
y: 0
|
|
671
|
-
}),
|
|
672
|
-
rotation: finiteSchema.default(0),
|
|
673
|
-
parentId: idSchema.nullable().optional(),
|
|
674
|
-
locked: z.boolean().optional(),
|
|
675
|
-
metadata: jsonObjectSchema.optional(),
|
|
676
|
-
size: BoardAuthoringSizeSchema.optional()
|
|
677
|
-
};
|
|
678
|
-
const styledBaseFields = {
|
|
679
|
-
...sizedBaseFields,
|
|
680
|
-
style: BoardItemStyleSchema.optional()
|
|
681
|
-
};
|
|
682
|
-
const { position: _positionField, size: _sizeField, ...strokeBaseFields } = sizedBaseFields;
|
|
683
|
-
const strokeStyledBaseFields = {
|
|
684
|
-
...strokeBaseFields,
|
|
685
|
-
rotation: z.literal(0).default(0),
|
|
686
|
-
style: BoardItemStyleSchema.optional()
|
|
687
|
-
};
|
|
688
|
-
const BoardTextAuthoringItemSchema = z.object({
|
|
689
|
-
...styledBaseFields,
|
|
690
|
-
type: z.literal("text"),
|
|
691
|
-
props: z.object({
|
|
692
|
-
text: z.string().default(""),
|
|
693
|
-
fontSize: finiteSchema.min(2).max(512).default(24)
|
|
694
|
-
}).strict()
|
|
695
|
-
}).strict();
|
|
696
|
-
const BoardGeoAuthoringItemSchema = z.object({
|
|
697
|
-
...styledBaseFields,
|
|
698
|
-
type: z.literal("geo"),
|
|
699
|
-
props: z.object({
|
|
700
|
-
shape: z.enum(BOARD_AUTHORING_GEO_KINDS).default("rectangle"),
|
|
701
|
-
text: z.string().default("")
|
|
702
|
-
}).strict()
|
|
703
|
-
}).strict();
|
|
704
|
-
const BoardDrawAuthoringItemSchema = z.object({
|
|
705
|
-
...strokeStyledBaseFields,
|
|
706
|
-
type: z.literal("draw"),
|
|
707
|
-
props: z.object({ points: z.array(pointSchema).min(1) }).strict()
|
|
708
|
-
}).strict();
|
|
709
|
-
const BoardArrowAuthoringItemSchema = z.object({
|
|
710
|
-
...strokeStyledBaseFields,
|
|
711
|
-
type: z.literal("arrow"),
|
|
712
|
-
props: z.object({
|
|
713
|
-
start: worldPointSchema,
|
|
714
|
-
end: worldPointSchema,
|
|
715
|
-
bend: finiteSchema.min(-.85).max(.85).default(0),
|
|
716
|
-
arrowStart: z.boolean().default(false),
|
|
717
|
-
arrowEnd: z.boolean().default(true),
|
|
718
|
-
label: z.string().default("")
|
|
719
|
-
}).strict()
|
|
720
|
-
}).strict();
|
|
721
|
-
const BoardFrameAuthoringItemSchema = z.object({
|
|
722
|
-
...styledBaseFields,
|
|
723
|
-
type: z.literal("frame"),
|
|
724
|
-
props: z.object({ label: z.string().default("Frame") }).strict()
|
|
725
|
-
}).strict();
|
|
726
|
-
const fileBackedFields = {
|
|
727
|
-
...sizedBaseFields,
|
|
728
|
-
style: z.object({}).strict().optional(),
|
|
729
|
-
source: BoardItemSourceSchema
|
|
730
|
-
};
|
|
731
|
-
const builtinItemSchemas = [
|
|
732
|
-
BoardTextAuthoringItemSchema,
|
|
733
|
-
BoardGeoAuthoringItemSchema,
|
|
734
|
-
BoardDrawAuthoringItemSchema,
|
|
735
|
-
BoardArrowAuthoringItemSchema,
|
|
736
|
-
BoardFrameAuthoringItemSchema,
|
|
737
|
-
z.object({
|
|
738
|
-
...fileBackedFields,
|
|
739
|
-
type: z.literal("image"),
|
|
740
|
-
props: z.object({ crop: cropSchema.optional() }).strict()
|
|
741
|
-
}).strict(),
|
|
742
|
-
z.object({
|
|
743
|
-
...fileBackedFields,
|
|
744
|
-
type: z.literal("video"),
|
|
745
|
-
props: z.object({}).strict()
|
|
746
|
-
}).strict(),
|
|
747
|
-
z.object({
|
|
748
|
-
...fileBackedFields,
|
|
749
|
-
type: z.literal("audio"),
|
|
750
|
-
props: z.object({}).strict()
|
|
751
|
-
}).strict(),
|
|
752
|
-
z.object({
|
|
753
|
-
...fileBackedFields,
|
|
754
|
-
type: z.literal("file"),
|
|
755
|
-
props: z.object({}).strict()
|
|
756
|
-
}).strict(),
|
|
757
|
-
z.object({
|
|
758
|
-
...sizedBaseFields,
|
|
759
|
-
type: z.literal("task"),
|
|
760
|
-
props: z.object({
|
|
761
|
-
taskRunId: z.string().min(1),
|
|
762
|
-
snapshot: z.record(z.string(), z.unknown())
|
|
763
|
-
}).strict(),
|
|
764
|
-
style: z.object({}).strict().optional()
|
|
765
|
-
}).strict()
|
|
766
|
-
];
|
|
767
|
-
const BoardBuiltinAuthoringItemSchema = z.discriminatedUnion("type", builtinItemSchemas);
|
|
768
|
-
const BoardExtensionAuthoringItemSchema = z.object({
|
|
769
|
-
...sizedBaseFields,
|
|
770
|
-
type: extensionTypeSchema,
|
|
771
|
-
kindVersion: z.number().int().positive(),
|
|
772
|
-
props: jsonObjectSchema,
|
|
773
|
-
style: jsonObjectSchema.optional(),
|
|
774
|
-
source: z.object({
|
|
775
|
-
kind: z.string().min(1).max(80),
|
|
776
|
-
ref: z.string().min(1).max(4096),
|
|
777
|
-
snapshot: jsonObjectSchema.optional()
|
|
778
|
-
}).strict().optional()
|
|
779
|
-
}).strict();
|
|
780
|
-
const BoardAuthoringItemSchema = z.union([BoardBuiltinAuthoringItemSchema, BoardExtensionAuthoringItemSchema]);
|
|
781
|
-
/** JSON Merge Patch semantics, constrained to the stable Item envelope. */
|
|
782
|
-
const BoardItemPatchSchema = z.object({
|
|
783
|
-
position: BoardAuthoringPositionPatchSchema.optional(),
|
|
784
|
-
size: BoardAuthoringSizeSchema.nullable().optional(),
|
|
785
|
-
rotation: finiteSchema.optional(),
|
|
786
|
-
parentId: idSchema.nullable().optional(),
|
|
787
|
-
locked: z.boolean().nullable().optional(),
|
|
788
|
-
props: jsonObjectSchema.optional(),
|
|
789
|
-
style: jsonObjectSchema.nullable().optional(),
|
|
790
|
-
source: jsonObjectSchema.nullable().optional(),
|
|
791
|
-
metadata: jsonObjectSchema.nullable().optional()
|
|
792
|
-
}).strict().refine((patch) => Object.keys(patch).length > 0, "item patch is empty");
|
|
793
|
-
const BoardConnectionInputSchema = BoardConnectionSchema;
|
|
794
|
-
const stripServerFields = (value) => {
|
|
795
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return value;
|
|
796
|
-
const { boardId: _boardId, revision: _revision, ...input } = value;
|
|
797
|
-
return input;
|
|
798
|
-
};
|
|
799
|
-
const BoardEffectCommandInputSchema = z.preprocess(stripServerFields, BoardEffectInputSchema);
|
|
800
|
-
const BoardCompositionCommandInputSchema = z.preprocess(stripServerFields, BoardCompositionInputSchema);
|
|
801
|
-
const BoardSemanticCommandSchema = z.discriminatedUnion("type", [
|
|
802
|
-
z.object({
|
|
803
|
-
type: z.literal("board.patch"),
|
|
804
|
-
patch: z.object({
|
|
805
|
-
title: z.string().min(1).max(255).optional(),
|
|
806
|
-
metadata: jsonObjectSchema.nullable().optional(),
|
|
807
|
-
metadataPatch: jsonObjectSchema.optional()
|
|
808
|
-
}).strict().refine((patch) => Object.keys(patch).length > 0, "board patch is empty")
|
|
809
|
-
}).strict(),
|
|
810
|
-
z.object({
|
|
811
|
-
type: z.literal("item.create"),
|
|
812
|
-
item: BoardAuthoringItemSchema
|
|
813
|
-
}).strict(),
|
|
814
|
-
z.object({
|
|
815
|
-
type: z.literal("item.patch"),
|
|
816
|
-
itemId: idSchema,
|
|
817
|
-
patch: BoardItemPatchSchema
|
|
818
|
-
}).strict(),
|
|
819
|
-
z.object({
|
|
820
|
-
type: z.literal("item.replace"),
|
|
821
|
-
itemId: idSchema,
|
|
822
|
-
item: BoardAuthoringItemSchema
|
|
823
|
-
}).strict(),
|
|
824
|
-
z.object({
|
|
825
|
-
type: z.literal("item.delete"),
|
|
826
|
-
itemId: idSchema,
|
|
827
|
-
cascade: z.boolean().default(false)
|
|
828
|
-
}).strict(),
|
|
829
|
-
z.object({
|
|
830
|
-
type: z.literal("item.reorder"),
|
|
831
|
-
itemId: idSchema,
|
|
832
|
-
index: z.number().int().nonnegative()
|
|
833
|
-
}).strict(),
|
|
834
|
-
z.object({
|
|
835
|
-
type: z.literal("connection.create"),
|
|
836
|
-
connection: BoardConnectionInputSchema
|
|
837
|
-
}).strict(),
|
|
838
|
-
z.object({
|
|
839
|
-
type: z.literal("connection.patch"),
|
|
840
|
-
connectionId: idSchema,
|
|
841
|
-
patch: BoardConnectionPatchSchema
|
|
842
|
-
}).strict(),
|
|
843
|
-
z.object({
|
|
844
|
-
type: z.literal("connection.delete"),
|
|
845
|
-
connectionId: idSchema
|
|
846
|
-
}).strict(),
|
|
847
|
-
z.object({
|
|
848
|
-
type: z.literal("effect.apply"),
|
|
849
|
-
effect: BoardEffectCommandInputSchema
|
|
850
|
-
}).strict(),
|
|
851
|
-
z.object({
|
|
852
|
-
type: z.literal("effect.delete"),
|
|
853
|
-
effectId: idSchema
|
|
854
|
-
}).strict(),
|
|
855
|
-
z.object({
|
|
856
|
-
type: z.literal("composition.apply"),
|
|
857
|
-
composition: BoardCompositionCommandInputSchema
|
|
858
|
-
}).strict(),
|
|
859
|
-
z.object({
|
|
860
|
-
type: z.literal("composition.delete"),
|
|
861
|
-
compositionId: idSchema
|
|
862
|
-
}).strict()
|
|
863
|
-
]);
|
|
864
|
-
const BoardSemanticMutationSchema = z.object({
|
|
865
|
-
mutationId: idSchema,
|
|
866
|
-
baseVersion: z.number().int().nonnegative(),
|
|
867
|
-
clientId: idSchema.optional(),
|
|
868
|
-
undoGroupId: idSchema.optional(),
|
|
869
|
-
/** Validate (including server-side reference checks) without writing. */
|
|
870
|
-
dryRun: z.boolean().default(false),
|
|
871
|
-
commands: z.array(BoardSemanticCommandSchema).min(1).max(5e4)
|
|
872
|
-
}).strict();
|
|
873
|
-
z.object({
|
|
874
|
-
include: z.array(z.enum([
|
|
875
|
-
"items",
|
|
876
|
-
"connections",
|
|
877
|
-
"effects",
|
|
878
|
-
"compositions",
|
|
879
|
-
"playback"
|
|
880
|
-
])).optional(),
|
|
881
|
-
itemIds: z.array(idSchema).max(5e4).optional(),
|
|
882
|
-
connectionIds: z.array(idSchema).max(5e4).optional(),
|
|
883
|
-
effectIds: z.array(idSchema).max(5e4).optional(),
|
|
884
|
-
compositionIds: z.array(idSchema).max(5e4).optional(),
|
|
885
|
-
viewport: z.object({
|
|
886
|
-
x: finiteSchema,
|
|
887
|
-
y: finiteSchema,
|
|
888
|
-
width: finiteSchema.positive(),
|
|
889
|
-
height: finiteSchema.positive()
|
|
890
|
-
}).optional()
|
|
891
|
-
}).strict();
|
|
892
395
|
//#endregion
|
|
893
396
|
//#region ../protocol/dist/space-hooks.js
|
|
894
397
|
const SPACE_HOOK_WEBHOOK_SECRET_HEADER = "x-cohub-webhook-secret";
|
|
@@ -912,6 +415,9 @@ const DESKTOP_COMMAND_VERSION = 1;
|
|
|
912
415
|
const DESKTOP_COMMAND_PAYLOAD_MAX_BYTES = 32768;
|
|
913
416
|
const DESKTOP_COMMAND_MAX_BYTES = 40960;
|
|
914
417
|
const DESKTOP_COMMAND_LAUNCH_MAX_LENGTH = NAVIGATION_LAUNCH_MAX_LENGTH;
|
|
418
|
+
/** A command nobody accepts within this window settles as `no_active_client`. */
|
|
419
|
+
const DESKTOP_COMMAND_ACCEPT_TIMEOUT_MS = 1e4;
|
|
420
|
+
const DESKTOP_COMMAND_OPEN_TIMEOUT_MS = 3e4;
|
|
915
421
|
const DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS = 6e5;
|
|
916
422
|
const DESKTOP_COMMAND_MAX_TIMEOUT_MS = 432e5;
|
|
917
423
|
const DESKTOP_COMMAND_SETTLEMENT_GRACE_SECONDS = 600;
|
|
@@ -935,6 +441,7 @@ const isTerminalDesktopCommandStatus = (status) => DESKTOP_COMMAND_TERMINAL_STAT
|
|
|
935
441
|
function resolveOpenSurface(requested, declared) {
|
|
936
442
|
return (requested ?? declared) === "overlay" ? "overlay" : void 0;
|
|
937
443
|
}
|
|
444
|
+
const defaultDesktopCommandTimeoutMs = (command) => command.call ? DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS : DESKTOP_COMMAND_OPEN_TIMEOUT_MS;
|
|
938
445
|
const METHOD_RE = new RegExp(`^[A-Za-z][A-Za-z0-9_.:-]{0,63}$`);
|
|
939
446
|
const isDesktopCallMethod = (value) => typeof value === "string" && METHOD_RE.test(value);
|
|
940
447
|
const DESKTOP_COMMAND_ID_RE = /^[A-Za-z0-9_-]{1,64}$/;
|
|
@@ -1468,14 +975,16 @@ var SearchApi = class {
|
|
|
1468
975
|
for (const type of input.types ?? []) params.append("type", type);
|
|
1469
976
|
if (input.spaceId) params.set("spaceId", input.spaceId);
|
|
1470
977
|
if (input.labelRef) params.set("labelRef", input.labelRef);
|
|
1471
|
-
if (input.groupTurns === false) params.set("groupTurns", "false");
|
|
1472
978
|
return this.transport.request(`/api/search?${params.toString()}`, { fetch: customFetch });
|
|
1473
979
|
}
|
|
1474
980
|
/** Default (empty-query) command palette data: viewer-relative space signals. */
|
|
1475
981
|
overview(input, customFetch) {
|
|
1476
982
|
const params = new URLSearchParams();
|
|
1477
983
|
if (input?.spaceLimit !== void 0) params.set("spaceLimit", String(input.spaceLimit));
|
|
1478
|
-
|
|
984
|
+
for (const recent of input?.recentSpaces ?? []) {
|
|
985
|
+
params.append("recentSpaceId", recent.id);
|
|
986
|
+
params.append("recentSpaceAt", new Date(recent.timestamp).toISOString());
|
|
987
|
+
}
|
|
1479
988
|
for (const spaceId of input?.recentSpaceIds ?? []) params.append("recentSpaceId", spaceId);
|
|
1480
989
|
const query = params.toString();
|
|
1481
990
|
return this.transport.request(`/api/palette/overview${query ? `?${query}` : ""}`, { fetch: customFetch });
|
|
@@ -1542,6 +1051,354 @@ function ensureRealtimeConnected(websocketClient) {
|
|
|
1542
1051
|
});
|
|
1543
1052
|
}
|
|
1544
1053
|
//#endregion
|
|
1054
|
+
//#region src/display-stats.ts
|
|
1055
|
+
const num = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1056
|
+
function perFrame(seconds, previousSeconds, frames, previousFrames) {
|
|
1057
|
+
const count = frames - previousFrames;
|
|
1058
|
+
return count > 0 ? Math.round((seconds - previousSeconds) * 1e3 / count) : null;
|
|
1059
|
+
}
|
|
1060
|
+
function readDisplayStats(report, previous, fallbackRttMs = null) {
|
|
1061
|
+
const stats = {
|
|
1062
|
+
path: null,
|
|
1063
|
+
relayProtocol: null,
|
|
1064
|
+
rttMs: fallbackRttMs,
|
|
1065
|
+
fps: null,
|
|
1066
|
+
bitrate: null,
|
|
1067
|
+
width: null,
|
|
1068
|
+
height: null,
|
|
1069
|
+
jitterBufferMs: null,
|
|
1070
|
+
decodeMs: null,
|
|
1071
|
+
packetsLost: null,
|
|
1072
|
+
freezes: null
|
|
1073
|
+
};
|
|
1074
|
+
let counters = null;
|
|
1075
|
+
report.forEach((entry) => {
|
|
1076
|
+
if (entry.type === "candidate-pair" && entry.nominated && entry.state === "succeeded") {
|
|
1077
|
+
const local = report.get(String(entry.localCandidateId));
|
|
1078
|
+
const remote = report.get(String(entry.remoteCandidateId));
|
|
1079
|
+
stats.path = local?.candidateType === "relay" || remote?.candidateType === "relay" ? "relay" : "direct";
|
|
1080
|
+
if (local?.candidateType === "relay" && typeof local.relayProtocol === "string") stats.relayProtocol = local.relayProtocol;
|
|
1081
|
+
const rtt = num(entry.currentRoundTripTime);
|
|
1082
|
+
if (rtt !== null) stats.rttMs = Math.round(rtt * 1e3);
|
|
1083
|
+
}
|
|
1084
|
+
if (entry.type === "inbound-rtp" && entry.kind === "video") {
|
|
1085
|
+
stats.fps = num(entry.framesPerSecond);
|
|
1086
|
+
stats.width = num(entry.frameWidth);
|
|
1087
|
+
stats.height = num(entry.frameHeight);
|
|
1088
|
+
stats.packetsLost = num(entry.packetsLost);
|
|
1089
|
+
stats.freezes = num(entry.freezeCount);
|
|
1090
|
+
counters = {
|
|
1091
|
+
at: num(entry.timestamp) ?? 0,
|
|
1092
|
+
bytes: num(entry.bytesReceived) ?? 0,
|
|
1093
|
+
jitterBufferDelay: num(entry.jitterBufferDelay) ?? 0,
|
|
1094
|
+
jitterBufferEmitted: num(entry.jitterBufferEmittedCount) ?? 0,
|
|
1095
|
+
decodeTime: num(entry.totalDecodeTime) ?? 0,
|
|
1096
|
+
decoded: num(entry.framesDecoded) ?? 0
|
|
1097
|
+
};
|
|
1098
|
+
if (previous && counters.at > previous.at) {
|
|
1099
|
+
stats.bitrate = Math.round((counters.bytes - previous.bytes) * 8 * 1e3 / (counters.at - previous.at));
|
|
1100
|
+
stats.jitterBufferMs = perFrame(counters.jitterBufferDelay, previous.jitterBufferDelay, counters.jitterBufferEmitted, previous.jitterBufferEmitted);
|
|
1101
|
+
stats.decodeMs = perFrame(counters.decodeTime, previous.decodeTime, counters.decoded, previous.decoded);
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
});
|
|
1105
|
+
return {
|
|
1106
|
+
stats,
|
|
1107
|
+
counters
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
//#endregion
|
|
1111
|
+
//#region src/display-session.ts
|
|
1112
|
+
const DEFAULT_GATHER_TIMEOUT_MS = 2e3;
|
|
1113
|
+
const DEFAULT_CONNECT_TIMEOUT_MS = 2e4;
|
|
1114
|
+
const INPUT_BACKLOG_BYTES = 65536;
|
|
1115
|
+
var DisplayConnection = class DisplayConnection {
|
|
1116
|
+
api;
|
|
1117
|
+
displayId;
|
|
1118
|
+
control;
|
|
1119
|
+
pc;
|
|
1120
|
+
stream = new MediaStream();
|
|
1121
|
+
currentState = "connecting";
|
|
1122
|
+
currentDisplay = null;
|
|
1123
|
+
reason = null;
|
|
1124
|
+
sessionId = null;
|
|
1125
|
+
listeners = /* @__PURE__ */ new Map();
|
|
1126
|
+
controlChannel;
|
|
1127
|
+
inputChannel;
|
|
1128
|
+
pingTimer = null;
|
|
1129
|
+
connectTimer = null;
|
|
1130
|
+
pingId = 0;
|
|
1131
|
+
lastPingAt = 0;
|
|
1132
|
+
rtt = null;
|
|
1133
|
+
counters = null;
|
|
1134
|
+
isPaused = false;
|
|
1135
|
+
constructor(api, displayId, control, pc) {
|
|
1136
|
+
this.api = api;
|
|
1137
|
+
this.displayId = displayId;
|
|
1138
|
+
this.control = control;
|
|
1139
|
+
this.pc = pc;
|
|
1140
|
+
pc.addTransceiver("video", { direction: "recvonly" });
|
|
1141
|
+
pc.ontrack = (event) => {
|
|
1142
|
+
const receiver = event.receiver;
|
|
1143
|
+
if ("jitterBufferTarget" in receiver) receiver.jitterBufferTarget = 0;
|
|
1144
|
+
this.stream.addTrack(event.track);
|
|
1145
|
+
};
|
|
1146
|
+
pc.onconnectionstatechange = () => {
|
|
1147
|
+
if (pc.connectionState === "connected") this.setState("connected");
|
|
1148
|
+
else if (pc.connectionState === "failed") this.close("failed");
|
|
1149
|
+
};
|
|
1150
|
+
this.controlChannel = pc.createDataChannel(DISPLAY_CONTROL_CHANNEL);
|
|
1151
|
+
this.controlChannel.onmessage = (event) => this.onControl(event.data);
|
|
1152
|
+
this.controlChannel.onopen = () => {
|
|
1153
|
+
if (this.isPaused) this.sendControl({ type: "pause" });
|
|
1154
|
+
this.startPings();
|
|
1155
|
+
};
|
|
1156
|
+
this.controlChannel.onclose = () => this.close(this.reason ?? "closed");
|
|
1157
|
+
this.inputChannel = control ? pc.createDataChannel(DISPLAY_INPUT_CHANNEL) : null;
|
|
1158
|
+
}
|
|
1159
|
+
static async open(api, displayId, options = {}) {
|
|
1160
|
+
const { iceServers } = await api.iceServers({ signal: options.signal });
|
|
1161
|
+
const pc = new RTCPeerConnection({
|
|
1162
|
+
iceServers,
|
|
1163
|
+
bundlePolicy: "max-bundle"
|
|
1164
|
+
});
|
|
1165
|
+
const connection = new DisplayConnection(api, displayId, options.control ?? true, pc);
|
|
1166
|
+
try {
|
|
1167
|
+
await pc.setLocalDescription(await pc.createOffer());
|
|
1168
|
+
await gathered(pc, options.gatherTimeoutMs ?? DEFAULT_GATHER_TIMEOUT_MS, options.signal);
|
|
1169
|
+
const offer = pc.localDescription?.sdp;
|
|
1170
|
+
if (!offer) throw new Error("The browser produced no offer");
|
|
1171
|
+
const session = await api.openSession(displayId, {
|
|
1172
|
+
offer,
|
|
1173
|
+
control: connection.control
|
|
1174
|
+
}, { signal: options.signal });
|
|
1175
|
+
connection.sessionId = session.sessionId;
|
|
1176
|
+
await pc.setRemoteDescription({
|
|
1177
|
+
type: "answer",
|
|
1178
|
+
sdp: session.answer
|
|
1179
|
+
});
|
|
1180
|
+
connection.connectTimer = setTimeout(() => connection.close("connect_timeout"), options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS);
|
|
1181
|
+
return connection;
|
|
1182
|
+
} catch (error) {
|
|
1183
|
+
connection.close("failed");
|
|
1184
|
+
throw error;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
get state() {
|
|
1188
|
+
return this.currentState;
|
|
1189
|
+
}
|
|
1190
|
+
get display() {
|
|
1191
|
+
return this.currentDisplay;
|
|
1192
|
+
}
|
|
1193
|
+
get closeReason() {
|
|
1194
|
+
return this.currentState === "closed" ? this.reason : null;
|
|
1195
|
+
}
|
|
1196
|
+
on(event, listener) {
|
|
1197
|
+
let set = this.listeners.get(event);
|
|
1198
|
+
if (!set) {
|
|
1199
|
+
set = /* @__PURE__ */ new Set();
|
|
1200
|
+
this.listeners.set(event, set);
|
|
1201
|
+
}
|
|
1202
|
+
set.add(listener);
|
|
1203
|
+
return () => set.delete(listener);
|
|
1204
|
+
}
|
|
1205
|
+
emit(event, value) {
|
|
1206
|
+
for (const listener of this.listeners.get(event) ?? []) listener(value);
|
|
1207
|
+
}
|
|
1208
|
+
setState(state) {
|
|
1209
|
+
if (this.currentState === state || this.currentState === "closed") return;
|
|
1210
|
+
this.currentState = state;
|
|
1211
|
+
if (state === "connected" && this.connectTimer) {
|
|
1212
|
+
clearTimeout(this.connectTimer);
|
|
1213
|
+
this.connectTimer = null;
|
|
1214
|
+
}
|
|
1215
|
+
this.emit("state", state);
|
|
1216
|
+
}
|
|
1217
|
+
sendInput(events) {
|
|
1218
|
+
const channel = this.inputChannel;
|
|
1219
|
+
if (channel?.readyState !== "open" || events.length === 0) return false;
|
|
1220
|
+
if (channel.bufferedAmount > INPUT_BACKLOG_BYTES && events.every((event) => event.type === "pointer" && event.action === "move")) return false;
|
|
1221
|
+
channel.send(JSON.stringify({ events }));
|
|
1222
|
+
return true;
|
|
1223
|
+
}
|
|
1224
|
+
requestKeyframe() {
|
|
1225
|
+
this.sendControl({ type: "keyframe" });
|
|
1226
|
+
}
|
|
1227
|
+
get paused() {
|
|
1228
|
+
return this.isPaused;
|
|
1229
|
+
}
|
|
1230
|
+
pause() {
|
|
1231
|
+
if (this.isPaused) return;
|
|
1232
|
+
this.isPaused = true;
|
|
1233
|
+
this.sendControl({ type: "pause" });
|
|
1234
|
+
}
|
|
1235
|
+
resume() {
|
|
1236
|
+
if (!this.isPaused) return;
|
|
1237
|
+
this.isPaused = false;
|
|
1238
|
+
this.counters = null;
|
|
1239
|
+
this.sendControl({ type: "resume" });
|
|
1240
|
+
}
|
|
1241
|
+
async stats() {
|
|
1242
|
+
const { stats, counters } = readDisplayStats(await this.pc.getStats(), this.counters, this.rtt);
|
|
1243
|
+
this.counters = counters;
|
|
1244
|
+
return stats;
|
|
1245
|
+
}
|
|
1246
|
+
close(reason = "closed") {
|
|
1247
|
+
if (this.currentState === "closed") return;
|
|
1248
|
+
this.reason = reason;
|
|
1249
|
+
if (this.pingTimer) clearInterval(this.pingTimer);
|
|
1250
|
+
if (this.connectTimer) clearTimeout(this.connectTimer);
|
|
1251
|
+
if (this.sessionId) this.api.closeSession(this.displayId, this.sessionId).catch(() => void 0);
|
|
1252
|
+
this.sessionId = null;
|
|
1253
|
+
for (const track of this.stream.getTracks()) track.stop();
|
|
1254
|
+
this.pc.close();
|
|
1255
|
+
this.currentState = "closed";
|
|
1256
|
+
this.emit("state", "closed");
|
|
1257
|
+
}
|
|
1258
|
+
startPings() {
|
|
1259
|
+
const ping = () => {
|
|
1260
|
+
this.pingId += 1;
|
|
1261
|
+
this.lastPingAt = performance.now();
|
|
1262
|
+
this.sendControl({
|
|
1263
|
+
type: "ping",
|
|
1264
|
+
id: this.pingId
|
|
1265
|
+
});
|
|
1266
|
+
};
|
|
1267
|
+
ping();
|
|
1268
|
+
this.pingTimer = setInterval(ping, DISPLAY_PING_INTERVAL_MS);
|
|
1269
|
+
}
|
|
1270
|
+
sendControl(message) {
|
|
1271
|
+
if (this.controlChannel.readyState === "open") this.controlChannel.send(JSON.stringify(message));
|
|
1272
|
+
}
|
|
1273
|
+
onControl(data) {
|
|
1274
|
+
if (typeof data !== "string") return;
|
|
1275
|
+
let message;
|
|
1276
|
+
try {
|
|
1277
|
+
message = JSON.parse(data);
|
|
1278
|
+
} catch {
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
switch (message.type) {
|
|
1282
|
+
case "ready":
|
|
1283
|
+
case "display":
|
|
1284
|
+
this.currentDisplay = message.display;
|
|
1285
|
+
this.emit("display", message.display);
|
|
1286
|
+
break;
|
|
1287
|
+
case "pong":
|
|
1288
|
+
if (message.id === this.pingId) this.rtt = Math.round(performance.now() - this.lastPingAt);
|
|
1289
|
+
break;
|
|
1290
|
+
case "error":
|
|
1291
|
+
this.emit("error", {
|
|
1292
|
+
code: message.code,
|
|
1293
|
+
message: message.message
|
|
1294
|
+
});
|
|
1295
|
+
break;
|
|
1296
|
+
case "closed":
|
|
1297
|
+
this.sessionId = null;
|
|
1298
|
+
this.close(message.reason);
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
};
|
|
1302
|
+
function gathered(pc, timeoutMs, signal) {
|
|
1303
|
+
if (pc.iceGatheringState === "complete") return Promise.resolve();
|
|
1304
|
+
return new Promise((resolve, reject) => {
|
|
1305
|
+
const done = () => {
|
|
1306
|
+
clearTimeout(timer);
|
|
1307
|
+
pc.removeEventListener("icegatheringstatechange", check);
|
|
1308
|
+
signal?.removeEventListener("abort", abort);
|
|
1309
|
+
resolve();
|
|
1310
|
+
};
|
|
1311
|
+
const check = () => {
|
|
1312
|
+
if (pc.iceGatheringState === "complete") done();
|
|
1313
|
+
};
|
|
1314
|
+
const abort = () => {
|
|
1315
|
+
clearTimeout(timer);
|
|
1316
|
+
pc.removeEventListener("icegatheringstatechange", check);
|
|
1317
|
+
reject(signal?.reason ?? new DOMException("Aborted", "AbortError"));
|
|
1318
|
+
};
|
|
1319
|
+
const timer = setTimeout(done, timeoutMs);
|
|
1320
|
+
pc.addEventListener("icegatheringstatechange", check);
|
|
1321
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
1322
|
+
});
|
|
1323
|
+
}
|
|
1324
|
+
function connectDisplay(api, displayId, options) {
|
|
1325
|
+
return DisplayConnection.open(api, displayId, options);
|
|
1326
|
+
}
|
|
1327
|
+
//#endregion
|
|
1328
|
+
//#region src/apis/displays.ts
|
|
1329
|
+
const ICE_REFRESH_MARGIN_MS = 3e5;
|
|
1330
|
+
var SpaceDisplaysApi = class {
|
|
1331
|
+
transport;
|
|
1332
|
+
spaceId;
|
|
1333
|
+
ice = null;
|
|
1334
|
+
constructor(transport, spaceId) {
|
|
1335
|
+
this.transport = transport;
|
|
1336
|
+
this.spaceId = spaceId;
|
|
1337
|
+
}
|
|
1338
|
+
path(displayId, suffix = "") {
|
|
1339
|
+
return `/api/spaces/${this.spaceId}/displays/${encodeURIComponent(displayId)}${suffix}`;
|
|
1340
|
+
}
|
|
1341
|
+
list(options = {}) {
|
|
1342
|
+
return this.transport.request(`/api/spaces/${this.spaceId}/displays`, { signal: options.signal });
|
|
1343
|
+
}
|
|
1344
|
+
startVirtual(params = {}) {
|
|
1345
|
+
return this.transport.request(`/api/spaces/${this.spaceId}/displays/virtual`, {
|
|
1346
|
+
method: "POST",
|
|
1347
|
+
headers: { "Content-Type": "application/json" },
|
|
1348
|
+
body: JSON.stringify(params)
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
stopVirtual() {
|
|
1352
|
+
return this.transport.request(`/api/spaces/${this.spaceId}/displays/virtual`, { method: "DELETE" });
|
|
1353
|
+
}
|
|
1354
|
+
tree(displayId, params = {}, options = {}) {
|
|
1355
|
+
const query = new URLSearchParams();
|
|
1356
|
+
if (params.maxElements) query.set("maxElements", String(params.maxElements));
|
|
1357
|
+
if (params.actionable) query.set("actionable", "true");
|
|
1358
|
+
const search = query.size ? `?${query}` : "";
|
|
1359
|
+
return this.transport.request(this.path(displayId, `/tree${search}`), { signal: options.signal });
|
|
1360
|
+
}
|
|
1361
|
+
capture(displayId, params = {}, options = {}) {
|
|
1362
|
+
const query = new URLSearchParams();
|
|
1363
|
+
if (params.format) query.set("format", params.format);
|
|
1364
|
+
if (params.quality) query.set("quality", String(params.quality));
|
|
1365
|
+
if (params.maxSize) query.set("maxSize", String(params.maxSize));
|
|
1366
|
+
const search = query.size ? `?${query}` : "";
|
|
1367
|
+
return this.transport.request(this.path(displayId, `/capture${search}`), { signal: options.signal });
|
|
1368
|
+
}
|
|
1369
|
+
input(displayId, events, options = {}) {
|
|
1370
|
+
return this.transport.request(this.path(displayId, "/input"), {
|
|
1371
|
+
method: "POST",
|
|
1372
|
+
headers: { "Content-Type": "application/json" },
|
|
1373
|
+
body: JSON.stringify({ events }),
|
|
1374
|
+
signal: options.signal
|
|
1375
|
+
});
|
|
1376
|
+
}
|
|
1377
|
+
async iceServers(options = {}) {
|
|
1378
|
+
if (this.ice && this.ice.refreshAt > Date.now()) return this.ice.value;
|
|
1379
|
+
const value = await this.transport.request(`/api/spaces/${this.spaceId}/rtc/ice-servers`, { signal: options.signal });
|
|
1380
|
+
this.ice = {
|
|
1381
|
+
value,
|
|
1382
|
+
refreshAt: Date.parse(value.expiresAt) - ICE_REFRESH_MARGIN_MS
|
|
1383
|
+
};
|
|
1384
|
+
return value;
|
|
1385
|
+
}
|
|
1386
|
+
connect(displayId, options) {
|
|
1387
|
+
return connectDisplay(this, displayId, options);
|
|
1388
|
+
}
|
|
1389
|
+
openSession(displayId, body, options = {}) {
|
|
1390
|
+
return this.transport.request(this.path(displayId, "/sessions"), {
|
|
1391
|
+
method: "POST",
|
|
1392
|
+
headers: { "Content-Type": "application/json" },
|
|
1393
|
+
body: JSON.stringify(body),
|
|
1394
|
+
signal: options.signal
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
closeSession(displayId, sessionId) {
|
|
1398
|
+
return this.transport.request(this.path(displayId, `/sessions/${sessionId}`), { method: "DELETE" });
|
|
1399
|
+
}
|
|
1400
|
+
};
|
|
1401
|
+
//#endregion
|
|
1545
1402
|
//#region src/session-patch-reducer.ts
|
|
1546
1403
|
const blockSubPathPattern = /^\/message\/content\/blocks\/(\d+)\/(.+)$/;
|
|
1547
1404
|
const blockPathPattern = /^\/message\/content\/blocks\/(\d+)$/;
|
|
@@ -2161,6 +2018,9 @@ var SessionGenerationStreamClient = class SessionGenerationStreamClient {
|
|
|
2161
2018
|
messageOrdinal = null;
|
|
2162
2019
|
intermediateMessages = [];
|
|
2163
2020
|
patchState = null;
|
|
2021
|
+
recovery = null;
|
|
2022
|
+
resyncedMessageKey = null;
|
|
2023
|
+
disposed = false;
|
|
2164
2024
|
constructor(websocketClient, spaceId, sessionId, fetchStreamSnapshot) {
|
|
2165
2025
|
this.websocketClient = websocketClient;
|
|
2166
2026
|
this.spaceId = spaceId;
|
|
@@ -2171,48 +2031,80 @@ var SessionGenerationStreamClient = class SessionGenerationStreamClient {
|
|
|
2171
2031
|
if (!this.websocketClient) throw new Error("realtime transport is not configured for this client");
|
|
2172
2032
|
ensureRealtimeConnected(this.websocketClient);
|
|
2173
2033
|
const stream = new SessionGenerationStreamClient(this.websocketClient, this.spaceId, this.sessionId, this.fetchStreamSnapshot);
|
|
2174
|
-
const shouldRecover = options.recover === true || options.initialSnapshot !== void 0;
|
|
2175
|
-
let recovering = shouldRecover;
|
|
2176
|
-
let disposed = false;
|
|
2177
|
-
let recoveryAborted = false;
|
|
2178
|
-
let bufferedEvents = [];
|
|
2179
|
-
let bufferedEventsReplayed = false;
|
|
2180
2034
|
const releaseRoom = this.websocketClient.retainRooms([getRealtimeSpaceRoom(this.spaceId)]);
|
|
2181
2035
|
const unsubscribe = this.websocketClient.on("event", (event) => {
|
|
2182
2036
|
if (event.spaceId !== this.spaceId || event.sessionId !== this.sessionId) return;
|
|
2183
|
-
|
|
2184
|
-
bufferedEvents.push(event);
|
|
2185
|
-
if (bufferedEvents.length > SNAPSHOT_RECOVERY_MAX_BUFFERED_EVENTS) {
|
|
2186
|
-
recoveryAborted = true;
|
|
2187
|
-
recovering = false;
|
|
2188
|
-
stream.replayBufferedEvents(bufferedEvents, handlers);
|
|
2189
|
-
bufferedEvents = [];
|
|
2190
|
-
bufferedEventsReplayed = true;
|
|
2191
|
-
}
|
|
2192
|
-
return;
|
|
2193
|
-
}
|
|
2194
|
-
stream.handleEvent(event, handlers);
|
|
2195
|
-
});
|
|
2196
|
-
if (shouldRecover) stream.recoverFromSnapshot(handlers, options, () => !recoveryAborted && !disposed).finally(() => {
|
|
2197
|
-
if (disposed || bufferedEventsReplayed) return;
|
|
2198
|
-
recovering = false;
|
|
2199
|
-
stream.replayBufferedEvents(bufferedEvents, handlers);
|
|
2200
|
-
bufferedEventsReplayed = true;
|
|
2037
|
+
stream.receive(event, handlers);
|
|
2201
2038
|
});
|
|
2039
|
+
const { initialSnapshot } = options;
|
|
2040
|
+
if (initialSnapshot !== void 0) stream.recover(handlers, async () => initialSnapshot, options);
|
|
2041
|
+
else if (options.recover === true) stream.recover(handlers, () => stream.loadSnapshot(), options);
|
|
2202
2042
|
return () => {
|
|
2203
|
-
|
|
2043
|
+
stream.dispose();
|
|
2204
2044
|
unsubscribe();
|
|
2205
2045
|
releaseRoom();
|
|
2206
2046
|
};
|
|
2207
2047
|
}
|
|
2208
|
-
|
|
2209
|
-
|
|
2048
|
+
receive(event, handlers) {
|
|
2049
|
+
if (this.disposed) return;
|
|
2050
|
+
const recovery = this.recovery;
|
|
2051
|
+
if (recovery && isGenerationRealtimeEvent(event)) {
|
|
2052
|
+
recovery.events.push(event);
|
|
2053
|
+
if (recovery.events.length > SNAPSHOT_RECOVERY_MAX_BUFFERED_EVENTS) this.finishRecovery(recovery, handlers);
|
|
2054
|
+
return;
|
|
2055
|
+
}
|
|
2056
|
+
this.handleEvent(event, handlers);
|
|
2057
|
+
}
|
|
2058
|
+
dispose() {
|
|
2059
|
+
this.disposed = true;
|
|
2060
|
+
this.recovery = null;
|
|
2061
|
+
}
|
|
2062
|
+
async loadSnapshot() {
|
|
2063
|
+
if (!this.fetchStreamSnapshot) return null;
|
|
2064
|
+
try {
|
|
2065
|
+
return (await withTimeout(this.fetchStreamSnapshot(), SNAPSHOT_RECOVERY_TIMEOUT_MS)).snapshot;
|
|
2066
|
+
} catch (error) {
|
|
2210
2067
|
console.warn("[SessionGenerationStreamClient] Failed to recover stream snapshot:", error);
|
|
2211
|
-
return
|
|
2212
|
-
}
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2068
|
+
return null;
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
async recover(handlers, load, options, resync) {
|
|
2072
|
+
const recovery = { events: resync ? [resync.event] : [] };
|
|
2073
|
+
this.recovery = recovery;
|
|
2074
|
+
const snapshot = await load().catch(() => null);
|
|
2075
|
+
if (this.recovery !== recovery) return;
|
|
2076
|
+
const expectedTurnId = resync?.turnId;
|
|
2077
|
+
if (snapshot && (!expectedTurnId || !snapshot.turnId || snapshot.turnId === expectedTurnId)) this.seedFromSnapshot(snapshot, handlers, options);
|
|
2078
|
+
this.finishRecovery(recovery, handlers);
|
|
2079
|
+
}
|
|
2080
|
+
finishRecovery(recovery, handlers) {
|
|
2081
|
+
if (this.recovery !== recovery) return;
|
|
2082
|
+
this.recovery = null;
|
|
2083
|
+
this.replayBufferedEvents(recovery.events, handlers);
|
|
2084
|
+
}
|
|
2085
|
+
shouldResync(turnId, messageId) {
|
|
2086
|
+
if (!this.fetchStreamSnapshot || this.recovery) return false;
|
|
2087
|
+
const key = `${turnId ?? ""}:${messageId ?? ""}`;
|
|
2088
|
+
if (key === this.resyncedMessageKey) return false;
|
|
2089
|
+
this.resyncedMessageKey = key;
|
|
2090
|
+
return true;
|
|
2091
|
+
}
|
|
2092
|
+
liveState() {
|
|
2093
|
+
return this.reducer.get({
|
|
2094
|
+
spaceId: this.spaceId,
|
|
2095
|
+
sessionId: this.sessionId
|
|
2096
|
+
});
|
|
2097
|
+
}
|
|
2098
|
+
isForeignTurn(turnId) {
|
|
2099
|
+
if (!turnId) return false;
|
|
2100
|
+
const live = this.liveState();
|
|
2101
|
+
return (live.status === "streaming" || live.status === "pending") && live.turnId != null && live.turnId !== turnId;
|
|
2102
|
+
}
|
|
2103
|
+
isLiveMessage(message) {
|
|
2104
|
+
if (this.messageId == null && this.messageOrdinal == null) return true;
|
|
2105
|
+
const meta = message.meta ?? {};
|
|
2106
|
+
if (typeof meta.messageOrdinal === "number" && this.messageOrdinal != null) return meta.messageOrdinal === this.messageOrdinal;
|
|
2107
|
+
return typeof meta.streamMessageId === "string" && meta.streamMessageId === this.messageId;
|
|
2216
2108
|
}
|
|
2217
2109
|
seedFromSnapshot(snapshot, handlers, options) {
|
|
2218
2110
|
if (snapshot.spaceId !== this.spaceId || snapshot.sessionId !== this.sessionId) return false;
|
|
@@ -2251,7 +2143,7 @@ var SessionGenerationStreamClient = class SessionGenerationStreamClient {
|
|
|
2251
2143
|
const sameMessage = sameTurn && Boolean(this.messageId && (identity.messageId && identity.messageId === this.messageId || identity.messageId == null && identity.messageOrdinal != null && identity.messageOrdinal === this.messageOrdinal));
|
|
2252
2144
|
const olderMessageInSameTurn = sameTurn && identity.messageOrdinal != null && this.messageOrdinal != null && identity.messageOrdinal < this.messageOrdinal;
|
|
2253
2145
|
if ((sameMessage || olderMessageInSameTurn) && this.patchState && seq != null && seq <= this.patchState.patchSeq) continue;
|
|
2254
|
-
this.
|
|
2146
|
+
this.receive(event, handlers);
|
|
2255
2147
|
}
|
|
2256
2148
|
}
|
|
2257
2149
|
emit(handlers, event) {
|
|
@@ -2373,36 +2265,41 @@ var SessionGenerationStreamClient = class SessionGenerationStreamClient {
|
|
|
2373
2265
|
anchorUserMessageId
|
|
2374
2266
|
};
|
|
2375
2267
|
const result = this.reducer.applyPatch(input);
|
|
2268
|
+
if (!result.applied && result.reason === "version_mismatch" && this.shouldResync(turnId, messageId)) {
|
|
2269
|
+
this.recover(handlers, () => this.loadSnapshot(), void 0, {
|
|
2270
|
+
turnId,
|
|
2271
|
+
event
|
|
2272
|
+
});
|
|
2273
|
+
return;
|
|
2274
|
+
}
|
|
2376
2275
|
this.handleAppliedState(handlers, "patch", result, event, messageId, messageOrdinal);
|
|
2377
2276
|
}
|
|
2378
2277
|
handlePersisted(event, handlers) {
|
|
2379
2278
|
const message = event.payload.message;
|
|
2380
2279
|
if (!isRecord(message)) return;
|
|
2381
2280
|
const commit = parseAssistantMessageCommit(message);
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
this.
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
});
|
|
2405
|
-
this.resetCurrentMessage();
|
|
2281
|
+
const turnId = getTurnIdFromMessage(commit.message);
|
|
2282
|
+
const key = {
|
|
2283
|
+
spaceId: this.spaceId,
|
|
2284
|
+
sessionId: this.sessionId,
|
|
2285
|
+
turnId
|
|
2286
|
+
};
|
|
2287
|
+
if (commit.kind !== "ignored" && !this.isForeignTurn(turnId)) {
|
|
2288
|
+
if (commit.kind === "intermediate") {
|
|
2289
|
+
const intermediate = messageRecordToIntermediate(commit.message);
|
|
2290
|
+
if (intermediate) this.addIntermediateMessage(intermediate);
|
|
2291
|
+
if (this.isLiveMessage(commit.message)) {
|
|
2292
|
+
this.reducer.start({
|
|
2293
|
+
...key,
|
|
2294
|
+
turnId: turnId ?? this.liveState().turnId
|
|
2295
|
+
});
|
|
2296
|
+
this.resetCurrentMessage();
|
|
2297
|
+
}
|
|
2298
|
+
} else {
|
|
2299
|
+
if (commit.kind === "final") this.reducer.complete(key);
|
|
2300
|
+
else this.reducer.fail(key);
|
|
2301
|
+
this.resetCurrentMessage();
|
|
2302
|
+
}
|
|
2406
2303
|
}
|
|
2407
2304
|
this.emit(handlers, {
|
|
2408
2305
|
type: "commit",
|
|
@@ -2414,17 +2311,16 @@ var SessionGenerationStreamClient = class SessionGenerationStreamClient {
|
|
|
2414
2311
|
const turn = event.payload.turn;
|
|
2415
2312
|
if (!isRecord(turn)) return;
|
|
2416
2313
|
const typedTurn = turn;
|
|
2417
|
-
if (typedTurn.
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
}
|
|
2427
|
-
this.resetCurrentMessage();
|
|
2314
|
+
if (!this.isForeignTurn(typedTurn.id)) {
|
|
2315
|
+
const key = {
|
|
2316
|
+
spaceId: this.spaceId,
|
|
2317
|
+
sessionId: this.sessionId,
|
|
2318
|
+
turnId: typedTurn.id
|
|
2319
|
+
};
|
|
2320
|
+
if (typedTurn.status === "interrupted") this.reducer.interrupt(key);
|
|
2321
|
+
else this.reducer.complete(key);
|
|
2322
|
+
this.resetCurrentMessage();
|
|
2323
|
+
}
|
|
2428
2324
|
this.emit(handlers, {
|
|
2429
2325
|
type: "finalized",
|
|
2430
2326
|
turn: typedTurn,
|
|
@@ -2631,17 +2527,22 @@ var SpacesApi = class {
|
|
|
2631
2527
|
constructor(transport) {
|
|
2632
2528
|
this.transport = transport;
|
|
2633
2529
|
}
|
|
2634
|
-
list(customFetch) {
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2530
|
+
list(optionsOrFetch = {}, customFetch) {
|
|
2531
|
+
const options = typeof optionsOrFetch === "function" ? {} : optionsOrFetch;
|
|
2532
|
+
const fetch = typeof optionsOrFetch === "function" ? optionsOrFetch : customFetch;
|
|
2533
|
+
const params = new URLSearchParams();
|
|
2534
|
+
params.set("filter", options.filter ?? "recent");
|
|
2535
|
+
if (options.limit) params.set("limit", String(options.limit));
|
|
2536
|
+
if (options.cursor) params.set("cursor", options.cursor);
|
|
2537
|
+
if (options.query) params.set("q", options.query);
|
|
2538
|
+
if (options.name) params.set("name", options.name);
|
|
2539
|
+
for (const recent of options.recentSpaces ?? []) {
|
|
2540
|
+
params.append("recentSpaceId", recent.id);
|
|
2541
|
+
params.append("recentSpaceAt", new Date(recent.timestamp).toISOString());
|
|
2542
|
+
}
|
|
2543
|
+
return this.transport.request(`/api/spaces?${params}`, { fetch });
|
|
2639
2544
|
}
|
|
2640
|
-
/**
|
|
2641
|
-
* Resolve the user's default space (owned/member home, else most recent).
|
|
2642
|
-
* When the account has no accessible space, the API creates a blank Home
|
|
2643
|
-
* space (`slug=home`) and returns it.
|
|
2644
|
-
*/
|
|
2545
|
+
/** Landing Space within the user's own Spaces; use `ensureHome()` for a write target. */
|
|
2645
2546
|
getDefault(customFetch) {
|
|
2646
2547
|
return this.transport.request("/api/spaces/default", {
|
|
2647
2548
|
method: "GET",
|
|
@@ -2654,6 +2555,16 @@ var SpacesApi = class {
|
|
|
2654
2555
|
getBySlug(username, slug, customFetch) {
|
|
2655
2556
|
return this.transport.request(`/api/spaces/by-slug/${encodeURIComponent(username)}/${encodeURIComponent(slug)}`, { fetch: customFetch });
|
|
2656
2557
|
}
|
|
2558
|
+
getOwnedBySlug(slug, customFetch) {
|
|
2559
|
+
return this.transport.request(`/api/me/spaces/by-slug/${encodeURIComponent(slug)}`, { fetch: customFetch });
|
|
2560
|
+
}
|
|
2561
|
+
/** The account's own Home Space, created on first use. */
|
|
2562
|
+
ensureHome(customFetch) {
|
|
2563
|
+
return this.transport.request("/api/me/spaces/home", {
|
|
2564
|
+
method: "POST",
|
|
2565
|
+
fetch: customFetch
|
|
2566
|
+
});
|
|
2567
|
+
}
|
|
2657
2568
|
create(input, headers) {
|
|
2658
2569
|
return this.transport.request("/api/spaces", {
|
|
2659
2570
|
method: "POST",
|
|
@@ -2754,7 +2665,7 @@ var SpaceFilesApi = class {
|
|
|
2754
2665
|
}
|
|
2755
2666
|
read(path, customFetch, signal) {
|
|
2756
2667
|
const params = new URLSearchParams({ path });
|
|
2757
|
-
return this.transport.
|
|
2668
|
+
return this.transport.requestRevalidated(`/api/spaces/${this.spaceId}/fs/file?${params.toString()}`, {
|
|
2758
2669
|
fetch: customFetch,
|
|
2759
2670
|
signal
|
|
2760
2671
|
});
|
|
@@ -2817,15 +2728,33 @@ var SpaceFilesApi = class {
|
|
|
2817
2728
|
fetch: customFetch
|
|
2818
2729
|
});
|
|
2819
2730
|
}
|
|
2820
|
-
async
|
|
2731
|
+
async open(path, options = {}) {
|
|
2821
2732
|
const params = new URLSearchParams({ path });
|
|
2822
|
-
const
|
|
2823
|
-
|
|
2824
|
-
|
|
2733
|
+
const deadlineAt = Date.now() + Math.max(0, options.timeoutMs ?? 6e4);
|
|
2734
|
+
while (true) {
|
|
2735
|
+
const raw = await this.transport.raw(`/api/spaces/${this.spaceId}/fs/download?${params.toString()}`, {
|
|
2736
|
+
fetch: options.fetch,
|
|
2737
|
+
signal: options.signal
|
|
2738
|
+
});
|
|
2739
|
+
const { response } = raw;
|
|
2740
|
+
if (response.status !== 202) return {
|
|
2741
|
+
response,
|
|
2742
|
+
filename: getFilenameFromContentDisposition(response.headers.get("content-disposition")) ?? path.split("/").pop() ?? "download",
|
|
2743
|
+
mimeType: response.headers.get("content-type") ?? "application/octet-stream"
|
|
2744
|
+
};
|
|
2745
|
+
const body = await raw.json().catch(() => null);
|
|
2746
|
+
const remainingMs = deadlineAt - Date.now();
|
|
2747
|
+
if (remainingMs <= 0) throw new HttpError("File is being prepared. Please retry shortly.", 202, body);
|
|
2748
|
+
const retryAfterMs = typeof body?.retryAfterMs === "number" ? body.retryAfterMs : 2e3;
|
|
2749
|
+
await waitForSpaceFileRetry(Math.min(Math.max(250, Math.min(retryAfterMs, 2e3)), remainingMs), options.signal);
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
async download(path, customFetch) {
|
|
2753
|
+
const { response, filename, mimeType } = await this.open(path, { fetch: customFetch });
|
|
2825
2754
|
return {
|
|
2826
|
-
blob,
|
|
2827
|
-
filename
|
|
2828
|
-
mimeType
|
|
2755
|
+
blob: await response.blob(),
|
|
2756
|
+
filename,
|
|
2757
|
+
mimeType
|
|
2829
2758
|
};
|
|
2830
2759
|
}
|
|
2831
2760
|
write(input) {
|
|
@@ -2835,6 +2764,31 @@ var SpaceFilesApi = class {
|
|
|
2835
2764
|
body: JSON.stringify(input)
|
|
2836
2765
|
});
|
|
2837
2766
|
}
|
|
2767
|
+
copy(input) {
|
|
2768
|
+
return this.transport.request(`/api/spaces/${this.spaceId}/fs/copy`, {
|
|
2769
|
+
method: "POST",
|
|
2770
|
+
headers: { "Content-Type": "application/json" },
|
|
2771
|
+
body: JSON.stringify(input)
|
|
2772
|
+
});
|
|
2773
|
+
}
|
|
2774
|
+
getCopy(copyId, options = {}) {
|
|
2775
|
+
const params = new URLSearchParams();
|
|
2776
|
+
if (options.waitMs) params.set("waitMs", String(options.waitMs));
|
|
2777
|
+
const query = params.toString();
|
|
2778
|
+
return this.transport.request(`/api/spaces/${this.spaceId}/fs/copies/${encodeURIComponent(copyId)}${query ? `?${query}` : ""}`, { signal: options.signal });
|
|
2779
|
+
}
|
|
2780
|
+
async waitForCopy(copy, options = {}) {
|
|
2781
|
+
let current = copy;
|
|
2782
|
+
while (current.status !== "completed" || !current.result) {
|
|
2783
|
+
if (current.progress) options.onProgress?.(current.progress);
|
|
2784
|
+
options.signal?.throwIfAborted();
|
|
2785
|
+
current = await this.getCopy(current.copyId, {
|
|
2786
|
+
waitMs: options.pollMs ?? 5e3,
|
|
2787
|
+
signal: options.signal
|
|
2788
|
+
});
|
|
2789
|
+
}
|
|
2790
|
+
return current.result;
|
|
2791
|
+
}
|
|
2838
2792
|
createDir(path, mutationId) {
|
|
2839
2793
|
return this.transport.request(`/api/spaces/${this.spaceId}/fs/dir`, {
|
|
2840
2794
|
method: "POST",
|
|
@@ -3111,6 +3065,11 @@ var SessionClient = class {
|
|
|
3111
3065
|
get(customFetch) {
|
|
3112
3066
|
return this.transport.request(`/api/sessions/${this.id}`, { fetch: customFetch });
|
|
3113
3067
|
}
|
|
3068
|
+
async stats(options = {}) {
|
|
3069
|
+
const stats = readSessionStats({ stats: (await this.transport.request(`/api/sessions/${this.id}/stats`, options)).stats });
|
|
3070
|
+
if (!stats) throw new Error("Invalid session statistics");
|
|
3071
|
+
return { stats };
|
|
3072
|
+
}
|
|
3114
3073
|
/** Space files changed by this session's agent write/edit tool calls. */
|
|
3115
3074
|
files(options = {}) {
|
|
3116
3075
|
const query = options.limit ? `?limit=${options.limit}` : "";
|
|
@@ -3695,34 +3654,111 @@ var BoardRealtimeClient = class {
|
|
|
3695
3654
|
var BoardClient = class {
|
|
3696
3655
|
spaceId;
|
|
3697
3656
|
id;
|
|
3657
|
+
transport;
|
|
3698
3658
|
websocketClient;
|
|
3699
3659
|
realtime;
|
|
3700
|
-
boards;
|
|
3701
3660
|
constructor(spaceId, id, transport, websocketClient) {
|
|
3702
3661
|
this.spaceId = spaceId;
|
|
3703
3662
|
this.id = id;
|
|
3663
|
+
this.transport = transport;
|
|
3704
3664
|
this.websocketClient = websocketClient;
|
|
3705
|
-
this.boards = new SpaceBoardsApi(transport, spaceId, websocketClient);
|
|
3706
3665
|
this.realtime = new BoardRealtimeClient(websocketClient, spaceId, id);
|
|
3707
3666
|
}
|
|
3708
|
-
|
|
3709
|
-
return this.boards
|
|
3667
|
+
get path() {
|
|
3668
|
+
return `/api/spaces/${this.spaceId}/boards/${this.id}`;
|
|
3710
3669
|
}
|
|
3711
|
-
|
|
3712
|
-
|
|
3670
|
+
/** Read the Board, or part of it. Every value is complete, defaults included. */
|
|
3671
|
+
get(input = {}, customFetch) {
|
|
3672
|
+
const params = new URLSearchParams();
|
|
3673
|
+
if (input.only?.length) params.set("only", input.only.join(","));
|
|
3674
|
+
if (input.rect) params.set("rect", [
|
|
3675
|
+
input.rect.x,
|
|
3676
|
+
input.rect.y,
|
|
3677
|
+
input.rect.width,
|
|
3678
|
+
input.rect.height
|
|
3679
|
+
].join(","));
|
|
3680
|
+
if (input.within) params.set("within", input.within);
|
|
3681
|
+
if (input.items?.length) params.set("items", input.items.join(","));
|
|
3682
|
+
if (input.animations?.length) params.set("animations", input.animations.join(","));
|
|
3683
|
+
if (input.limit !== void 0) params.set("limit", String(input.limit));
|
|
3684
|
+
if (input.cursor) params.set("cursor", input.cursor);
|
|
3685
|
+
const query = params.toString();
|
|
3686
|
+
return this.transport.request(`${this.path}${query ? `?${query}` : ""}`, { fetch: customFetch });
|
|
3713
3687
|
}
|
|
3714
|
-
|
|
3715
|
-
|
|
3688
|
+
/** Merge a patch into the Board: fields merge, `null` deletes. */
|
|
3689
|
+
apply(patch, options = {}) {
|
|
3690
|
+
return this.transport.request(`${this.path}/apply`, {
|
|
3691
|
+
method: "POST",
|
|
3692
|
+
headers: { "Content-Type": "application/json" },
|
|
3693
|
+
body: JSON.stringify({
|
|
3694
|
+
...options,
|
|
3695
|
+
patch,
|
|
3696
|
+
mutationId: options.mutationId ?? randomBoardId()
|
|
3697
|
+
})
|
|
3698
|
+
});
|
|
3716
3699
|
}
|
|
3717
|
-
/**
|
|
3718
|
-
|
|
3719
|
-
|
|
3700
|
+
/** Transactions newest first, each with the before/after state of what it changed. */
|
|
3701
|
+
history(input = {}, customFetch) {
|
|
3702
|
+
const params = new URLSearchParams();
|
|
3703
|
+
if (input.before !== void 0) params.set("before", String(input.before));
|
|
3704
|
+
if (input.limit !== void 0) params.set("limit", String(input.limit));
|
|
3705
|
+
const query = params.toString();
|
|
3706
|
+
return this.transport.request(`${this.path}/history${query ? `?${query}` : ""}`, { fetch: customFetch });
|
|
3720
3707
|
}
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3708
|
+
/** Return the Board to an earlier version as a new write. */
|
|
3709
|
+
restore(version) {
|
|
3710
|
+
return this.transport.request(`${this.path}/restore`, {
|
|
3711
|
+
method: "POST",
|
|
3712
|
+
headers: { "Content-Type": "application/json" },
|
|
3713
|
+
body: JSON.stringify({ version })
|
|
3714
|
+
});
|
|
3715
|
+
}
|
|
3716
|
+
playback(command) {
|
|
3717
|
+
return this.transport.request(`${this.path}/playback`, {
|
|
3718
|
+
method: "POST",
|
|
3719
|
+
headers: { "Content-Type": "application/json" },
|
|
3720
|
+
body: JSON.stringify(command)
|
|
3721
|
+
}).then((response) => response.playback);
|
|
3722
|
+
}
|
|
3723
|
+
/** Shared playback: every viewer follows the same clock. */
|
|
3724
|
+
play(animationId, options = {}) {
|
|
3725
|
+
return this.playback({
|
|
3726
|
+
commandId: randomBoardId(),
|
|
3727
|
+
type: "play",
|
|
3728
|
+
animationId,
|
|
3729
|
+
...options
|
|
3730
|
+
});
|
|
3731
|
+
}
|
|
3732
|
+
pause() {
|
|
3733
|
+
return this.playback({
|
|
3734
|
+
commandId: randomBoardId(),
|
|
3735
|
+
type: "pause"
|
|
3736
|
+
});
|
|
3737
|
+
}
|
|
3738
|
+
resume() {
|
|
3739
|
+
return this.playback({
|
|
3740
|
+
commandId: randomBoardId(),
|
|
3741
|
+
type: "resume"
|
|
3742
|
+
});
|
|
3743
|
+
}
|
|
3744
|
+
seek(position) {
|
|
3745
|
+
return this.playback({
|
|
3746
|
+
commandId: randomBoardId(),
|
|
3747
|
+
type: "seek",
|
|
3748
|
+
position
|
|
3749
|
+
});
|
|
3750
|
+
}
|
|
3751
|
+
/** Continue past the marker a presentation is holding at. */
|
|
3752
|
+
next() {
|
|
3753
|
+
return this.playback({
|
|
3754
|
+
commandId: randomBoardId(),
|
|
3755
|
+
type: "next"
|
|
3756
|
+
});
|
|
3757
|
+
}
|
|
3758
|
+
stop() {
|
|
3759
|
+
return this.playback({
|
|
3760
|
+
commandId: randomBoardId(),
|
|
3761
|
+
type: "stop"
|
|
3726
3762
|
});
|
|
3727
3763
|
}
|
|
3728
3764
|
updateAwareness(seq, update) {
|
|
@@ -3734,18 +3770,6 @@ var BoardClient = class {
|
|
|
3734
3770
|
update
|
|
3735
3771
|
});
|
|
3736
3772
|
}
|
|
3737
|
-
play(command) {
|
|
3738
|
-
return this.boards.play(this.id, command);
|
|
3739
|
-
}
|
|
3740
|
-
pause(command) {
|
|
3741
|
-
return this.boards.pause(this.id, command);
|
|
3742
|
-
}
|
|
3743
|
-
seek(command) {
|
|
3744
|
-
return this.boards.seek(this.id, command);
|
|
3745
|
-
}
|
|
3746
|
-
stop(command) {
|
|
3747
|
-
return this.boards.stop(this.id, command);
|
|
3748
|
-
}
|
|
3749
3773
|
subscribe(handlers) {
|
|
3750
3774
|
return this.realtime.subscribe(handlers);
|
|
3751
3775
|
}
|
|
@@ -3767,66 +3791,14 @@ var SpaceBoardsApi = class {
|
|
|
3767
3791
|
byId(boardId) {
|
|
3768
3792
|
return new BoardClient(this.spaceId, boardId, this.transport, this.websocketClient);
|
|
3769
3793
|
}
|
|
3770
|
-
|
|
3771
|
-
|
|
3794
|
+
/** Create a `.board` file and its Board, optionally with an initial document. */
|
|
3795
|
+
create(input) {
|
|
3772
3796
|
return this.transport.request(`/api/spaces/${this.spaceId}/boards`, {
|
|
3773
3797
|
method: "POST",
|
|
3774
3798
|
headers: { "Content-Type": "application/json" },
|
|
3775
3799
|
body: JSON.stringify(input)
|
|
3776
3800
|
});
|
|
3777
3801
|
}
|
|
3778
|
-
authoring(boardId, input = {}, customFetch) {
|
|
3779
|
-
if (input.include?.length === 0) throw new Error("Board authoring include must not be empty.");
|
|
3780
|
-
const params = new URLSearchParams();
|
|
3781
|
-
for (const section of input.include ?? []) params.append("include", section);
|
|
3782
|
-
if (input.itemIds?.length) params.set("itemIds", input.itemIds.join(","));
|
|
3783
|
-
if (input.connectionIds?.length) params.set("connectionIds", input.connectionIds.join(","));
|
|
3784
|
-
if (input.effectIds?.length) params.set("effectIds", input.effectIds.join(","));
|
|
3785
|
-
if (input.compositionIds?.length) params.set("compositionIds", input.compositionIds.join(","));
|
|
3786
|
-
if (input.viewport) params.set("viewport", JSON.stringify(input.viewport));
|
|
3787
|
-
const query = params.toString();
|
|
3788
|
-
return this.transport.request(`/api/spaces/${this.spaceId}/boards/${boardId}/authoring${query ? `?${query}` : ""}`, { fetch: customFetch });
|
|
3789
|
-
}
|
|
3790
|
-
mutateSemantic(boardId, mutation) {
|
|
3791
|
-
return this.transport.request(`/api/spaces/${this.spaceId}/boards/${boardId}/mutations`, {
|
|
3792
|
-
method: "POST",
|
|
3793
|
-
headers: { "Content-Type": "application/json" },
|
|
3794
|
-
body: JSON.stringify(mutation)
|
|
3795
|
-
});
|
|
3796
|
-
}
|
|
3797
|
-
summary(boardId, customFetch) {
|
|
3798
|
-
return this.transport.request(`/api/spaces/${this.spaceId}/boards/${boardId}/summary`, { fetch: customFetch });
|
|
3799
|
-
}
|
|
3800
|
-
capabilities(boardId, customFetch) {
|
|
3801
|
-
return this.transport.request(`/api/spaces/${this.spaceId}/boards/${boardId}/capabilities`, { fetch: customFetch });
|
|
3802
|
-
}
|
|
3803
|
-
transactions(boardId, input = {}, customFetch) {
|
|
3804
|
-
const params = new URLSearchParams();
|
|
3805
|
-
if (input.before !== void 0) params.set("before", String(input.before));
|
|
3806
|
-
if (input.limit !== void 0) params.set("limit", String(input.limit));
|
|
3807
|
-
if (input.snapshot !== void 0) params.set("snapshot", String(input.snapshot));
|
|
3808
|
-
const query = params.toString();
|
|
3809
|
-
return this.transport.request(`/api/spaces/${this.spaceId}/boards/${boardId}/transactions${query ? `?${query}` : ""}`, { fetch: customFetch });
|
|
3810
|
-
}
|
|
3811
|
-
playback(boardId, command) {
|
|
3812
|
-
return this.transport.request(`/api/spaces/${this.spaceId}/boards/${boardId}/playback`, {
|
|
3813
|
-
method: "POST",
|
|
3814
|
-
headers: { "Content-Type": "application/json" },
|
|
3815
|
-
body: JSON.stringify(command)
|
|
3816
|
-
});
|
|
3817
|
-
}
|
|
3818
|
-
play(boardId, command) {
|
|
3819
|
-
return this.playback(boardId, command);
|
|
3820
|
-
}
|
|
3821
|
-
pause(boardId, command) {
|
|
3822
|
-
return this.playback(boardId, command);
|
|
3823
|
-
}
|
|
3824
|
-
seek(boardId, command) {
|
|
3825
|
-
return this.playback(boardId, command);
|
|
3826
|
-
}
|
|
3827
|
-
stop(boardId, command) {
|
|
3828
|
-
return this.playback(boardId, command);
|
|
3829
|
-
}
|
|
3830
3802
|
};
|
|
3831
3803
|
var SpaceCheckpointFilesApi = class {
|
|
3832
3804
|
transport;
|
|
@@ -3966,6 +3938,7 @@ var SpaceClient = class {
|
|
|
3966
3938
|
webhooks;
|
|
3967
3939
|
env;
|
|
3968
3940
|
sandbox;
|
|
3941
|
+
displays;
|
|
3969
3942
|
invitations;
|
|
3970
3943
|
labels;
|
|
3971
3944
|
boards;
|
|
@@ -3989,6 +3962,7 @@ var SpaceClient = class {
|
|
|
3989
3962
|
this.webhooks = new SpaceWebhooksApi(transport, id);
|
|
3990
3963
|
this.env = new SpaceEnvApi(transport, id);
|
|
3991
3964
|
this.sandbox = new SpaceSandboxApi(transport, id);
|
|
3965
|
+
this.displays = new SpaceDisplaysApi(transport, id);
|
|
3992
3966
|
this.invitations = new SpaceInvitationsApi(transport, id);
|
|
3993
3967
|
this.labels = new SpaceLabelsApi(transport, id);
|
|
3994
3968
|
this.boards = new SpaceBoardsApi(transport, id, websocketClient);
|
|
@@ -4394,6 +4368,7 @@ var UserApi = class {
|
|
|
4394
4368
|
if (options?.limit !== void 0) params.set("limit", String(options.limit));
|
|
4395
4369
|
if (options?.cursor) params.set("cursor", options.cursor);
|
|
4396
4370
|
if (options?.source?.length) params.set("source", options.source.join(","));
|
|
4371
|
+
if (options?.includeForks) params.set("includeForks", "1");
|
|
4397
4372
|
const query = params.toString();
|
|
4398
4373
|
return this.transport.request(`/api/me/sessions${query ? `?${query}` : ""}`, { fetch });
|
|
4399
4374
|
}
|
|
@@ -4430,6 +4405,53 @@ var UserLabelsApi = class {
|
|
|
4430
4405
|
constructor(transport) {
|
|
4431
4406
|
this.transport = transport;
|
|
4432
4407
|
}
|
|
4408
|
+
list() {
|
|
4409
|
+
return this.transport.request("/api/me/labels");
|
|
4410
|
+
}
|
|
4411
|
+
create(labelRef) {
|
|
4412
|
+
return this.transport.request("/api/me/labels", {
|
|
4413
|
+
method: "POST",
|
|
4414
|
+
headers: { "Content-Type": "application/json" },
|
|
4415
|
+
body: JSON.stringify({ labelRef })
|
|
4416
|
+
});
|
|
4417
|
+
}
|
|
4418
|
+
resolve(labelRefs) {
|
|
4419
|
+
return this.transport.request("/api/me/labels/resolve", {
|
|
4420
|
+
method: "POST",
|
|
4421
|
+
headers: { "Content-Type": "application/json" },
|
|
4422
|
+
body: JSON.stringify({ labelRefs })
|
|
4423
|
+
});
|
|
4424
|
+
}
|
|
4425
|
+
update(labelRef, input) {
|
|
4426
|
+
return this.transport.request("/api/me/labels/by-ref", {
|
|
4427
|
+
method: "PATCH",
|
|
4428
|
+
headers: { "Content-Type": "application/json" },
|
|
4429
|
+
body: JSON.stringify({
|
|
4430
|
+
labelRef,
|
|
4431
|
+
...input
|
|
4432
|
+
})
|
|
4433
|
+
});
|
|
4434
|
+
}
|
|
4435
|
+
delete(labelRef) {
|
|
4436
|
+
return this.transport.request(`/api/me/labels/by-ref?${new URLSearchParams({ ref: labelRef })}`, { method: "DELETE" });
|
|
4437
|
+
}
|
|
4438
|
+
reorder(labelRefs) {
|
|
4439
|
+
return this.transport.request("/api/me/labels/reorder", {
|
|
4440
|
+
method: "POST",
|
|
4441
|
+
headers: { "Content-Type": "application/json" },
|
|
4442
|
+
body: JSON.stringify({ labelRefs })
|
|
4443
|
+
});
|
|
4444
|
+
}
|
|
4445
|
+
patchResources(resourceRefs, input) {
|
|
4446
|
+
return this.transport.request("/api/me/resources/space/labels/batch", {
|
|
4447
|
+
method: "PATCH",
|
|
4448
|
+
headers: { "Content-Type": "application/json" },
|
|
4449
|
+
body: JSON.stringify({
|
|
4450
|
+
resourceRefs,
|
|
4451
|
+
...input
|
|
4452
|
+
})
|
|
4453
|
+
});
|
|
4454
|
+
}
|
|
4433
4455
|
getResourceLabels(resourceType, resourceRef) {
|
|
4434
4456
|
const params = new URLSearchParams({ resourceRef });
|
|
4435
4457
|
return this.transport.request(`/api/me/resources/${resourceType}/labels?${params.toString()}`);
|
|
@@ -4602,10 +4624,9 @@ var AppsApi = class {
|
|
|
4602
4624
|
//#endregion
|
|
4603
4625
|
//#region src/apis/desktop-commands.ts
|
|
4604
4626
|
const DEFAULT_POLL_INTERVAL_MS = 300;
|
|
4605
|
-
const
|
|
4606
|
-
|
|
4607
|
-
if (!Number.isFinite(
|
|
4608
|
-
return value;
|
|
4627
|
+
const assertTimeoutMs = (timeoutMs) => {
|
|
4628
|
+
if (timeoutMs === void 0) return;
|
|
4629
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0 || timeoutMs > 432e5) throw new RangeError(`timeoutMs must be between 1 and ${DESKTOP_COMMAND_MAX_TIMEOUT_MS} milliseconds`);
|
|
4609
4630
|
};
|
|
4610
4631
|
const sleep = (ms, signal) => new Promise((resolve, reject) => {
|
|
4611
4632
|
if (signal?.aborted) {
|
|
@@ -4637,6 +4658,10 @@ var DesktopCommandsApi = class {
|
|
|
4637
4658
|
get(commandId) {
|
|
4638
4659
|
return this.transport.request(`/api/desktop/commands/${encodeURIComponent(commandId)}`);
|
|
4639
4660
|
}
|
|
4661
|
+
/** The target desktop accepts before running; `accepted: false` means drop it. */
|
|
4662
|
+
accept(commandId) {
|
|
4663
|
+
return this.transport.request(`/api/desktop/commands/${encodeURIComponent(commandId)}/accept`, { method: "POST" });
|
|
4664
|
+
}
|
|
4640
4665
|
reportResult(commandId, input) {
|
|
4641
4666
|
return this.transport.request(`/api/desktop/commands/${encodeURIComponent(commandId)}/result`, {
|
|
4642
4667
|
method: "POST",
|
|
@@ -4645,15 +4670,17 @@ var DesktopCommandsApi = class {
|
|
|
4645
4670
|
});
|
|
4646
4671
|
}
|
|
4647
4672
|
async run(input, options = {}) {
|
|
4673
|
+
assertTimeoutMs(options.timeoutMs);
|
|
4648
4674
|
const { command } = await this.create(input);
|
|
4649
4675
|
if (isTerminalDesktopCommandStatus(command.status)) return command;
|
|
4650
4676
|
return this.wait(command.commandId, options);
|
|
4651
4677
|
}
|
|
4652
4678
|
async wait(commandId, options = {}) {
|
|
4653
|
-
|
|
4679
|
+
assertTimeoutMs(options.timeoutMs);
|
|
4680
|
+
const startedAt = Date.now();
|
|
4654
4681
|
const pollIntervalMs = Math.max(50, options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS);
|
|
4655
|
-
const deadline = Date.now() + timeoutMs;
|
|
4656
4682
|
let latest = (await this.get(commandId)).command;
|
|
4683
|
+
const deadline = startedAt + (options.timeoutMs ?? defaultDesktopCommandTimeoutMs(latest.command));
|
|
4657
4684
|
while (!isTerminalDesktopCommandStatus(latest.status)) {
|
|
4658
4685
|
const remaining = deadline - Date.now();
|
|
4659
4686
|
if (remaining <= 0) break;
|
|
@@ -4780,4 +4807,4 @@ var CohubHttpClient = class {
|
|
|
4780
4807
|
};
|
|
4781
4808
|
const createHttpClient = (options) => new CohubHttpClient(options);
|
|
4782
4809
|
//#endregion
|
|
4783
|
-
export {
|
|
4810
|
+
export { resolveOpenSurface as $, ReferencesApi as A, DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS as B, SessionPatchReducer as C, connectDisplay as D, DisplayConnection as E, ModelsApi as F, DESKTOP_COMMAND_SETTLEMENT_GRACE_SECONDS as G, DESKTOP_COMMAND_OPEN_TIMEOUT_MS as H, GenerationsApi as I, defaultDesktopCommandTimeoutMs as J, DESKTOP_COMMAND_TERMINAL_TTL_SECONDS as K, CronJobsApi as L, PublicAssetsApi as M, SkillsApi as N, ensureRealtimeConnected as O, PromptsApi as P, parseDesktopCommandId as Q, ChannelsApi as R, parseAssistantMessageCommit as S, SpaceDisplaysApi as T, DESKTOP_COMMAND_PAYLOAD_MAX_BYTES as U, DESKTOP_COMMAND_MAX_TIMEOUT_MS as V, DESKTOP_COMMAND_PENDING_TTL_SECONDS as W, isTerminalDesktopCommandStatus as X, isDesktopCallMethod as Y, parseDesktopCommand as Z, PublicInviteApi as _, DesktopCommandsApi as a, normalizeDisplayPoint as at, SessionGenerationStreamClient as b, ReferralsApi as c, TasksApi as d, NAVIGATION_ERROR_MESSAGE_MAX_LENGTH as et, BoardClient as f, SpacesApi as g, SpaceWebhooksApi as h, WorkCommerceApi as i, DISPLAY_SYSTEM_ACTIONS as it, SearchApi as j, SessionAccessApi as k, UsersApi as l, SpacePublicFilesApi as m, createHttpClient as n, DISPLAY_INPUT_MAX_SCHEDULE_MS as nt, UiCommandsApi as o, splitDisplayText as ot, SpaceClient as p, DESKTOP_COMMAND_VERSION as q, AppCommerceApi as r, DISPLAY_INPUT_MAX_TEXT as rt, AppsApi as s, CohubHttpClient as t, DISPLAY_ELEMENT_REF_PATTERN as tt, UserApi as u, buildSpaceInvitePath as v, createSessionPatchReducer as w, createSessionGenerationStreamClient as x, buildSpacePath as y, DESKTOP_COMMAND_ACCEPT_TIMEOUT_MS as z };
|