@shadowob/sdk 1.1.7 → 1.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -32,15 +32,717 @@ var index_exports = {};
32
32
  __export(index_exports, {
33
33
  CLIENT_EVENTS: () => import_shared.CLIENT_EVENTS,
34
34
  SERVER_EVENTS: () => import_shared.SERVER_EVENTS,
35
+ SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT: () => SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT,
36
+ SHADOW_SERVER_APP_COMMAND_EVENTS: () => SHADOW_SERVER_APP_COMMAND_EVENTS,
37
+ SHADOW_SERVER_APP_COMMAND_FAILED_EVENT: () => SHADOW_SERVER_APP_COMMAND_FAILED_EVENT,
38
+ SHADOW_SERVER_APP_PROTOCOL: () => SHADOW_SERVER_APP_PROTOCOL,
39
+ ShadowBridge: () => ShadowBridge,
35
40
  ShadowClient: () => ShadowClient,
41
+ ShadowServerAppCommandError: () => ShadowServerAppCommandError,
42
+ ShadowServerAppOutbox: () => ShadowServerAppOutbox,
43
+ ShadowServerAppRuntime: () => ShadowServerAppRuntime,
36
44
  ShadowSocket: () => ShadowSocket,
37
45
  ShadowVoiceConsumer: () => ShadowVoiceConsumer,
46
+ buildShadowServerAppInboxDelivery: () => buildShadowServerAppInboxDelivery,
47
+ buildShadowServerAppInboxTaskRequest: () => buildShadowServerAppInboxTaskRequest,
38
48
  channelRoom: () => channelRoom,
49
+ createShadowServerAppManifest: () => createShadowServerAppManifest,
50
+ defineShadowServerApp: () => defineShadowServerApp,
51
+ extractShadowServerAppBearerToken: () => extractShadowServerAppBearerToken,
52
+ getShadowServerAppChannelMessageDeliveries: () => getShadowServerAppChannelMessageDeliveries,
53
+ getShadowServerAppChannelMessageErrors: () => getShadowServerAppChannelMessageErrors,
54
+ getShadowServerAppTaskCardId: () => getShadowServerAppTaskCardId,
55
+ introspectShadowServerAppToken: () => introspectShadowServerAppToken,
56
+ normalizeShadowServerAppCommandInput: () => normalizeShadowServerAppCommandInput,
57
+ parseShadowServerAppCommandRequest: () => parseShadowServerAppCommandRequest,
58
+ shadowServerAppActorAvatarUrl: () => shadowServerAppActorAvatarUrl,
59
+ shadowServerAppActorDisplayName: () => shadowServerAppActorDisplayName,
60
+ shadowServerAppActorRef: () => shadowServerAppActorRef,
61
+ shadowServerAppError: () => shadowServerAppError,
62
+ shadowServerAppInboxTaskEndpoint: () => shadowServerAppInboxTaskEndpoint,
39
63
  threadRoom: () => threadRoom,
40
- userRoom: () => userRoom
64
+ userRoom: () => userRoom,
65
+ validateShadowServerAppJsonSchema: () => validateShadowServerAppJsonSchema
41
66
  });
42
67
  module.exports = __toCommonJS(index_exports);
43
68
 
