@openchamber/sdk 1.23.2-preview.1

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.
Files changed (75) hide show
  1. package/API.md +529 -0
  2. package/DOCUMENTATION.md +81 -0
  3. package/GUEST_SERVICES.md +166 -0
  4. package/LICENSE +21 -0
  5. package/README.md +186 -0
  6. package/dist/api-version.d.ts +6 -0
  7. package/dist/api-version.js +5 -0
  8. package/dist/contract.d.ts +471 -0
  9. package/dist/contract.js +256 -0
  10. package/dist/host-version.d.ts +16 -0
  11. package/dist/host-version.js +50 -0
  12. package/dist/host.d.ts +101 -0
  13. package/dist/host.js +606 -0
  14. package/dist/index.d.ts +12 -0
  15. package/dist/index.js +7 -0
  16. package/dist/manifest.d.ts +298 -0
  17. package/dist/manifest.js +224 -0
  18. package/dist/parse.d.ts +279 -0
  19. package/dist/parse.js +379 -0
  20. package/dist/protocol.d.ts +1092 -0
  21. package/dist/protocol.js +450 -0
  22. package/dist/schemas.d.ts +4 -0
  23. package/dist/schemas.js +6 -0
  24. package/dist/ui/badge.d.ts +10 -0
  25. package/dist/ui/badge.js +26 -0
  26. package/dist/ui/banner.d.ts +13 -0
  27. package/dist/ui/banner.js +48 -0
  28. package/dist/ui/button.d.ts +14 -0
  29. package/dist/ui/button.js +49 -0
  30. package/dist/ui/checkbox.d.ts +12 -0
  31. package/dist/ui/checkbox.js +45 -0
  32. package/dist/ui/dom.d.ts +15 -0
  33. package/dist/ui/dom.js +60 -0
  34. package/dist/ui/empty.d.ts +11 -0
  35. package/dist/ui/empty.js +44 -0
  36. package/dist/ui/field.d.ts +18 -0
  37. package/dist/ui/field.js +49 -0
  38. package/dist/ui/icons.d.ts +10 -0
  39. package/dist/ui/icons.js +23 -0
  40. package/dist/ui/index.d.ts +35 -0
  41. package/dist/ui/index.js +17 -0
  42. package/dist/ui/list.d.ts +26 -0
  43. package/dist/ui/list.js +90 -0
  44. package/dist/ui/menu.d.ts +20 -0
  45. package/dist/ui/menu.js +111 -0
  46. package/dist/ui/navigation.d.ts +18 -0
  47. package/dist/ui/navigation.js +38 -0
  48. package/dist/ui/option.d.ts +16 -0
  49. package/dist/ui/option.js +35 -0
  50. package/dist/ui/popup.d.ts +5 -0
  51. package/dist/ui/popup.js +43 -0
  52. package/dist/ui/progress.d.ts +11 -0
  53. package/dist/ui/progress.js +44 -0
  54. package/dist/ui/search.d.ts +11 -0
  55. package/dist/ui/search.js +62 -0
  56. package/dist/ui/select.d.ts +22 -0
  57. package/dist/ui/select.js +164 -0
  58. package/dist/ui/separator.d.ts +6 -0
  59. package/dist/ui/separator.js +26 -0
  60. package/dist/ui/spinner.d.ts +8 -0
  61. package/dist/ui/spinner.js +29 -0
  62. package/dist/ui/style.d.ts +1 -0
  63. package/dist/ui/style.js +190 -0
  64. package/dist/ui/tabs.d.ts +15 -0
  65. package/dist/ui/tabs.js +61 -0
  66. package/dist/ui/text.d.ts +23 -0
  67. package/dist/ui/text.js +89 -0
  68. package/dist/ui/theme.d.ts +22 -0
  69. package/dist/ui/theme.js +69 -0
  70. package/dist/workspace-schemas.d.ts +136 -0
  71. package/dist/workspace-schemas.js +44 -0
  72. package/dist/workspace.d.ts +109 -0
  73. package/dist/workspace.js +4 -0
  74. package/package.json +55 -0
  75. package/scripts/bundle-guest.ts +44 -0
