@neta-art/cohub 8.17.0 → 8.18.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.
@@ -1,503 +1,77 @@
1
1
  import { D as getRealtimeSpaceRoom, E as getRealtimeBoardRoom, n as HttpTransport, t as HttpError, y as isUuid } from "./transport.js";
2
2
  import { l as resolveExecutionToken, o as resolveApiBaseUrl } from "./environment.js";
3
3
  import { z } from "zod";
4
- //#region src/apis/channels.ts
5
- var ChannelsApi = class {
6
- transport;
7
- constructor(transport) {
8
- this.transport = transport;
9
- }
10
- list(customFetch) {
11
- return this.transport.request("/api/channels", {
12
- method: "GET",
13
- fetch: customFetch
14
- });
15
- }
16
- create(data) {
17
- return this.transport.request("/api/channels", {
18
- method: "POST",
19
- headers: { "Content-Type": "application/json" },
20
- body: JSON.stringify(data)
21
- });
22
- }
23
- startWeChatLogin(data) {
24
- return this.transport.request("/api/channels/wechat/login/start", {
25
- method: "POST",
26
- headers: { "Content-Type": "application/json" },
27
- body: JSON.stringify(data)
28
- });
29
- }
30
- waitWeChatLogin(data) {
31
- return this.transport.request("/api/channels/wechat/login/wait", {
32
- method: "POST",
33
- headers: { "Content-Type": "application/json" },
34
- body: JSON.stringify(data)
35
- });
36
- }
37
- delete(id) {
38
- return this.transport.request(`/api/channels/${id}`, { method: "DELETE" });
39
- }
40
- };
41
- //#endregion
42
- //#region src/apis/cron-jobs.ts
43
- const toQuery = (params) => {
44
- const query = new URLSearchParams();
45
- for (const [key, value] of Object.entries(params)) if (value !== void 0 && value !== null && value !== "") query.set(key, String(value));
46
- const text = query.toString();
47
- return text ? `?${text}` : "";
4
+ //#region ../protocol/dist/board-constants.js
5
+ const DEFAULT_BOARD_RENDER_LIMITS = {
6
+ particles: 2e4,
7
+ vertices: 5e5,
8
+ dynamicVertices: 15e4,
9
+ drawCalls: 400,
10
+ filterPasses: 24,
11
+ renderTexturePixels: 16777216,
12
+ textureBytes: 536870912,
13
+ bufferBytes: 268435456,
14
+ simulationSteps: 1e5
48
15
  };
49
- var CronJobsApi = class {
50
- transport;
51
- constructor(transport) {
52
- this.transport = transport;
53
- }
54
- list(spaceId) {
55
- const query = toQuery({ spaceId });
56
- return this.transport.request(`/api/cron-jobs${query}`);
57
- }
58
- get(id) {
59
- return this.transport.request(`/api/cron-jobs/${id}`);
60
- }
61
- update(id, patch) {
62
- return this.transport.request(`/api/cron-jobs/${id}`, {
63
- method: "PATCH",
64
- headers: { "Content-Type": "application/json" },
65
- body: JSON.stringify(patch)
66
- });
67
- }
68
- delete(id) {
69
- return this.transport.request(`/api/cron-jobs/${id}`, { method: "DELETE" });
70
- }
71
- toggle(id, enabled) {
72
- return this.update(id, { enabled });
73
- }
74
- runs(cronJobId, options = {}) {
75
- const query = toQuery({
76
- limit: options.limit,
77
- cursor: options.cursor
78
- });
79
- return this.transport.request(`/api/cron-jobs/${cronJobId}/runs${query}`);
16
+ const BOARD_BUILTIN_CLIP_KINDS = [
17
+ "motion.path",
18
+ "draw.reveal",
19
+ "text.reveal",
20
+ "effects.particles",
21
+ "effects.trail",
22
+ "effects.impact",
23
+ "effects.flash",
24
+ "camera.focus",
25
+ "camera.shake"
26
+ ];
27
+ const BOARD_BUILTIN_EFFECT_KINDS = [
28
+ "effects.pulse",
29
+ "effects.float",
30
+ "effects.deal"
31
+ ];
32
+ const BOARD_ARROW_STROKE_SIZE = 2.5;
33
+ const BOARD_CONNECTION_STROKE_SIZE = 2.5;
34
+ function clipSchema(id) {
35
+ switch (id) {
36
+ case "motion.path": return { params: { points: {
37
+ coordinateSpace: "world-offset",
38
+ unit: "board"
39
+ } } };
40
+ case "effects.particles": return { params: { bounds: {
41
+ coordinateSpace: "world",
42
+ unit: "board"
43
+ } } };
44
+ case "camera.focus": return { params: {
45
+ focus: {
46
+ coordinateSpace: "world",
47
+ unit: "board"
48
+ },
49
+ padding: {
50
+ coordinateSpace: "screen",
51
+ unit: "css-px"
52
+ },
53
+ minZoom: { unit: "ratio" },
54
+ maxZoom: { unit: "ratio" }
55
+ } };
56
+ case "camera.shake": return { params: { amount: {
57
+ coordinateSpace: "screen-offset",
58
+ unit: "css-px"
59
+ } } };
60
+ default: return;
80
61
  }
81
- };
82
- //#endregion
83
- //#region src/apis/generations.ts
84
- const DEFAULT_INTERVAL_MS = 1500;
85
- const DEFAULT_TIMEOUT_MS = 18e5;
86
- function sleep$1(ms, signal) {
87
- if (signal?.aborted) return Promise.reject(signal.reason ?? /* @__PURE__ */ new Error("Generation wait aborted"));
88
- return new Promise((resolve, reject) => {
89
- const cleanup = () => signal?.removeEventListener("abort", onAbort);
90
- const timer = setTimeout(() => {
91
- cleanup();
92
- resolve();
93
- }, ms);
94
- const onAbort = () => {
95
- clearTimeout(timer);
96
- cleanup();
97
- reject(signal?.reason ?? /* @__PURE__ */ new Error("Generation wait aborted"));
98
- };
99
- signal?.addEventListener("abort", onAbort, { once: true });
100
- });
101
62
  }
102
- function isGenerationTaskResult(value) {
103
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
104
- const record = value;
105
- if (typeof record.model !== "string" || !Array.isArray(record.output)) return false;
106
- if (record.requestId !== void 0 && typeof record.requestId !== "string") return false;
107
- if (record.cost !== void 0 && (typeof record.cost !== "number" || !Number.isFinite(record.cost))) return false;
108
- if (record.billing !== void 0 && record.billing !== null) {
109
- if (!record.billing || typeof record.billing !== "object" || Array.isArray(record.billing)) return false;
110
- const billing = record.billing;
111
- if (billing.officialCostUsd !== void 0 && (typeof billing.officialCostUsd !== "number" || !Number.isFinite(billing.officialCostUsd))) return false;
112
- if (typeof billing.amountUsd !== "number" || !Number.isFinite(billing.amountUsd)) return false;
113
- if (billing.discountMultiplier !== void 0 && (typeof billing.discountMultiplier !== "number" || !Number.isFinite(billing.discountMultiplier) || billing.discountMultiplier < 0 || billing.discountMultiplier > 1)) return false;
114
- if (typeof billing.usageType !== "string") return false;
115
- if (billing.status !== "recorded" && billing.status !== "overage" && billing.status !== "skipped") return false;
116
- if (billing.reason !== void 0 && billing.reason !== null && typeof billing.reason !== "string") return false;
117
- }
118
- return true;
119
- }
120
- var GenerationsApi = class {
121
- transport;
122
- constructor(transport) {
123
- this.transport = transport;
124
- }
125
- async create(request) {
126
- return this.transport.request("/api/generations", {
127
- method: "POST",
128
- headers: { "Content-Type": "application/json" },
129
- body: JSON.stringify(request)
130
- });
131
- }
132
- async wait(taskRunId, options = {}) {
133
- const intervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS;
134
- const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
135
- const startedAt = Date.now();
136
- while (true) {
137
- if (options.signal?.aborted) throw options.signal.reason ?? /* @__PURE__ */ new Error("Generation wait aborted");
138
- const detail = await this.transport.request(`/api/tasks/${taskRunId}`);
139
- options.onPoll?.(detail);
140
- if (detail.run.taskType !== "generation") throw new Error(`Task is not a generation task: ${detail.run.taskType}`);
141
- if (detail.run.status === "completed") {
142
- if (!isGenerationTaskResult(detail.run.result)) throw new Error("Generation task completed without a valid result");
143
- return detail.run.result;
144
- }
145
- if (detail.run.status === "failed") {
146
- const message = detail.run.errorMessage || "Generation task failed";
147
- throw new Error(`${message}\ntask ID: ${taskRunId}`);
148
- }
149
- const remainingMs = timeoutMs - (Date.now() - startedAt);
150
- if (remainingMs <= 0) throw new Error(`Generation task timed out after ${timeoutMs}ms`);
151
- await sleep$1(Math.min(intervalMs, remainingMs), options.signal);
152
- }
153
- }
154
- async createAndWait(request, options) {
155
- const created = await this.create(request);
156
- return this.wait(created.taskRunId, options);
157
- }
158
- };
159
- //#endregion
160
- //#region src/apis/models.ts
161
- const MULTIMODAL_MODEL_TYPE = "multimodal";
162
- var ModelsApi = class {
163
- transport;
164
- constructor(transport) {
165
- this.transport = transport;
166
- }
167
- async list() {
168
- return this.transport.request("/api/models");
169
- }
170
- async listMultimodal() {
171
- return this.transport.request(`/api/models?modelType=${MULTIMODAL_MODEL_TYPE}`);
172
- }
173
- async status() {
174
- return this.transport.request("/api/models/status");
175
- }
176
- };
177
- //#endregion
178
- //#region src/apis/prompts.ts
179
- var PromptsApi = class {
180
- transport;
181
- constructor(transport) {
182
- this.transport = transport;
183
- }
184
- async list(options, customFetch) {
185
- const params = new URLSearchParams();
186
- if (options?.spaceId) params.set("spaceId", options.spaceId);
187
- const query = params.toString();
188
- const path = query ? `/api/prompts?${query}` : "/api/prompts";
189
- return this.transport.request(path, { fetch: customFetch });
190
- }
191
- };
192
- //#endregion
193
- //#region src/apis/skills.ts
194
- var SkillsApi = class {
195
- transport;
196
- constructor(transport) {
197
- this.transport = transport;
198
- }
199
- async list(options, customFetch) {
200
- const params = new URLSearchParams();
201
- if (options?.spaceId) params.set("spaceId", options.spaceId);
202
- const query = params.toString();
203
- const path = query ? `/api/skills?${query}` : "/api/skills";
204
- return this.transport.request(path, { fetch: customFetch });
205
- }
206
- };
207
- //#endregion
208
- //#region src/apis/public-assets.ts
209
- function createAbortError() {
210
- if (typeof DOMException === "function") return new DOMException("The operation was aborted.", "AbortError");
211
- const error = /* @__PURE__ */ new Error("The operation was aborted.");
212
- error.name = "AbortError";
213
- return error;
214
- }
215
- function uploadWithProgress(input) {
216
- return new Promise((resolve, reject) => {
217
- if (input.signal?.aborted) {
218
- reject(createAbortError());
219
- return;
220
- }
221
- const xhr = new XMLHttpRequest();
222
- let settled = false;
223
- let lastRatio = 0;
224
- const handleSignalAbort = () => xhr.abort();
225
- const finish = (callback) => {
226
- if (settled) return;
227
- settled = true;
228
- input.signal?.removeEventListener("abort", handleSignalAbort);
229
- callback();
230
- };
231
- const emitProgress = (loaded, total) => {
232
- const ratio = Math.max(lastRatio, Math.min(1, total > 0 ? loaded / total : 0));
233
- lastRatio = ratio;
234
- try {
235
- input.onProgress({
236
- loadedBytes: Math.round(input.fileBytes * ratio),
237
- totalBytes: input.fileBytes,
238
- ratio
239
- });
240
- } catch {}
241
- };
242
- try {
243
- xhr.open(input.method, input.url);
244
- for (const [key, value] of Object.entries(input.headers ?? {})) {
245
- if (/[\r\n\0]/.test(value)) continue;
246
- xhr.setRequestHeader(key, value);
247
- }
248
- xhr.upload.onprogress = (event) => {
249
- if (event.lengthComputable) emitProgress(event.loaded, event.total);
250
- };
251
- xhr.onload = () => {
252
- if (xhr.status >= 200 && xhr.status < 300) {
253
- emitProgress(1, 1);
254
- finish(resolve);
255
- return;
256
- }
257
- const detail = xhr.responseText?.trim();
258
- finish(() => reject(/* @__PURE__ */ new Error(`Public asset upload failed: HTTP ${xhr.status}${detail ? ` — ${detail}` : ""}`)));
259
- };
260
- xhr.onerror = () => finish(() => reject(/* @__PURE__ */ new Error("Public asset upload failed")));
261
- xhr.onabort = () => finish(() => reject(createAbortError()));
262
- input.signal?.addEventListener("abort", handleSignalAbort, { once: true });
263
- xhr.send(input.body);
264
- } catch (error) {
265
- finish(() => reject(error));
266
- }
267
- });
268
- }
269
- var PublicAssetsApi = class {
270
- transport;
271
- constructor(transport) {
272
- this.transport = transport;
273
- }
274
- createUpload(input, options = {}) {
275
- return this.transport.request("/api/public-assets/uploads", {
276
- method: "POST",
277
- headers: { "Content-Type": "application/json" },
278
- body: JSON.stringify(input),
279
- signal: options.signal
280
- });
281
- }
282
- async upload(input) {
283
- if (input.signal?.aborted) throw createAbortError();
284
- const plan = await this.createUpload({
285
- purpose: input.purpose,
286
- uploadProtocol: "presigned_put_v1",
287
- spaceId: input.spaceId,
288
- sessionId: input.sessionId,
289
- file: {
290
- size: input.file.size,
291
- mimeType: input.mimeType,
292
- filename: input.filename
293
- }
294
- }, { signal: input.signal });
295
- if (input.signal?.aborted) throw createAbortError();
296
- let response;
297
- if (input.onProgress && typeof XMLHttpRequest === "function") {
298
- await uploadWithProgress({
299
- method: "PUT",
300
- url: plan.asset.uploadUrl,
301
- headers: plan.asset.uploadHeaders,
302
- body: input.file,
303
- fileBytes: input.file.size,
304
- onProgress: input.onProgress,
305
- signal: input.signal
306
- });
307
- return plan.asset;
308
- }
309
- response = await fetch(plan.asset.uploadUrl, {
310
- method: "PUT",
311
- headers: plan.asset.uploadHeaders,
312
- body: input.file,
313
- signal: input.signal
314
- });
315
- if (!response.ok) {
316
- const detail = await response.text().catch(() => "");
317
- throw new Error(`Public asset upload failed: HTTP ${response.status}${detail ? ` — ${detail}` : ""}`);
318
- }
319
- return plan.asset;
320
- }
321
- /** Durable public upload for any chat attachment (image or file). No space required. */
322
- uploadAppSource(input) {
323
- return this.upload({
324
- purpose: "app_source",
325
- spaceId: input.spaceId,
326
- sessionId: input.sessionId,
327
- file: input.file,
328
- mimeType: input.mimeType,
329
- filename: input.filename,
330
- onProgress: input.onProgress,
331
- signal: input.signal
332
- });
333
- }
334
- uploadChatAttachment(input) {
335
- return this.upload({
336
- purpose: "chat_attachment",
337
- spaceId: input.spaceId,
338
- sessionId: input.sessionId,
339
- file: input.file,
340
- mimeType: input.mimeType,
341
- filename: input.filename,
342
- onProgress: input.onProgress,
343
- signal: input.signal
344
- });
345
- }
346
- /** Preprocessed chat image (webp/jpeg). Same durable path as uploadChatAttachment. */
347
- uploadChatImageAttachment(input) {
348
- return this.uploadChatAttachment(input);
349
- }
350
- };
351
- //#endregion
352
- //#region src/apis/search.ts
353
- var SearchApi = class {
354
- transport;
355
- constructor(transport) {
356
- this.transport = transport;
357
- }
358
- query(input, customFetch) {
359
- const params = new URLSearchParams({ q: input.q });
360
- if (input.limit !== void 0) params.set("limit", String(input.limit));
361
- for (const type of input.types ?? []) params.append("type", type);
362
- if (input.spaceId) params.set("spaceId", input.spaceId);
363
- if (input.labelRef) params.set("labelRef", input.labelRef);
364
- if (input.groupTurns === false) params.set("groupTurns", "false");
365
- return this.transport.request(`/api/search?${params.toString()}`, { fetch: customFetch });
366
- }
367
- /** Default (empty-query) command palette data: viewer-relative space signals. */
368
- overview(input, customFetch) {
369
- const params = new URLSearchParams();
370
- if (input?.spaceLimit !== void 0) params.set("spaceLimit", String(input.spaceLimit));
371
- if (input?.sessionLimit !== void 0) params.set("sessionLimit", String(input.sessionLimit));
372
- for (const spaceId of input?.recentSpaceIds ?? []) params.append("recentSpaceId", spaceId);
373
- const query = params.toString();
374
- return this.transport.request(`/api/palette/overview${query ? `?${query}` : ""}`, { fetch: customFetch });
375
- }
376
- };
377
- //#endregion
378
- //#region src/apis/references.ts
379
- const toSelector = (ref) => typeof ref === "string" ? ref : `${ref.type}:${ref.id}`;
380
- /**
381
- * Neutral access to the resource reference index. `query` lists references
382
- * touching a resource; `aggregate` returns grouped counts for a space. Callers
383
- * assemble collaboration graphs, lineage views, rankings, or trends from the
384
- * returned data.
385
- */
386
- var ReferencesApi = class {
387
- transport;
388
- constructor(transport) {
389
- this.transport = transport;
390
- }
391
- query(input, customFetch) {
392
- const params = new URLSearchParams({ source: toSelector(input.source) });
393
- if (input.direction) params.set("direction", input.direction);
394
- if (input.kinds && input.kinds.length > 0) params.set("kinds", input.kinds.join(","));
395
- if (input.days !== void 0) params.set("days", String(input.days));
396
- if (input.limit !== void 0) params.set("limit", String(input.limit));
397
- return this.transport.request(`/api/references?${params.toString()}`, { fetch: customFetch });
398
- }
399
- aggregate(input, customFetch) {
400
- const params = new URLSearchParams({ space: input.spaceId });
401
- if (input.groupBy) params.set("groupBy", input.groupBy);
402
- if (input.kinds && input.kinds.length > 0) params.set("kinds", input.kinds.join(","));
403
- if (input.days !== void 0) params.set("days", String(input.days));
404
- if (input.limit !== void 0) params.set("limit", String(input.limit));
405
- return this.transport.request(`/api/references/aggregate?${params.toString()}`, { fetch: customFetch });
406
- }
407
- };
408
- //#endregion
409
- //#region src/apis/session-access.ts
410
- var SessionAccessApi = class {
411
- transport;
412
- constructor(transport) {
413
- this.transport = transport;
414
- }
415
- get(sessionId) {
416
- return this.transport.request(`/api/sessions/${sessionId}/access`);
417
- }
418
- set(sessionId, body) {
419
- return this.transport.request(`/api/sessions/${sessionId}/access`, {
420
- method: "PATCH",
421
- headers: { "Content-Type": "application/json" },
422
- body: JSON.stringify(body)
423
- });
424
- }
425
- remove(sessionId) {
426
- return this.transport.request(`/api/sessions/${sessionId}/access`, { method: "DELETE" });
427
- }
428
- };
429
- //#endregion
430
- //#region ../protocol/dist/board-constants.js
431
- const DEFAULT_BOARD_RENDER_LIMITS = {
432
- particles: 2e4,
433
- vertices: 5e5,
434
- dynamicVertices: 15e4,
435
- drawCalls: 400,
436
- filterPasses: 24,
437
- renderTexturePixels: 16777216,
438
- textureBytes: 536870912,
439
- bufferBytes: 268435456,
440
- simulationSteps: 1e5
441
- };
442
- const BOARD_BUILTIN_CLIP_KINDS = [
443
- "motion.path",
444
- "draw.reveal",
445
- "text.reveal",
446
- "effects.particles",
447
- "effects.trail",
448
- "effects.impact",
449
- "effects.flash",
450
- "camera.focus",
451
- "camera.shake"
452
- ];
453
- const BOARD_BUILTIN_EFFECT_KINDS = [
454
- "effects.pulse",
455
- "effects.float",
456
- "effects.deal"
457
- ];
458
- const BOARD_ARROW_STROKE_SIZE = 2.5;
459
- const BOARD_CONNECTION_STROKE_SIZE = 2.5;
460
- function clipSchema(id) {
461
- switch (id) {
462
- case "motion.path": return { params: { points: {
463
- coordinateSpace: "world-offset",
464
- unit: "board"
465
- } } };
466
- case "effects.particles": return { params: { bounds: {
467
- coordinateSpace: "world",
468
- unit: "board"
469
- } } };
470
- case "camera.focus": return { params: {
471
- focus: {
472
- coordinateSpace: "world",
473
- unit: "board"
474
- },
475
- padding: {
476
- coordinateSpace: "screen",
477
- unit: "css-px"
478
- },
479
- minZoom: { unit: "ratio" },
480
- maxZoom: { unit: "ratio" }
481
- } };
482
- case "camera.shake": return { params: { amount: {
483
- coordinateSpace: "screen-offset",
484
- unit: "css-px"
485
- } } };
486
- default: return;
487
- }
488
- }
489
- function effectSchema(id) {
490
- switch (id) {
491
- case "effects.deal": return { params: {
492
- lift: { unit: "board" },
493
- swing: { unit: "board" },
494
- curve: { unit: "board" },
495
- tilt: { unit: "degree" },
496
- scale: { unit: "ratio" },
497
- duration: { unit: "ms" },
498
- landing: { unit: "ms" }
499
- } };
500
- default: return;
63
+ function effectSchema(id) {
64
+ switch (id) {
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;
501
75
  }
502
76
  }
503
77
  const BOARD_BUILTIN_CAPABILITIES = [...BOARD_BUILTIN_CLIP_KINDS.map((id) => {
@@ -1052,479 +626,905 @@ const BoardAuthoringPositionSchema = z.object({
1052
626
  x: finiteSchema,
1053
627
  y: finiteSchema
1054
628
  }).strict();
1055
- const BoardAuthoringSizeSchema = z.object({
1056
- width: finiteSchema.positive(),
1057
- height: finiteSchema.positive()
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()
1058
703
  }).strict();
1059
- const BoardAuthoringPositionPatchSchema = z.object({
1060
- x: finiteSchema.optional(),
1061
- y: finiteSchema.optional()
704
+ const BoardDrawAuthoringItemSchema = z.object({
705
+ ...strokeStyledBaseFields,
706
+ type: z.literal("draw"),
707
+ props: z.object({ points: z.array(pointSchema).min(1) }).strict()
1062
708
  }).strict();
1063
- const pointSchema = z.object({
1064
- x: finiteSchema,
1065
- y: finiteSchema,
1066
- p: finiteSchema.min(0).max(1).default(.5)
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()
1067
720
  }).strict();
1068
- const worldPointSchema = z.object({
1069
- x: finiteSchema,
1070
- y: finiteSchema
721
+ const BoardFrameAuthoringItemSchema = z.object({
722
+ ...styledBaseFields,
723
+ type: z.literal("frame"),
724
+ props: z.object({ label: z.string().default("Frame") }).strict()
1071
725
  }).strict();
1072
- const cropSchema = z.object({
1073
- x: finiteSchema.min(0).max(1),
1074
- y: finiteSchema.min(0).max(1),
1075
- w: finiteSchema.min(0).max(1),
1076
- h: finiteSchema.min(0).max(1)
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()
1077
779
  }).strict();
1078
- const BoardItemStyleSchema = z.object({
1079
- color: z.enum(BOARD_AUTHORING_COLOR_IDS).optional(),
1080
- strokeWidth: finiteSchema.min(1).max(64).optional(),
1081
- fillOpacity: finiteSchema.min(0).max(1).optional()
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)
1082
872
  }).strict();
1083
- const sourceSnapshotSchema = z.record(z.string(), z.unknown());
1084
- function isSafeBoardSourcePath(value) {
1085
- return value.length > 0 && value.length <= 4096 && !value.startsWith("/") && !value.startsWith("\\") && !value.includes("\\") && value.split("/").every((part) => part.length > 0 && part !== "." && part !== "..");
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
+ //#endregion
893
+ //#region ../protocol/dist/space-hooks.js
894
+ const SPACE_HOOK_WEBHOOK_SECRET_HEADER = "x-cohub-webhook-secret";
895
+ //#endregion
896
+ //#region ../protocol/dist/navigation.js
897
+ const NAVIGATION_LAUNCH_MAX_LENGTH = 2048;
898
+ const NAVIGATION_ERROR_MESSAGE_MAX_LENGTH = 2e3;
899
+ //#endregion
900
+ //#region ../protocol/dist/desktop-command.js
901
+ /**
902
+ * Lets an agent drive the Cohub desktop that originated the app. Routing comes
903
+ * from request provenance, never a caller-supplied target, so a command only
904
+ * reaches the actor's own instances.
905
+ *
906
+ * The canonical command is `desktop.open`. The legacy `preview.show` shape is
907
+ * accepted on input and normalized, so older clients keep working, but commands
908
+ * are always stored and dispatched in the canonical form.
909
+ */
910
+ const DESKTOP_COMMAND_VERSION = 1;
911
+ /** Persisted and broadcast, so every field is capped; MAX_BYTES bounds the whole. */
912
+ const DESKTOP_COMMAND_PAYLOAD_MAX_BYTES = 32768;
913
+ const DESKTOP_COMMAND_MAX_BYTES = 40960;
914
+ const DESKTOP_COMMAND_LAUNCH_MAX_LENGTH = NAVIGATION_LAUNCH_MAX_LENGTH;
915
+ const DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS = 6e5;
916
+ const DESKTOP_COMMAND_MAX_TIMEOUT_MS = 432e5;
917
+ const DESKTOP_COMMAND_SETTLEMENT_GRACE_SECONDS = 600;
918
+ /** Keeps pending commands reportable for the full wait window plus settlement grace. */
919
+ const DESKTOP_COMMAND_PENDING_TTL_SECONDS = 43800;
920
+ const DESKTOP_COMMAND_TERMINAL_TTL_SECONDS = 1800;
921
+ const DESKTOP_COMMAND_TERMINAL_STATUSES = [
922
+ "applied",
923
+ "no_active_client",
924
+ "desktop_host_unavailable",
925
+ "rejected",
926
+ "unsupported",
927
+ "timeout"
928
+ ];
929
+ const isTerminalDesktopCommandStatus = (status) => DESKTOP_COMMAND_TERMINAL_STATUSES.includes(status);
930
+ /**
931
+ * The surface a desktop open should use: an explicit request wins, then
932
+ * whatever the App declared at publish time. `window` is the implicit
933
+ * default and yields `undefined` so the command stays compact.
934
+ */
935
+ function resolveOpenSurface(requested, declared) {
936
+ return (requested ?? declared) === "overlay" ? "overlay" : void 0;
1086
937
  }
1087
- const BoardItemSourceSchema = z.object({
1088
- kind: z.literal("space-file"),
1089
- path: z.string().refine(isSafeBoardSourcePath, "source path must be a safe relative Board file path"),
1090
- snapshot: sourceSnapshotSchema.optional()
1091
- }).strict();
1092
- const sizedBaseFields = {
1093
- id: idSchema,
1094
- position: BoardAuthoringPositionSchema.default({
1095
- x: 0,
1096
- y: 0
1097
- }),
1098
- rotation: finiteSchema.default(0),
1099
- parentId: idSchema.nullable().optional(),
1100
- locked: z.boolean().optional(),
1101
- metadata: jsonObjectSchema.optional(),
1102
- size: BoardAuthoringSizeSchema.optional()
938
+ const METHOD_RE = new RegExp(`^[A-Za-z][A-Za-z0-9_.:-]{0,63}$`);
939
+ const isDesktopCallMethod = (value) => typeof value === "string" && METHOD_RE.test(value);
940
+ const DESKTOP_COMMAND_ID_RE = /^[A-Za-z0-9_-]{1,64}$/;
941
+ const parseDesktopCommandId = (value) => {
942
+ if (typeof value !== "string") return null;
943
+ const trimmed = value.trim();
944
+ return DESKTOP_COMMAND_ID_RE.test(trimmed) ? trimmed : null;
1103
945
  };
1104
- const styledBaseFields = {
1105
- ...sizedBaseFields,
1106
- style: BoardItemStyleSchema.optional()
946
+ const isRecord$1 = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
947
+ const asTrimmed = (value) => {
948
+ if (typeof value !== "string") return null;
949
+ const trimmed = value.trim();
950
+ return trimmed ? trimmed : null;
1107
951
  };
1108
- const { position: _positionField, size: _sizeField, ...strokeBaseFields } = sizedBaseFields;
1109
- const strokeStyledBaseFields = {
1110
- ...strokeBaseFields,
1111
- rotation: z.literal(0).default(0),
1112
- style: BoardItemStyleSchema.optional()
952
+ const parseLaunch = (value) => {
953
+ if (!isRecord$1(value)) return void 0;
954
+ const search = asTrimmed(value.search);
955
+ const hash = asTrimmed(value.hash);
956
+ if (!search && !hash) return void 0;
957
+ return {
958
+ ...search ? { search: search.startsWith("?") ? search : `?${search}` } : {},
959
+ ...hash ? { hash: hash.startsWith("#") ? hash : `#${hash}` } : {}
960
+ };
1113
961
  };
1114
- const BoardTextAuthoringItemSchema = z.object({
1115
- ...styledBaseFields,
1116
- type: z.literal("text"),
1117
- props: z.object({
1118
- text: z.string().default(""),
1119
- fontSize: finiteSchema.min(2).max(512).default(24)
1120
- }).strict()
1121
- }).strict();
1122
- const BoardGeoAuthoringItemSchema = z.object({
1123
- ...styledBaseFields,
1124
- type: z.literal("geo"),
1125
- props: z.object({
1126
- shape: z.enum(BOARD_AUTHORING_GEO_KINDS).default("rectangle"),
1127
- text: z.string().default("")
1128
- }).strict()
1129
- }).strict();
1130
- const BoardDrawAuthoringItemSchema = z.object({
1131
- ...strokeStyledBaseFields,
1132
- type: z.literal("draw"),
1133
- props: z.object({ points: z.array(pointSchema).min(1) }).strict()
1134
- }).strict();
1135
- const BoardArrowAuthoringItemSchema = z.object({
1136
- ...strokeStyledBaseFields,
1137
- type: z.literal("arrow"),
1138
- props: z.object({
1139
- start: worldPointSchema,
1140
- end: worldPointSchema,
1141
- bend: finiteSchema.min(-.85).max(.85).default(0),
1142
- arrowStart: z.boolean().default(false),
1143
- arrowEnd: z.boolean().default(true),
1144
- label: z.string().default("")
1145
- }).strict()
1146
- }).strict();
1147
- const BoardFrameAuthoringItemSchema = z.object({
1148
- ...styledBaseFields,
1149
- type: z.literal("frame"),
1150
- props: z.object({ label: z.string().default("Frame") }).strict()
1151
- }).strict();
1152
- const fileBackedFields = {
1153
- ...sizedBaseFields,
1154
- style: z.object({}).strict().optional(),
1155
- source: BoardItemSourceSchema
962
+ const measureDesktopCommandPayload = (value) => {
963
+ if (value === void 0) return 0;
964
+ try {
965
+ return new TextEncoder().encode(JSON.stringify(value) ?? "").length;
966
+ } catch {
967
+ return null;
968
+ }
969
+ };
970
+ /**
971
+ * Parse a desktop command from untrusted input.
972
+ *
973
+ * Accepts both the canonical `desktop.open` shape (`target` / `call`) and the
974
+ * legacy `preview.show` shape (`preview` / `request`); the result is always
975
+ * canonical so every later stage handles a single form.
976
+ */
977
+ const parseDesktopCommand = (input) => {
978
+ if (!isRecord$1(input)) return {
979
+ command: null,
980
+ error: "command must be an object"
981
+ };
982
+ const legacy = input.type === "preview.show";
983
+ if (input.type !== "desktop.open" && !legacy) return {
984
+ command: null,
985
+ error: "command.type must be one of: desktop.open"
986
+ };
987
+ const target = isRecord$1(input.target) ? input.target : isRecord$1(input.preview) ? input.preview : null;
988
+ if (!target) return {
989
+ command: null,
990
+ error: "command.target is required"
991
+ };
992
+ const kind = target.kind === "app" || legacy && target.kind === "work" ? "app" : target.kind === "file" ? "file" : null;
993
+ if (!kind) return {
994
+ command: null,
995
+ error: "command.target.kind must be one of: app, file"
996
+ };
997
+ if (kind === "file") {
998
+ const path = asTrimmed(target.path);
999
+ if (!path) return {
1000
+ command: null,
1001
+ error: "command.target.path is required"
1002
+ };
1003
+ if (path.length > 2048 || path.startsWith("/") || path.includes("\0") || path.includes("\\") || path.split("/").some((segment) => segment === "..")) return {
1004
+ command: null,
1005
+ error: "command.target.path must be a relative Space file path"
1006
+ };
1007
+ if (input.call !== void 0 && input.call !== null) return {
1008
+ command: null,
1009
+ error: "command.call is only supported for app targets"
1010
+ };
1011
+ if (legacy && input.request !== void 0 && input.request !== null) return {
1012
+ command: null,
1013
+ error: "command.call is only supported for app targets"
1014
+ };
1015
+ return {
1016
+ command: {
1017
+ type: "desktop.open",
1018
+ target: {
1019
+ kind: "file",
1020
+ path
1021
+ }
1022
+ },
1023
+ error: null
1024
+ };
1025
+ }
1026
+ const appId = asTrimmed(target.appId ?? target.workId);
1027
+ if (!appId) return {
1028
+ command: null,
1029
+ error: "command.target.appId is required"
1030
+ };
1031
+ if (!isUuid(appId)) return {
1032
+ command: null,
1033
+ error: "command.target.appId must be an App id"
1034
+ };
1035
+ const label = asTrimmed(target.label);
1036
+ if (label && label.length > 200) return {
1037
+ command: null,
1038
+ error: `command.target.label exceeds 200 characters`
1039
+ };
1040
+ const launch = parseLaunch(target.launch);
1041
+ if (launch) {
1042
+ for (const [field, value] of [["search", launch.search], ["hash", launch.hash]]) if (value && value.length > DESKTOP_COMMAND_LAUNCH_MAX_LENGTH) return {
1043
+ command: null,
1044
+ error: `command.target.launch.${field} exceeds ${DESKTOP_COMMAND_LAUNCH_MAX_LENGTH} characters`
1045
+ };
1046
+ }
1047
+ const callSource = input.call !== void 0 ? input.call : input.request;
1048
+ let call;
1049
+ if (callSource !== void 0 && callSource !== null) {
1050
+ if (!isRecord$1(callSource)) return {
1051
+ command: null,
1052
+ error: "command.call must be an object"
1053
+ };
1054
+ const method = asTrimmed(callSource.method);
1055
+ if (!method) return {
1056
+ command: null,
1057
+ error: "command.call.method is required"
1058
+ };
1059
+ if (!isDesktopCallMethod(method)) return {
1060
+ command: null,
1061
+ error: "command.call.method has an unsupported format"
1062
+ };
1063
+ const size = measureDesktopCommandPayload(callSource.input);
1064
+ if (size === null) return {
1065
+ command: null,
1066
+ error: "command.call.input must be JSON-serializable"
1067
+ };
1068
+ if (size > 32768) return {
1069
+ command: null,
1070
+ error: `command.call.input exceeds ${DESKTOP_COMMAND_PAYLOAD_MAX_BYTES} bytes`
1071
+ };
1072
+ call = {
1073
+ method,
1074
+ ...callSource.input === void 0 ? {} : { input: callSource.input }
1075
+ };
1076
+ }
1077
+ const surface = target.surface === "overlay" ? "overlay" : void 0;
1078
+ const command = {
1079
+ type: "desktop.open",
1080
+ target: {
1081
+ kind: "app",
1082
+ appId,
1083
+ ...label ? { label } : {},
1084
+ ...launch ? { launch } : {},
1085
+ ...surface ? { surface } : {}
1086
+ },
1087
+ ...call ? { call } : {}
1088
+ };
1089
+ const totalSize = measureDesktopCommandPayload(command);
1090
+ if (totalSize === null) return {
1091
+ command: null,
1092
+ error: "command must be JSON-serializable"
1093
+ };
1094
+ if (totalSize > 40960) return {
1095
+ command: null,
1096
+ error: `command exceeds ${DESKTOP_COMMAND_MAX_BYTES} bytes`
1097
+ };
1098
+ return {
1099
+ command,
1100
+ error: null
1101
+ };
1102
+ };
1103
+ //#endregion
1104
+ //#region src/apis/channels.ts
1105
+ var ChannelsApi = class {
1106
+ transport;
1107
+ constructor(transport) {
1108
+ this.transport = transport;
1109
+ }
1110
+ list(customFetch) {
1111
+ return this.transport.request("/api/channels", {
1112
+ method: "GET",
1113
+ fetch: customFetch
1114
+ });
1115
+ }
1116
+ create(data) {
1117
+ return this.transport.request("/api/channels", {
1118
+ method: "POST",
1119
+ headers: { "Content-Type": "application/json" },
1120
+ body: JSON.stringify(data)
1121
+ });
1122
+ }
1123
+ startWeChatLogin(data) {
1124
+ return this.transport.request("/api/channels/wechat/login/start", {
1125
+ method: "POST",
1126
+ headers: { "Content-Type": "application/json" },
1127
+ body: JSON.stringify(data)
1128
+ });
1129
+ }
1130
+ waitWeChatLogin(data) {
1131
+ return this.transport.request("/api/channels/wechat/login/wait", {
1132
+ method: "POST",
1133
+ headers: { "Content-Type": "application/json" },
1134
+ body: JSON.stringify(data)
1135
+ });
1136
+ }
1137
+ delete(id) {
1138
+ return this.transport.request(`/api/channels/${id}`, { method: "DELETE" });
1139
+ }
1156
1140
  };
1157
- const builtinItemSchemas = [
1158
- BoardTextAuthoringItemSchema,
1159
- BoardGeoAuthoringItemSchema,
1160
- BoardDrawAuthoringItemSchema,
1161
- BoardArrowAuthoringItemSchema,
1162
- BoardFrameAuthoringItemSchema,
1163
- z.object({
1164
- ...fileBackedFields,
1165
- type: z.literal("image"),
1166
- props: z.object({ crop: cropSchema.optional() }).strict()
1167
- }).strict(),
1168
- z.object({
1169
- ...fileBackedFields,
1170
- type: z.literal("video"),
1171
- props: z.object({}).strict()
1172
- }).strict(),
1173
- z.object({
1174
- ...fileBackedFields,
1175
- type: z.literal("audio"),
1176
- props: z.object({}).strict()
1177
- }).strict(),
1178
- z.object({
1179
- ...fileBackedFields,
1180
- type: z.literal("file"),
1181
- props: z.object({}).strict()
1182
- }).strict(),
1183
- z.object({
1184
- ...sizedBaseFields,
1185
- type: z.literal("task"),
1186
- props: z.object({
1187
- taskRunId: z.string().min(1),
1188
- snapshot: z.record(z.string(), z.unknown())
1189
- }).strict(),
1190
- style: z.object({}).strict().optional()
1191
- }).strict()
1192
- ];
1193
- const BoardBuiltinAuthoringItemSchema = z.discriminatedUnion("type", builtinItemSchemas);
1194
- const BoardExtensionAuthoringItemSchema = z.object({
1195
- ...sizedBaseFields,
1196
- type: extensionTypeSchema,
1197
- kindVersion: z.number().int().positive(),
1198
- props: jsonObjectSchema,
1199
- style: jsonObjectSchema.optional(),
1200
- source: z.object({
1201
- kind: z.string().min(1).max(80),
1202
- ref: z.string().min(1).max(4096),
1203
- snapshot: jsonObjectSchema.optional()
1204
- }).strict().optional()
1205
- }).strict();
1206
- const BoardAuthoringItemSchema = z.union([BoardBuiltinAuthoringItemSchema, BoardExtensionAuthoringItemSchema]);
1207
- /** JSON Merge Patch semantics, constrained to the stable Item envelope. */
1208
- const BoardItemPatchSchema = z.object({
1209
- position: BoardAuthoringPositionPatchSchema.optional(),
1210
- size: BoardAuthoringSizeSchema.nullable().optional(),
1211
- rotation: finiteSchema.optional(),
1212
- parentId: idSchema.nullable().optional(),
1213
- locked: z.boolean().nullable().optional(),
1214
- props: jsonObjectSchema.optional(),
1215
- style: jsonObjectSchema.nullable().optional(),
1216
- source: jsonObjectSchema.nullable().optional(),
1217
- metadata: jsonObjectSchema.nullable().optional()
1218
- }).strict().refine((patch) => Object.keys(patch).length > 0, "item patch is empty");
1219
- const BoardConnectionInputSchema = BoardConnectionSchema;
1220
- const stripServerFields = (value) => {
1221
- if (!value || typeof value !== "object" || Array.isArray(value)) return value;
1222
- const { boardId: _boardId, revision: _revision, ...input } = value;
1223
- return input;
1141
+ //#endregion
1142
+ //#region src/apis/cron-jobs.ts
1143
+ const toQuery = (params) => {
1144
+ const query = new URLSearchParams();
1145
+ for (const [key, value] of Object.entries(params)) if (value !== void 0 && value !== null && value !== "") query.set(key, String(value));
1146
+ const text = query.toString();
1147
+ return text ? `?${text}` : "";
1148
+ };
1149
+ var CronJobsApi = class {
1150
+ transport;
1151
+ constructor(transport) {
1152
+ this.transport = transport;
1153
+ }
1154
+ list(spaceId) {
1155
+ const query = toQuery({ spaceId });
1156
+ return this.transport.request(`/api/cron-jobs${query}`);
1157
+ }
1158
+ get(id) {
1159
+ return this.transport.request(`/api/cron-jobs/${id}`);
1160
+ }
1161
+ update(id, patch) {
1162
+ return this.transport.request(`/api/cron-jobs/${id}`, {
1163
+ method: "PATCH",
1164
+ headers: { "Content-Type": "application/json" },
1165
+ body: JSON.stringify(patch)
1166
+ });
1167
+ }
1168
+ delete(id) {
1169
+ return this.transport.request(`/api/cron-jobs/${id}`, { method: "DELETE" });
1170
+ }
1171
+ toggle(id, enabled) {
1172
+ return this.update(id, { enabled });
1173
+ }
1174
+ runs(cronJobId, options = {}) {
1175
+ const query = toQuery({
1176
+ limit: options.limit,
1177
+ cursor: options.cursor
1178
+ });
1179
+ return this.transport.request(`/api/cron-jobs/${cronJobId}/runs${query}`);
1180
+ }
1224
1181
  };
1225
- const BoardEffectCommandInputSchema = z.preprocess(stripServerFields, BoardEffectInputSchema);
1226
- const BoardCompositionCommandInputSchema = z.preprocess(stripServerFields, BoardCompositionInputSchema);
1227
- const BoardSemanticCommandSchema = z.discriminatedUnion("type", [
1228
- z.object({
1229
- type: z.literal("board.patch"),
1230
- patch: z.object({
1231
- title: z.string().min(1).max(255).optional(),
1232
- metadata: jsonObjectSchema.nullable().optional(),
1233
- metadataPatch: jsonObjectSchema.optional()
1234
- }).strict().refine((patch) => Object.keys(patch).length > 0, "board patch is empty")
1235
- }).strict(),
1236
- z.object({
1237
- type: z.literal("item.create"),
1238
- item: BoardAuthoringItemSchema
1239
- }).strict(),
1240
- z.object({
1241
- type: z.literal("item.patch"),
1242
- itemId: idSchema,
1243
- patch: BoardItemPatchSchema
1244
- }).strict(),
1245
- z.object({
1246
- type: z.literal("item.replace"),
1247
- itemId: idSchema,
1248
- item: BoardAuthoringItemSchema
1249
- }).strict(),
1250
- z.object({
1251
- type: z.literal("item.delete"),
1252
- itemId: idSchema,
1253
- cascade: z.boolean().default(false)
1254
- }).strict(),
1255
- z.object({
1256
- type: z.literal("item.reorder"),
1257
- itemId: idSchema,
1258
- index: z.number().int().nonnegative()
1259
- }).strict(),
1260
- z.object({
1261
- type: z.literal("connection.create"),
1262
- connection: BoardConnectionInputSchema
1263
- }).strict(),
1264
- z.object({
1265
- type: z.literal("connection.patch"),
1266
- connectionId: idSchema,
1267
- patch: BoardConnectionPatchSchema
1268
- }).strict(),
1269
- z.object({
1270
- type: z.literal("connection.delete"),
1271
- connectionId: idSchema
1272
- }).strict(),
1273
- z.object({
1274
- type: z.literal("effect.apply"),
1275
- effect: BoardEffectCommandInputSchema
1276
- }).strict(),
1277
- z.object({
1278
- type: z.literal("effect.delete"),
1279
- effectId: idSchema
1280
- }).strict(),
1281
- z.object({
1282
- type: z.literal("composition.apply"),
1283
- composition: BoardCompositionCommandInputSchema
1284
- }).strict(),
1285
- z.object({
1286
- type: z.literal("composition.delete"),
1287
- compositionId: idSchema
1288
- }).strict()
1289
- ]);
1290
- const BoardSemanticMutationSchema = z.object({
1291
- mutationId: idSchema,
1292
- baseVersion: z.number().int().nonnegative(),
1293
- clientId: idSchema.optional(),
1294
- undoGroupId: idSchema.optional(),
1295
- /** Validate (including server-side reference checks) without writing. */
1296
- dryRun: z.boolean().default(false),
1297
- commands: z.array(BoardSemanticCommandSchema).min(1).max(5e4)
1298
- }).strict();
1299
- z.object({
1300
- include: z.array(z.enum([
1301
- "items",
1302
- "connections",
1303
- "effects",
1304
- "compositions",
1305
- "playback"
1306
- ])).optional(),
1307
- itemIds: z.array(idSchema).max(5e4).optional(),
1308
- connectionIds: z.array(idSchema).max(5e4).optional(),
1309
- effectIds: z.array(idSchema).max(5e4).optional(),
1310
- compositionIds: z.array(idSchema).max(5e4).optional(),
1311
- viewport: z.object({
1312
- x: finiteSchema,
1313
- y: finiteSchema,
1314
- width: finiteSchema.positive(),
1315
- height: finiteSchema.positive()
1316
- }).optional()
1317
- }).strict();
1318
1182
  //#endregion
1319
- //#region ../protocol/dist/space-hooks.js
1320
- const SPACE_HOOK_WEBHOOK_SECRET_HEADER = "x-cohub-webhook-secret";
1183
+ //#region src/apis/generations.ts
1184
+ const DEFAULT_INTERVAL_MS = 1500;
1185
+ const DEFAULT_TIMEOUT_MS = 18e5;
1186
+ function sleep$1(ms, signal) {
1187
+ if (signal?.aborted) return Promise.reject(signal.reason ?? /* @__PURE__ */ new Error("Generation wait aborted"));
1188
+ return new Promise((resolve, reject) => {
1189
+ const cleanup = () => signal?.removeEventListener("abort", onAbort);
1190
+ const timer = setTimeout(() => {
1191
+ cleanup();
1192
+ resolve();
1193
+ }, ms);
1194
+ const onAbort = () => {
1195
+ clearTimeout(timer);
1196
+ cleanup();
1197
+ reject(signal?.reason ?? /* @__PURE__ */ new Error("Generation wait aborted"));
1198
+ };
1199
+ signal?.addEventListener("abort", onAbort, { once: true });
1200
+ });
1201
+ }
1202
+ function isGenerationTaskResult(value) {
1203
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1204
+ const record = value;
1205
+ if (typeof record.model !== "string" || !Array.isArray(record.output)) return false;
1206
+ if (record.requestId !== void 0 && typeof record.requestId !== "string") return false;
1207
+ if (record.cost !== void 0 && (typeof record.cost !== "number" || !Number.isFinite(record.cost))) return false;
1208
+ if (record.billing !== void 0 && record.billing !== null) {
1209
+ if (!record.billing || typeof record.billing !== "object" || Array.isArray(record.billing)) return false;
1210
+ const billing = record.billing;
1211
+ if (billing.officialCostUsd !== void 0 && (typeof billing.officialCostUsd !== "number" || !Number.isFinite(billing.officialCostUsd))) return false;
1212
+ if (typeof billing.amountUsd !== "number" || !Number.isFinite(billing.amountUsd)) return false;
1213
+ if (billing.discountMultiplier !== void 0 && (typeof billing.discountMultiplier !== "number" || !Number.isFinite(billing.discountMultiplier) || billing.discountMultiplier < 0 || billing.discountMultiplier > 1)) return false;
1214
+ if (typeof billing.usageType !== "string") return false;
1215
+ if (billing.status !== "recorded" && billing.status !== "overage" && billing.status !== "skipped") return false;
1216
+ if (billing.reason !== void 0 && billing.reason !== null && typeof billing.reason !== "string") return false;
1217
+ }
1218
+ return true;
1219
+ }
1220
+ var GenerationsApi = class {
1221
+ transport;
1222
+ constructor(transport) {
1223
+ this.transport = transport;
1224
+ }
1225
+ async create(request) {
1226
+ return this.transport.request("/api/generations", {
1227
+ method: "POST",
1228
+ headers: { "Content-Type": "application/json" },
1229
+ body: JSON.stringify(request)
1230
+ });
1231
+ }
1232
+ async wait(taskRunId, options = {}) {
1233
+ const intervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS;
1234
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
1235
+ const startedAt = Date.now();
1236
+ while (true) {
1237
+ if (options.signal?.aborted) throw options.signal.reason ?? /* @__PURE__ */ new Error("Generation wait aborted");
1238
+ const detail = await this.transport.request(`/api/tasks/${taskRunId}`);
1239
+ options.onPoll?.(detail);
1240
+ if (detail.run.taskType !== "generation") throw new Error(`Task is not a generation task: ${detail.run.taskType}`);
1241
+ if (detail.run.status === "completed") {
1242
+ if (!isGenerationTaskResult(detail.run.result)) throw new Error("Generation task completed without a valid result");
1243
+ return detail.run.result;
1244
+ }
1245
+ if (detail.run.status === "failed") {
1246
+ const message = detail.run.errorMessage || "Generation task failed";
1247
+ throw new Error(`${message}\ntask ID: ${taskRunId}`);
1248
+ }
1249
+ const remainingMs = timeoutMs - (Date.now() - startedAt);
1250
+ if (remainingMs <= 0) throw new Error(`Generation task timed out after ${timeoutMs}ms`);
1251
+ await sleep$1(Math.min(intervalMs, remainingMs), options.signal);
1252
+ }
1253
+ }
1254
+ async createAndWait(request, options) {
1255
+ const created = await this.create(request);
1256
+ return this.wait(created.taskRunId, options);
1257
+ }
1258
+ };
1321
1259
  //#endregion
1322
- //#region ../protocol/dist/navigation.js
1323
- const NAVIGATION_LAUNCH_MAX_LENGTH = 2048;
1324
- const NAVIGATION_ERROR_MESSAGE_MAX_LENGTH = 2e3;
1260
+ //#region src/apis/models.ts
1261
+ const MULTIMODAL_MODEL_TYPE = "multimodal";
1262
+ var ModelsApi = class {
1263
+ transport;
1264
+ constructor(transport) {
1265
+ this.transport = transport;
1266
+ }
1267
+ async list() {
1268
+ return this.transport.request("/api/models");
1269
+ }
1270
+ async listMultimodal() {
1271
+ return this.transport.request(`/api/models?modelType=${MULTIMODAL_MODEL_TYPE}`);
1272
+ }
1273
+ async status() {
1274
+ return this.transport.request("/api/models/status");
1275
+ }
1276
+ };
1325
1277
  //#endregion
1326
- //#region ../protocol/dist/desktop-command.js
1327
- /**
1328
- * Lets an agent drive the Cohub desktop that originated the app. Routing comes
1329
- * from request provenance, never a caller-supplied target, so a command only
1330
- * reaches the actor's own instances.
1331
- *
1332
- * The canonical command is `desktop.open`. The legacy `preview.show` shape is
1333
- * accepted on input and normalized, so older clients keep working, but commands
1334
- * are always stored and dispatched in the canonical form.
1335
- */
1336
- const DESKTOP_COMMAND_VERSION = 1;
1337
- /** Persisted and broadcast, so every field is capped; MAX_BYTES bounds the whole. */
1338
- const DESKTOP_COMMAND_PAYLOAD_MAX_BYTES = 32768;
1339
- const DESKTOP_COMMAND_MAX_BYTES = 40960;
1340
- const DESKTOP_COMMAND_LAUNCH_MAX_LENGTH = NAVIGATION_LAUNCH_MAX_LENGTH;
1341
- const DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS = 6e5;
1342
- const DESKTOP_COMMAND_MAX_TIMEOUT_MS = 432e5;
1343
- const DESKTOP_COMMAND_SETTLEMENT_GRACE_SECONDS = 600;
1344
- /** Keeps pending commands reportable for the full wait window plus settlement grace. */
1345
- const DESKTOP_COMMAND_PENDING_TTL_SECONDS = 43800;
1346
- const DESKTOP_COMMAND_TERMINAL_TTL_SECONDS = 1800;
1347
- const DESKTOP_COMMAND_TERMINAL_STATUSES = [
1348
- "applied",
1349
- "no_active_client",
1350
- "desktop_host_unavailable",
1351
- "rejected",
1352
- "unsupported",
1353
- "timeout"
1354
- ];
1355
- const isTerminalDesktopCommandStatus = (status) => DESKTOP_COMMAND_TERMINAL_STATUSES.includes(status);
1356
- /**
1357
- * The surface a desktop open should use: an explicit request wins, then
1358
- * whatever the App declared at publish time. `window` is the implicit
1359
- * default and yields `undefined` so the command stays compact.
1360
- */
1361
- function resolveOpenSurface(requested, declared) {
1362
- return (requested ?? declared) === "overlay" ? "overlay" : void 0;
1363
- }
1364
- const METHOD_RE = new RegExp(`^[A-Za-z][A-Za-z0-9_.:-]{0,63}$`);
1365
- const isDesktopCallMethod = (value) => typeof value === "string" && METHOD_RE.test(value);
1366
- const DESKTOP_COMMAND_ID_RE = /^[A-Za-z0-9_-]{1,64}$/;
1367
- const parseDesktopCommandId = (value) => {
1368
- if (typeof value !== "string") return null;
1369
- const trimmed = value.trim();
1370
- return DESKTOP_COMMAND_ID_RE.test(trimmed) ? trimmed : null;
1278
+ //#region src/apis/prompts.ts
1279
+ var PromptsApi = class {
1280
+ transport;
1281
+ constructor(transport) {
1282
+ this.transport = transport;
1283
+ }
1284
+ async list(options, customFetch) {
1285
+ const params = new URLSearchParams();
1286
+ if (options?.spaceId) params.set("spaceId", options.spaceId);
1287
+ const query = params.toString();
1288
+ const path = query ? `/api/prompts?${query}` : "/api/prompts";
1289
+ return this.transport.request(path, { fetch: customFetch });
1290
+ }
1371
1291
  };
1372
- const isRecord$1 = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
1373
- const asTrimmed = (value) => {
1374
- if (typeof value !== "string") return null;
1375
- const trimmed = value.trim();
1376
- return trimmed ? trimmed : null;
1292
+ //#endregion
1293
+ //#region src/apis/skills.ts
1294
+ var SkillsApi = class {
1295
+ transport;
1296
+ constructor(transport) {
1297
+ this.transport = transport;
1298
+ }
1299
+ async list(options, customFetch) {
1300
+ const params = new URLSearchParams();
1301
+ if (options?.spaceId) params.set("spaceId", options.spaceId);
1302
+ const query = params.toString();
1303
+ const path = query ? `/api/skills?${query}` : "/api/skills";
1304
+ return this.transport.request(path, { fetch: customFetch });
1305
+ }
1377
1306
  };
1378
- const parseLaunch = (value) => {
1379
- if (!isRecord$1(value)) return void 0;
1380
- const search = asTrimmed(value.search);
1381
- const hash = asTrimmed(value.hash);
1382
- if (!search && !hash) return void 0;
1383
- return {
1384
- ...search ? { search: search.startsWith("?") ? search : `?${search}` } : {},
1385
- ...hash ? { hash: hash.startsWith("#") ? hash : `#${hash}` } : {}
1386
- };
1307
+ //#endregion
1308
+ //#region src/apis/public-assets.ts
1309
+ function createAbortError() {
1310
+ if (typeof DOMException === "function") return new DOMException("The operation was aborted.", "AbortError");
1311
+ const error = /* @__PURE__ */ new Error("The operation was aborted.");
1312
+ error.name = "AbortError";
1313
+ return error;
1314
+ }
1315
+ function uploadWithProgress(input) {
1316
+ return new Promise((resolve, reject) => {
1317
+ if (input.signal?.aborted) {
1318
+ reject(createAbortError());
1319
+ return;
1320
+ }
1321
+ const xhr = new XMLHttpRequest();
1322
+ let settled = false;
1323
+ let lastRatio = 0;
1324
+ const handleSignalAbort = () => xhr.abort();
1325
+ const finish = (callback) => {
1326
+ if (settled) return;
1327
+ settled = true;
1328
+ input.signal?.removeEventListener("abort", handleSignalAbort);
1329
+ callback();
1330
+ };
1331
+ const emitProgress = (loaded, total) => {
1332
+ const ratio = Math.max(lastRatio, Math.min(1, total > 0 ? loaded / total : 0));
1333
+ lastRatio = ratio;
1334
+ try {
1335
+ input.onProgress({
1336
+ loadedBytes: Math.round(input.fileBytes * ratio),
1337
+ totalBytes: input.fileBytes,
1338
+ ratio
1339
+ });
1340
+ } catch {}
1341
+ };
1342
+ try {
1343
+ xhr.open(input.method, input.url);
1344
+ for (const [key, value] of Object.entries(input.headers ?? {})) {
1345
+ if (/[\r\n\0]/.test(value)) continue;
1346
+ xhr.setRequestHeader(key, value);
1347
+ }
1348
+ xhr.upload.onprogress = (event) => {
1349
+ if (event.lengthComputable) emitProgress(event.loaded, event.total);
1350
+ };
1351
+ xhr.onload = () => {
1352
+ if (xhr.status >= 200 && xhr.status < 300) {
1353
+ emitProgress(1, 1);
1354
+ finish(resolve);
1355
+ return;
1356
+ }
1357
+ const detail = xhr.responseText?.trim();
1358
+ finish(() => reject(/* @__PURE__ */ new Error(`Public asset upload failed: HTTP ${xhr.status}${detail ? ` — ${detail}` : ""}`)));
1359
+ };
1360
+ xhr.onerror = () => finish(() => reject(/* @__PURE__ */ new Error("Public asset upload failed")));
1361
+ xhr.onabort = () => finish(() => reject(createAbortError()));
1362
+ input.signal?.addEventListener("abort", handleSignalAbort, { once: true });
1363
+ xhr.send(input.body);
1364
+ } catch (error) {
1365
+ finish(() => reject(error));
1366
+ }
1367
+ });
1368
+ }
1369
+ var PublicAssetsApi = class {
1370
+ transport;
1371
+ constructor(transport) {
1372
+ this.transport = transport;
1373
+ }
1374
+ createUpload(input, options = {}) {
1375
+ return this.transport.request("/api/public-assets/uploads", {
1376
+ method: "POST",
1377
+ headers: { "Content-Type": "application/json" },
1378
+ body: JSON.stringify(input),
1379
+ signal: options.signal
1380
+ });
1381
+ }
1382
+ async upload(input) {
1383
+ if (input.signal?.aborted) throw createAbortError();
1384
+ const plan = await this.createUpload({
1385
+ purpose: input.purpose,
1386
+ uploadProtocol: "presigned_put_v1",
1387
+ spaceId: input.spaceId,
1388
+ sessionId: input.sessionId,
1389
+ file: {
1390
+ size: input.file.size,
1391
+ mimeType: input.mimeType,
1392
+ filename: input.filename
1393
+ }
1394
+ }, { signal: input.signal });
1395
+ if (input.signal?.aborted) throw createAbortError();
1396
+ let response;
1397
+ if (input.onProgress && typeof XMLHttpRequest === "function") {
1398
+ await uploadWithProgress({
1399
+ method: "PUT",
1400
+ url: plan.asset.uploadUrl,
1401
+ headers: plan.asset.uploadHeaders,
1402
+ body: input.file,
1403
+ fileBytes: input.file.size,
1404
+ onProgress: input.onProgress,
1405
+ signal: input.signal
1406
+ });
1407
+ return plan.asset;
1408
+ }
1409
+ response = await fetch(plan.asset.uploadUrl, {
1410
+ method: "PUT",
1411
+ headers: plan.asset.uploadHeaders,
1412
+ body: input.file,
1413
+ signal: input.signal
1414
+ });
1415
+ if (!response.ok) {
1416
+ const detail = await response.text().catch(() => "");
1417
+ throw new Error(`Public asset upload failed: HTTP ${response.status}${detail ? ` — ${detail}` : ""}`);
1418
+ }
1419
+ return plan.asset;
1420
+ }
1421
+ /** Durable public upload for any chat attachment (image or file). No space required. */
1422
+ uploadAppSource(input) {
1423
+ return this.upload({
1424
+ purpose: "app_source",
1425
+ spaceId: input.spaceId,
1426
+ sessionId: input.sessionId,
1427
+ file: input.file,
1428
+ mimeType: input.mimeType,
1429
+ filename: input.filename,
1430
+ onProgress: input.onProgress,
1431
+ signal: input.signal
1432
+ });
1433
+ }
1434
+ uploadChatAttachment(input) {
1435
+ return this.upload({
1436
+ purpose: "chat_attachment",
1437
+ spaceId: input.spaceId,
1438
+ sessionId: input.sessionId,
1439
+ file: input.file,
1440
+ mimeType: input.mimeType,
1441
+ filename: input.filename,
1442
+ onProgress: input.onProgress,
1443
+ signal: input.signal
1444
+ });
1445
+ }
1446
+ /** Preprocessed chat image (webp/jpeg). Same durable path as uploadChatAttachment. */
1447
+ uploadChatImageAttachment(input) {
1448
+ return this.uploadChatAttachment(input);
1449
+ }
1387
1450
  };
1388
- const measureDesktopCommandPayload = (value) => {
1389
- if (value === void 0) return 0;
1390
- try {
1391
- return new TextEncoder().encode(JSON.stringify(value) ?? "").length;
1392
- } catch {
1393
- return null;
1451
+ //#endregion
1452
+ //#region src/apis/search.ts
1453
+ var SearchApi = class {
1454
+ transport;
1455
+ constructor(transport) {
1456
+ this.transport = transport;
1457
+ }
1458
+ query(input, customFetch) {
1459
+ const params = new URLSearchParams({ q: input.q });
1460
+ if (input.limit !== void 0) params.set("limit", String(input.limit));
1461
+ for (const type of input.types ?? []) params.append("type", type);
1462
+ if (input.spaceId) params.set("spaceId", input.spaceId);
1463
+ if (input.labelRef) params.set("labelRef", input.labelRef);
1464
+ if (input.groupTurns === false) params.set("groupTurns", "false");
1465
+ return this.transport.request(`/api/search?${params.toString()}`, { fetch: customFetch });
1466
+ }
1467
+ /** Default (empty-query) command palette data: viewer-relative space signals. */
1468
+ overview(input, customFetch) {
1469
+ const params = new URLSearchParams();
1470
+ if (input?.spaceLimit !== void 0) params.set("spaceLimit", String(input.spaceLimit));
1471
+ if (input?.sessionLimit !== void 0) params.set("sessionLimit", String(input.sessionLimit));
1472
+ for (const spaceId of input?.recentSpaceIds ?? []) params.append("recentSpaceId", spaceId);
1473
+ const query = params.toString();
1474
+ return this.transport.request(`/api/palette/overview${query ? `?${query}` : ""}`, { fetch: customFetch });
1394
1475
  }
1395
1476
  };
1477
+ //#endregion
1478
+ //#region src/apis/references.ts
1479
+ const toSelector = (ref) => typeof ref === "string" ? ref : `${ref.type}:${ref.id}`;
1396
1480
  /**
1397
- * Parse a desktop command from untrusted input.
1398
- *
1399
- * Accepts both the canonical `desktop.open` shape (`target` / `call`) and the
1400
- * legacy `preview.show` shape (`preview` / `request`); the result is always
1401
- * canonical so every later stage handles a single form.
1481
+ * Neutral access to the resource reference index. `query` lists references
1482
+ * touching a resource; `aggregate` returns grouped counts for a space. Callers
1483
+ * assemble collaboration graphs, lineage views, rankings, or trends from the
1484
+ * returned data.
1402
1485
  */
1403
- const parseDesktopCommand = (input) => {
1404
- if (!isRecord$1(input)) return {
1405
- command: null,
1406
- error: "command must be an object"
1407
- };
1408
- const legacy = input.type === "preview.show";
1409
- if (input.type !== "desktop.open" && !legacy) return {
1410
- command: null,
1411
- error: "command.type must be one of: desktop.open"
1412
- };
1413
- const target = isRecord$1(input.target) ? input.target : isRecord$1(input.preview) ? input.preview : null;
1414
- if (!target) return {
1415
- command: null,
1416
- error: "command.target is required"
1417
- };
1418
- const kind = target.kind === "app" || legacy && target.kind === "work" ? "app" : target.kind === "file" ? "file" : null;
1419
- if (!kind) return {
1420
- command: null,
1421
- error: "command.target.kind must be one of: app, file"
1422
- };
1423
- if (kind === "file") {
1424
- const path = asTrimmed(target.path);
1425
- if (!path) return {
1426
- command: null,
1427
- error: "command.target.path is required"
1428
- };
1429
- if (path.length > 2048 || path.startsWith("/") || path.includes("\0") || path.includes("\\") || path.split("/").some((segment) => segment === "..")) return {
1430
- command: null,
1431
- error: "command.target.path must be a relative Space file path"
1432
- };
1433
- if (input.call !== void 0 && input.call !== null) return {
1434
- command: null,
1435
- error: "command.call is only supported for app targets"
1436
- };
1437
- if (legacy && input.request !== void 0 && input.request !== null) return {
1438
- command: null,
1439
- error: "command.call is only supported for app targets"
1440
- };
1441
- return {
1442
- command: {
1443
- type: "desktop.open",
1444
- target: {
1445
- kind: "file",
1446
- path
1447
- }
1448
- },
1449
- error: null
1450
- };
1486
+ var ReferencesApi = class {
1487
+ transport;
1488
+ constructor(transport) {
1489
+ this.transport = transport;
1451
1490
  }
1452
- const appId = asTrimmed(target.appId ?? target.workId);
1453
- if (!appId) return {
1454
- command: null,
1455
- error: "command.target.appId is required"
1456
- };
1457
- if (!isUuid(appId)) return {
1458
- command: null,
1459
- error: "command.target.appId must be an App id"
1460
- };
1461
- const label = asTrimmed(target.label);
1462
- if (label && label.length > 200) return {
1463
- command: null,
1464
- error: `command.target.label exceeds 200 characters`
1465
- };
1466
- const launch = parseLaunch(target.launch);
1467
- if (launch) {
1468
- for (const [field, value] of [["search", launch.search], ["hash", launch.hash]]) if (value && value.length > DESKTOP_COMMAND_LAUNCH_MAX_LENGTH) return {
1469
- command: null,
1470
- error: `command.target.launch.${field} exceeds ${DESKTOP_COMMAND_LAUNCH_MAX_LENGTH} characters`
1471
- };
1491
+ query(input, customFetch) {
1492
+ const params = new URLSearchParams({ source: toSelector(input.source) });
1493
+ if (input.direction) params.set("direction", input.direction);
1494
+ if (input.kinds && input.kinds.length > 0) params.set("kinds", input.kinds.join(","));
1495
+ if (input.days !== void 0) params.set("days", String(input.days));
1496
+ if (input.limit !== void 0) params.set("limit", String(input.limit));
1497
+ return this.transport.request(`/api/references?${params.toString()}`, { fetch: customFetch });
1472
1498
  }
1473
- const callSource = input.call !== void 0 ? input.call : input.request;
1474
- let call;
1475
- if (callSource !== void 0 && callSource !== null) {
1476
- if (!isRecord$1(callSource)) return {
1477
- command: null,
1478
- error: "command.call must be an object"
1479
- };
1480
- const method = asTrimmed(callSource.method);
1481
- if (!method) return {
1482
- command: null,
1483
- error: "command.call.method is required"
1484
- };
1485
- if (!isDesktopCallMethod(method)) return {
1486
- command: null,
1487
- error: "command.call.method has an unsupported format"
1488
- };
1489
- const size = measureDesktopCommandPayload(callSource.input);
1490
- if (size === null) return {
1491
- command: null,
1492
- error: "command.call.input must be JSON-serializable"
1493
- };
1494
- if (size > 32768) return {
1495
- command: null,
1496
- error: `command.call.input exceeds ${DESKTOP_COMMAND_PAYLOAD_MAX_BYTES} bytes`
1497
- };
1498
- call = {
1499
- method,
1500
- ...callSource.input === void 0 ? {} : { input: callSource.input }
1501
- };
1499
+ aggregate(input, customFetch) {
1500
+ const params = new URLSearchParams({ space: input.spaceId });
1501
+ if (input.groupBy) params.set("groupBy", input.groupBy);
1502
+ if (input.kinds && input.kinds.length > 0) params.set("kinds", input.kinds.join(","));
1503
+ if (input.days !== void 0) params.set("days", String(input.days));
1504
+ if (input.limit !== void 0) params.set("limit", String(input.limit));
1505
+ return this.transport.request(`/api/references/aggregate?${params.toString()}`, { fetch: customFetch });
1506
+ }
1507
+ };
1508
+ //#endregion
1509
+ //#region src/apis/session-access.ts
1510
+ var SessionAccessApi = class {
1511
+ transport;
1512
+ constructor(transport) {
1513
+ this.transport = transport;
1514
+ }
1515
+ get(sessionId) {
1516
+ return this.transport.request(`/api/sessions/${sessionId}/access`);
1517
+ }
1518
+ set(sessionId, body) {
1519
+ return this.transport.request(`/api/sessions/${sessionId}/access`, {
1520
+ method: "PATCH",
1521
+ headers: { "Content-Type": "application/json" },
1522
+ body: JSON.stringify(body)
1523
+ });
1524
+ }
1525
+ remove(sessionId) {
1526
+ return this.transport.request(`/api/sessions/${sessionId}/access`, { method: "DELETE" });
1502
1527
  }
1503
- const surface = target.surface === "overlay" ? "overlay" : void 0;
1504
- const command = {
1505
- type: "desktop.open",
1506
- target: {
1507
- kind: "app",
1508
- appId,
1509
- ...label ? { label } : {},
1510
- ...launch ? { launch } : {},
1511
- ...surface ? { surface } : {}
1512
- },
1513
- ...call ? { call } : {}
1514
- };
1515
- const totalSize = measureDesktopCommandPayload(command);
1516
- if (totalSize === null) return {
1517
- command: null,
1518
- error: "command must be JSON-serializable"
1519
- };
1520
- if (totalSize > 40960) return {
1521
- command: null,
1522
- error: `command exceeds ${DESKTOP_COMMAND_MAX_BYTES} bytes`
1523
- };
1524
- return {
1525
- command,
1526
- error: null
1527
- };
1528
1528
  };
1529
1529
  //#endregion
1530
1530
  //#region src/realtime.ts
@@ -3972,6 +3972,35 @@ var SpaceClient = class {
3972
3972
  get(customFetch) {
3973
3973
  return this.transport.request(`/api/spaces/${this.id}`, { fetch: customFetch });
3974
3974
  }
3975
+ prepareRuntimeArchive(index, options = {}) {
3976
+ return this.transport.request(`/api/spaces/${this.id}/runtime/archives/prepare`, {
3977
+ method: "POST",
3978
+ headers: { "Content-Type": "application/json" },
3979
+ body: JSON.stringify(index),
3980
+ signal: options.signal
3981
+ });
3982
+ }
3983
+ commitRuntimeArchive(index, options = {}) {
3984
+ return this.transport.request(`/api/spaces/${this.id}/runtime/archives/commit`, {
3985
+ method: "POST",
3986
+ headers: { "Content-Type": "application/json" },
3987
+ body: JSON.stringify(index),
3988
+ signal: options.signal
3989
+ });
3990
+ }
3991
+ getRuntimeArchive(sessionId, turnId, options = {}) {
3992
+ return this.transport.request(`/api/spaces/${this.id}/runtime/archives/${sessionId}/${turnId}`, { signal: options.signal });
3993
+ }
3994
+ getRuntime(customFetch) {
3995
+ return this.transport.request(`/api/spaces/${this.id}/runtime`, { fetch: customFetch });
3996
+ }
3997
+ confirmRuntimeStopped(confirmation) {
3998
+ return this.transport.request(`/api/spaces/${this.id}/runtime/confirm-stopped`, {
3999
+ method: "POST",
4000
+ headers: { "Content-Type": "application/json" },
4001
+ body: JSON.stringify(confirmation)
4002
+ });
4003
+ }
3975
4004
  getStartup(customFetch) {
3976
4005
  return this.transport.request(`/api/spaces/${this.id}/startup`, { fetch: customFetch });
3977
4006
  }
@@ -4720,4 +4749,4 @@ var CohubHttpClient = class {
4720
4749
  };
4721
4750
  const createHttpClient = (options) => new CohubHttpClient(options);
4722
4751
  //#endregion
4723
- export { BoardConnectionSchema as $, DESKTOP_COMMAND_SETTLEMENT_GRACE_SECONDS as A, BoardItemPatchSchema as B, SessionPatchReducer as C, DESKTOP_COMMAND_MAX_TIMEOUT_MS as D, DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS as E, parseDesktopCommand as F, parseBoardEffectInput as G, BoardSemanticMutationSchema as H, parseDesktopCommandId as I, BoardCompositionInputSchema as J, BOARD_ANIMATION_CHANNELS as K, resolveOpenSurface as L, DESKTOP_COMMAND_VERSION as M, isDesktopCallMethod as N, DESKTOP_COMMAND_PAYLOAD_MAX_BYTES as O, isTerminalDesktopCommandStatus as P, parseBoardCompositionInput as Q, NAVIGATION_ERROR_MESSAGE_MAX_LENGTH as R, parseAssistantMessageCommit as S, ensureRealtimeConnected as T, BoardEffectInputSchema as U, BoardSemanticCommandSchema as V, BoardEffectSchema as W, BoardProceduralClipSchema as X, BoardCompositionSchema as Y, BoardTrackSchema as Z, PublicInviteApi as _, DesktopCommandsApi as a, SessionAccessApi as at, SessionGenerationStreamClient as b, ReferralsApi as c, PublicAssetsApi as ct, TasksApi as d, ModelsApi as dt, BOARD_ARROW_STROKE_SIZE as et, BoardClient as f, GenerationsApi as ft, SpacesApi as g, SpaceWebhooksApi as h, WorkCommerceApi as i, DEFAULT_BOARD_RENDER_LIMITS as it, DESKTOP_COMMAND_TERMINAL_TTL_SECONDS as j, DESKTOP_COMMAND_PENDING_TTL_SECONDS as k, UsersApi as l, SkillsApi as lt, SpacePublicFilesApi as m, ChannelsApi as mt, createHttpClient as n, BOARD_BUILTIN_CLIP_KINDS as nt, UiCommandsApi as o, ReferencesApi as ot, SpaceClient as p, CronJobsApi as pt, BOARD_ANIMATION_CHANNEL_CAPABILITIES as q, AppCommerceApi as r, BOARD_BUILTIN_EFFECT_KINDS as rt, AppsApi as s, SearchApi as st, CohubHttpClient as t, BOARD_BUILTIN_CAPABILITIES as tt, UserApi as u, PromptsApi as ut, buildSpaceInvitePath as v, createSessionPatchReducer as w, createSessionGenerationStreamClient as x, buildSpacePath as y, BoardAuthoringItemSchema as z };
4752
+ export { BoardEffectInputSchema as $, SkillsApi as A, DESKTOP_COMMAND_SETTLEMENT_GRACE_SECONDS as B, SessionPatchReducer as C, ReferencesApi as D, SessionAccessApi as E, ChannelsApi as F, parseDesktopCommand as G, DESKTOP_COMMAND_VERSION as H, DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS as I, NAVIGATION_ERROR_MESSAGE_MAX_LENGTH as J, parseDesktopCommandId as K, DESKTOP_COMMAND_MAX_TIMEOUT_MS as L, ModelsApi as M, GenerationsApi as N, SearchApi as O, CronJobsApi as P, BoardSemanticMutationSchema as Q, DESKTOP_COMMAND_PAYLOAD_MAX_BYTES as R, parseAssistantMessageCommit as S, ensureRealtimeConnected as T, isDesktopCallMethod as U, DESKTOP_COMMAND_TERMINAL_TTL_SECONDS as V, isTerminalDesktopCommandStatus as W, BoardItemPatchSchema as X, BoardAuthoringItemSchema as Y, BoardSemanticCommandSchema as Z, PublicInviteApi as _, DesktopCommandsApi as a, BoardCompositionSchema as at, SessionGenerationStreamClient as b, ReferralsApi as c, parseBoardCompositionInput as ct, TasksApi as d, BOARD_BUILTIN_CAPABILITIES as dt, BoardEffectSchema as et, BoardClient as f, BOARD_BUILTIN_CLIP_KINDS as ft, SpacesApi as g, SpaceWebhooksApi as h, WorkCommerceApi as i, BoardCompositionInputSchema as it, PromptsApi as j, PublicAssetsApi as k, UsersApi as l, BoardConnectionSchema as lt, SpacePublicFilesApi as m, DEFAULT_BOARD_RENDER_LIMITS as mt, createHttpClient as n, BOARD_ANIMATION_CHANNELS as nt, UiCommandsApi as o, BoardProceduralClipSchema as ot, SpaceClient as p, BOARD_BUILTIN_EFFECT_KINDS as pt, resolveOpenSurface as q, AppCommerceApi as r, BOARD_ANIMATION_CHANNEL_CAPABILITIES as rt, AppsApi as s, BoardTrackSchema as st, CohubHttpClient as t, parseBoardEffectInput as tt, UserApi as u, BOARD_ARROW_STROKE_SIZE as ut, buildSpaceInvitePath as v, createSessionPatchReducer as w, createSessionGenerationStreamClient as x, buildSpacePath as y, DESKTOP_COMMAND_PENDING_TTL_SECONDS as z };