69
+ // src/server-app.ts
70
+ var SHADOW_SERVER_APP_PROTOCOL = "shadow.app/1";
71
+ var SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT = "server_app.command.completed";
72
+ var SHADOW_SERVER_APP_COMMAND_FAILED_EVENT = "server_app.command.failed";
73
+ var SHADOW_SERVER_APP_COMMAND_EVENTS = [
74
+ SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT,
75
+ SHADOW_SERVER_APP_COMMAND_FAILED_EVENT
76
+ ];
77
+ function isProtocolRecord(value) {
78
+ return !!value && typeof value === "object" && !Array.isArray(value);
79
+ }
80
+ function optionalProtocolString(value) {
81
+ return typeof value === "string" && value ? value : void 0;
82
+ }
83
+ function protocolPathSegment(value) {
84
+ return encodeURIComponent(value);
85
+ }
86
+ function shadowServerAppInboxTaskEndpoint(serverIdOrSlug, target) {
87
+ if ("channelId" in target && target.channelId) {
88
+ return `/api/channels/${protocolPathSegment(target.channelId)}/inbox/tasks`;
89
+ }
90
+ if ("agentId" in target && target.agentId) {
91
+ return `/api/servers/${protocolPathSegment(serverIdOrSlug)}/inboxes/${protocolPathSegment(
92
+ target.agentId
93
+ )}/tasks`;
94
+ }
95
+ throw new Error("Missing Inbox task target");
96
+ }
97
+ function buildShadowServerAppInboxTaskRequest(input) {
98
+ const appId = input.app.id ?? input.app.appId ?? input.app.appKey;
99
+ const serverAppData = isProtocolRecord(input.task.data?.serverApp) ? input.task.data.serverApp : {};
100
+ return {
101
+ endpoint: shadowServerAppInboxTaskEndpoint(input.serverIdOrSlug, input.target),
102
+ body: {
103
+ title: input.task.title,
104
+ body: input.task.body,
105
+ priority: input.task.priority,
106
+ idempotencyKey: input.task.idempotencyKey,
107
+ source: {
108
+ kind: "server_app",
109
+ id: appId,
110
+ appId,
111
+ appKey: input.app.appKey,
112
+ ...input.app.serverId ? { serverId: input.app.serverId } : {},
113
+ ...input.commandName ? { command: input.commandName } : {},
114
+ label: input.app.label ?? input.app.name ?? input.app.appKey,
115
+ ...input.task.resource ? { resource: input.task.resource } : {}
116
+ },
117
+ data: {
118
+ ...input.task.data ?? {},
119
+ serverApp: {
120
+ ...serverAppData,
121
+ appKey: input.app.appKey,
122
+ ...input.commandName ? { command: input.commandName } : {}
123
+ }
124
+ }
125
+ }
126
+ };
127
+ }
128
+ function getShadowServerAppTaskCardId(message) {
129
+ const metadata = isProtocolRecord(message) ? message.metadata : null;
130
+ const cards = isProtocolRecord(metadata) && Array.isArray(metadata.cards) ? metadata.cards : [];
131
+ for (const item of cards) {
132
+ if (!isProtocolRecord(item)) continue;
133
+ if (item.kind === "task" && typeof item.id === "string" && item.id) return item.id;
134
+ }
135
+ return null;
136
+ }
137
+ function buildShadowServerAppInboxDelivery(input) {
138
+ const message = isProtocolRecord(input.message) ? input.message : {};
139
+ return {
140
+ ..."agentId" in input.target && input.target.agentId ? { agentId: input.target.agentId } : {},
141
+ channelId: optionalProtocolString(message.channelId),
142
+ messageId: optionalProtocolString(message.id),
143
+ cardId: getShadowServerAppTaskCardId(message),
144
+ idempotencyKey: input.idempotencyKey
145
+ };
146
+ }
147
+ function shadowFromPayload(payload) {
148
+ if (payload.protocol === SHADOW_SERVER_APP_PROTOCOL) {
149
+ return payload;
150
+ }
151
+ const shadow = isProtocolRecord(payload.shadow) ? payload.shadow : null;
152
+ if (shadow?.protocol === SHADOW_SERVER_APP_PROTOCOL) {
153
+ return shadow;
154
+ }
155
+ return null;
156
+ }
157
+ function mergeShadowResult(value, shadow) {
158
+ if (!shadow) return value;
159
+ const existing = shadowFromPayload(value);
160
+ if (!existing) return { ...value, shadow };
161
+ return {
162
+ ...value,
163
+ shadow: {
164
+ protocol: SHADOW_SERVER_APP_PROTOCOL,
165
+ outbox: {
166
+ ...existing.outbox ?? {},
167
+ ...shadow.outbox ?? {}
168
+ }
169
+ }
170
+ };
171
+ }
172
+ function getShadowServerAppInboxDeliveries(payload) {
173
+ if (!isProtocolRecord(payload)) return [];
174
+ const shadow = shadowFromPayload(payload);
175
+ return shadow?.outbox?.deliveries ?? [];
176
+ }
177
+ function getShadowServerAppInboxErrors(payload) {
178
+ if (!isProtocolRecord(payload)) return [];
179
+ const shadow = shadowFromPayload(payload);
180
+ return shadow?.outbox?.errors ?? [];
181
+ }
182
+ function getShadowServerAppChannelMessageDeliveries(payload) {
183
+ if (!isProtocolRecord(payload)) return [];
184
+ const shadow = shadowFromPayload(payload);
185
+ return shadow?.outbox?.channelMessageDeliveries ?? [];
186
+ }
187
+ function getShadowServerAppChannelMessageErrors(payload) {
188
+ if (!isProtocolRecord(payload)) return [];
189
+ const shadow = shadowFromPayload(payload);
190
+ return shadow?.outbox?.channelMessageErrors ?? [];
191
+ }
192
+ function unwrapShadowServerAppCommandPayload(payload) {
193
+ if (isProtocolRecord(payload) && payload.ok === false) {
194
+ throw new Error(typeof payload.error === "string" ? payload.error : "Command failed");
195
+ }
196
+ if (isProtocolRecord(payload) && "result" in payload && payload.result !== void 0) {
197
+ const nested = unwrapShadowServerAppCommandPayload(payload.result);
198
+ const shadow = shadowFromPayload(payload);
199
+ if (isProtocolRecord(nested)) return mergeShadowResult(nested, shadow);
200
+ return nested;
201
+ }
202
+ return payload;
203
+ }
204
+ var ShadowServerAppOutbox = class {
205
+ inboxTasks = [];
206
+ channelMessages = [];
207
+ enqueueInboxTask(task) {
208
+ this.inboxTasks.push(task);
209
+ return this;
210
+ }
211
+ enqueueInboxTasks(tasks) {
212
+ for (const task of tasks) this.enqueueInboxTask(task);
213
+ return this;
214
+ }
215
+ sendChannelMessage(message) {
216
+ this.channelMessages.push(message);
217
+ return this;
218
+ }
219
+ sendChannelMessages(messages) {
220
+ for (const message of messages) this.sendChannelMessage(message);
221
+ return this;
222
+ }
223
+ toShadow() {
224
+ return {
225
+ protocol: SHADOW_SERVER_APP_PROTOCOL,
226
+ outbox: {
227
+ ...this.inboxTasks.length > 0 ? { inboxTasks: [...this.inboxTasks] } : {},
228
+ ...this.channelMessages.length > 0 ? { channelMessages: [...this.channelMessages] } : {}
229
+ }
230
+ };
231
+ }
232
+ attachTo(result) {
233
+ return { ...result, shadow: this.toShadow() };
234
+ }
235
+ };
236
+ function trimTrailingSlash(value) {
237
+ return value.replace(/\/$/, "");
238
+ }
239
+ function joinBasePath(baseUrl, path) {
240
+ const cleanBase = trimTrailingSlash(baseUrl);
241
+ const cleanPath = path.startsWith("/") ? path : `/${path}`;
242
+ return `${cleanBase}${cleanPath}`;
243
+ }
244
+ function extractShadowServerAppBearerToken(value) {
245
+ if (!value) return null;
246
+ return value.toLowerCase().startsWith("bearer ") ? value.slice(7).trim() : null;
247
+ }
248
+ function normalizeShadowServerAppCommandInput(value) {
249
+ if (value && typeof value === "object" && !Array.isArray(value) && "input" in value && Object.keys(value).every((key) => key === "input" || key === "channelId")) {
250
+ return value.input ?? {};
251
+ }
252
+ return value;
253
+ }
254
+ function createShadowServerAppManifest(manifest, options = {}) {
255
+ const publicBaseUrl = trimTrailingSlash(
256
+ options.publicBaseUrl ?? `http://localhost:${options.port ?? 4201}`
257
+ );
258
+ const apiBaseUrl = trimTrailingSlash(options.apiBaseUrl ?? publicBaseUrl);
259
+ const iframePath = options.iframePath ?? "/shadow/server";
260
+ const iconPath = options.iconPath ?? "/assets/icon.svg";
261
+ return {
262
+ ...manifest,
263
+ iconUrl: joinBasePath(publicBaseUrl, iconPath),
264
+ iframe: manifest.iframe ? {
265
+ ...manifest.iframe,
266
+ entry: joinBasePath(publicBaseUrl, iframePath),
267
+ allowedOrigins: options.allowedOrigins ?? [publicBaseUrl]
268
+ } : manifest.iframe,
269
+ api: {
270
+ ...manifest.api,
271
+ baseUrl: apiBaseUrl
272
+ }
273
+ };
274
+ }
275
+ function defineShadowServerApp(manifest, options = {}) {
276
+ return new ShadowServerAppRuntime(manifest, options);
277
+ }
278
+ var ShadowServerAppCommandError = class extends Error {
279
+ status;
280
+ issues;
281
+ constructor(status, error, issues) {
282
+ super(error);
283
+ this.name = "ShadowServerAppCommandError";
284
+ this.status = status;
285
+ this.issues = issues;
286
+ }
287
+ };
288
+ function shadowServerAppError(status, error, issues) {
289
+ return new ShadowServerAppCommandError(status, error, issues);
290
+ }
291
+ var ShadowServerAppRuntime = class {
292
+ constructor(sourceManifest, options = {}) {
293
+ this.sourceManifest = sourceManifest;
294
+ this.options = options;
295
+ }
296
+ manifest(options = {}) {
297
+ return createShadowServerAppManifest(this.sourceManifest, options);
298
+ }
299
+ defineCommands(handlers) {
300
+ return handlers;
301
+ }
302
+ actor(envelopeOrContext) {
303
+ return shadowServerAppActorRef(envelopeOrContext);
304
+ }
305
+ error(status, error, issues) {
306
+ return shadowServerAppError(status, error, issues);
307
+ }
308
+ async parseCommand(commandName, request) {
309
+ return parseShadowServerAppCommandRequest(
310
+ {
311
+ ...request,
312
+ expectedCommand: commandName,
313
+ shadowBaseUrl: this.options.shadowBaseUrl,
314
+ fetchImpl: this.options.fetchImpl
315
+ }
316
+ );
317
+ }
318
+ async executeCommand(commandName, request, handlers) {
319
+ const parsed = await this.parseCommand(commandName, request);
320
+ if (!parsed.ok) return parseErrorResult(parsed);
321
+ return this.executeEnvelope(commandName, parsed.envelope, handlers);
322
+ }
323
+ async executeLocal(commandName, input, context, handlers) {
324
+ return this.executeEnvelope(
325
+ commandName,
326
+ {
327
+ input,
328
+ context: {
329
+ ...context,
330
+ command: commandName
331
+ }
332
+ },
333
+ handlers
334
+ );
335
+ }
336
+ async executeEnvelope(commandName, envelope, handlers) {
337
+ const command = this.sourceManifest.commands.find((item) => item.name === commandName);
338
+ if (!command) return failureResult(404, "command_not_found");
339
+ const validation = validateShadowServerAppJsonSchema(command.inputSchema, envelope.input);
340
+ if (!validation.ok) return failureResult(422, "invalid_input", validation.issues);
341
+ const handler = handlers[commandName];
342
+ if (!handler) return failureResult(404, "command_not_found");
343
+ try {
344
+ const result = await handler(envelope.input, {
345
+ context: envelope.context,
346
+ actor: this.actor(envelope)
347
+ });
348
+ return { ok: true, status: 200, body: { ok: true, result } };
349
+ } catch (error) {
350
+ if (error instanceof ShadowServerAppCommandError) {
351
+ return failureResult(error.status, error.message, error.issues);
352
+ }
353
+ throw error;
354
+ }
355
+ }
356
+ };
357
+ async function introspectShadowServerAppToken(input) {
358
+ const baseUrl = trimTrailingSlash(input.shadowBaseUrl ?? "http://localhost:3002");
359
+ const fetchImpl = input.fetchImpl ?? fetch;
360
+ const response = await fetchImpl(
361
+ `${baseUrl}/api/servers/${encodeURIComponent(input.serverId)}/apps/${encodeURIComponent(
362
+ input.appKey
363
+ )}/oauth/introspect`,
364
+ {
365
+ method: "POST",
366
+ headers: {
367
+ Authorization: `Bearer ${input.token}`,
368
+ "Content-Type": "application/json"
369
+ },
370
+ body: JSON.stringify({ token: input.token })
371
+ }
372
+ );
373
+ if (!response.ok) return null;
374
+ const payload = await response.json();
375
+ return payload.active ? payload : null;
376
+ }
377
+ async function parseShadowServerAppCommandRequest(input) {
378
+ const token = extractShadowServerAppBearerToken(input.authorizationHeader);
379
+ const serverId = input.serverIdHeader;
380
+ const appKey = input.appKeyHeader;
381
+ if (!token || !serverId || !appKey) {
382
+ return { ok: false, status: 401, error: "missing_oauth" };
383
+ }
384
+ const introspection = await introspectShadowServerAppToken({
385
+ token,
386
+ serverId,
387
+ appKey,
388
+ shadowBaseUrl: input.shadowBaseUrl,
389
+ fetchImpl: input.fetchImpl
390
+ }).catch(() => null);
391
+ const context = introspection?.shadow;
392
+ if (!context) return { ok: false, status: 401, error: "invalid_token" };
393
+ if (context.command !== input.expectedCommand) {
394
+ return { ok: false, status: 403, error: "wrong_command" };
395
+ }
396
+ let commandInput;
397
+ if (input.requestInput !== void 0) {
398
+ commandInput = input.requestInput;
399
+ } else {
400
+ let body;
401
+ try {
402
+ body = JSON.parse(input.requestBody ?? "{}");
403
+ } catch {
404
+ return { ok: false, status: 400, error: "invalid_json" };
405
+ }
406
+ commandInput = body.input ?? {};
407
+ }
408
+ return {
409
+ ok: true,
410
+ envelope: {
411
+ input: commandInput,
412
+ context
413
+ }
414
+ };
415
+ }
416
+ function validateShadowServerAppJsonSchema(schema, value) {
417
+ if (!schema) return { ok: true };
418
+ const issues = [];
419
+ validateJsonSchemaValue(schema, value, "", issues);
420
+ return issues.length ? { ok: false, issues } : { ok: true };
421
+ }
422
+ function shadowServerAppActorDisplayName(envelopeOrContext) {
423
+ const context = "context" in envelopeOrContext ? envelopeOrContext.context : envelopeOrContext;
424
+ const actor = context.actor;
425
+ const profile = actor.profile;
426
+ return profile?.displayName?.trim() || profile?.username?.trim() || (actor.buddyAgentId ? `Buddy ${actor.buddyAgentId.slice(0, 8)}` : null) || (actor.userId ? `${actor.kind}:${actor.userId.slice(0, 8)}` : null) || `${actor.kind}:unknown`;
427
+ }
428
+ function shadowServerAppActorAvatarUrl(envelopeOrContext) {
429
+ const context = "context" in envelopeOrContext ? envelopeOrContext.context : envelopeOrContext;
430
+ return context.actor.profile?.avatarUrl ?? null;
431
+ }
432
+ function shadowServerAppActorRef(envelopeOrContext) {
433
+ const context = "context" in envelopeOrContext ? envelopeOrContext.context : envelopeOrContext;
434
+ const actor = context.actor;
435
+ return {
436
+ kind: actor.kind,
437
+ id: actor.buddyAgentId ?? actor.userId ?? actor.ownerId ?? "unknown",
438
+ userId: actor.userId ?? null,
439
+ buddyAgentId: actor.buddyAgentId ?? null,
440
+ ownerId: actor.ownerId ?? null,
441
+ displayName: shadowServerAppActorDisplayName(context),
442
+ avatarUrl: shadowServerAppActorAvatarUrl(context)
443
+ };
444
+ }
445
+ function parseErrorResult(error) {
446
+ return failureResult(error.status, error.error, error.issues);
447
+ }
448
+ function failureResult(status, error, issues) {
449
+ return {
450
+ ok: false,
451
+ status,
452
+ body: issues === void 0 ? { ok: false, error } : { ok: false, error, issues }
453
+ };
454
+ }
455
+ function validateJsonSchemaValue(schema, value, path, issues) {
456
+ if (Array.isArray(schema.oneOf)) {
457
+ const matches = schema.oneOf.some((option) => {
458
+ const nestedIssues = [];
459
+ if (option && typeof option === "object" && !Array.isArray(option)) {
460
+ validateJsonSchemaValue(
461
+ option,
462
+ value,
463
+ path,
464
+ nestedIssues
465
+ );
466
+ }
467
+ return nestedIssues.length === 0;
468
+ });
469
+ if (!matches) issues.push({ path, message: "Expected value matching one schema option" });
470
+ return;
471
+ }
472
+ const enumValues = schema.enum;
473
+ if (Array.isArray(enumValues) && !enumValues.includes(value)) {
474
+ issues.push({ path, message: `Expected one of ${enumValues.map(String).join(", ")}` });
475
+ return;
476
+ }
477
+ const type = schema.type;
478
+ if (type === "object") {
479
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
480
+ issues.push({ path, message: "Expected object" });
481
+ return;
482
+ }
483
+ const record = value;
484
+ const properties = schema.properties && typeof schema.properties === "object" && !Array.isArray(schema.properties) ? schema.properties : {};
485
+ const required = Array.isArray(schema.required) ? schema.required.map(String) : [];
486
+ for (const key of required) {
487
+ if (!(key in record)) issues.push({ path: joinJsonPath(path, key), message: "Required" });
488
+ }
489
+ for (const [key, propertySchema] of Object.entries(properties)) {
490
+ if (record[key] !== void 0) {
491
+ validateJsonSchemaValue(propertySchema, record[key], joinJsonPath(path, key), issues);
492
+ }
493
+ }
494
+ const additionalProperties = schema.additionalProperties && typeof schema.additionalProperties === "object" && !Array.isArray(schema.additionalProperties) ? schema.additionalProperties : null;
495
+ if (additionalProperties) {
496
+ for (const [key, nestedValue] of Object.entries(record)) {
497
+ if (!(key in properties)) {
498
+ validateJsonSchemaValue(
499
+ additionalProperties,
500
+ nestedValue,
501
+ joinJsonPath(path, key),
502
+ issues
503
+ );
504
+ }
505
+ }
506
+ } else if (schema.additionalProperties === false) {
507
+ for (const key of Object.keys(record)) {
508
+ if (!(key in properties)) {
509
+ issues.push({ path: joinJsonPath(path, key), message: "Unknown property" });
510
+ }
511
+ }
512
+ }
513
+ return;
514
+ }
515
+ if (type === "array") {
516
+ if (!Array.isArray(value)) {
517
+ issues.push({ path, message: "Expected array" });
518
+ return;
519
+ }
520
+ const maxItems = typeof schema.maxItems === "number" ? schema.maxItems : null;
521
+ if (maxItems !== null && value.length > maxItems) {
522
+ issues.push({ path, message: `Expected at most ${maxItems} items` });
523
+ }
524
+ const itemSchema = schema.items && typeof schema.items === "object" && !Array.isArray(schema.items) ? schema.items : null;
525
+ if (itemSchema) {
526
+ value.forEach(
527
+ (item, index) => validateJsonSchemaValue(itemSchema, item, `${path}[${index}]`, issues)
528
+ );
529
+ }
530
+ return;
531
+ }
532
+ if (type === "string") {
533
+ if (typeof value !== "string") {
534
+ issues.push({ path, message: "Expected string" });
535
+ return;
536
+ }
537
+ const maxLength = typeof schema.maxLength === "number" ? schema.maxLength : null;
538
+ const minLength = typeof schema.minLength === "number" ? schema.minLength : null;
539
+ if (minLength !== null && value.length < minLength) {
540
+ issues.push({ path, message: `Expected at least ${minLength} characters` });
541
+ }
542
+ if (maxLength !== null && value.length > maxLength) {
543
+ issues.push({ path, message: `Expected at most ${maxLength} characters` });
544
+ }
545
+ return;
546
+ }
547
+ if (type === "number" || type === "integer") {
548
+ if (typeof value !== "number" || !Number.isFinite(value)) {
549
+ issues.push({ path, message: "Expected number" });
550
+ return;
551
+ }
552
+ if (type === "integer" && !Number.isInteger(value)) {
553
+ issues.push({ path, message: "Expected integer" });
554
+ }
555
+ return;
556
+ }
557
+ if (type === "boolean" && typeof value !== "boolean") {
558
+ issues.push({ path, message: "Expected boolean" });
559
+ }
560
+ }
561
+ function joinJsonPath(parent, key) {
562
+ return parent ? `${parent}.${key}` : key;
563
+ }
564
+
565
+ // src/bridge.ts
566
+ var ShadowBridge = class _ShadowBridge {
567
+ static commandRequestType = "shadow.app.command.request";
568
+ static commandResponseType = "shadow.app.command.response";
569
+ static inboxesRequestType = "shadow.app.inboxes.request";
570
+ static inboxesResponseType = "shadow.app.inboxes.response";
571
+ static enqueueInboxTaskRequestType = "shadow.app.inbox.enqueue.request";
572
+ static enqueueInboxTaskResponseType = "shadow.app.inbox.enqueue.response";
573
+ static openBuddyCreatorRequestType = "shadow.app.buddy.create.request";
574
+ static openBuddyCreatorResponseType = "shadow.app.buddy.create.response";
575
+ static inboxDeliveries(payload) {
576
+ return getShadowServerAppInboxDeliveries(payload);
577
+ }
578
+ static inboxErrors(payload) {
579
+ return getShadowServerAppInboxErrors(payload);
580
+ }
581
+ static channelMessageDeliveries(payload) {
582
+ return getShadowServerAppChannelMessageDeliveries(payload);
583
+ }
584
+ static channelMessageErrors(payload) {
585
+ return getShadowServerAppChannelMessageErrors(payload);
586
+ }
587
+ static unwrapCommandPayload(payload) {
588
+ return unwrapShadowServerAppCommandPayload(payload);
589
+ }
590
+ appKey;
591
+ targetOrigin;
592
+ timeoutMs;
593
+ win;
594
+ hasLaunchContext;
595
+ pending = /* @__PURE__ */ new Map();
596
+ onMessage = (event) => {
597
+ let data = event.data;
598
+ if (typeof data === "string") {
599
+ try {
600
+ data = JSON.parse(data || "{}");
601
+ } catch {
602
+ return;
603
+ }
604
+ }
605
+ if (!data || typeof data !== "object") return;
606
+ const record = data;
607
+ if (typeof record.requestId !== "string" || typeof record.type !== "string") return;
608
+ const entry = this.pending.get(record.requestId);
609
+ if (!entry || record.type !== entry.responseType) return;
610
+ this.pending.delete(record.requestId);
611
+ if (record.ok) entry.resolve(record.result);
612
+ else
613
+ entry.reject(
614
+ new Error(typeof record.error === "string" ? record.error : "Bridge request failed")
615
+ );
616
+ };
617
+ constructor(options) {
618
+ this.appKey = options.appKey;
619
+ this.targetOrigin = options.targetOrigin ?? "*";
620
+ this.timeoutMs = options.timeoutMs ?? 6e4;
621
+ this.win = options.windowRef ?? (typeof window === "undefined" ? null : window);
622
+ this.hasLaunchContext = this.resolveLaunchContext();
623
+ this.win?.addEventListener("message", this.onMessage);
624
+ }
625
+ dispose() {
626
+ this.win?.removeEventListener("message", this.onMessage);
627
+ for (const entry of this.pending.values()) {
628
+ entry.reject(new Error("ShadowBridge disposed"));
629
+ }
630
+ this.pending.clear();
631
+ }
632
+ isAvailable() {
633
+ if (!this.win) return false;
634
+ return this.hasLaunchContext && (this.win.parent !== this.win || !!this.win.ReactNativeWebView);
635
+ }
636
+ command(commandName, input, options = {}) {
637
+ return this.request(
638
+ _ShadowBridge.commandRequestType,
639
+ _ShadowBridge.commandResponseType,
640
+ {
641
+ commandName,
642
+ input,
643
+ ...options.channelId ? { channelId: options.channelId } : {},
644
+ ...options.task ? { task: options.task } : {}
645
+ },
646
+ options.timeoutMs
647
+ ).then(
648
+ (payload) => unwrapShadowServerAppCommandPayload(payload)
649
+ );
650
+ }
651
+ inboxes(options = {}) {
652
+ return this.request(
653
+ _ShadowBridge.inboxesRequestType,
654
+ _ShadowBridge.inboxesResponseType,
655
+ {},
656
+ options.timeoutMs ?? 15e3
657
+ );
658
+ }
659
+ enqueueInboxTask(input, options = {}) {
660
+ return this.request(
661
+ _ShadowBridge.enqueueInboxTaskRequestType,
662
+ _ShadowBridge.enqueueInboxTaskResponseType,
663
+ input,
664
+ options.timeoutMs
665
+ );
666
+ }
667
+ openBuddyCreator(input = {}, options = {}) {
668
+ return this.request(
669
+ _ShadowBridge.openBuddyCreatorRequestType,
670
+ _ShadowBridge.openBuddyCreatorResponseType,
671
+ input,
672
+ options.timeoutMs ?? 10 * 60 * 1e3
673
+ );
674
+ }
675
+ unwrapCommandPayload(payload) {
676
+ return unwrapShadowServerAppCommandPayload(payload);
677
+ }
678
+ inboxDeliveries(payload) {
679
+ return getShadowServerAppInboxDeliveries(payload);
680
+ }
681
+ inboxErrors(payload) {
682
+ return getShadowServerAppInboxErrors(payload);
683
+ }
684
+ channelMessageDeliveries(payload) {
685
+ return getShadowServerAppChannelMessageDeliveries(payload);
686
+ }
687
+ channelMessageErrors(payload) {
688
+ return getShadowServerAppChannelMessageErrors(payload);
689
+ }
690
+ request(requestType, responseType, payload, timeoutMs = this.timeoutMs) {
691
+ if (!this.isAvailable()) {
692
+ return Promise.reject(
693
+ new Error("ShadowBridge is not available outside a Shadow launch frame")
694
+ );
695
+ }
696
+ const requestId = `req_${Math.random().toString(36).slice(2)}`;
697
+ this.postMessage({
698
+ type: requestType,
699
+ requestId,
700
+ appKey: this.appKey,
701
+ ...payload
702
+ });
703
+ return new Promise((resolve, reject) => {
704
+ this.pending.set(requestId, {
705
+ responseType,
706
+ resolve,
707
+ reject
708
+ });
709
+ this.win?.setTimeout(() => {
710
+ if (!this.pending.has(requestId)) return;
711
+ this.pending.delete(requestId);
712
+ reject(new Error("Bridge request timed out"));
713
+ }, timeoutMs);
714
+ });
715
+ }
716
+ postMessage(message) {
717
+ if (!this.win) return;
718
+ if (this.win.ReactNativeWebView) {
719
+ this.win.ReactNativeWebView.postMessage(JSON.stringify(message));
720
+ return;
721
+ }
722
+ this.win.parent.postMessage(message, this.targetOrigin);
723
+ }
724
+ resolveLaunchContext() {
725
+ if (!this.win) return false;
726
+ const storageKey = `shadow.bridge.launch:${this.appKey}`;
727
+ const memoryContexts = this.win.__shadowBridgeLaunchContexts ??= {};
728
+ const hasLaunchToken = new URLSearchParams(this.win.location.search).has("shadow_launch");
729
+ if (hasLaunchToken) {
730
+ memoryContexts[this.appKey] = true;
731
+ try {
732
+ this.win.sessionStorage?.setItem(storageKey, "1");
733
+ } catch {
734
+ }
735
+ return true;
736
+ }
737
+ if (memoryContexts[this.appKey]) return true;
738
+ try {
739
+ return this.win.sessionStorage?.getItem(storageKey) === "1";
740
+ } catch {
741
+ return false;
742
+ }
743
+ }
744
+ };
745
+
44
746
  // src/client.ts