@@ -0,0 +1,450 @@
1
+ import { z } from 'zod';
2
+ import { guestSessionWorktreeSchema, guestStorageRequestSchema, guestStorageResultSchema, guestWorkspaceQuerySchema, guestWorkspaceSnapshotSchema } from "./workspace-schemas.js";
3
+ import { OPENCHAMBER_SDK_API_VERSION, OPENCHAMBER_SDK_CHANNEL } from "./api-version.js";
4
+ import { SERVICE_STATUS_VALUES, ATTACH_PROVIDER_ID, GUEST_ACCOUNT_MAX, GUEST_ATTACH_AUTHOR_MAX, GUEST_BADGE_MAX, GUEST_ITEM_MESSAGE_TEXT_MAX, GUEST_ITEM_SESSION_MAX, GUEST_RESOLVE_ERROR_MAX, GUEST_ATTACH_BRANCH_MAX, GUEST_ATTACH_DATA_MAX, GUEST_ATTACH_ID_MAX, GUEST_ATTACH_TEXT_MAX, GUEST_ATTACH_TITLE_MAX, GUEST_ATTACH_URL_MAX, GUEST_CLIPBOARD_TEXT_MAX, GUEST_COMPOSE_TEXT_MAX, GUEST_FILE_CONTENT_MAX, GUEST_FILE_ENTRY_KINDS, GUEST_FILE_LIST_MAX, GUEST_GENERATE_OUTPUT_TOKENS_MAX, GUEST_GENERATE_PROMPT_MAX, GUEST_GENERATE_SYSTEM_MAX, GUEST_GENERATE_TEXT_MAX, GUEST_FILE_PATH_MAX, GUEST_FILE_STAT_KINDS, GUEST_REQUEST_BODY_MAX, GUEST_REQUEST_PATH_MAX, GUEST_REQUEST_RESPONSE_MAX, GUEST_SESSION_AGENT_MAX, GUEST_SESSION_MODEL_MAX, GUEST_SETTING_VALUE_MAX, GUEST_TOAST_MAX, SESSION_LIFECYCLE_PHASES, SETTING_KEY, START_SESSION_SENT, isGuestFilePath, isGuestRequestPath, resolveHostRequestErrorCode, } from "./contract.js";
5
+ const envelope = {
6
+ channel: z.literal(OPENCHAMBER_SDK_CHANNEL),
7
+ v: z.literal(OPENCHAMBER_SDK_API_VERSION),
8
+ };
9
+ const themeTokensSchema = z.object({
10
+ background: z.string().min(1),
11
+ elevated: z.string().min(1),
12
+ foreground: z.string().min(1),
13
+ muted: z.string().min(1),
14
+ subtle: z.string().min(1),
15
+ border: z.string().min(1),
16
+ hover: z.string().min(1),
17
+ selection: z.string().min(1),
18
+ focus: z.string().min(1),
19
+ primary: z.string().min(1),
20
+ mutedSurface: z.string().min(1),
21
+ elevatedForeground: z.string().min(1),
22
+ active: z.string().min(1),
23
+ selectionForeground: z.string().min(1),
24
+ primaryForeground: z.string().min(1),
25
+ success: z.string().min(1),
26
+ warning: z.string().min(1),
27
+ error: z.string().min(1),
28
+ info: z.string().min(1),
29
+ font: z.string().min(1),
30
+ mono: z.string().min(1),
31
+ radius: z.string().min(1),
32
+ });
33
+ const sessionSnapshotSchema = z.object({
34
+ id: z.string().min(1),
35
+ title: z.string().min(1),
36
+ busy: z.boolean().optional().default(false),
37
+ model: z.string().min(1).max(GUEST_SESSION_MODEL_MAX).optional(),
38
+ agent: z.string().min(1).max(GUEST_SESSION_AGENT_MAX).optional(),
39
+ }).nullable();
40
+ const guestConnectionSchema = z.object({
41
+ connected: z.boolean(),
42
+ account: z.string().max(GUEST_ACCOUNT_MAX),
43
+ });
44
+ const guestSettingsSchema = z.record(z.string().regex(SETTING_KEY), z.string().max(GUEST_SETTING_VALUE_MAX));
45
+ const requestResultPayloadSchema = z.object({
46
+ status: z.number().int().min(100).max(599),
47
+ body: z.string().max(GUEST_REQUEST_RESPONSE_MAX),
48
+ });
49
+ const resultWorktree = z.object({ directory: z.string(), name: z.string(), branch: z.string(), status: z.enum(['ready', 'pending', 'invalid', 'missing']) });
50
+ const startSessionResultPayloadSchema = z.union([z.object({
51
+ sessionId: z.string().min(1),
52
+ sent: z.enum(START_SESSION_SENT),
53
+ directory: z.string().optional(), worktree: resultWorktree.optional(), linked: z.boolean().optional(),
54
+ }), z.object({ sessionId: z.null(), sent: z.literal('skipped'), directory: z.string(), worktree: resultWorktree, failure: z.enum(['bootstrap-failed', 'session-create-failed']) })]);
55
+ const promptResultPayloadSchema = z.object({
56
+ sent: z.enum(START_SESSION_SENT),
57
+ });
58
+ const serviceStatusResultPayloadSchema = z.object({
59
+ status: z.enum(SERVICE_STATUS_VALUES),
60
+ });
61
+ const fileReadResultPayloadSchema = z.object({
62
+ content: z.string().max(GUEST_FILE_CONTENT_MAX),
63
+ });
64
+ const fileWriteResultPayloadSchema = z.object({
65
+ written: z.literal(true),
66
+ });
67
+ const fileListResultPayloadSchema = z.object({
68
+ entries: z.array(z.object({
69
+ name: z.string().min(1),
70
+ kind: z.enum(GUEST_FILE_ENTRY_KINDS),
71
+ })).max(GUEST_FILE_LIST_MAX),
72
+ });
73
+ const fileStatResultPayloadSchema = z.object({
74
+ kind: z.enum(GUEST_FILE_STAT_KINDS),
75
+ size: z.number().int().min(0),
76
+ mtime: z.number().int().min(0),
77
+ });
78
+ const generateResultPayloadSchema = z.object({
79
+ text: z.string().max(GUEST_GENERATE_TEXT_MAX),
80
+ });
81
+ const hostResultPayloadSchema = z.union([
82
+ guestStorageResultSchema,
83
+ guestWorkspaceSnapshotSchema,
84
+ startSessionResultPayloadSchema,
85
+ requestResultPayloadSchema,
86
+ promptResultPayloadSchema,
87
+ serviceStatusResultPayloadSchema,
88
+ fileReadResultPayloadSchema,
89
+ fileWriteResultPayloadSchema,
90
+ fileListResultPayloadSchema,
91
+ fileStatResultPayloadSchema,
92
+ generateResultPayloadSchema,
93
+ ]);
94
+ const attachPayloadSchema = z.object({
95
+ providerId: z.string().trim().regex(ATTACH_PROVIDER_ID),
96
+ id: z.string().trim().min(1).max(GUEST_ATTACH_ID_MAX),
97
+ title: z.string().trim().min(1).max(GUEST_ATTACH_TITLE_MAX),
98
+ url: z.string().trim().min(1).max(GUEST_ATTACH_URL_MAX),
99
+ text: z.string().trim().min(1).max(GUEST_ATTACH_TEXT_MAX).optional(),
100
+ kind: z.enum(['issue', 'pull']).optional(),
101
+ author: z.string().trim().min(1).max(GUEST_ATTACH_AUTHOR_MAX).optional(),
102
+ branches: z.object({
103
+ head: z.string().trim().min(1).max(GUEST_ATTACH_BRANCH_MAX),
104
+ base: z.string().trim().min(1).max(GUEST_ATTACH_BRANCH_MAX),
105
+ }).optional(),
106
+ data: z.json().refine((value) => JSON.stringify(value).length <= GUEST_ATTACH_DATA_MAX).optional(),
107
+ });
108
+ const guestItemRoleSchema = z.enum(['user', 'assistant']);
109
+ const messageItemSchema = z.object({
110
+ kind: z.literal('message'),
111
+ action: z.string().trim().min(1).max(64),
112
+ sessionId: z.string().min(1),
113
+ sessionTitle: z.string(),
114
+ directory: z.string().nullable(),
115
+ messageId: z.string().min(1),
116
+ role: guestItemRoleSchema,
117
+ text: z.string().max(GUEST_ITEM_MESSAGE_TEXT_MAX),
118
+ });
119
+ const sessionItemSchema = z.object({
120
+ kind: z.literal('session'),
121
+ action: z.string().trim().min(1).max(64),
122
+ sessionId: z.string().min(1),
123
+ sessionTitle: z.string(),
124
+ directory: z.string().nullable(),
125
+ messages: z.array(z.object({
126
+ id: z.string().min(1),
127
+ role: guestItemRoleSchema,
128
+ text: z.string().max(GUEST_ITEM_MESSAGE_TEXT_MAX),
129
+ createdAt: z.number().int().min(0),
130
+ })).optional(),
131
+ truncated: z.boolean().optional(),
132
+ }).refine((value) => JSON.stringify(value).length <= GUEST_ITEM_SESSION_MAX);
133
+ // Message and session items carry a literal `kind`; the chip's optional
134
+ // `kind` is `issue` / `pull`, so the three never overlap.
135
+ const guestItemSchema = z.union([messageItemSchema, sessionItemSchema, attachPayloadSchema]).nullable();
136
+ const readyPayloadSchema = z.object({
137
+ theme: z.object({
138
+ mode: z.enum(['light', 'dark']),
139
+ tokens: themeTokensSchema,
140
+ }),
141
+ locale: z.string().min(1),
142
+ directory: z.string().nullable(),
143
+ session: sessionSnapshotSchema,
144
+ surface: z.enum(['panel', 'dialog', 'page']),
145
+ connection: guestConnectionSchema,
146
+ settings: guestSettingsSchema,
147
+ item: guestItemSchema,
148
+ });
149
+ const hostResultSchema = z.object({
150
+ ...envelope,
151
+ type: z.literal('result'),
152
+ id: z.string().min(1),
153
+ ok: z.boolean(),
154
+ error: z.string().min(1).optional(),
155
+ code: z.string().min(1).optional(),
156
+ payload: hostResultPayloadSchema.optional(),
157
+ }).transform((message, ctx) => {
158
+ if (message.ok) {
159
+ if (message.payload) {
160
+ return {
161
+ channel: message.channel,
162
+ v: message.v,
163
+ type: 'result',
164
+ id: message.id,
165
+ ok: true,
166
+ payload: message.payload,
167
+ };
168
+ }
169
+ return {
170
+ channel: message.channel,
171
+ v: message.v,
172
+ type: 'result',
173
+ id: message.id,
174
+ ok: true,
175
+ };
176
+ }
177
+ if (!message.error) {
178
+ ctx.addIssue({ code: 'custom', message: 'Failed result needs an error string.' });
179
+ return z.NEVER;
180
+ }
181
+ return {
182
+ channel: message.channel,
183
+ v: message.v,
184
+ type: 'result',
185
+ id: message.id,
186
+ ok: false,
187
+ error: message.error,
188
+ code: resolveHostRequestErrorCode(message.code),
189
+ };
190
+ });
191
+ export const hostMessageSchema = z.union([
192
+ z.object({ ...envelope, type: z.literal('workspace'), payload: z.object({ subscriptionId: z.string().min(1).max(128), snapshot: guestWorkspaceSnapshotSchema }) }),
193
+ z.object({
194
+ ...envelope,
195
+ type: z.literal('ready'),
196
+ payload: readyPayloadSchema,
197
+ }),
198
+ z.object({
199
+ ...envelope,
200
+ type: z.literal('directory'),
201
+ payload: z.object({
202
+ directory: z.string().nullable(),
203
+ }),
204
+ }),
205
+ z.object({
206
+ ...envelope,
207
+ type: z.literal('session'),
208
+ payload: z.object({
209
+ session: sessionSnapshotSchema,
210
+ }),
211
+ }),
212
+ z.object({
213
+ ...envelope,
214
+ type: z.literal('connection'),
215
+ payload: z.object({
216
+ connection: guestConnectionSchema,
217
+ }),
218
+ }),
219
+ z.object({
220
+ ...envelope,
221
+ type: z.literal('settings'),
222
+ payload: z.object({
223
+ settings: guestSettingsSchema,
224
+ }),
225
+ }),
226
+ z.object({
227
+ ...envelope,
228
+ type: z.literal('session-lifecycle'),
229
+ payload: z.object({
230
+ sessionId: z.string().min(1),
231
+ phase: z.enum(SESSION_LIFECYCLE_PHASES),
232
+ }),
233
+ }),
234
+ z.object({
235
+ ...envelope,
236
+ type: z.literal('item'),
237
+ payload: z.object({
238
+ item: guestItemSchema,
239
+ }),
240
+ }),
241
+ z.object({
242
+ ...envelope,
243
+ type: z.literal('resolve'),
244
+ id: z.string().min(1),
245
+ payload: z.object({
246
+ command: z.string().min(1),
247
+ args: z.string(),
248
+ }),
249
+ }),
250
+ hostResultSchema,
251
+ ]);
252
+ const filePathSchema = z.string().min(1).max(GUEST_FILE_PATH_MAX).refine(isGuestFilePath);
253
+ export const guestMessageSchema = z.discriminatedUnion('type', [
254
+ z.object({ ...envelope, type: z.literal('workspace-read'), id: z.string().min(1), payload: guestWorkspaceQuerySchema }),
255
+ z.object({ ...envelope, type: z.literal('workspace-subscribe'), id: z.string().min(1), payload: z.object({ subscriptionId: z.string().min(1).max(128), query: guestWorkspaceQuerySchema }) }),
256
+ z.object({ ...envelope, type: z.literal('workspace-unsubscribe'), id: z.string().min(1), payload: z.object({ subscriptionId: z.string().min(1).max(128) }) }),
257
+ z.object({ ...envelope, type: z.literal('storage'), id: z.string().min(1), payload: guestStorageRequestSchema }),
258
+ z.object({ ...envelope, type: z.literal('open-session'), id: z.string().min(1), payload: z.object({ sessionId: z.string().min(1).max(1024) }) }),
259
+ z.object({
260
+ ...envelope,
261
+ type: z.literal('hello'),
262
+ }),
263
+ z.object({
264
+ ...envelope,
265
+ type: z.literal('toast'),
266
+ id: z.string().min(1),
267
+ payload: z.object({
268
+ kind: z.enum(['info', 'success', 'error']),
269
+ message: z.string().trim().min(1).max(GUEST_TOAST_MAX),
270
+ }),
271
+ }),
272
+ z.object({
273
+ ...envelope,
274
+ type: z.literal('open-url'),
275
+ id: z.string().min(1),
276
+ payload: z.object({
277
+ url: z.string().trim().min(1).max(GUEST_ATTACH_URL_MAX),
278
+ }),
279
+ }),
280
+ z.object({
281
+ ...envelope,
282
+ type: z.literal('open-surface'),
283
+ id: z.string().min(1),
284
+ payload: z.object({
285
+ surfaceId: z.string().trim().min(1).max(64),
286
+ }),
287
+ }),
288
+ z.object({
289
+ ...envelope,
290
+ type: z.literal('clipboard-write'),
291
+ id: z.string().min(1),
292
+ payload: z.object({
293
+ text: z.string().min(1).max(GUEST_CLIPBOARD_TEXT_MAX),
294
+ }),
295
+ }),
296
+ z.object({
297
+ ...envelope,
298
+ type: z.literal('compose'),
299
+ id: z.string().min(1),
300
+ payload: z.object({
301
+ text: z.string().trim().min(1).max(GUEST_COMPOSE_TEXT_MAX),
302
+ mode: z.enum(['replace', 'append']).optional(),
303
+ }),
304
+ }),
305
+ z.object({
306
+ ...envelope,
307
+ type: z.literal('attach'),
308
+ id: z.string().min(1),
309
+ payload: attachPayloadSchema,
310
+ }),
311
+ z.object({
312
+ ...envelope,
313
+ type: z.literal('start-session'),
314
+ id: z.string().min(1),
315
+ payload: attachPayloadSchema.extend({
316
+ worktree: guestSessionWorktreeSchema.optional(),
317
+ projectId: z.string().trim().min(1).max(1024).optional(),
318
+ navigation: z.enum(['preserve', 'open']).optional(),
319
+ }),
320
+ }),
321
+ z.object({
322
+ ...envelope,
323
+ type: z.literal('prompt'),
324
+ id: z.string().min(1),
325
+ payload: z.object({
326
+ text: z.string().trim().min(1).max(GUEST_COMPOSE_TEXT_MAX),
327
+ send: z.boolean().optional(),
328
+ }),
329
+ }),
330
+ z.object({
331
+ ...envelope,
332
+ type: z.literal('session-link'),
333
+ id: z.string().min(1),
334
+ payload: attachPayloadSchema,
335
+ }),
336
+ z.object({
337
+ ...envelope,
338
+ type: z.literal('close'),
339
+ id: z.string().min(1),
340
+ }),
341
+ z.object({
342
+ ...envelope,
343
+ type: z.literal('oauth-start'),
344
+ id: z.string().min(1),
345
+ }),
346
+ z.object({
347
+ ...envelope,
348
+ type: z.literal('oauth-disconnect'),
349
+ id: z.string().min(1),
350
+ }),
351
+ z.object({
352
+ ...envelope,
353
+ type: z.literal('request'),
354
+ id: z.string().min(1),
355
+ payload: z.object({
356
+ method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']),
357
+ path: z.string().trim().min(1).max(GUEST_REQUEST_PATH_MAX).refine(isGuestRequestPath),
358
+ query: z.record(z.string().min(1).max(128), z.string().max(2_000)).optional(),
359
+ body: z.string().max(GUEST_REQUEST_BODY_MAX).optional(),
360
+ }),
361
+ }),
362
+ z.object({
363
+ ...envelope,
364
+ type: z.literal('service-request'),
365
+ id: z.string().min(1),
366
+ payload: z.object({
367
+ method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']),
368
+ path: z.string().trim().min(1).max(GUEST_REQUEST_PATH_MAX).refine(isGuestRequestPath),
369
+ query: z.record(z.string().min(1).max(128), z.string().max(2_000)).optional(),
370
+ body: z.string().max(GUEST_REQUEST_BODY_MAX).optional(),
371
+ }),
372
+ }),
373
+ z.object({
374
+ ...envelope,
375
+ type: z.literal('service-status'),
376
+ id: z.string().min(1),
377
+ }),
378
+ z.object({
379
+ ...envelope,
380
+ type: z.literal('file-read'),
381
+ id: z.string().min(1),
382
+ payload: z.object({ path: filePathSchema }),
383
+ }),
384
+ z.object({
385
+ ...envelope,
386
+ type: z.literal('file-write'),
387
+ id: z.string().min(1),
388
+ payload: z.object({
389
+ path: filePathSchema,
390
+ content: z.string().max(GUEST_FILE_CONTENT_MAX),
391
+ }),
392
+ }),
393
+ z.object({
394
+ ...envelope,
395
+ type: z.literal('file-list'),
396
+ id: z.string().min(1),
397
+ payload: z.object({ path: filePathSchema }),
398
+ }),
399
+ z.object({
400
+ ...envelope,
401
+ type: z.literal('file-stat'),
402
+ id: z.string().min(1),
403
+ payload: z.object({ path: filePathSchema }),
404
+ }),
405
+ z.object({
406
+ ...envelope,
407
+ type: z.literal('generate'),
408
+ id: z.string().min(1),
409
+ payload: z.object({
410
+ prompt: z.string().trim().min(1).max(GUEST_GENERATE_PROMPT_MAX),
411
+ system: z.string().trim().min(1).max(GUEST_GENERATE_SYSTEM_MAX).optional(),
412
+ maxOutputTokens: z.number().int().min(1).max(GUEST_GENERATE_OUTPUT_TOKENS_MAX).optional(),
413
+ }),
414
+ }),
415
+ z.object({
416
+ ...envelope,
417
+ type: z.literal('badge'),
418
+ id: z.string().min(1),
419
+ payload: z.object({
420
+ count: z.number().int().min(0).max(GUEST_BADGE_MAX).nullable(),
421
+ }),
422
+ }),
423
+ z.object({
424
+ ...envelope,
425
+ type: z.literal('resolve-result'),
426
+ id: z.string().min(1),
427
+ payload: z.union([
428
+ z.object({ item: attachPayloadSchema.nullable() }),
429
+ z.object({ error: z.string().trim().min(1).max(GUEST_RESOLVE_ERROR_MAX) }),
430
+ ]),
431
+ }),
432
+ ]);
433
+ // The hand-written contract types and the schemas must describe the same wire
434
+ // shapes; either drifting from the other fails this file's type-check.
435
+ const assertHostParsed = (value) => value;
436
+ const assertHostContract = (value) => value;
437
+ const assertGuestParsed = (value) => value;
438
+ const assertGuestContract = (value) => value;
439
+ void assertHostParsed;
440
+ void assertHostContract;
441
+ void assertGuestParsed;
442
+ void assertGuestContract;
443
+ export const parseHostMessage = (document) => {
444
+ const parsed = hostMessageSchema.safeParse(document);
445
+ return parsed.success ? parsed.data : null;
446
+ };
447
+ export const parseGuestMessage = (document) => {
448
+ const parsed = guestMessageSchema.safeParse(document);
449
+ return parsed.success ? parsed.data : null;
450
+ };
@@ -0,0 +1,4 @@
1
+ export { openChamberManifestSchema, PACKAGE_VERSION_PATTERN, packageManifestSchema, parseManifest, parseManifestJson, } from './parse.ts';
2
+ export type { ManifestDocument } from './parse.ts';
3
+ export { guestStorageRequestSchema, guestStorageResultSchema, guestWorkspaceSnapshotSchema } from './workspace-schemas.ts';
4
+ export { guestMessageSchema, hostMessageSchema, parseGuestMessage, parseHostMessage, } from './protocol.ts';
@@ -0,0 +1,6 @@
1
+ // Host-side parsing. OpenChamber (browser host and server) imports this to
2
+ // validate an untrusted guest's manifest and messages with zod. Guests import
3
+ // `@openchamber/sdk`, which carries no schema library.
4
+ export { openChamberManifestSchema, PACKAGE_VERSION_PATTERN, packageManifestSchema, parseManifest, parseManifestJson, } from "./parse.js";
5
+ export { guestStorageRequestSchema, guestStorageResultSchema, guestWorkspaceSnapshotSchema } from "./workspace-schemas.js";
6
+ export { guestMessageSchema, hostMessageSchema, parseGuestMessage, parseHostMessage, } from "./protocol.js";
@@ -0,0 +1,10 @@
1
+ import { type Handle } from './dom.ts';
2
+ export type Tone = 'neutral' | 'primary' | 'success' | 'warning' | 'error' | 'info';
3
+ export type BadgeProps = {
4
+ label: string;
5
+ tone?: Tone;
6
+ };
7
+ export type BadgeHandle = Handle<BadgeProps>;
8
+ /** Writes `data-tone` for toned colours; neutral removes it. */
9
+ export declare const applyTone: (node: Element, tone: Tone | undefined) => void;
10
+ export declare const mountBadge: (root: Element, initial: BadgeProps) => BadgeHandle;
@@ -0,0 +1,26 @@
1
+ import { el, ensureStyle, setAttr, setText } from "./dom.js";
2
+ import { UI_CSS } from "./style.js";
3
+ /** Writes `data-tone` for toned colours; neutral removes it. */
4
+ export const applyTone = (node, tone) => {
5
+ setAttr(node, 'data-tone', tone && tone !== 'neutral' ? tone : null);
6
+ };
7
+ export const mountBadge = (root, initial) => {
8
+ ensureStyle(UI_CSS);
9
+ let props = initial;
10
+ const node = el('span', 'oc-sdk oc-sdk-badge');
11
+ root.append(node);
12
+ const paint = () => {
13
+ setText(node, props.label);
14
+ applyTone(node, props.tone);
15
+ };
16
+ paint();
17
+ return {
18
+ update: (next) => {
19
+ props = { ...props, ...next };
20
+ paint();
21
+ },
22
+ dispose: () => {
23
+ node.remove();
24
+ },
25
+ };
26
+ };
@@ -0,0 +1,13 @@
1
+ import { type Handle } from './dom.ts';
2
+ export type BannerTone = 'info' | 'success' | 'warning' | 'error';
3
+ export type BannerProps = {
4
+ tone: BannerTone;
5
+ title: string;
6
+ body?: string;
7
+ action?: {
8
+ label: string;
9
+ onClick: () => void;
10
+ };
11
+ };
12
+ export type BannerHandle = Handle<BannerProps>;
13
+ export declare const mountBanner: (root: Element, initial: BannerProps) => BannerHandle;
@@ -0,0 +1,48 @@
1
+ import { mountButton } from "./button.js";
2
+ import { el, ensureStyle, setText } from "./dom.js";
3
+ import { UI_CSS } from "./style.js";
4
+ export const mountBanner = (root, initial) => {
5
+ ensureStyle(UI_CSS);
6
+ let props = initial;
7
+ const node = el('div', 'oc-sdk oc-sdk-banner');
8
+ const text = el('div', 'oc-sdk-banner-text');
9
+ const title = el('div', 'oc-sdk-banner-title');
10
+ const body = el('div', 'oc-sdk-banner-body');
11
+ const slot = el('div', 'oc-sdk-banner-action');
12
+ text.append(title, body);
13
+ node.append(text, slot);
14
+ root.append(node);
15
+ let action = null;
16
+ const paint = () => {
17
+ node.dataset.tone = props.tone;
18
+ node.setAttribute('role', props.tone === 'error' || props.tone === 'warning' ? 'alert' : 'status');
19
+ setText(title, props.title);
20
+ setText(body, props.body);
21
+ body.hidden = !props.body;
22
+ slot.hidden = !props.action;
23
+ if (!props.action) {
24
+ action?.dispose();
25
+ action = null;
26
+ return;
27
+ }
28
+ const next = { label: props.action.label, onClick: props.action.onClick };
29
+ if (action) {
30
+ action.update(next);
31
+ }
32
+ else {
33
+ action = mountButton(slot, { ...next, variant: 'outline', size: 'xs' });
34
+ }
35
+ };
36
+ paint();
37
+ return {
38
+ update: (next) => {
39
+ props = { ...props, ...next };
40
+ paint();
41
+ },
42
+ dispose: () => {
43
+ action?.dispose();
44
+ action = null;
45
+ node.remove();
46
+ },
47
+ };
48
+ };
@@ -0,0 +1,14 @@
1
+ import { type Handle } from './dom.ts';
2
+ export type ButtonVariant = 'default' | 'secondary' | 'outline' | 'ghost' | 'destructive';
3
+ export type ButtonSize = 'default' | 'sm' | 'xs';
4
+ export type ButtonProps = {
5
+ label: string;
6
+ variant?: ButtonVariant;
7
+ size?: ButtonSize;
8
+ disabled?: boolean;
9
+ /** Shows a spinner in front of the label and blocks clicks. */
10
+ loading?: boolean;
11
+ onClick: () => void;
12
+ };
13
+ export type ButtonHandle = Handle<ButtonProps>;
14
+ export declare const mountButton: (root: Element, initial: ButtonProps) => ButtonHandle;
@@ -0,0 +1,49 @@
1
+ import { button, ensureStyle, setText } from "./dom.js";
2
+ import { UI_CSS } from "./style.js";
3
+ const ring = () => {
4
+ const spinner = document.createElement('span');
5
+ spinner.className = 'oc-sdk-spinner-ring';
6
+ spinner.setAttribute('aria-hidden', 'true');
7
+ return spinner;
8
+ };
9
+ export const mountButton = (root, initial) => {
10
+ ensureStyle(UI_CSS);
11
+ let props = initial;
12
+ const node = button('oc-sdk oc-sdk-btn');
13
+ const spinner = ring();
14
+ const label = document.createElement('span');
15
+ node.append(label);
16
+ root.append(node);
17
+ const paint = () => {
18
+ node.dataset.variant = props.variant ?? 'default';
19
+ node.dataset.size = props.size ?? 'default';
20
+ node.disabled = Boolean(props.disabled) || Boolean(props.loading);
21
+ node.dataset.loading = props.loading ? 'true' : 'false';
22
+ node.setAttribute('aria-busy', props.loading ? 'true' : 'false');
23
+ if (props.loading && spinner.parentNode !== node) {
24
+ node.prepend(spinner);
25
+ }
26
+ else if (!props.loading && spinner.parentNode === node) {
27
+ spinner.remove();
28
+ }
29
+ setText(label, props.label);
30
+ };
31
+ const onClick = () => {
32
+ if (props.disabled || props.loading) {
33
+ return;
34
+ }
35
+ props.onClick();
36
+ };
37
+ node.addEventListener('click', onClick);
38
+ paint();
39
+ return {
40
+ update: (next) => {
41
+ props = { ...props, ...next };
42
+ paint();
43
+ },
44
+ dispose: () => {
45
+ node.removeEventListener('click', onClick);
46
+ node.remove();
47
+ },
48
+ };
49
+ };
@@ -0,0 +1,12 @@
1
+ import { type Handle } from './dom.ts';
2
+ export type CheckboxProps = {
3
+ label: string;
4
+ checked: boolean;
5
+ onChange: (checked: boolean) => void;
6
+ disabled?: boolean;
7
+ /** Muted line under the label. */
8
+ description?: string;
9
+ };
10
+ export type CheckboxHandle = Handle<CheckboxProps>;
11
+ export declare const mountCheckbox: (root: Element, initial: CheckboxProps) => CheckboxHandle;
12
+ export declare const mountSwitch: (root: Element, initial: CheckboxProps) => CheckboxHandle;
@@ -0,0 +1,45 @@
1
+ import { button, el, ensureStyle, setText } from "./dom.js";
2
+ import { icon } from "./icons.js";
3
+ import { UI_CSS } from "./style.js";
4
+ const mountToggle = (root, initial, role) => {
5
+ ensureStyle(UI_CSS);
6
+ let props = initial;
7
+ const node = button('oc-sdk oc-sdk-check');
8
+ node.setAttribute('role', role);
9
+ const control = el('span', role === 'switch' ? 'oc-sdk-check-thumb' : 'oc-sdk-check-box');
10
+ if (role === 'checkbox') {
11
+ control.append(icon('check', 12));
12
+ }
13
+ const text = el('span', 'oc-sdk-check-text');
14
+ const label = el('span', 'oc-sdk-check-label');
15
+ const description = el('span', 'oc-sdk-check-desc');
16
+ text.append(label, description);
17
+ node.append(control, text);
18
+ root.append(node);
19
+ const paint = () => {
20
+ node.setAttribute('aria-checked', props.checked ? 'true' : 'false');
21
+ node.disabled = Boolean(props.disabled);
22
+ setText(label, props.label);
23
+ setText(description, props.description);
24
+ description.hidden = !props.description;
25
+ };
26
+ const onClick = () => {
27
+ if (!props.disabled) {
28
+ props.onChange(!props.checked);
29
+ }
30
+ };
31
+ node.addEventListener('click', onClick);
32
+ paint();
33
+ return {
34
+ update: (next) => {
35
+ props = { ...props, ...next };
36
+ paint();
37
+ },
38
+ dispose: () => {
39
+ node.removeEventListener('click', onClick);
40
+ node.remove();
41
+ },
42
+ };
43
+ };
44
+ export const mountCheckbox = (root, initial) => (mountToggle(root, initial, 'checkbox'));
45
+ export const mountSwitch = (root, initial) => (mountToggle(root, initial, 'switch'));