45
747
  function sanitizeErrorBody(body) {
46
748
  if (!body) return "(empty response)";
@@ -71,6 +773,9 @@ var ShadowClient = class {
71
773
  this.baseUrl = baseUrl.replace(/\/api\/?$/, "");
72
774
  }
73
775
  baseUrl;
776
+ serverAppEventStreamUrl(eventStreamPath) {
777
+ return new URL(eventStreamPath, `${this.baseUrl}/`).toString();
778
+ }
74
779
  isShadowPrivateMediaUrl(value) {
75
780
  if (value.startsWith("/shadow/uploads/") || value.startsWith("/api/media/signed/")) {
76
781
  return true;
@@ -161,6 +866,18 @@ var ShadowClient = class {
161
866
  body: JSON.stringify(data)
162
867
  });
163
868
  }
869
+ async startPasswordReset(data) {
870
+ return this.request("/api/auth/password-reset/start", {
871
+ method: "POST",
872
+ body: JSON.stringify(data)
873
+ });
874
+ }
875
+ async completePasswordReset(data) {
876
+ return this.request("/api/auth/password-reset/complete", {
877
+ method: "POST",
878
+ body: JSON.stringify(data)
879
+ });
880
+ }
164
881
  async refreshToken(refreshToken) {
165
882
  return this.request("/api/auth/refresh", {
166
883
  method: "POST",
@@ -246,6 +963,27 @@ var ShadowClient = class {
246
963
  async generateAgentToken(agentId) {
247
964
  return this.request(`/api/agents/${agentId}/token`, { method: "POST" });
248
965
  }
966
+ async listConnectorComputers() {
967
+ return this.request("/api/connector/computers");
968
+ }
969
+ async createConnectorBootstrap(data) {
970
+ return this.request("/api/connector/computers/bootstrap", {
971
+ method: "POST",
972
+ body: JSON.stringify(data)
973
+ });
974
+ }
975
+ async createAgentOnConnectorComputer(computerId, data) {
976
+ return this.request(`/api/connector/computers/${computerId}/buddies`, {
977
+ method: "POST",
978
+ body: JSON.stringify(data)
979
+ });
980
+ }
981
+ async configureAgentOnConnectorComputer(computerId, agentId, data) {
982
+ return this.request(`/api/connector/computers/${computerId}/buddies/${agentId}/configure`, {
983
+ method: "POST",
984
+ body: JSON.stringify(data)
985
+ });
986
+ }
249
987
  async startAgent(agentId) {
250
988
  return this.request(`/api/agents/${agentId}/start`, { method: "POST" });
251
989
  }
@@ -291,6 +1029,7 @@ var ShadowClient = class {
291
1029
  const policy = {
292
1030
  serverId,
293
1031
  ...data.channelId !== void 0 ? { channelId: data.channelId } : {},
1032
+ ...data.listen !== void 0 ? { listen: data.listen } : {},
294
1033
  ...data.mentionOnly !== void 0 ? { mentionOnly: data.mentionOnly } : {},
295
1034
  ...data.reply !== void 0 ? { reply: data.reply } : {},
296
1035
  ...data.config !== void 0 ? { config: data.config } : {}
@@ -447,6 +1186,7 @@ var ShadowClient = class {
447
1186
  const form = new FormData();
448
1187
  form.set("input", JSON.stringify(data.input ?? {}));
449
1188
  if (data.channelId) form.set("channelId", data.channelId);
1189
+ if (data.task) form.set("task", JSON.stringify(data.task));
450
1190
  form.set(data.field ?? "file", data.file, data.filename);
451
1191
  return this.request(
452
1192
  `/api/servers/${serverIdOrSlug}/apps/${encodeURIComponent(appKey)}/commands/${encodeURIComponent(
@@ -669,6 +1409,83 @@ var ShadowClient = class {
669
1409
  async getMessage(messageId) {
670
1410
  return this.request(`/api/messages/${messageId}`);
671
1411
  }
1412
+ async listBuddyInboxes() {
1413
+ return this.request("/api/buddy-inboxes");
1414
+ }
1415
+ async listServerBuddyInboxes(serverIdOrSlug) {
1416
+ return this.request(`/api/servers/${serverIdOrSlug}/inboxes`);
1417
+ }
1418
+ async ensureBuddyInbox(serverIdOrSlug, agentId) {
1419
+ return this.request(`/api/servers/${serverIdOrSlug}/inboxes/${agentId}`, {
1420
+ method: "POST"
1421
+ });
1422
+ }
1423
+ async getBuddyInboxAdmissionPolicy(serverIdOrSlug, agentId) {
1424
+ return this.request(`/api/servers/${serverIdOrSlug}/inboxes/${agentId}/admission-policy`);
1425
+ }
1426
+ async updateBuddyInboxAdmissionPolicy(serverIdOrSlug, agentId, policy) {
1427
+ return this.request(`/api/servers/${serverIdOrSlug}/inboxes/${agentId}/admission-policy`, {
1428
+ method: "PUT",
1429
+ body: JSON.stringify(policy)
1430
+ });
1431
+ }
1432
+ async listBuddyInboxAdmissionPending(serverIdOrSlug, agentId) {
1433
+ return this.request(`/api/servers/${serverIdOrSlug}/inboxes/${agentId}/admission-pending`);
1434
+ }
1435
+ async approveBuddyInboxAdmissionPending(serverIdOrSlug, agentId, pendingId) {
1436
+ return this.request(
1437
+ `/api/servers/${serverIdOrSlug}/inboxes/${agentId}/admission-pending/${pendingId}/approve`,
1438
+ { method: "POST" }
1439
+ );
1440
+ }
1441
+ async rejectBuddyInboxAdmissionPending(serverIdOrSlug, agentId, pendingId) {
1442
+ return this.request(
1443
+ `/api/servers/${serverIdOrSlug}/inboxes/${agentId}/admission-pending/${pendingId}/reject`,
1444
+ { method: "POST" }
1445
+ );
1446
+ }
1447
+ async enqueueInboxTaskForAgent(serverIdOrSlug, agentId, task) {
1448
+ return this.request(`/api/servers/${serverIdOrSlug}/inboxes/${agentId}/tasks`, {
1449
+ method: "POST",
1450
+ body: JSON.stringify(task)
1451
+ });
1452
+ }
1453
+ async enqueueInboxTask(channelId, task) {
1454
+ return this.request(`/api/channels/${channelId}/inbox/tasks`, {
1455
+ method: "POST",
1456
+ body: JSON.stringify(task)
1457
+ });
1458
+ }
1459
+ async claimTaskCard(messageId, cardId, data = {}) {
1460
+ return this.request(`/api/messages/${messageId}/cards/${cardId}/claim`, {
1461
+ method: "POST",
1462
+ body: JSON.stringify(data)
1463
+ });
1464
+ }
1465
+ async updateTaskCard(messageId, cardId, data) {
1466
+ return this.request(`/api/messages/${messageId}/cards/${cardId}`, {
1467
+ method: "PATCH",
1468
+ body: JSON.stringify(data)
1469
+ });
1470
+ }
1471
+ async retryTaskCard(messageId, cardId, data = {}) {
1472
+ return this.request(`/api/messages/${messageId}/cards/${cardId}/retry`, {
1473
+ method: "POST",
1474
+ body: JSON.stringify(data)
1475
+ });
1476
+ }
1477
+ async claimNextInboxTask(serverIdOrSlug, agentId, data = {}) {
1478
+ return this.request(`/api/servers/${serverIdOrSlug}/inboxes/${agentId}/claim-next`, {
1479
+ method: "POST",
1480
+ body: JSON.stringify(data)
1481
+ });
1482
+ }
1483
+ async promoteMessageToInboxTask(messageId, data) {
1484
+ return this.request(`/api/messages/${messageId}/inbox/tasks`, {
1485
+ method: "POST",
1486
+ body: JSON.stringify(data)
1487
+ });
1488
+ }
672
1489
  async submitInteractiveAction(messageId, input) {
673
1490
  return this.request(`/api/messages/${messageId}/interactive`, {
674
1491
  method: "POST",
@@ -820,6 +1637,19 @@ var ShadowClient = class {
820
1637
  formData.append("messageId", messageId);
821
1638
  } else if (messageId) {
822
1639
  if (messageId.messageId) formData.append("messageId", messageId.messageId);
1640
+ if (messageId.kind) formData.append("kind", messageId.kind);
1641
+ if (typeof messageId.durationMs === "number") {
1642
+ formData.append("durationMs", String(messageId.durationMs));
1643
+ }
1644
+ if (messageId.waveformPeaks) {
1645
+ formData.append("waveformPeaks", JSON.stringify(messageId.waveformPeaks));
1646
+ }
1647
+ if (messageId.transcriptText) formData.append("transcriptText", messageId.transcriptText);
1648
+ if (messageId.transcriptLanguage) {
1649
+ formData.append("transcriptLanguage", messageId.transcriptLanguage);
1650
+ }
1651
+ if (messageId.transcriptSource)
1652
+ formData.append("transcriptSource", messageId.transcriptSource);
823
1653
  }
824
1654
  const url = `${this.baseUrl}/api/media/upload`;
825
1655
  const res = await fetch(url, {
@@ -835,6 +1665,43 @@ var ShadowClient = class {
835
1665
  }
836
1666
  return res.json();
837
1667
  }
1668
+ async sendVoiceMessage(channelId, file, filename, contentType, opts) {
1669
+ const message = await this.sendMessage(channelId, "\u200B", {
1670
+ threadId: opts.threadId,
1671
+ replyToId: opts.replyToId
1672
+ });
1673
+ await this.uploadMedia(file, filename, contentType, {
1674
+ messageId: message.id,
1675
+ kind: "voice",
1676
+ durationMs: opts.durationMs,
1677
+ waveformPeaks: opts.waveformPeaks,
1678
+ transcriptText: opts.transcriptText,
1679
+ transcriptLanguage: opts.transcriptLanguage,
1680
+ transcriptSource: opts.transcriptSource
1681
+ });
1682
+ return this.getMessage(message.id);
1683
+ }
1684
+ async markVoicePlayed(attachmentId, input) {
1685
+ return this.request(`/api/attachments/${attachmentId}/voice-playback`, {
1686
+ method: "PUT",
1687
+ body: JSON.stringify(input ?? {})
1688
+ });
1689
+ }
1690
+ async requestVoiceTranscript(attachmentId, input) {
1691
+ return this.request(`/api/attachments/${attachmentId}/transcript`, {
1692
+ method: "POST",
1693
+ body: JSON.stringify({
1694
+ mode: "server",
1695
+ ...input?.language ? { language: input.language } : {}
1696
+ })
1697
+ });
1698
+ }
1699
+ async updateVoiceTranscript(attachmentId, input) {
1700
+ return this.request(`/api/attachments/${attachmentId}/transcript`, {
1701
+ method: "PUT",
1702
+ body: JSON.stringify(input)
1703
+ });
1704
+ }
838
1705
  async resolveAttachmentMediaUrl(attachmentId, options) {
839
1706
  const params = new URLSearchParams();
840
1707
  params.set("disposition", options?.disposition ?? "inline");
@@ -1090,9 +1957,14 @@ var ShadowClient = class {
1090
1957
  return this.request(`/api/auth/sessions/${sessionId}`, { method: "DELETE" });
1091
1958
  }
1092
1959
  async changePassword(data) {
1960
+ const oldPassword = data.oldPassword ?? data.currentPassword;
1093
1961
  return this.request("/api/auth/password", {
1094
1962
  method: "PUT",
1095
- body: JSON.stringify(data)
1963
+ body: JSON.stringify({
1964
+ oldPassword,
1965
+ newPassword: data.newPassword,
1966
+ confirmPassword: data.confirmPassword ?? data.newPassword
1967
+ })
1096
1968
  });
1097
1969
  }
1098
1970
  async getDashboard() {
@@ -1101,7 +1973,13 @@ var ShadowClient = class {
1101
1973
  async loginWithGoogleIdToken(idToken) {
1102
1974
  return this.request("/api/auth/google/id-token", {
1103
1975
  method: "POST",
1104
- body: JSON.stringify({ idToken })
1976
+ body: JSON.stringify({ credential: idToken })
1977
+ });
1978
+ }
1979
+ async loginWithAppleIdentityToken(data) {
1980
+ return this.request("/api/auth/oauth/apple/mobile", {
1981
+ method: "POST",
1982
+ body: JSON.stringify(data)
1105
1983
  });
1106
1984
  }
1107
1985
  // ── Friendships ───────────────────────────────────────────────────────
@@ -1850,6 +2728,24 @@ var ShadowClient = class {
1850
2728
  const suffix = qs.toString();
1851
2729
  return this.request(`/api/discover/business${suffix ? `?${suffix}` : ""}`);
1852
2730
  }
2731
+ async discoverMarketplaceProducts(params) {
2732
+ const qs = new URLSearchParams();
2733
+ if (params?.q) qs.set("q", params.q);
2734
+ if (params?.tag) qs.set("tag", params.tag);
2735
+ if (params?.category) qs.set("category", params.category);
2736
+ if (params?.scope) qs.set("scope", params.scope);
2737
+ if (params?.limit) qs.set("limit", String(params.limit));
2738
+ if (params?.offset) qs.set("offset", String(params.offset));
2739
+ const suffix = qs.toString();
2740
+ return this.request(`/api/discover/marketplace/products${suffix ? `?${suffix}` : ""}`);
2741
+ }
2742
+ async discoverMarketplaceCategories(params) {
2743
+ const qs = new URLSearchParams();
2744
+ if (params?.q) qs.set("q", params.q);
2745
+ if (params?.limit) qs.set("limit", String(params.limit));
2746
+ const suffix = qs.toString();
2747
+ return this.request(`/api/discover/marketplace/categories${suffix ? `?${suffix}` : ""}`);
2748
+ }
1853
2749
  async discoverBusinessHub(params) {
1854
2750
  return this.discoverCommerce(params);
1855
2751
  }
@@ -2317,10 +3213,35 @@ var ShadowVoiceConsumer = class {
2317
3213
  0 && (module.exports = {
2318
3214
  CLIENT_EVENTS,
2319
3215
  SERVER_EVENTS,
3216
+ SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT,
3217
+ SHADOW_SERVER_APP_COMMAND_EVENTS,
3218
+ SHADOW_SERVER_APP_COMMAND_FAILED_EVENT,
3219
+ SHADOW_SERVER_APP_PROTOCOL,
3220
+ ShadowBridge,
2320
3221
  ShadowClient,
3222
+ ShadowServerAppCommandError,
3223
+ ShadowServerAppOutbox,
3224
+ ShadowServerAppRuntime,
2321
3225
  ShadowSocket,
2322
3226
  ShadowVoiceConsumer,
3227
+ buildShadowServerAppInboxDelivery,
3228
+ buildShadowServerAppInboxTaskRequest,
2323
3229
  channelRoom,
3230
+ createShadowServerAppManifest,
3231
+ defineShadowServerApp,
3232
+ extractShadowServerAppBearerToken,
3233
+ getShadowServerAppChannelMessageDeliveries,
3234
+ getShadowServerAppChannelMessageErrors,
3235
+ getShadowServerAppTaskCardId,
3236
+ introspectShadowServerAppToken,
3237
+ normalizeShadowServerAppCommandInput,
3238
+ parseShadowServerAppCommandRequest,
3239
+ shadowServerAppActorAvatarUrl,
3240
+ shadowServerAppActorDisplayName,
3241
+ shadowServerAppActorRef,
3242
+ shadowServerAppError,
3243
+ shadowServerAppInboxTaskEndpoint,
2324
3244
  threadRoom,
2325
- userRoom
3245
+ userRoom,
3246
+ validateShadowServerAppJsonSchema
2326
3247
  });