🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@shadowob/sdk

Package Overview
Dependencies
Maintainers
1
Versions
77
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@shadowob/sdk - npm Package Compare versions

Comparing version
1.1.63
to
1.1.64
+942
dist/chunk-62WH33SI.js
// src/server-app.ts
import {
BUDDY_INBOX_DELIVERY_PERMISSION
} from "@shadowob/shared";
var SHADOW_SERVER_APP_PROTOCOL = "shadow.app/1";
var SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT = "server_app.command.completed";
var SHADOW_SERVER_APP_COMMAND_FAILED_EVENT = "server_app.command.failed";
var SHADOW_SERVER_APP_COMMAND_EVENTS = [
SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT,
SHADOW_SERVER_APP_COMMAND_FAILED_EVENT
];
var ShadowServerAppHttpError = class extends Error {
status;
payload;
constructor(status, message, payload) {
super(message);
this.name = "ShadowServerAppHttpError";
this.status = status;
this.payload = payload;
}
};
function isProtocolRecord(value) {
return !!value && typeof value === "object" && !Array.isArray(value);
}
function optionalProtocolString(value) {
return typeof value === "string" && value ? value : void 0;
}
function protocolPathSegment(value) {
return encodeURIComponent(value);
}
function shadowServerAppInboxTaskEndpoint(serverIdOrSlug, target) {
if ("channelId" in target && target.channelId) {
return `/api/channels/${protocolPathSegment(target.channelId)}/inbox/tasks`;
}
if ("agentId" in target && target.agentId) {
return `/api/servers/${protocolPathSegment(serverIdOrSlug)}/inboxes/${protocolPathSegment(
target.agentId
)}/tasks`;
}
throw new Error("Missing Inbox task target");
}
function buildShadowServerAppInboxTaskRequest(input) {
const appId = input.app.id ?? input.app.appId ?? input.app.appKey;
const serverAppData = isProtocolRecord(input.task.data?.serverApp) ? input.task.data.serverApp : {};
return {
endpoint: shadowServerAppInboxTaskEndpoint(input.serverIdOrSlug, input.target),
body: {
title: input.task.title,
body: input.task.body,
priority: input.task.priority,
tags: input.task.tags,
idempotencyKey: input.task.idempotencyKey,
requirements: input.task.requirements,
outputContract: input.task.outputContract,
privacy: input.task.privacy,
app: {
id: appId,
appId,
appKey: input.app.appKey,
name: input.app.name ?? input.app.label ?? input.app.appKey,
label: input.app.label ?? input.app.name ?? input.app.appKey,
...input.app.iconUrl ? { iconUrl: input.app.iconUrl } : {}
},
source: {
kind: "server_app",
id: appId,
appId,
appKey: input.app.appKey,
...input.app.name ? { appName: input.app.name } : {},
...input.app.iconUrl ? { iconUrl: input.app.iconUrl } : {},
...input.app.serverId ? { serverId: input.app.serverId } : {},
...input.commandName ? { command: input.commandName } : {},
label: input.app.label ?? input.app.name ?? input.app.appKey,
...input.task.resource ? { resource: input.task.resource } : {}
},
data: {
...input.task.data ?? {},
serverApp: {
...serverAppData,
appKey: input.app.appKey,
name: input.app.name ?? input.app.label ?? input.app.appKey,
label: input.app.label ?? input.app.name ?? input.app.appKey,
...input.app.iconUrl ? { iconUrl: input.app.iconUrl } : {},
...input.commandName ? { command: input.commandName } : {}
}
}
}
};
}
function getShadowServerAppTaskCardId(message) {
const metadata = isProtocolRecord(message) ? message.metadata : null;
const cards = isProtocolRecord(metadata) && Array.isArray(metadata.cards) ? metadata.cards : [];
for (const item of cards) {
if (!isProtocolRecord(item)) continue;
if (item.kind === "task" && typeof item.id === "string" && item.id) return item.id;
}
return null;
}
function buildShadowServerAppInboxDelivery(input) {
const message = isProtocolRecord(input.message) ? input.message : {};
return {
..."agentId" in input.target && input.target.agentId ? { agentId: input.target.agentId } : {},
channelId: optionalProtocolString(message.channelId),
messageId: optionalProtocolString(message.id),
cardId: getShadowServerAppTaskCardId(message),
idempotencyKey: input.idempotencyKey
};
}
function shadowFromPayload(payload) {
if (payload.protocol === SHADOW_SERVER_APP_PROTOCOL) {
return payload;
}
const shadow = isProtocolRecord(payload.shadow) ? payload.shadow : null;
if (shadow?.protocol === SHADOW_SERVER_APP_PROTOCOL) {
return shadow;
}
return null;
}
function mergeShadowResult(value, shadow) {
if (!shadow) return value;
const existing = shadowFromPayload(value);
if (!existing) return { ...value, shadow };
return {
...value,
shadow: {
protocol: SHADOW_SERVER_APP_PROTOCOL,
outbox: {
...existing.outbox ?? {},
...shadow.outbox ?? {}
}
}
};
}
function getShadowServerAppInboxDeliveries(payload) {
if (!isProtocolRecord(payload)) return [];
const shadow = shadowFromPayload(payload);
return shadow?.outbox?.deliveries ?? [];
}
function getShadowServerAppInboxErrors(payload) {
if (!isProtocolRecord(payload)) return [];
const shadow = shadowFromPayload(payload);
return shadow?.outbox?.errors ?? [];
}
function getShadowServerAppPendingInboxTasks(payload, depth = 0) {
if (!isProtocolRecord(payload) || depth > 4) return [];
const shadow = shadowFromPayload(payload);
const tasks = shadow?.outbox?.inboxTasks ?? [];
return "result" in payload && payload.result !== void 0 ? [...tasks, ...getShadowServerAppPendingInboxTasks(payload.result, depth + 1)] : tasks;
}
function getShadowServerAppChannelMessageDeliveries(payload) {
if (!isProtocolRecord(payload)) return [];
const shadow = shadowFromPayload(payload);
return shadow?.outbox?.channelMessageDeliveries ?? [];
}
function getShadowServerAppChannelMessageErrors(payload) {
if (!isProtocolRecord(payload)) return [];
const shadow = shadowFromPayload(payload);
return shadow?.outbox?.channelMessageErrors ?? [];
}
function getShadowServerAppPendingChannelMessages(payload, depth = 0) {
if (!isProtocolRecord(payload) || depth > 4) return [];
const shadow = shadowFromPayload(payload);
const messages = shadow?.outbox?.channelMessages ?? [];
return "result" in payload && payload.result !== void 0 ? [...messages, ...getShadowServerAppPendingChannelMessages(payload.result, depth + 1)] : messages;
}
function hasShadowServerAppPendingOutbox(payload) {
return getShadowServerAppPendingInboxTasks(payload).length > 0 || getShadowServerAppPendingChannelMessages(payload).length > 0;
}
function isDomainResultWithEvents(payload) {
return Array.isArray(payload.events) && ("cursor" in payload || "result" in payload);
}
function isCommandPayloadEnvelope(payload) {
if (isDomainResultWithEvents(payload)) return false;
return payload.ok === true || payload.ok === false || shadowFromPayload(payload) !== null;
}
function unwrapShadowServerAppCommandPayload(payload) {
if (isProtocolRecord(payload) && payload.ok === false) {
throw new Error(typeof payload.error === "string" ? payload.error : "Command failed");
}
if (isProtocolRecord(payload) && "result" in payload && payload.result !== void 0 && isCommandPayloadEnvelope(payload)) {
const nested = unwrapShadowServerAppCommandPayload(payload.result);
const shadow = shadowFromPayload(payload);
if (isProtocolRecord(nested)) return mergeShadowResult(nested, shadow);
return nested;
}
return payload;
}
async function readShadowServerAppResponsePayload(response) {
const text = await response.text().catch(() => "");
if (!text.trim()) return null;
try {
return JSON.parse(text);
} catch {
if (!response.ok) return { ok: false, error: text };
throw new ShadowServerAppHttpError(response.status, "Command returned invalid JSON", text);
}
}
function shadowServerAppResponseErrorMessage(status, payload, fallback = "Command failed") {
if (isProtocolRecord(payload) && typeof payload.error === "string" && payload.error) {
return payload.error;
}
if (typeof payload === "string" && payload.trim()) return payload;
return status ? `${fallback} (${status})` : fallback;
}
async function readShadowServerAppCommandResponse(response) {
const payload = await readShadowServerAppResponsePayload(response);
if (!response.ok || isProtocolRecord(payload) && payload.ok === false) {
throw new ShadowServerAppHttpError(
response.status,
shadowServerAppResponseErrorMessage(response.status, payload),
payload
);
}
return unwrapShadowServerAppCommandPayload(payload);
}
var ShadowServerAppOutbox = class {
inboxTasks = [];
channelMessages = [];
enqueueInboxTask(task) {
this.inboxTasks.push(task);
return this;
}
enqueueInboxTasks(tasks) {
for (const task of tasks) this.enqueueInboxTask(task);
return this;
}
sendChannelMessage(message) {
this.channelMessages.push(message);
return this;
}
sendChannelMessages(messages) {
for (const message of messages) this.sendChannelMessage(message);
return this;
}
toShadow() {
return {
protocol: SHADOW_SERVER_APP_PROTOCOL,
outbox: {
...this.inboxTasks.length > 0 ? { inboxTasks: [...this.inboxTasks] } : {},
...this.channelMessages.length > 0 ? { channelMessages: [...this.channelMessages] } : {}
}
};
}
attachTo(result) {
return { ...result, shadow: this.toShadow() };
}
};
function trimTrailingSlash(value) {
return value.replace(/\/+$/, "");
}
function joinBasePath(baseUrl, path) {
const cleanBase = trimTrailingSlash(baseUrl);
const cleanPath = path.startsWith("/") ? path : `/${path}`;
return `${cleanBase}${cleanPath}`;
}
var SHADOW_SERVER_APP_PUBLIC_AVATAR_CACHE_CONTROL = "public, max-age=31536000, immutable";
function firstEnvironmentValue(env, keys, fallback) {
for (const key of keys) {
const value = env[key]?.trim();
if (value) return value;
}
return fallback;
}
function shadowServerAppApiBaseUrl(env = {}) {
return trimTrailingSlash(
firstEnvironmentValue(
env,
["SHADOWOB_INTERNAL_SERVER_URL", "SHADOWOB_SERVER_URL"],
"http://localhost:3002"
)
);
}
function shadowServerAppPublicBaseUrl(env = {}) {
return trimTrailingSlash(
firstEnvironmentValue(
env,
[
"SHADOWOB_PUBLIC_BASE_URL",
"SHADOWOB_WEB_BASE_URL",
"SHADOWOB_OAUTH_AUTHORIZE_BASE_URL",
"OAUTH_BASE_URL",
"SHADOWOB_SERVER_URL"
],
"http://localhost:3000"
)
);
}
function shadowServerAppPublicUrl(pathOrUrl, env = {}) {
if (!pathOrUrl.startsWith("/")) return pathOrUrl;
return joinBasePath(shadowServerAppPublicBaseUrl(env), pathOrUrl);
}
function isShadowServerAppSignedMediaUrl(value, env = {}) {
const mediaUrl = value.trim();
if (mediaUrl.startsWith("/api/media/signed/")) return true;
try {
return new URL(mediaUrl, shadowServerAppPublicBaseUrl(env)).pathname.startsWith(
"/api/media/signed/"
);
} catch {
return false;
}
}
function normalizeShadowServerAppAvatarUrl(value, env = {}) {
if (typeof value !== "string") return null;
const avatarUrl = value.trim();
if (!avatarUrl || avatarUrl.length > 500) return null;
if (isShadowServerAppSignedMediaUrl(avatarUrl, env)) return null;
return shadowServerAppPublicUrl(avatarUrl, env);
}
function shadowServerAppAvatarRedirectUrl(requestUrl, env = {}) {
return shadowServerAppPublicUrl(new URL(requestUrl).pathname, env);
}
function urlOrigin(value) {
try {
return new URL(value).origin;
} catch {
return null;
}
}
function rebasePublicAssetUrl(value, sourceOrigin, publicBaseUrl) {
if (!sourceOrigin) return value;
try {
const url = new URL(value);
if (url.origin !== sourceOrigin) return value;
return joinBasePath(publicBaseUrl, `${url.pathname}${url.search}${url.hash}`);
} catch {
return value;
}
}
function extractShadowServerAppBearerToken(value) {
if (!value) return null;
return value.toLowerCase().startsWith("bearer ") ? value.slice(7).trim() : null;
}
function decodeBase64UrlJson(value) {
try {
const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, "=");
const binary = globalThis.atob(padded);
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
return JSON.parse(new TextDecoder().decode(bytes));
} catch {
return null;
}
}
function decodeShadowServerAppLaunchTokenHint(token) {
if (!token) return null;
const parts = token.split(".");
if (parts.length !== 3 || parts[0] !== "sat_v1") return null;
const payload = decodeBase64UrlJson(parts[1]);
if (typeof payload?.serverId !== "string" || typeof payload.appKey !== "string") return null;
return { serverId: payload.serverId, appKey: payload.appKey };
}
async function fetchShadowServerAppLaunchInboxes(options) {
const hint = decodeShadowServerAppLaunchTokenHint(options.launchToken);
if (!hint || !options.launchToken) return { inboxes: [] };
const fetchFn = options.fetch ?? fetch;
const baseUrl = trimTrailingSlash(options.shadowApiBaseUrl ?? "http://localhost:3002");
const response = await fetchFn(
`${baseUrl}/api/servers/${encodeURIComponent(hint.serverId)}/apps/${encodeURIComponent(
hint.appKey
)}/launch/inboxes`,
{ headers: { Authorization: `Bearer ${options.launchToken}` } }
);
if (!response.ok) {
const message = await response.text().catch(() => "");
throw new Error(`Shadow launch inbox lookup failed (${response.status}): ${message}`);
}
return await response.json();
}
async function introspectShadowServerAppLaunchToken(options) {
const hint = decodeShadowServerAppLaunchTokenHint(options.launchToken);
if (!hint || !options.launchToken) return null;
const fetchFn = options.fetch ?? fetch;
const baseUrl = trimTrailingSlash(options.shadowApiBaseUrl ?? "http://localhost:3002");
const response = await fetchFn(
`${baseUrl}/api/servers/${encodeURIComponent(hint.serverId)}/apps/${encodeURIComponent(
hint.appKey
)}/launch/introspect`,
{
method: "POST",
headers: { Authorization: `Bearer ${options.launchToken}` }
}
);
if (!response.ok) {
const payload2 = await readShadowServerAppResponsePayload(response).catch(() => null);
return {
active: false,
error: shadowServerAppResponseErrorMessage(response.status, payload2, "invalid_launch_token")
};
}
const payload = await response.json().catch(() => null);
return typeof payload?.active === "boolean" ? payload : null;
}
function shadowServerAppLaunchIntrospectionError(introspection) {
return introspection?.error ?? introspection?.reason ?? introspection?.error_description ?? "invalid_launch_token";
}
function shadowServerAppLaunchCommandContextFromIntrospection(options, introspection) {
const shadow = introspection.active ? introspection.shadow : null;
if (!shadow) return null;
const command = options.manifest.commands.find((item) => item.name === options.commandName);
return {
protocol: SHADOW_SERVER_APP_PROTOCOL,
serverId: shadow.serverId,
serverAppId: shadow.serverAppId ?? "launch",
appKey: shadow.appKey || options.manifest.appKey,
command: options.commandName,
actor: shadow.actor,
channelId: shadow.channelId ?? null,
resources: shadow.resources ?? null,
task: shadow.task,
permission: command?.permission ?? shadow.permission ?? "server_app.runtime",
action: command?.action ?? shadow.action ?? "read",
dataClass: command?.dataClass ?? shadow.dataClass ?? "server-private"
};
}
async function resolveShadowServerAppLaunchCommandContextResolution(options) {
const introspection = await introspectShadowServerAppLaunchToken(options);
if (!introspection?.active) {
return {
context: null,
introspection,
error: shadowServerAppLaunchIntrospectionError(introspection)
};
}
const context = shadowServerAppLaunchCommandContextFromIntrospection(options, introspection);
return {
context,
introspection,
error: context ? null : shadowServerAppLaunchIntrospectionError(introspection)
};
}
async function resolveShadowServerAppLaunchCommandContext(options) {
const resolution = await resolveShadowServerAppLaunchCommandContextResolution(options);
return resolution.context;
}
async function deliverShadowServerAppLaunchOutbox(options) {
const hint = decodeShadowServerAppLaunchTokenHint(options.launchToken);
if (!hint || !options.launchToken) return options.result;
const fetchFn = options.fetch ?? fetch;
const baseUrl = trimTrailingSlash(options.shadowApiBaseUrl ?? "http://localhost:3002");
const response = await fetchFn(
`${baseUrl}/api/servers/${encodeURIComponent(hint.serverId)}/apps/${encodeURIComponent(
hint.appKey
)}/launch/outbox`,
{
method: "POST",
headers: {
Authorization: `Bearer ${options.launchToken}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
commandName: options.commandName,
result: options.result
})
}
);
if (!response.ok) {
const payload = await readShadowServerAppResponsePayload(response);
throw new ShadowServerAppHttpError(
response.status,
shadowServerAppResponseErrorMessage(
response.status,
payload,
"Shadow launch outbox delivery failed"
),
payload
);
}
return readShadowServerAppResponsePayload(response);
}
function normalizeShadowServerAppCommandInput(value) {
if (value && typeof value === "object" && !Array.isArray(value) && "input" in value && Object.keys(value).every((key) => key === "input" || key === "channelId")) {
return value.input ?? {};
}
return value;
}
function createShadowServerAppManifest(manifest, options = {}) {
const publicBaseUrl = trimTrailingSlash(
options.publicBaseUrl ?? `http://localhost:${options.port ?? 4201}`
);
const apiBaseUrl = trimTrailingSlash(options.apiBaseUrl ?? publicBaseUrl);
const iframeAllowedOrigins = (options.allowedOrigins ?? [publicBaseUrl]).map(
(origin) => urlOrigin(origin)
);
const iframePath = options.iframePath ?? "/shadow/server";
const iconPath = options.iconPath ?? "/assets/icon.svg";
const sourceAssetOrigin = urlOrigin(manifest.iconUrl);
return {
...manifest,
iconUrl: joinBasePath(publicBaseUrl, iconPath),
marketplace: manifest.marketplace ? {
...manifest.marketplace,
coverImageUrl: manifest.marketplace.coverImageUrl ? rebasePublicAssetUrl(
manifest.marketplace.coverImageUrl,
sourceAssetOrigin,
publicBaseUrl
) : manifest.marketplace.coverImageUrl,
gallery: manifest.marketplace.gallery?.map((item) => ({
...item,
url: rebasePublicAssetUrl(item.url, sourceAssetOrigin, publicBaseUrl)
})),
links: manifest.marketplace.links?.map((item) => ({
...item,
url: rebasePublicAssetUrl(item.url, sourceAssetOrigin, publicBaseUrl)
}))
} : manifest.marketplace,
iframe: manifest.iframe ? {
...manifest.iframe,
entry: joinBasePath(publicBaseUrl, iframePath),
allowedOrigins: iframeAllowedOrigins
} : manifest.iframe,
api: {
...manifest.api,
baseUrl: apiBaseUrl
}
};
}
function defineShadowServerApp(manifest, options = {}) {
return new ShadowServerAppRuntime(manifest, options);
}
var createShadowServerAppRuntime = defineShadowServerApp;
var ShadowServerAppCommandError = class extends Error {
status;
issues;
constructor(status, error, issues) {
super(error);
this.name = "ShadowServerAppCommandError";
this.status = status;
this.issues = issues;
}
};
function shadowServerAppError(status, error, issues) {
return new ShadowServerAppCommandError(status, error, issues);
}
var ShadowServerAppRuntime = class {
constructor(sourceManifest, options = {}) {
this.sourceManifest = sourceManifest;
this.options = options;
}
sourceManifest;
options;
manifest(options = {}) {
return createShadowServerAppManifest(this.sourceManifest, options);
}
defineCommands(handlers) {
return handlers;
}
actor(envelopeOrContext) {
return shadowServerAppActorRef(envelopeOrContext);
}
error(status, error, issues) {
return shadowServerAppError(status, error, issues);
}
async parseCommand(commandName, request) {
return parseShadowServerAppCommandRequest(
{
...request,
expectedCommand: commandName,
shadowBaseUrl: this.options.shadowBaseUrl,
fetchImpl: this.options.fetchImpl
}
);
}
async executeCommand(commandName, request, handlers) {
const parsed = await this.parseCommand(commandName, request);
if (!parsed.ok) return parseErrorResult(parsed);
return this.executeEnvelope(commandName, parsed.envelope, handlers);
}
async executeLocal(commandName, input, context, handlers) {
return this.executeEnvelope(
commandName,
{
input,
context: {
...context,
command: commandName
}
},
handlers
);
}
async executeEnvelope(commandName, envelope, handlers) {
const command = this.sourceManifest.commands.find((item) => item.name === commandName);
if (!command) return failureResult(404, "command_not_found");
const validation = validateShadowServerAppJsonSchema(command.inputSchema, envelope.input);
if (!validation.ok) return failureResult(422, "invalid_input", validation.issues);
const handler = handlers[commandName];
if (!handler) return failureResult(404, "command_not_found");
try {
const result = await handler(envelope.input, {
context: envelope.context,
actor: this.actor(envelope)
});
return { ok: true, status: 200, body: { ok: true, result } };
} catch (error) {
if (error instanceof ShadowServerAppCommandError) {
return failureResult(error.status, error.message, error.issues);
}
throw error;
}
}
};
async function introspectShadowServerAppToken(input) {
const baseUrl = trimTrailingSlash(input.shadowBaseUrl ?? "http://localhost:3002");
const fetchImpl = input.fetchImpl ?? fetch;
const response = await fetchImpl(
`${baseUrl}/api/servers/${encodeURIComponent(input.serverId)}/apps/${encodeURIComponent(
input.appKey
)}/oauth/introspect`,
{
method: "POST",
headers: {
Authorization: `Bearer ${input.token}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ token: input.token })
}
);
if (!response.ok) return null;
const payload = await response.json();
return payload.active ? payload : null;
}
async function parseShadowServerAppCommandRequest(input) {
const token = extractShadowServerAppBearerToken(input.authorizationHeader);
const serverId = input.serverIdHeader;
const appKey = input.appKeyHeader;
if (!token || !serverId || !appKey) {
return { ok: false, status: 401, error: "missing_oauth" };
}
const introspection = await introspectShadowServerAppToken({
token,
serverId,
appKey,
shadowBaseUrl: input.shadowBaseUrl,
fetchImpl: input.fetchImpl
}).catch(() => null);
const context = introspection?.shadow;
if (!context) return { ok: false, status: 401, error: "invalid_token" };
if (context.command !== input.expectedCommand) {
return { ok: false, status: 403, error: "wrong_command" };
}
let commandInput;
if (input.requestInput !== void 0) {
commandInput = input.requestInput;
} else {
let body;
try {
body = JSON.parse(input.requestBody ?? "{}");
} catch {
return { ok: false, status: 400, error: "invalid_json" };
}
commandInput = body.input ?? {};
}
return {
ok: true,
envelope: {
input: commandInput,
context
}
};
}
function validateShadowServerAppJsonSchema(schema, value) {
if (!schema) return { ok: true };
const issues = [];
validateJsonSchemaValue(schema, value, "", issues);
return issues.length ? { ok: false, issues } : { ok: true };
}
function shadowServerAppActorDisplayName(envelopeOrContext) {
const context = "context" in envelopeOrContext ? envelopeOrContext.context : envelopeOrContext;
const actor = context.actor;
const profile = actor.profile;
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`;
}
function shadowServerAppActorAvatarUrl(envelopeOrContext) {
const context = "context" in envelopeOrContext ? envelopeOrContext.context : envelopeOrContext;
return context.actor.profile?.avatarUrl ?? null;
}
function shadowServerAppActorRef(envelopeOrContext) {
const context = "context" in envelopeOrContext ? envelopeOrContext.context : envelopeOrContext;
const actor = context.actor;
return {
kind: actor.kind,
id: actor.buddyAgentId ?? actor.userId ?? actor.ownerId ?? "unknown",
userId: actor.userId ?? null,
buddyAgentId: actor.buddyAgentId ?? null,
ownerId: actor.ownerId ?? null,
displayName: shadowServerAppActorDisplayName(context),
avatarUrl: shadowServerAppActorAvatarUrl(context)
};
}
function isShadowServerAppActorRef(value) {
return !!value && typeof value === "object" && !Array.isArray(value) && typeof value.kind === "string" && typeof value.id === "string" && typeof value.displayName === "string";
}
function shadowServerAppIdentitySubjectKind(actor) {
if (actor.kind === "system") return "system";
if (actor.kind === "local") return "local";
if (actor.buddyAgentId) return "buddy";
if (actor.kind === "agent") return "agent";
if (actor.userId) return "user";
return "unknown";
}
function shadowServerAppIdentityKey(actorOrIdentity) {
const actor = isShadowServerAppActorRef(actorOrIdentity) ? actorOrIdentity : shadowServerAppActorRef(actorOrIdentity);
const subjectKind = shadowServerAppIdentitySubjectKind(actor);
if (subjectKind === "buddy" && actor.buddyAgentId) return `buddy:${actor.buddyAgentId}`;
if (actor.userId) return `user:${actor.userId}`;
if (actor.ownerId) return `owner:${actor.ownerId}`;
return `${subjectKind}:${actor.id || "unknown"}`;
}
function shadowServerAppIdentitySnapshot(actorOrContext) {
const actor = isShadowServerAppActorRef(actorOrContext) ? actorOrContext : shadowServerAppActorRef(actorOrContext);
return {
...actor,
subjectKind: shadowServerAppIdentitySubjectKind(actor),
stableKey: shadowServerAppIdentityKey(actor)
};
}
var shadowServerAppDisplayIdentity = shadowServerAppIdentitySnapshot;
function normalizeShadowServerAppClientMutationId(value) {
if (typeof value !== "string") return null;
const clean = value.trim();
if (!clean) return null;
return clean.slice(0, 160);
}
function normalizeShadowServerAppBaseCursor(value) {
if (typeof value !== "string") return null;
const clean = value.trim();
if (!clean) return null;
return clean.slice(0, 240);
}
function createShadowServerAppCollaborationResource(context, resource) {
return {
appKey: resource.appKey ?? context.appKey,
serverId: resource.serverId ?? context.serverId,
kind: resource.kind,
id: resource.id,
...resource.label !== void 0 ? { label: resource.label } : {},
...resource.projectId !== void 0 ? { projectId: resource.projectId } : {},
...resource.boardId !== void 0 ? { boardId: resource.boardId } : {}
};
}
function createShadowServerAppCollaborationCursor(input) {
const sequence = input.sequence ?? Date.now();
const occurredAt = input.occurredAt ?? (/* @__PURE__ */ new Date()).toISOString();
return `${input.resource.kind}:${input.resource.id}:${sequence}:${occurredAt}`;
}
function createShadowServerAppCollaborationEvent(input) {
const occurredAt = input.occurredAt ?? (/* @__PURE__ */ new Date()).toISOString();
const cursor = input.cursor ?? createShadowServerAppCollaborationCursor({
resource: input.resource,
occurredAt
});
return {
protocol: SHADOW_SERVER_APP_PROTOCOL,
type: input.type,
cursor,
occurredAt,
resource: input.resource,
actor: shadowServerAppIdentitySnapshot(input.actor),
payload: input.payload,
clientMutationId: normalizeShadowServerAppClientMutationId(input.clientMutationId),
baseCursor: normalizeShadowServerAppBaseCursor(input.baseCursor)
};
}
function parseErrorResult(error) {
return failureResult(error.status, error.error, error.issues);
}
function failureResult(status, error, issues) {
return {
ok: false,
status,
body: issues === void 0 ? { ok: false, error } : { ok: false, error, issues }
};
}
function validateJsonSchemaValue(schema, value, path, issues) {
if (Array.isArray(schema.oneOf)) {
const matches = schema.oneOf.some((option) => {
const nestedIssues = [];
if (option && typeof option === "object" && !Array.isArray(option)) {
validateJsonSchemaValue(
option,
value,
path,
nestedIssues
);
}
return nestedIssues.length === 0;
});
if (!matches) issues.push({ path, message: "Expected value matching one schema option" });
return;
}
const enumValues = schema.enum;
if (Array.isArray(enumValues) && !enumValues.includes(value)) {
issues.push({ path, message: `Expected one of ${enumValues.map(String).join(", ")}` });
return;
}
const type = schema.type;
if (type === "object") {
if (!value || typeof value !== "object" || Array.isArray(value)) {
issues.push({ path, message: "Expected object" });
return;
}
const record = value;
const properties = schema.properties && typeof schema.properties === "object" && !Array.isArray(schema.properties) ? schema.properties : {};
const required = Array.isArray(schema.required) ? schema.required.map(String) : [];
for (const key of required) {
if (!(key in record)) issues.push({ path: joinJsonPath(path, key), message: "Required" });
}
for (const [key, propertySchema] of Object.entries(properties)) {
if (record[key] !== void 0) {
validateJsonSchemaValue(propertySchema, record[key], joinJsonPath(path, key), issues);
}
}
const additionalProperties = schema.additionalProperties && typeof schema.additionalProperties === "object" && !Array.isArray(schema.additionalProperties) ? schema.additionalProperties : null;
if (additionalProperties) {
for (const [key, nestedValue] of Object.entries(record)) {
if (!(key in properties)) {
validateJsonSchemaValue(
additionalProperties,
nestedValue,
joinJsonPath(path, key),
issues
);
}
}
} else if (schema.additionalProperties === false) {
for (const key of Object.keys(record)) {
if (!(key in properties)) {
issues.push({ path: joinJsonPath(path, key), message: "Unknown property" });
}
}
}
return;
}
if (type === "array") {
if (!Array.isArray(value)) {
issues.push({ path, message: "Expected array" });
return;
}
const maxItems = typeof schema.maxItems === "number" ? schema.maxItems : null;
if (maxItems !== null && value.length > maxItems) {
issues.push({ path, message: `Expected at most ${maxItems} items` });
}
const itemSchema = schema.items && typeof schema.items === "object" && !Array.isArray(schema.items) ? schema.items : null;
if (itemSchema) {
value.forEach(
(item, index) => validateJsonSchemaValue(itemSchema, item, `${path}[${index}]`, issues)
);
}
return;
}
if (type === "string") {
if (typeof value !== "string") {
issues.push({ path, message: "Expected string" });
return;
}
const maxLength = typeof schema.maxLength === "number" ? schema.maxLength : null;
const minLength = typeof schema.minLength === "number" ? schema.minLength : null;
if (minLength !== null && value.length < minLength) {
issues.push({ path, message: `Expected at least ${minLength} characters` });
}
if (maxLength !== null && value.length > maxLength) {
issues.push({ path, message: `Expected at most ${maxLength} characters` });
}
return;
}
if (type === "number" || type === "integer") {
if (typeof value !== "number" || !Number.isFinite(value)) {
issues.push({ path, message: "Expected number" });
return;
}
if (type === "integer" && !Number.isInteger(value)) {
issues.push({ path, message: "Expected integer" });
}
return;
}
if (type === "boolean" && typeof value !== "boolean") {
issues.push({ path, message: "Expected boolean" });
}
}
function joinJsonPath(parent, key) {
return parent ? `${parent}.${key}` : key;
}
export {
SHADOW_SERVER_APP_PROTOCOL,
SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT,
SHADOW_SERVER_APP_COMMAND_FAILED_EVENT,
SHADOW_SERVER_APP_COMMAND_EVENTS,
ShadowServerAppHttpError,
shadowServerAppInboxTaskEndpoint,
buildShadowServerAppInboxTaskRequest,
getShadowServerAppTaskCardId,
buildShadowServerAppInboxDelivery,
getShadowServerAppInboxDeliveries,
getShadowServerAppInboxErrors,
getShadowServerAppPendingInboxTasks,
getShadowServerAppChannelMessageDeliveries,
getShadowServerAppChannelMessageErrors,
getShadowServerAppPendingChannelMessages,
hasShadowServerAppPendingOutbox,
unwrapShadowServerAppCommandPayload,
readShadowServerAppCommandResponse,
ShadowServerAppOutbox,
SHADOW_SERVER_APP_PUBLIC_AVATAR_CACHE_CONTROL,
shadowServerAppApiBaseUrl,
shadowServerAppPublicBaseUrl,
shadowServerAppPublicUrl,
isShadowServerAppSignedMediaUrl,
normalizeShadowServerAppAvatarUrl,
shadowServerAppAvatarRedirectUrl,
extractShadowServerAppBearerToken,
decodeShadowServerAppLaunchTokenHint,
fetchShadowServerAppLaunchInboxes,
introspectShadowServerAppLaunchToken,
shadowServerAppLaunchIntrospectionError,
resolveShadowServerAppLaunchCommandContextResolution,
resolveShadowServerAppLaunchCommandContext,
deliverShadowServerAppLaunchOutbox,
normalizeShadowServerAppCommandInput,
createShadowServerAppManifest,
defineShadowServerApp,
createShadowServerAppRuntime,
ShadowServerAppCommandError,
shadowServerAppError,
ShadowServerAppRuntime,
introspectShadowServerAppToken,
parseShadowServerAppCommandRequest,
validateShadowServerAppJsonSchema,
shadowServerAppActorDisplayName,
shadowServerAppActorAvatarUrl,
shadowServerAppActorRef,
shadowServerAppIdentityKey,
shadowServerAppIdentitySnapshot,
shadowServerAppDisplayIdentity,
normalizeShadowServerAppClientMutationId,
normalizeShadowServerAppBaseCursor,
createShadowServerAppCollaborationResource,
createShadowServerAppCollaborationCursor,
createShadowServerAppCollaborationEvent,
BUDDY_INBOX_DELIVERY_PERMISSION
};
import {
BUDDY_INBOX_DELIVERY_PERMISSION,
decodeShadowServerAppLaunchTokenHint,
deliverShadowServerAppLaunchOutbox,
getShadowServerAppChannelMessageDeliveries,
getShadowServerAppChannelMessageErrors,
getShadowServerAppInboxDeliveries,
getShadowServerAppInboxErrors,
hasShadowServerAppPendingOutbox,
readShadowServerAppCommandResponse,
unwrapShadowServerAppCommandPayload
} from "./chunk-62WH33SI.js";
// src/bridge.ts
var SHADOW_BRIDGE_CAPABILITIES = [
"copilot.open",
"workspace.open",
"buddy.create.open",
"buddy.inboxes.list",
"buddy.grant.ensure",
"oauth.authorize",
"launch.refresh",
"route.navigate",
"route.report",
"app.share.open"
];
function commandPath(basePath, commandName) {
return `${basePath.replace(/\/+$/u, "")}/${encodeURIComponent(commandName)}`;
}
function shadowServerAppMountedPathPrefix(windowRef) {
const win = windowRef ?? (typeof window === "undefined" ? null : window);
const pathname = win?.location?.pathname ?? "";
const segments = pathname.split("/").filter(Boolean);
const shadowIndex = segments.indexOf("shadow");
if (shadowIndex <= 0 || segments[shadowIndex + 1] !== "server") return "";
return `/${segments.slice(0, shadowIndex).join("/")}`;
}
function shadowServerAppMountedPath(path, windowRef) {
const normalized = path.startsWith("/") ? path : `/${path}`;
return `${shadowServerAppMountedPathPrefix(windowRef)}${normalized}`;
}
function isRecord(value) {
return !!value && typeof value === "object" && !Array.isArray(value);
}
function withoutUndefined(value) {
if (value === void 0) return {};
if (Array.isArray(value)) return value.map(withoutUndefined);
if (!isRecord(value)) return value;
return Object.fromEntries(
Object.entries(value).filter(([, entry]) => entry !== void 0).map(([key, entry]) => [key, withoutUndefined(entry)])
);
}
var ShadowServerAppBrowserClient = class {
bridge;
commandBasePath;
inboxesPath;
shadowApiBaseUrl;
fetchFn;
deliverLaunchOutboxFromBrowser;
win;
launchTokenValue;
launchEventStreamUrlValue;
launchExpiresInValue;
launchContextHandlers = /* @__PURE__ */ new Set();
unsubscribeLaunchUpdate;
constructor(options = {}) {
this.bridge = new ShadowBridge(options);
const windowRef = options.windowRef ?? (typeof window === "undefined" ? null : window);
this.commandBasePath = options.commandBasePath ?? shadowServerAppMountedPath("/api/commands", windowRef);
this.inboxesPath = options.inboxesPath ?? shadowServerAppMountedPath("/api/inboxes", windowRef);
this.shadowApiBaseUrl = options.shadowApiBaseUrl;
this.fetchFn = options.fetch;
this.deliverLaunchOutboxFromBrowser = options.deliverLaunchOutboxFromBrowser ?? false;
this.win = windowRef;
this.launchTokenValue = this.launchTokenFromLocation();
this.launchEventStreamUrlValue = this.launchEventStreamUrlFromLocation();
this.unsubscribeLaunchUpdate = this.bridge.onLaunchUpdate((context) => {
this.applyLaunchUpdate(context);
const snapshot = this.launchContext();
for (const handler of this.launchContextHandlers) {
void Promise.resolve(handler(snapshot)).catch(() => void 0);
}
});
}
bridgeAvailable() {
return this.bridge.isAvailable();
}
launchToken(param = "shadow_launch") {
return this.launchTokenValue ?? this.launchTokenFromLocation(param);
}
launchEventStreamUrl(param = "shadow_event_stream") {
return this.launchEventStreamUrlValue ?? this.launchEventStreamUrlFromLocation(param);
}
launchContext() {
return {
launchToken: this.launchToken(),
eventStreamUrl: this.launchEventStreamUrl(),
eventStreamPath: this.launchEventStreamUrl(),
...typeof this.launchExpiresInValue === "number" ? { expiresIn: this.launchExpiresInValue } : {}
};
}
onLaunchContextChange(handler) {
this.launchContextHandlers.add(handler);
return () => {
this.launchContextHandlers.delete(handler);
};
}
launchHeaders(headers = {}, options = {}) {
const token = this.launchToken(options.launchTokenParam);
return token ? { ...headers, "X-Shadow-Launch-Token": token } : headers;
}
async command(commandName, input = {}) {
if (!this.launchToken()) {
await this.refreshLaunch({ reason: "command_missing_launch" });
}
const path = commandPath(this.commandBasePath, commandName);
const init = {
method: "POST",
headers: this.launchHeaders({ "Content-Type": "application/json" }),
body: JSON.stringify({ input: withoutUndefined(input) })
};
let response = await this.fetch(path, init);
if (response.status === 401 && await this.refreshLaunch({ reason: "command_unauthorized" })) {
response = await this.fetch(path, {
...init,
headers: this.launchHeaders({ "Content-Type": "application/json" })
});
}
const result = await readShadowServerAppCommandResponse(response);
return this.deliverLaunchOutbox(commandName, result);
}
async commandForm(commandName, formData) {
if (!this.launchToken()) {
await this.refreshLaunch({ reason: "command_missing_launch" });
}
const path = commandPath(this.commandBasePath, commandName);
const init = {
method: "POST",
headers: this.launchHeaders(),
body: formData
};
let response = await this.fetch(path, init);
if (response.status === 401 && await this.refreshLaunch({ reason: "command_unauthorized" })) {
response = await this.fetch(path, {
...init,
headers: this.launchHeaders()
});
}
const result = await readShadowServerAppCommandResponse(response);
return this.deliverLaunchOutbox(commandName, result);
}
async refreshLaunch(input = {}) {
if (!this.bridge.isAvailable()) return null;
try {
const context = await this.bridge.refreshLaunch(input);
if (context?.launchToken) {
this.applyLaunchUpdate({
launchToken: context.launchToken,
eventStreamUrl: context.eventStreamUrl,
eventStreamPath: context.eventStreamPath,
expiresIn: context.expiresIn
});
const snapshot = this.launchContext();
for (const handler of this.launchContextHandlers) {
void Promise.resolve(handler(snapshot)).catch(() => void 0);
}
}
return context;
} catch {
return null;
}
}
async fetchWithLaunch(input, init = {}, options = {}) {
if (options.refresh) {
const refreshInput = options.refresh === true ? { reason: "fetch" } : options.refresh;
await this.refreshLaunch(refreshInput);
} else if (!this.launchToken()) {
await this.refreshLaunch({ reason: "missing_launch" });
}
let response = await this.fetch(input, this.withLaunchHeaders(init));
if (response.status !== 401 || !await this.refreshLaunch({ reason: "fetch_unauthorized" })) {
return response;
}
return this.fetch(input, this.withLaunchHeaders(init));
}
async listBuddyInboxes(options = {}) {
if (this.bridge.isAvailable()) {
try {
return await this.bridge.listBuddyInboxes(
{ refresh: options.refresh },
{ timeoutMs: options.timeoutMs ?? 4e3 }
);
} catch {
}
}
if (options.refresh || !this.launchToken()) {
await this.refreshLaunch({
reason: options.refresh ? "inboxes_refresh" : "inboxes_missing_launch"
});
}
if (this.launchToken()) {
return this.fetchLaunchBuddyInboxes(options);
}
return this.fetchLaunchBuddyInboxes(options);
}
async fetchLaunchBuddyInboxes(options) {
let response = await this.fetch(this.inboxesPath, { headers: this.launchHeaders() });
if (response.status === 401 && await this.refreshLaunch({ reason: "inboxes_unauthorized" })) {
response = await this.fetch(this.inboxesPath, { headers: this.launchHeaders() });
}
if (!response.ok) {
if (options.emptyOnError) return { inboxes: [] };
const message = await response.text().catch(() => "");
throw new Error(message || `Buddy inbox lookup failed (${response.status})`);
}
return await response.json();
}
async ensureBuddyTaskGrant(input) {
const buddyAgentId = input.agentId?.trim();
if (!buddyAgentId || !this.bridge.isAvailable()) return { granted: false, skipped: true };
return this.bridge.ensureBuddyGrant(
{
buddyAgentId,
permissions: input.permissions ?? [BUDDY_INBOX_DELIVERY_PERMISSION],
reason: input.reason
},
{ timeoutMs: input.timeoutMs ?? 6e3 }
);
}
openBuddyCreator(input = {}, options = {}) {
if (!this.bridge.isAvailable()) return Promise.resolve({ opened: false, agent: null });
return this.bridge.openBuddyCreator(input, options);
}
openCopilot(delivery, options = {}) {
return this.bridge.openCopilot(delivery, options);
}
openWorkspaceResource(input, options = {}) {
return this.bridge.openWorkspaceResource(input, options);
}
authorizeOAuth(input, options = {}) {
if (!this.bridge.isAvailable()) return Promise.resolve({ opened: false });
return this.bridge.authorizeOAuth(input, options);
}
routeChanged(path) {
return this.bridge.routeChanged(path);
}
onRouteNavigate(handler) {
return this.bridge.onRouteNavigate(handler);
}
shareApp(input = {}, options = {}) {
if (!this.bridge.isAvailable()) return Promise.resolve({ opened: false });
return this.bridge.shareApp(input, options);
}
inboxDeliveries(payload) {
return this.bridge.inboxDeliveries(payload);
}
inboxErrors(payload) {
return this.bridge.inboxErrors(payload);
}
channelMessageDeliveries(payload) {
return this.bridge.channelMessageDeliveries(payload);
}
channelMessageErrors(payload) {
return this.bridge.channelMessageErrors(payload);
}
async deliverLaunchOutbox(commandName, result) {
if (!this.deliverLaunchOutboxFromBrowser || !hasShadowServerAppPendingOutbox(result)) {
return result;
}
const launchToken = this.launchToken();
if (!decodeShadowServerAppLaunchTokenHint(launchToken)) return result;
return await deliverShadowServerAppLaunchOutbox({
commandName,
result,
launchToken,
shadowApiBaseUrl: this.shadowApiBaseUrl,
fetch: this.fetch.bind(this)
});
}
fetch(input, init) {
if (this.fetchFn) return this.fetchFn(input, init);
return globalThis.fetch(input, init);
}
dispose() {
this.unsubscribeLaunchUpdate();
this.launchContextHandlers.clear();
this.bridge.dispose();
}
launchTokenFromLocation(param = "shadow_launch") {
if (!this.win) return null;
return new URLSearchParams(this.win.location.search).get(param);
}
launchEventStreamUrlFromLocation(param = "shadow_event_stream") {
if (!this.win) return null;
return new URLSearchParams(this.win.location.search).get(param);
}
applyLaunchUpdate(context) {
this.launchTokenValue = context.launchToken;
this.launchEventStreamUrlValue = context.eventStreamUrl ?? context.eventStreamPath ?? null;
this.launchExpiresInValue = typeof context.expiresIn === "number" ? context.expiresIn : void 0;
}
withLaunchHeaders(init) {
const headers = new Headers(init.headers);
const token = this.launchToken();
if (token) headers.set("X-Shadow-Launch-Token", token);
return { ...init, headers };
}
};
function createShadowServerAppClient(options = {}) {
return new ShadowServerAppBrowserClient(options);
}
var createShadowServerAppBrowserClient = createShadowServerAppClient;
var ShadowBridge = class _ShadowBridge {
static capabilitiesRequestType = "shadow.app.capabilities.request";
static capabilitiesResponseType = "shadow.app.capabilities.response";
static openCopilotRequestType = "shadow.app.copilot.open.request";
static openCopilotResponseType = "shadow.app.copilot.open.response";
static openWorkspaceResourceRequestType = "shadow.app.workspace.open.request";
static openWorkspaceResourceResponseType = "shadow.app.workspace.open.response";
static openBuddyCreatorRequestType = "shadow.app.buddy.create.request";
static openBuddyCreatorResponseType = "shadow.app.buddy.create.response";
static listBuddyInboxesRequestType = "shadow.app.buddy.inboxes.request";
static listBuddyInboxesResponseType = "shadow.app.buddy.inboxes.response";
static ensureBuddyGrantRequestType = "shadow.app.buddy.grant.request";
static ensureBuddyGrantResponseType = "shadow.app.buddy.grant.response";
static authorizeOAuthRequestType = "shadow.app.oauth.authorize.request";
static authorizeOAuthResponseType = "shadow.app.oauth.authorize.response";
static launchUpdateType = "shadow.app.launch.update";
static routeNavigateType = "shadow.app.navigate";
static routeNavigateAckType = "shadow.app.navigate.ack";
static routeChangedType = "shadow.app.route.changed";
static shareAppRequestType = "shadow.app.share.request";
static shareAppResponseType = "shadow.app.share.response";
static refreshLaunchRequestType = "shadow.app.launch.refresh.request";
static refreshLaunchResponseType = "shadow.app.launch.refresh.response";
static launchUpdatedEventType = "shadow.app.launch.updated";
static inboxDeliveries(payload) {
return getShadowServerAppInboxDeliveries(payload);
}
static inboxErrors(payload) {
return getShadowServerAppInboxErrors(payload);
}
static channelMessageDeliveries(payload) {
return getShadowServerAppChannelMessageDeliveries(payload);
}
static channelMessageErrors(payload) {
return getShadowServerAppChannelMessageErrors(payload);
}
static unwrapCommandPayload(payload) {
return unwrapShadowServerAppCommandPayload(payload);
}
appKey;
targetOrigin;
timeoutMs;
win;
hasLaunchContext;
launchTokenValue = null;
pending = /* @__PURE__ */ new Map();
onMessage = (event) => {
let data = event.data;
if (typeof data === "string") {
try {
data = JSON.parse(data || "{}");
} catch {
return;
}
}
if (!data || typeof data !== "object") return;
const record = data;
if (record.type === _ShadowBridge.launchUpdatedEventType) {
this.applyLaunchContext(record.result ?? record.launch);
return;
}
if (typeof record.requestId !== "string" || typeof record.type !== "string") return;
const entry = this.pending.get(record.requestId);
if (!entry || record.type !== entry.responseType) return;
this.pending.delete(record.requestId);
if (record.ok) {
if (record.type === _ShadowBridge.refreshLaunchResponseType) {
this.applyLaunchContext(record.result);
}
entry.resolve(record.result);
} else
entry.reject(
new Error(typeof record.error === "string" ? record.error : "Bridge request failed")
);
};
constructor(options = {}) {
this.win = options.windowRef ?? (typeof window === "undefined" ? null : window);
this.appKey = options.appKey ?? this.resolveLaunchAppKey();
this.targetOrigin = options.targetOrigin ?? "*";
this.timeoutMs = options.timeoutMs ?? 6e4;
this.launchTokenValue = this.resolveLaunchToken();
this.hasLaunchContext = this.resolveLaunchContext();
this.win?.addEventListener("message", this.onMessage);
}
dispose() {
this.win?.removeEventListener("message", this.onMessage);
for (const entry of this.pending.values()) {
entry.reject(new Error("ShadowBridge disposed"));
}
this.pending.clear();
}
isAvailable() {
if (!this.win) return false;
return (this.hasLaunchContext || !!this.appKey) && (this.win.parent !== this.win || !!this.win.ReactNativeWebView);
}
launchToken(param = "shadow_launch") {
if (!this.win) return null;
if (param !== "shadow_launch") {
return new URLSearchParams(this.win.location.search).get(param);
}
return this.launchTokenValue ?? this.resolveLaunchToken();
}
launchHeaders(headers = {}, options = {}) {
const token = this.launchToken(options.launchTokenParam);
return token ? { ...headers, "X-Shadow-Launch-Token": token } : headers;
}
capabilities(options = {}) {
return this.request(
_ShadowBridge.capabilitiesRequestType,
_ShadowBridge.capabilitiesResponseType,
{},
options.timeoutMs ?? 15e3
);
}
openCopilot(deliveryOrInput, options = {}) {
const input = "delivery" in deliveryOrInput ? deliveryOrInput : { delivery: deliveryOrInput };
return this.request(
_ShadowBridge.openCopilotRequestType,
_ShadowBridge.openCopilotResponseType,
input,
options.timeoutMs ?? 15e3
);
}
openWorkspaceResource(input, options = {}) {
return this.request(
_ShadowBridge.openWorkspaceResourceRequestType,
_ShadowBridge.openWorkspaceResourceResponseType,
input,
options.timeoutMs ?? 15e3
);
}
openBuddyCreator(input = {}, options = {}) {
return this.request(
_ShadowBridge.openBuddyCreatorRequestType,
_ShadowBridge.openBuddyCreatorResponseType,
input,
options.timeoutMs ?? 10 * 60 * 1e3
);
}
listBuddyInboxes(input = {}, options = {}) {
return this.request(
_ShadowBridge.listBuddyInboxesRequestType,
_ShadowBridge.listBuddyInboxesResponseType,
input,
options.timeoutMs ?? 15e3
);
}
ensureBuddyGrant(input, options = {}) {
return this.request(
_ShadowBridge.ensureBuddyGrantRequestType,
_ShadowBridge.ensureBuddyGrantResponseType,
input,
options.timeoutMs ?? 3e4
);
}
authorizeOAuth(input, options = {}) {
const payload = typeof input === "string" ? { authorizeUrl: input } : input;
return this.request(
_ShadowBridge.authorizeOAuthRequestType,
_ShadowBridge.authorizeOAuthResponseType,
payload,
options.timeoutMs ?? 10 * 60 * 1e3
);
}
routeChanged(path) {
if (!this.isAvailable()) return false;
this.postMessage({
type: _ShadowBridge.routeChangedType,
...this.appKey ? { appKey: this.appKey } : {},
path
});
return true;
}
onRouteNavigate(handler) {
const win = this.win;
if (!win) return () => void 0;
const listener = (event) => {
let data = event.data;
if (typeof data === "string") {
try {
data = JSON.parse(data || "{}");
} catch {
return;
}
}
if (!data || typeof data !== "object") return;
const record = data;
if (record.type !== _ShadowBridge.routeNavigateType) return;
if (this.appKey && typeof record.appKey === "string" && record.appKey !== this.appKey) {
return;
}
if (typeof record.requestId !== "string" || typeof record.path !== "string") return;
const eventPayload = { path: record.path, requestId: record.requestId };
void Promise.resolve(handler(record.path, eventPayload)).catch(() => void 0).finally(() => {
this.postMessage({
type: _ShadowBridge.routeNavigateAckType,
requestId: record.requestId,
...this.appKey ? { appKey: this.appKey } : {}
});
});
};
win.addEventListener("message", listener);
return () => win.removeEventListener("message", listener);
}
onLaunchUpdate(handler) {
const win = this.win;
if (!win) return () => void 0;
const listener = (event) => {
let data = event.data;
if (typeof data === "string") {
try {
data = JSON.parse(data || "{}");
} catch {
return;
}
}
if (!data || typeof data !== "object") return;
const record = data;
if (record.type !== _ShadowBridge.launchUpdateType && record.type !== _ShadowBridge.launchUpdatedEventType) {
return;
}
if (this.appKey && typeof record.appKey === "string" && record.appKey !== this.appKey) {
return;
}
if (typeof record.launchToken !== "string" || !record.launchToken) return;
const eventStreamUrl = typeof record.eventStreamUrl === "string" && record.eventStreamUrl ? record.eventStreamUrl : typeof record.eventStreamPath === "string" && record.eventStreamPath ? record.eventStreamPath : null;
const eventStreamPath = typeof record.eventStreamPath === "string" && record.eventStreamPath ? record.eventStreamPath : typeof record.eventStreamUrl === "string" && record.eventStreamUrl ? record.eventStreamUrl : null;
void Promise.resolve(
handler({
launchToken: record.launchToken,
eventStreamUrl,
eventStreamPath,
...typeof record.expiresIn === "number" ? { expiresIn: record.expiresIn } : {}
})
).catch(() => void 0);
};
win.addEventListener("message", listener);
return () => win.removeEventListener("message", listener);
}
shareApp(input = {}, options = {}) {
return this.request(
_ShadowBridge.shareAppRequestType,
_ShadowBridge.shareAppResponseType,
input,
options.timeoutMs ?? 5 * 60 * 1e3
);
}
refreshLaunch(input = {}, options = {}) {
return this.request(
_ShadowBridge.refreshLaunchRequestType,
_ShadowBridge.refreshLaunchResponseType,
input,
options.timeoutMs ?? 15e3
);
}
unwrapCommandPayload(payload) {
return unwrapShadowServerAppCommandPayload(payload);
}
inboxDeliveries(payload) {
return getShadowServerAppInboxDeliveries(payload);
}
inboxErrors(payload) {
return getShadowServerAppInboxErrors(payload);
}
channelMessageDeliveries(payload) {
return getShadowServerAppChannelMessageDeliveries(payload);
}
channelMessageErrors(payload) {
return getShadowServerAppChannelMessageErrors(payload);
}
request(requestType, responseType, payload, timeoutMs = this.timeoutMs) {
if (!this.isAvailable()) {
return Promise.reject(
new Error("ShadowBridge is not available outside a Shadow launch frame")
);
}
const requestId = `req_${Math.random().toString(36).slice(2)}`;
return new Promise((resolve, reject) => {
this.pending.set(requestId, {
responseType,
resolve,
reject
});
this.postMessage({
type: requestType,
requestId,
...this.appKey ? { appKey: this.appKey } : {},
...payload
});
this.win?.setTimeout(() => {
if (!this.pending.has(requestId)) return;
this.pending.delete(requestId);
reject(new Error("Bridge request timed out"));
}, timeoutMs);
});
}
postMessage(message) {
if (!this.win) return;
if (this.win.ReactNativeWebView) {
this.win.ReactNativeWebView.postMessage(JSON.stringify(message));
return;
}
this.win.parent.postMessage(message, this.targetOrigin);
}
launchContextStorageKey() {
return this.appKey ? `shadow.bridge.launch:${this.appKey}` : null;
}
launchTokenStorageKey() {
return this.appKey ? `shadow.bridge.launch-token:${this.appKey}` : null;
}
rememberLaunchToken(token) {
if (!token) return;
const hint = decodeShadowServerAppLaunchTokenHint(token);
if (!this.appKey && hint?.appKey) this.appKey = hint.appKey;
this.launchTokenValue = token;
this.hasLaunchContext = true;
if (this.appKey) {
const memoryContexts = this.win.__shadowBridgeLaunchContexts ??= {};
const memoryTokens = this.win.__shadowBridgeLaunchTokens ??= {};
memoryContexts[this.appKey] = true;
memoryTokens[this.appKey] = token;
}
try {
const contextKey = this.launchContextStorageKey();
const tokenKey = this.launchTokenStorageKey();
if (contextKey) this.win?.sessionStorage?.setItem(contextKey, "1");
if (tokenKey) this.win?.sessionStorage?.setItem(tokenKey, token);
} catch {
}
}
resolveLaunchToken() {
if (!this.win) return null;
const urlToken = new URLSearchParams(this.win.location.search).get("shadow_launch");
if (urlToken) {
this.rememberLaunchToken(urlToken);
return urlToken;
}
const memoryToken = this.appKey ? this.win.__shadowBridgeLaunchTokens?.[this.appKey] : null;
if (memoryToken) return memoryToken;
try {
const tokenKey = this.launchTokenStorageKey();
return tokenKey ? this.win.sessionStorage?.getItem(tokenKey) ?? null : null;
} catch {
return null;
}
}
applyLaunchContext(value) {
if (!isRecord(value) || typeof value.launchToken !== "string") return false;
this.rememberLaunchToken(value.launchToken);
return true;
}
resolveLaunchContext() {
if (!this.win) return false;
const token = this.launchTokenValue ?? this.resolveLaunchToken();
if (token) {
this.rememberLaunchToken(token);
return true;
}
const storageKey = this.launchContextStorageKey();
const memoryContexts = this.win.__shadowBridgeLaunchContexts ??= {};
const hasLaunchToken = new URLSearchParams(this.win.location.search).has("shadow_launch");
if (hasLaunchToken) {
if (this.appKey) memoryContexts[this.appKey] = true;
try {
if (storageKey) this.win.sessionStorage?.setItem(storageKey, "1");
} catch {
}
return true;
}
if (this.appKey && memoryContexts[this.appKey]) return true;
try {
return storageKey ? this.win.sessionStorage?.getItem(storageKey) === "1" : false;
} catch {
return false;
}
}
resolveLaunchAppKey() {
if (!this.win) return void 0;
const token = new URLSearchParams(this.win.location.search).get("shadow_launch");
return decodeShadowServerAppLaunchTokenHint(token)?.appKey;
}
};
export {
SHADOW_BRIDGE_CAPABILITIES,
shadowServerAppMountedPathPrefix,
shadowServerAppMountedPath,
ShadowServerAppBrowserClient,
createShadowServerAppClient,
createShadowServerAppBrowserClient,
ShadowBridge
};

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

+42
-17

@@ -33,3 +33,2 @@ "use strict";

createShadowServerAppClient: () => createShadowServerAppClient,
createShadowServerAppRuntimeClient: () => createShadowServerAppRuntimeClient,
getShadowServerAppChannelMessageDeliveries: () => getShadowServerAppChannelMessageDeliveries,

@@ -259,3 +258,3 @@ getShadowServerAppChannelMessageErrors: () => getShadowServerAppChannelMessageErrors,

function trimTrailingSlash(value) {
return value.replace(/\/$/, "");
return value.replace(/\/+$/, "");
}

@@ -372,4 +371,4 @@ function decodeBase64UrlJson(value) {

const windowRef = options.windowRef ?? (typeof window === "undefined" ? null : window);
this.commandBasePath = options.commandBasePath ?? shadowServerAppMountedPath("/api/runtime/commands", windowRef);
this.inboxesPath = options.inboxesPath ?? shadowServerAppMountedPath("/api/runtime/inboxes", windowRef);
this.commandBasePath = options.commandBasePath ?? shadowServerAppMountedPath("/api/commands", windowRef);
this.inboxesPath = options.inboxesPath ?? shadowServerAppMountedPath("/api/inboxes", windowRef);
this.shadowApiBaseUrl = options.shadowApiBaseUrl;

@@ -417,2 +416,5 @@ this.fetchFn = options.fetch;

async command(commandName, input = {}) {
if (!this.launchToken()) {
await this.refreshLaunch({ reason: "command_missing_launch" });
}
const path = commandPath(this.commandBasePath, commandName);

@@ -434,2 +436,22 @@ const init = {

}
async commandForm(commandName, formData) {
if (!this.launchToken()) {
await this.refreshLaunch({ reason: "command_missing_launch" });
}
const path = commandPath(this.commandBasePath, commandName);
const init = {
method: "POST",
headers: this.launchHeaders(),
body: formData
};
let response = await this.fetch(path, init);
if (response.status === 401 && await this.refreshLaunch({ reason: "command_unauthorized" })) {
response = await this.fetch(path, {
...init,
headers: this.launchHeaders()
});
}
const result = await readShadowServerAppCommandResponse(response);
return this.deliverLaunchOutbox(commandName, result);
}
async refreshLaunch(input = {}) {

@@ -472,6 +494,20 @@ if (!this.bridge.isAvailable()) return null;

try {
return await this.bridge.listBuddyInboxes({ refresh: options.refresh });
return await this.bridge.listBuddyInboxes(
{ refresh: options.refresh },
{ timeoutMs: options.timeoutMs ?? 4e3 }
);
} catch {
}
}
if (options.refresh || !this.launchToken()) {
await this.refreshLaunch({
reason: options.refresh ? "inboxes_refresh" : "inboxes_missing_launch"
});
}
if (this.launchToken()) {
return this.fetchLaunchBuddyInboxes(options);
}
return this.fetchLaunchBuddyInboxes(options);
}
async fetchLaunchBuddyInboxes(options) {
let response = await this.fetch(this.inboxesPath, { headers: this.launchHeaders() });

@@ -497,3 +533,3 @@ if (response.status === 401 && await this.refreshLaunch({ reason: "inboxes_unauthorized" })) {

},
{ timeoutMs: input.timeoutMs ?? 6e4 }
{ timeoutMs: input.timeoutMs ?? 6e3 }
);

@@ -584,12 +620,2 @@ }

var createShadowServerAppBrowserClient = createShadowServerAppClient;
function createShadowServerAppRuntimeClient(options = {}) {
const windowRef = options.windowRef ?? (typeof window === "undefined" ? void 0 : window);
return new ShadowServerAppBrowserClient({
...options,
windowRef,
commandBasePath: options.commandBasePath ?? shadowServerAppMountedPath("/api/runtime/commands", windowRef),
inboxesPath: options.inboxesPath ?? shadowServerAppMountedPath("/api/runtime/inboxes", windowRef),
deliverLaunchOutboxFromBrowser: false
});
}
var ShadowBridge = class _ShadowBridge {

@@ -989,3 +1015,2 @@ static capabilitiesRequestType = "shadow.app.capabilities.request";

createShadowServerAppClient,
createShadowServerAppRuntimeClient,
getShadowServerAppChannelMessageDeliveries,

@@ -992,0 +1017,0 @@ getShadowServerAppChannelMessageErrors,

@@ -1,3 +0,3 @@

import { cM as ShadowServerAppInboxDelivery, cN as ShadowServerAppInboxDeliveryError, cf as ShadowServerAppChannelMessageDelivery, cg as ShadowServerAppChannelMessageDeliveryError, W as ShadowBuddyInboxSummary } from './server-app-DoVasgCH.cjs';
export { bv as SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT, bw as SHADOW_SERVER_APP_COMMAND_EVENTS, bx as SHADOW_SERVER_APP_COMMAND_FAILED_EVENT, ch as ShadowServerAppChannelMessageOutbox, cq as ShadowServerAppCommandEventType, cH as ShadowServerAppHostAppRef, cI as ShadowServerAppHostInboxTaskRequestInput, cO as ShadowServerAppInboxDeliveryFromMessageInput, cP as ShadowServerAppInboxTarget, cQ as ShadowServerAppInboxTaskOutbox, d5 as ShadowServerAppResolvedInboxTaskRequest, d6 as ShadowServerAppResultShadow, dt as buildShadowServerAppInboxDelivery, du as buildShadowServerAppInboxTaskRequest, dF as getShadowServerAppChannelMessageDeliveries, dG as getShadowServerAppChannelMessageErrors, dH as getShadowServerAppTaskCardId, dP as readShadowServerAppCommandResponse, dY as shadowServerAppInboxTaskEndpoint } from './server-app-DoVasgCH.cjs';
import { cO as ShadowServerAppInboxDelivery, cP as ShadowServerAppInboxDeliveryError, cg as ShadowServerAppChannelMessageDelivery, ch as ShadowServerAppChannelMessageDeliveryError, W as ShadowBuddyInboxSummary } from './server-app-DCoGGI9M.cjs';
export { bv as SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT, bw as SHADOW_SERVER_APP_COMMAND_EVENTS, bx as SHADOW_SERVER_APP_COMMAND_FAILED_EVENT, ci as ShadowServerAppChannelMessageOutbox, cr as ShadowServerAppCommandEventType, cJ as ShadowServerAppHostAppRef, cK as ShadowServerAppHostInboxTaskRequestInput, cQ as ShadowServerAppInboxDeliveryFromMessageInput, cR as ShadowServerAppInboxTarget, cS as ShadowServerAppInboxTaskOutbox, d8 as ShadowServerAppResolvedInboxTaskRequest, d9 as ShadowServerAppResultShadow, dw as buildShadowServerAppInboxDelivery, dx as buildShadowServerAppInboxTaskRequest, dI as getShadowServerAppChannelMessageDeliveries, dJ as getShadowServerAppChannelMessageErrors, dK as getShadowServerAppTaskCardId, dU as readShadowServerAppCommandResponse, e4 as shadowServerAppInboxTaskEndpoint } from './server-app-DCoGGI9M.cjs';
import '@shadowob/shared';

@@ -97,6 +97,2 @@

}
type ShadowServerAppRuntimeClientOptions = Omit<ShadowServerAppBrowserClientOptions, 'commandBasePath' | 'deliverLaunchOutboxFromBrowser' | 'inboxesPath'> & {
commandBasePath?: string;
inboxesPath?: string;
};
interface ShadowServerAppEnsureBuddyTaskGrantInput {

@@ -111,2 +107,3 @@ agentId?: string | null;

emptyOnError?: boolean;
timeoutMs?: number;
}

@@ -142,2 +139,3 @@ interface ShadowServerAppLaunchHeadersOptions {

command<TResult = unknown>(commandName: string, input?: unknown): Promise<TResult>;
commandForm<TResult = unknown>(commandName: string, formData: FormData): Promise<TResult>;
refreshLaunch(input?: ShadowBridgeRefreshLaunchInput): Promise<ShadowBridgeLaunchContext | null>;

@@ -148,2 +146,3 @@ fetchWithLaunch(input: RequestInfo | URL, init?: RequestInit, options?: ShadowServerAppFetchWithLaunchOptions): Promise<Response>;

}>;
private fetchLaunchBuddyInboxes;
ensureBuddyTaskGrant(input: ShadowServerAppEnsureBuddyTaskGrantInput): Promise<{

@@ -196,9 +195,4 @@ granted: boolean;

}
/**
* General browser client. Embedded Server Apps should use the runtime defaults
* or createShadowServerAppRuntimeClient(); pass explicit paths only for standalone tools.
*/
declare function createShadowServerAppClient(options?: ShadowServerAppBrowserClientOptions): ShadowServerAppBrowserClient;
declare const createShadowServerAppBrowserClient: typeof createShadowServerAppClient;
declare function createShadowServerAppRuntimeClient(options?: ShadowServerAppRuntimeClientOptions): ShadowServerAppBrowserClient;
declare class ShadowBridge {

@@ -306,2 +300,2 @@ static readonly capabilitiesRequestType = "shadow.app.capabilities.request";

export { SHADOW_BRIDGE_CAPABILITIES, ShadowBridge, type ShadowBridgeAuthorizeOAuthInput, type ShadowBridgeAuthorizeOAuthResult, type ShadowBridgeCapability, type ShadowBridgeEnsureBuddyGrantInput, type ShadowBridgeLaunchContext, type ShadowBridgeLaunchUpdateHandler, type ShadowBridgeLaunchUpdateInput, type ShadowBridgeListBuddyInboxesInput, type ShadowBridgeOpenBuddyCreatorInput, type ShadowBridgeOpenCopilotInput, type ShadowBridgeOpenWorkspaceResourceInput, type ShadowBridgeOptions, type ShadowBridgeRefreshLaunchInput, type ShadowBridgeRouteNavigateEvent, type ShadowBridgeRouteNavigateHandler, type ShadowBridgeShareAppInput, type ShadowBridgeShareAppResult, ShadowBuddyInboxSummary, ShadowServerAppBrowserClient, type ShadowServerAppBrowserClientOptions, ShadowServerAppChannelMessageDelivery, ShadowServerAppChannelMessageDeliveryError, type ShadowServerAppEnsureBuddyTaskGrantInput, type ShadowServerAppFetchWithLaunchOptions, ShadowServerAppInboxDelivery, ShadowServerAppInboxDeliveryError, type ShadowServerAppLaunchHeadersOptions, type ShadowServerAppListBuddyInboxesOptions, type ShadowServerAppRuntimeClientOptions, createShadowServerAppBrowserClient, createShadowServerAppClient, createShadowServerAppRuntimeClient, shadowServerAppMountedPath, shadowServerAppMountedPathPrefix };
export { SHADOW_BRIDGE_CAPABILITIES, ShadowBridge, type ShadowBridgeAuthorizeOAuthInput, type ShadowBridgeAuthorizeOAuthResult, type ShadowBridgeCapability, type ShadowBridgeEnsureBuddyGrantInput, type ShadowBridgeLaunchContext, type ShadowBridgeLaunchUpdateHandler, type ShadowBridgeLaunchUpdateInput, type ShadowBridgeListBuddyInboxesInput, type ShadowBridgeOpenBuddyCreatorInput, type ShadowBridgeOpenCopilotInput, type ShadowBridgeOpenWorkspaceResourceInput, type ShadowBridgeOptions, type ShadowBridgeRefreshLaunchInput, type ShadowBridgeRouteNavigateEvent, type ShadowBridgeRouteNavigateHandler, type ShadowBridgeShareAppInput, type ShadowBridgeShareAppResult, ShadowBuddyInboxSummary, ShadowServerAppBrowserClient, type ShadowServerAppBrowserClientOptions, ShadowServerAppChannelMessageDelivery, ShadowServerAppChannelMessageDeliveryError, type ShadowServerAppEnsureBuddyTaskGrantInput, type ShadowServerAppFetchWithLaunchOptions, ShadowServerAppInboxDelivery, ShadowServerAppInboxDeliveryError, type ShadowServerAppLaunchHeadersOptions, type ShadowServerAppListBuddyInboxesOptions, createShadowServerAppBrowserClient, createShadowServerAppClient, shadowServerAppMountedPath, shadowServerAppMountedPathPrefix };

@@ -1,3 +0,3 @@

import { cM as ShadowServerAppInboxDelivery, cN as ShadowServerAppInboxDeliveryError, cf as ShadowServerAppChannelMessageDelivery, cg as ShadowServerAppChannelMessageDeliveryError, W as ShadowBuddyInboxSummary } from './server-app-DoVasgCH.js';
export { bv as SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT, bw as SHADOW_SERVER_APP_COMMAND_EVENTS, bx as SHADOW_SERVER_APP_COMMAND_FAILED_EVENT, ch as ShadowServerAppChannelMessageOutbox, cq as ShadowServerAppCommandEventType, cH as ShadowServerAppHostAppRef, cI as ShadowServerAppHostInboxTaskRequestInput, cO as ShadowServerAppInboxDeliveryFromMessageInput, cP as ShadowServerAppInboxTarget, cQ as ShadowServerAppInboxTaskOutbox, d5 as ShadowServerAppResolvedInboxTaskRequest, d6 as ShadowServerAppResultShadow, dt as buildShadowServerAppInboxDelivery, du as buildShadowServerAppInboxTaskRequest, dF as getShadowServerAppChannelMessageDeliveries, dG as getShadowServerAppChannelMessageErrors, dH as getShadowServerAppTaskCardId, dP as readShadowServerAppCommandResponse, dY as shadowServerAppInboxTaskEndpoint } from './server-app-DoVasgCH.js';
import { cO as ShadowServerAppInboxDelivery, cP as ShadowServerAppInboxDeliveryError, cg as ShadowServerAppChannelMessageDelivery, ch as ShadowServerAppChannelMessageDeliveryError, W as ShadowBuddyInboxSummary } from './server-app-DCoGGI9M.js';
export { bv as SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT, bw as SHADOW_SERVER_APP_COMMAND_EVENTS, bx as SHADOW_SERVER_APP_COMMAND_FAILED_EVENT, ci as ShadowServerAppChannelMessageOutbox, cr as ShadowServerAppCommandEventType, cJ as ShadowServerAppHostAppRef, cK as ShadowServerAppHostInboxTaskRequestInput, cQ as ShadowServerAppInboxDeliveryFromMessageInput, cR as ShadowServerAppInboxTarget, cS as ShadowServerAppInboxTaskOutbox, d8 as ShadowServerAppResolvedInboxTaskRequest, d9 as ShadowServerAppResultShadow, dw as buildShadowServerAppInboxDelivery, dx as buildShadowServerAppInboxTaskRequest, dI as getShadowServerAppChannelMessageDeliveries, dJ as getShadowServerAppChannelMessageErrors, dK as getShadowServerAppTaskCardId, dU as readShadowServerAppCommandResponse, e4 as shadowServerAppInboxTaskEndpoint } from './server-app-DCoGGI9M.js';
import '@shadowob/shared';

@@ -97,6 +97,2 @@

}
type ShadowServerAppRuntimeClientOptions = Omit<ShadowServerAppBrowserClientOptions, 'commandBasePath' | 'deliverLaunchOutboxFromBrowser' | 'inboxesPath'> & {
commandBasePath?: string;
inboxesPath?: string;
};
interface ShadowServerAppEnsureBuddyTaskGrantInput {

@@ -111,2 +107,3 @@ agentId?: string | null;

emptyOnError?: boolean;
timeoutMs?: number;
}

@@ -142,2 +139,3 @@ interface ShadowServerAppLaunchHeadersOptions {

command<TResult = unknown>(commandName: string, input?: unknown): Promise<TResult>;
commandForm<TResult = unknown>(commandName: string, formData: FormData): Promise<TResult>;
refreshLaunch(input?: ShadowBridgeRefreshLaunchInput): Promise<ShadowBridgeLaunchContext | null>;

@@ -148,2 +146,3 @@ fetchWithLaunch(input: RequestInfo | URL, init?: RequestInit, options?: ShadowServerAppFetchWithLaunchOptions): Promise<Response>;

}>;
private fetchLaunchBuddyInboxes;
ensureBuddyTaskGrant(input: ShadowServerAppEnsureBuddyTaskGrantInput): Promise<{

@@ -196,9 +195,4 @@ granted: boolean;

}
/**
* General browser client. Embedded Server Apps should use the runtime defaults
* or createShadowServerAppRuntimeClient(); pass explicit paths only for standalone tools.
*/
declare function createShadowServerAppClient(options?: ShadowServerAppBrowserClientOptions): ShadowServerAppBrowserClient;
declare const createShadowServerAppBrowserClient: typeof createShadowServerAppClient;
declare function createShadowServerAppRuntimeClient(options?: ShadowServerAppRuntimeClientOptions): ShadowServerAppBrowserClient;
declare class ShadowBridge {

@@ -306,2 +300,2 @@ static readonly capabilitiesRequestType = "shadow.app.capabilities.request";

export { SHADOW_BRIDGE_CAPABILITIES, ShadowBridge, type ShadowBridgeAuthorizeOAuthInput, type ShadowBridgeAuthorizeOAuthResult, type ShadowBridgeCapability, type ShadowBridgeEnsureBuddyGrantInput, type ShadowBridgeLaunchContext, type ShadowBridgeLaunchUpdateHandler, type ShadowBridgeLaunchUpdateInput, type ShadowBridgeListBuddyInboxesInput, type ShadowBridgeOpenBuddyCreatorInput, type ShadowBridgeOpenCopilotInput, type ShadowBridgeOpenWorkspaceResourceInput, type ShadowBridgeOptions, type ShadowBridgeRefreshLaunchInput, type ShadowBridgeRouteNavigateEvent, type ShadowBridgeRouteNavigateHandler, type ShadowBridgeShareAppInput, type ShadowBridgeShareAppResult, ShadowBuddyInboxSummary, ShadowServerAppBrowserClient, type ShadowServerAppBrowserClientOptions, ShadowServerAppChannelMessageDelivery, ShadowServerAppChannelMessageDeliveryError, type ShadowServerAppEnsureBuddyTaskGrantInput, type ShadowServerAppFetchWithLaunchOptions, ShadowServerAppInboxDelivery, ShadowServerAppInboxDeliveryError, type ShadowServerAppLaunchHeadersOptions, type ShadowServerAppListBuddyInboxesOptions, type ShadowServerAppRuntimeClientOptions, createShadowServerAppBrowserClient, createShadowServerAppClient, createShadowServerAppRuntimeClient, shadowServerAppMountedPath, shadowServerAppMountedPathPrefix };
export { SHADOW_BRIDGE_CAPABILITIES, ShadowBridge, type ShadowBridgeAuthorizeOAuthInput, type ShadowBridgeAuthorizeOAuthResult, type ShadowBridgeCapability, type ShadowBridgeEnsureBuddyGrantInput, type ShadowBridgeLaunchContext, type ShadowBridgeLaunchUpdateHandler, type ShadowBridgeLaunchUpdateInput, type ShadowBridgeListBuddyInboxesInput, type ShadowBridgeOpenBuddyCreatorInput, type ShadowBridgeOpenCopilotInput, type ShadowBridgeOpenWorkspaceResourceInput, type ShadowBridgeOptions, type ShadowBridgeRefreshLaunchInput, type ShadowBridgeRouteNavigateEvent, type ShadowBridgeRouteNavigateHandler, type ShadowBridgeShareAppInput, type ShadowBridgeShareAppResult, ShadowBuddyInboxSummary, ShadowServerAppBrowserClient, type ShadowServerAppBrowserClientOptions, ShadowServerAppChannelMessageDelivery, ShadowServerAppChannelMessageDeliveryError, type ShadowServerAppEnsureBuddyTaskGrantInput, type ShadowServerAppFetchWithLaunchOptions, ShadowServerAppInboxDelivery, ShadowServerAppInboxDeliveryError, type ShadowServerAppLaunchHeadersOptions, type ShadowServerAppListBuddyInboxesOptions, createShadowServerAppBrowserClient, createShadowServerAppClient, shadowServerAppMountedPath, shadowServerAppMountedPathPrefix };

@@ -7,6 +7,5 @@ import {

createShadowServerAppClient,
createShadowServerAppRuntimeClient,
shadowServerAppMountedPath,
shadowServerAppMountedPathPrefix
} from "./chunk-DIKSJPZY.js";
} from "./chunk-EXPB3ASN.js";
import {

@@ -23,3 +22,3 @@ SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT,

shadowServerAppInboxTaskEndpoint
} from "./chunk-X2M6ERZU.js";
} from "./chunk-62WH33SI.js";
export {

@@ -36,3 +35,2 @@ SHADOW_BRIDGE_CAPABILITIES,

createShadowServerAppClient,
createShadowServerAppRuntimeClient,
getShadowServerAppChannelMessageDeliveries,

@@ -39,0 +37,0 @@ getShadowServerAppChannelMessageErrors,

@@ -28,2 +28,3 @@ "use strict";

SHADOW_SERVER_APP_PROTOCOL: () => SHADOW_SERVER_APP_PROTOCOL,
SHADOW_SERVER_APP_PUBLIC_AVATAR_CACHE_CONTROL: () => SHADOW_SERVER_APP_PUBLIC_AVATAR_CACHE_CONTROL,
ShadowServerAppCommandError: () => ShadowServerAppCommandError,

@@ -55,2 +56,4 @@ ShadowServerAppHttpError: () => ShadowServerAppHttpError,

introspectShadowServerAppToken: () => introspectShadowServerAppToken,
isShadowServerAppSignedMediaUrl: () => isShadowServerAppSignedMediaUrl,
normalizeShadowServerAppAvatarUrl: () => normalizeShadowServerAppAvatarUrl,
normalizeShadowServerAppBaseCursor: () => normalizeShadowServerAppBaseCursor,

@@ -62,5 +65,8 @@ normalizeShadowServerAppClientMutationId: () => normalizeShadowServerAppClientMutationId,

resolveShadowServerAppLaunchCommandContext: () => resolveShadowServerAppLaunchCommandContext,
resolveShadowServerAppLaunchCommandContextResolution: () => resolveShadowServerAppLaunchCommandContextResolution,
shadowServerAppActorAvatarUrl: () => shadowServerAppActorAvatarUrl,
shadowServerAppActorDisplayName: () => shadowServerAppActorDisplayName,
shadowServerAppActorRef: () => shadowServerAppActorRef,
shadowServerAppApiBaseUrl: () => shadowServerAppApiBaseUrl,
shadowServerAppAvatarRedirectUrl: () => shadowServerAppAvatarRedirectUrl,
shadowServerAppDisplayIdentity: () => shadowServerAppDisplayIdentity,

@@ -71,2 +77,5 @@ shadowServerAppError: () => shadowServerAppError,

shadowServerAppInboxTaskEndpoint: () => shadowServerAppInboxTaskEndpoint,
shadowServerAppLaunchIntrospectionError: () => shadowServerAppLaunchIntrospectionError,
shadowServerAppPublicBaseUrl: () => shadowServerAppPublicBaseUrl,
shadowServerAppPublicUrl: () => shadowServerAppPublicUrl,
unwrapShadowServerAppCommandPayload: () => unwrapShadowServerAppCommandPayload,

@@ -321,3 +330,3 @@ validateShadowServerAppJsonSchema: () => validateShadowServerAppJsonSchema

function trimTrailingSlash(value) {
return value.replace(/\/$/, "");
return value.replace(/\/+$/, "");
}

@@ -329,2 +338,59 @@ function joinBasePath(baseUrl, path) {

}
var SHADOW_SERVER_APP_PUBLIC_AVATAR_CACHE_CONTROL = "public, max-age=31536000, immutable";
function firstEnvironmentValue(env, keys, fallback) {
for (const key of keys) {
const value = env[key]?.trim();
if (value) return value;
}
return fallback;
}
function shadowServerAppApiBaseUrl(env = {}) {
return trimTrailingSlash(
firstEnvironmentValue(
env,
["SHADOWOB_INTERNAL_SERVER_URL", "SHADOWOB_SERVER_URL"],
"http://localhost:3002"
)
);
}
function shadowServerAppPublicBaseUrl(env = {}) {
return trimTrailingSlash(
firstEnvironmentValue(
env,
[
"SHADOWOB_PUBLIC_BASE_URL",
"SHADOWOB_WEB_BASE_URL",
"SHADOWOB_OAUTH_AUTHORIZE_BASE_URL",
"OAUTH_BASE_URL",
"SHADOWOB_SERVER_URL"
],
"http://localhost:3000"
)
);
}
function shadowServerAppPublicUrl(pathOrUrl, env = {}) {
if (!pathOrUrl.startsWith("/")) return pathOrUrl;
return joinBasePath(shadowServerAppPublicBaseUrl(env), pathOrUrl);
}
function isShadowServerAppSignedMediaUrl(value, env = {}) {
const mediaUrl = value.trim();
if (mediaUrl.startsWith("/api/media/signed/")) return true;
try {
return new URL(mediaUrl, shadowServerAppPublicBaseUrl(env)).pathname.startsWith(
"/api/media/signed/"
);
} catch {
return false;
}
}
function normalizeShadowServerAppAvatarUrl(value, env = {}) {
if (typeof value !== "string") return null;
const avatarUrl = value.trim();
if (!avatarUrl || avatarUrl.length > 500) return null;
if (isShadowServerAppSignedMediaUrl(avatarUrl, env)) return null;
return shadowServerAppPublicUrl(avatarUrl, env);
}
function shadowServerAppAvatarRedirectUrl(requestUrl, env = {}) {
return shadowServerAppPublicUrl(new URL(requestUrl).pathname, env);
}
function urlOrigin(value) {

@@ -401,9 +467,17 @@ try {

);
if (!response.ok) return null;
if (!response.ok) {
const payload2 = await readShadowServerAppResponsePayload(response).catch(() => null);
return {
active: false,
error: shadowServerAppResponseErrorMessage(response.status, payload2, "invalid_launch_token")
};
}
const payload = await response.json().catch(() => null);
return payload?.active ? payload : null;
return typeof payload?.active === "boolean" ? payload : null;
}
async function resolveShadowServerAppLaunchCommandContext(options) {
const introspection = await introspectShadowServerAppLaunchToken(options);
const shadow = introspection?.shadow;
function shadowServerAppLaunchIntrospectionError(introspection) {
return introspection?.error ?? introspection?.reason ?? introspection?.error_description ?? "invalid_launch_token";
}
function shadowServerAppLaunchCommandContextFromIntrospection(options, introspection) {
const shadow = introspection.active ? introspection.shadow : null;
if (!shadow) return null;

@@ -426,2 +500,22 @@ const command = options.manifest.commands.find((item) => item.name === options.commandName);

}
async function resolveShadowServerAppLaunchCommandContextResolution(options) {
const introspection = await introspectShadowServerAppLaunchToken(options);
if (!introspection?.active) {
return {
context: null,
introspection,
error: shadowServerAppLaunchIntrospectionError(introspection)
};
}
const context = shadowServerAppLaunchCommandContextFromIntrospection(options, introspection);
return {
context,
introspection,
error: context ? null : shadowServerAppLaunchIntrospectionError(introspection)
};
}
async function resolveShadowServerAppLaunchCommandContext(options) {
const resolution = await resolveShadowServerAppLaunchCommandContextResolution(options);
return resolution.context;
}
async function deliverShadowServerAppLaunchOutbox(options) {

@@ -882,2 +976,3 @@ const hint = decodeShadowServerAppLaunchTokenHint(options.launchToken);

SHADOW_SERVER_APP_PROTOCOL,
SHADOW_SERVER_APP_PUBLIC_AVATAR_CACHE_CONTROL,
ShadowServerAppCommandError,

@@ -909,2 +1004,4 @@ ShadowServerAppHttpError,

introspectShadowServerAppToken,
isShadowServerAppSignedMediaUrl,
normalizeShadowServerAppAvatarUrl,
normalizeShadowServerAppBaseCursor,

@@ -916,5 +1013,8 @@ normalizeShadowServerAppClientMutationId,

resolveShadowServerAppLaunchCommandContext,
resolveShadowServerAppLaunchCommandContextResolution,
shadowServerAppActorAvatarUrl,
shadowServerAppActorDisplayName,
shadowServerAppActorRef,
shadowServerAppApiBaseUrl,
shadowServerAppAvatarRedirectUrl,
shadowServerAppDisplayIdentity,

@@ -925,4 +1025,7 @@ shadowServerAppError,

shadowServerAppInboxTaskEndpoint,
shadowServerAppLaunchIntrospectionError,
shadowServerAppPublicBaseUrl,
shadowServerAppPublicUrl,
unwrapShadowServerAppCommandPayload,
validateShadowServerAppJsonSchema
});

@@ -1,2 +0,2 @@

export { bn as JsonSchemaToType, bv as SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT, bw as SHADOW_SERVER_APP_COMMAND_EVENTS, bx as SHADOW_SERVER_APP_COMMAND_FAILED_EVENT, by as SHADOW_SERVER_APP_PROTOCOL, c2 as ShadowServerAppActorRef, c3 as ShadowServerAppBridgeAuthorizeOAuthRequest, c4 as ShadowServerAppBridgeCapabilitiesRequest, c5 as ShadowServerAppBridgeFailureResponse, c6 as ShadowServerAppBridgeOpenCopilotRequest, d_ as ShadowServerAppBridgeOpenWorkspaceResourceRequest, c7 as ShadowServerAppBridgeRequest, c8 as ShadowServerAppBridgeResponse, c9 as ShadowServerAppBridgeResponseType, ca as ShadowServerAppBridgeRouteChangedEvent, cb as ShadowServerAppBridgeRouteNavigateAck, cc as ShadowServerAppBridgeRouteNavigateRequest, cd as ShadowServerAppBridgeShareAppRequest, ce as ShadowServerAppBridgeSuccessResponse, cf as ShadowServerAppChannelMessageDelivery, cg as ShadowServerAppChannelMessageDeliveryError, ch as ShadowServerAppChannelMessageOutbox, ci as ShadowServerAppCollaborationEvent, cj as ShadowServerAppCollaborationMutation, ck as ShadowServerAppCollaborationResource, cn as ShadowServerAppCommandContext, co as ShadowServerAppCommandEnvelope, cp as ShadowServerAppCommandError, cq as ShadowServerAppCommandEventType, cr as ShadowServerAppCommandFailureResponse, cs as ShadowServerAppCommandHandler, ct as ShadowServerAppCommandHandlerContext, cu as ShadowServerAppCommandHandlers, cv as ShadowServerAppCommandInput, cw as ShadowServerAppCommandName, cx as ShadowServerAppCommandParseError, cy as ShadowServerAppCommandParseResult, cz as ShadowServerAppCommandParseSuccess, cA as ShadowServerAppCommandRequestInput, cB as ShadowServerAppCommandResponse, d$ as ShadowServerAppCommandRuntimeRequest, cC as ShadowServerAppCommandSuccessResponse, cD as ShadowServerAppExecutionFailure, cE as ShadowServerAppExecutionResult, cF as ShadowServerAppExecutionSuccess, cG as ShadowServerAppFetch, cH as ShadowServerAppHostAppRef, cI as ShadowServerAppHostInboxTaskRequestInput, cJ as ShadowServerAppHttpError, cK as ShadowServerAppIdentitySnapshot, cL as ShadowServerAppIdentitySubjectKind, cM as ShadowServerAppInboxDelivery, cN as ShadowServerAppInboxDeliveryError, cO as ShadowServerAppInboxDeliveryFromMessageInput, cP as ShadowServerAppInboxTarget, cQ as ShadowServerAppInboxTaskOutbox, cR as ShadowServerAppInboxTaskPriority, cS as ShadowServerAppInboxTaskResource, cT as ShadowServerAppIntrospectionInput, cU as ShadowServerAppLaunchCommandContextOptions, cV as ShadowServerAppLaunchFetchOptions, cW as ShadowServerAppLaunchIntrospection, cX as ShadowServerAppLaunchOutboxDeliveryOptions, cY as ShadowServerAppLaunchTokenHint, cZ as ShadowServerAppManifestOptions, d2 as ShadowServerAppOutbox, d3 as ShadowServerAppOutboxPayload, d5 as ShadowServerAppResolvedInboxTaskRequest, d6 as ShadowServerAppResultShadow, d7 as ShadowServerAppResultWithShadow, d8 as ShadowServerAppRuntime, d9 as ShadowServerAppRuntimeOptions, da as ShadowServerAppValidationIssue, dt as buildShadowServerAppInboxDelivery, du as buildShadowServerAppInboxTaskRequest, dv as createShadowServerAppCollaborationCursor, dw as createShadowServerAppCollaborationEvent, dx as createShadowServerAppCollaborationResource, dy as createShadowServerAppManifest, dz as createShadowServerAppRuntime, dA as decodeShadowServerAppLaunchTokenHint, dB as defineShadowServerApp, dC as deliverShadowServerAppLaunchOutbox, dD as extractShadowServerAppBearerToken, dE as fetchShadowServerAppLaunchInboxes, dF as getShadowServerAppChannelMessageDeliveries, dG as getShadowServerAppChannelMessageErrors, e0 as getShadowServerAppInboxDeliveries, e1 as getShadowServerAppInboxErrors, e2 as getShadowServerAppPendingChannelMessages, e3 as getShadowServerAppPendingInboxTasks, dH as getShadowServerAppTaskCardId, dI as hasShadowServerAppPendingOutbox, dJ as introspectShadowServerAppLaunchToken, dK as introspectShadowServerAppToken, dL as normalizeShadowServerAppBaseCursor, dM as normalizeShadowServerAppClientMutationId, dN as normalizeShadowServerAppCommandInput, dO as parseShadowServerAppCommandRequest, dP as readShadowServerAppCommandResponse, dQ as resolveShadowServerAppLaunchCommandContext, dR as shadowServerAppActorAvatarUrl, dS as shadowServerAppActorDisplayName, dT as shadowServerAppActorRef, dU as shadowServerAppDisplayIdentity, dV as shadowServerAppError, dW as shadowServerAppIdentityKey, dX as shadowServerAppIdentitySnapshot, dY as shadowServerAppInboxTaskEndpoint, e4 as unwrapShadowServerAppCommandPayload, dZ as validateShadowServerAppJsonSchema } from './server-app-DoVasgCH.cjs';
export { bn as JsonSchemaToType, bv as SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT, bw as SHADOW_SERVER_APP_COMMAND_EVENTS, bx as SHADOW_SERVER_APP_COMMAND_FAILED_EVENT, by as SHADOW_SERVER_APP_PROTOCOL, bz as SHADOW_SERVER_APP_PUBLIC_AVATAR_CACHE_CONTROL, c3 as ShadowServerAppActorRef, c4 as ShadowServerAppBridgeAuthorizeOAuthRequest, c5 as ShadowServerAppBridgeCapabilitiesRequest, c6 as ShadowServerAppBridgeFailureResponse, c7 as ShadowServerAppBridgeOpenCopilotRequest, e9 as ShadowServerAppBridgeOpenWorkspaceResourceRequest, c8 as ShadowServerAppBridgeRequest, c9 as ShadowServerAppBridgeResponse, ca as ShadowServerAppBridgeResponseType, cb as ShadowServerAppBridgeRouteChangedEvent, cc as ShadowServerAppBridgeRouteNavigateAck, cd as ShadowServerAppBridgeRouteNavigateRequest, ce as ShadowServerAppBridgeShareAppRequest, cf as ShadowServerAppBridgeSuccessResponse, cg as ShadowServerAppChannelMessageDelivery, ch as ShadowServerAppChannelMessageDeliveryError, ci as ShadowServerAppChannelMessageOutbox, cj as ShadowServerAppCollaborationEvent, ck as ShadowServerAppCollaborationMutation, cl as ShadowServerAppCollaborationResource, co as ShadowServerAppCommandContext, cp as ShadowServerAppCommandEnvelope, cq as ShadowServerAppCommandError, cr as ShadowServerAppCommandEventType, cs as ShadowServerAppCommandFailureResponse, ct as ShadowServerAppCommandHandler, cu as ShadowServerAppCommandHandlerContext, cv as ShadowServerAppCommandHandlers, cw as ShadowServerAppCommandInput, cx as ShadowServerAppCommandName, cy as ShadowServerAppCommandParseError, cz as ShadowServerAppCommandParseResult, cA as ShadowServerAppCommandParseSuccess, cB as ShadowServerAppCommandRequestInput, cC as ShadowServerAppCommandResponse, ea as ShadowServerAppCommandRuntimeRequest, cD as ShadowServerAppCommandSuccessResponse, cE as ShadowServerAppEnvironment, cF as ShadowServerAppExecutionFailure, cG as ShadowServerAppExecutionResult, cH as ShadowServerAppExecutionSuccess, cI as ShadowServerAppFetch, cJ as ShadowServerAppHostAppRef, cK as ShadowServerAppHostInboxTaskRequestInput, cL as ShadowServerAppHttpError, cM as ShadowServerAppIdentitySnapshot, cN as ShadowServerAppIdentitySubjectKind, cO as ShadowServerAppInboxDelivery, cP as ShadowServerAppInboxDeliveryError, cQ as ShadowServerAppInboxDeliveryFromMessageInput, cR as ShadowServerAppInboxTarget, cS as ShadowServerAppInboxTaskOutbox, cT as ShadowServerAppInboxTaskPriority, cU as ShadowServerAppInboxTaskResource, cV as ShadowServerAppIntrospectionInput, cW as ShadowServerAppLaunchCommandContextOptions, cX as ShadowServerAppLaunchCommandContextResolution, cY as ShadowServerAppLaunchFetchOptions, cZ as ShadowServerAppLaunchIntrospection, c_ as ShadowServerAppLaunchOutboxDeliveryOptions, c$ as ShadowServerAppLaunchTokenHint, d0 as ShadowServerAppManifestOptions, d5 as ShadowServerAppOutbox, d6 as ShadowServerAppOutboxPayload, d8 as ShadowServerAppResolvedInboxTaskRequest, d9 as ShadowServerAppResultShadow, da as ShadowServerAppResultWithShadow, db as ShadowServerAppRuntime, dc as ShadowServerAppRuntimeOptions, dd as ShadowServerAppValidationIssue, dw as buildShadowServerAppInboxDelivery, dx as buildShadowServerAppInboxTaskRequest, dy as createShadowServerAppCollaborationCursor, dz as createShadowServerAppCollaborationEvent, dA as createShadowServerAppCollaborationResource, dB as createShadowServerAppManifest, dC as createShadowServerAppRuntime, dD as decodeShadowServerAppLaunchTokenHint, dE as defineShadowServerApp, dF as deliverShadowServerAppLaunchOutbox, dG as extractShadowServerAppBearerToken, dH as fetchShadowServerAppLaunchInboxes, dI as getShadowServerAppChannelMessageDeliveries, dJ as getShadowServerAppChannelMessageErrors, eb as getShadowServerAppInboxDeliveries, ec as getShadowServerAppInboxErrors, ed as getShadowServerAppPendingChannelMessages, ee as getShadowServerAppPendingInboxTasks, dK as getShadowServerAppTaskCardId, dL as hasShadowServerAppPendingOutbox, dM as introspectShadowServerAppLaunchToken, dN as introspectShadowServerAppToken, dO as isShadowServerAppSignedMediaUrl, dP as normalizeShadowServerAppAvatarUrl, dQ as normalizeShadowServerAppBaseCursor, dR as normalizeShadowServerAppClientMutationId, dS as normalizeShadowServerAppCommandInput, dT as parseShadowServerAppCommandRequest, dU as readShadowServerAppCommandResponse, dV as resolveShadowServerAppLaunchCommandContext, dW as resolveShadowServerAppLaunchCommandContextResolution, dX as shadowServerAppActorAvatarUrl, dY as shadowServerAppActorDisplayName, dZ as shadowServerAppActorRef, d_ as shadowServerAppApiBaseUrl, d$ as shadowServerAppAvatarRedirectUrl, e0 as shadowServerAppDisplayIdentity, e1 as shadowServerAppError, e2 as shadowServerAppIdentityKey, e3 as shadowServerAppIdentitySnapshot, e4 as shadowServerAppInboxTaskEndpoint, e5 as shadowServerAppLaunchIntrospectionError, e6 as shadowServerAppPublicBaseUrl, e7 as shadowServerAppPublicUrl, ef as unwrapShadowServerAppCommandPayload, e8 as validateShadowServerAppJsonSchema } from './server-app-DCoGGI9M.cjs';
export { BUDDY_INBOX_DELIVERY_PERMISSION, BuddyInboxPlatformPermission } from '@shadowob/shared';

@@ -1,2 +0,2 @@

export { bn as JsonSchemaToType, bv as SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT, bw as SHADOW_SERVER_APP_COMMAND_EVENTS, bx as SHADOW_SERVER_APP_COMMAND_FAILED_EVENT, by as SHADOW_SERVER_APP_PROTOCOL, c2 as ShadowServerAppActorRef, c3 as ShadowServerAppBridgeAuthorizeOAuthRequest, c4 as ShadowServerAppBridgeCapabilitiesRequest, c5 as ShadowServerAppBridgeFailureResponse, c6 as ShadowServerAppBridgeOpenCopilotRequest, d_ as ShadowServerAppBridgeOpenWorkspaceResourceRequest, c7 as ShadowServerAppBridgeRequest, c8 as ShadowServerAppBridgeResponse, c9 as ShadowServerAppBridgeResponseType, ca as ShadowServerAppBridgeRouteChangedEvent, cb as ShadowServerAppBridgeRouteNavigateAck, cc as ShadowServerAppBridgeRouteNavigateRequest, cd as ShadowServerAppBridgeShareAppRequest, ce as ShadowServerAppBridgeSuccessResponse, cf as ShadowServerAppChannelMessageDelivery, cg as ShadowServerAppChannelMessageDeliveryError, ch as ShadowServerAppChannelMessageOutbox, ci as ShadowServerAppCollaborationEvent, cj as ShadowServerAppCollaborationMutation, ck as ShadowServerAppCollaborationResource, cn as ShadowServerAppCommandContext, co as ShadowServerAppCommandEnvelope, cp as ShadowServerAppCommandError, cq as ShadowServerAppCommandEventType, cr as ShadowServerAppCommandFailureResponse, cs as ShadowServerAppCommandHandler, ct as ShadowServerAppCommandHandlerContext, cu as ShadowServerAppCommandHandlers, cv as ShadowServerAppCommandInput, cw as ShadowServerAppCommandName, cx as ShadowServerAppCommandParseError, cy as ShadowServerAppCommandParseResult, cz as ShadowServerAppCommandParseSuccess, cA as ShadowServerAppCommandRequestInput, cB as ShadowServerAppCommandResponse, d$ as ShadowServerAppCommandRuntimeRequest, cC as ShadowServerAppCommandSuccessResponse, cD as ShadowServerAppExecutionFailure, cE as ShadowServerAppExecutionResult, cF as ShadowServerAppExecutionSuccess, cG as ShadowServerAppFetch, cH as ShadowServerAppHostAppRef, cI as ShadowServerAppHostInboxTaskRequestInput, cJ as ShadowServerAppHttpError, cK as ShadowServerAppIdentitySnapshot, cL as ShadowServerAppIdentitySubjectKind, cM as ShadowServerAppInboxDelivery, cN as ShadowServerAppInboxDeliveryError, cO as ShadowServerAppInboxDeliveryFromMessageInput, cP as ShadowServerAppInboxTarget, cQ as ShadowServerAppInboxTaskOutbox, cR as ShadowServerAppInboxTaskPriority, cS as ShadowServerAppInboxTaskResource, cT as ShadowServerAppIntrospectionInput, cU as ShadowServerAppLaunchCommandContextOptions, cV as ShadowServerAppLaunchFetchOptions, cW as ShadowServerAppLaunchIntrospection, cX as ShadowServerAppLaunchOutboxDeliveryOptions, cY as ShadowServerAppLaunchTokenHint, cZ as ShadowServerAppManifestOptions, d2 as ShadowServerAppOutbox, d3 as ShadowServerAppOutboxPayload, d5 as ShadowServerAppResolvedInboxTaskRequest, d6 as ShadowServerAppResultShadow, d7 as ShadowServerAppResultWithShadow, d8 as ShadowServerAppRuntime, d9 as ShadowServerAppRuntimeOptions, da as ShadowServerAppValidationIssue, dt as buildShadowServerAppInboxDelivery, du as buildShadowServerAppInboxTaskRequest, dv as createShadowServerAppCollaborationCursor, dw as createShadowServerAppCollaborationEvent, dx as createShadowServerAppCollaborationResource, dy as createShadowServerAppManifest, dz as createShadowServerAppRuntime, dA as decodeShadowServerAppLaunchTokenHint, dB as defineShadowServerApp, dC as deliverShadowServerAppLaunchOutbox, dD as extractShadowServerAppBearerToken, dE as fetchShadowServerAppLaunchInboxes, dF as getShadowServerAppChannelMessageDeliveries, dG as getShadowServerAppChannelMessageErrors, e0 as getShadowServerAppInboxDeliveries, e1 as getShadowServerAppInboxErrors, e2 as getShadowServerAppPendingChannelMessages, e3 as getShadowServerAppPendingInboxTasks, dH as getShadowServerAppTaskCardId, dI as hasShadowServerAppPendingOutbox, dJ as introspectShadowServerAppLaunchToken, dK as introspectShadowServerAppToken, dL as normalizeShadowServerAppBaseCursor, dM as normalizeShadowServerAppClientMutationId, dN as normalizeShadowServerAppCommandInput, dO as parseShadowServerAppCommandRequest, dP as readShadowServerAppCommandResponse, dQ as resolveShadowServerAppLaunchCommandContext, dR as shadowServerAppActorAvatarUrl, dS as shadowServerAppActorDisplayName, dT as shadowServerAppActorRef, dU as shadowServerAppDisplayIdentity, dV as shadowServerAppError, dW as shadowServerAppIdentityKey, dX as shadowServerAppIdentitySnapshot, dY as shadowServerAppInboxTaskEndpoint, e4 as unwrapShadowServerAppCommandPayload, dZ as validateShadowServerAppJsonSchema } from './server-app-DoVasgCH.js';
export { bn as JsonSchemaToType, bv as SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT, bw as SHADOW_SERVER_APP_COMMAND_EVENTS, bx as SHADOW_SERVER_APP_COMMAND_FAILED_EVENT, by as SHADOW_SERVER_APP_PROTOCOL, bz as SHADOW_SERVER_APP_PUBLIC_AVATAR_CACHE_CONTROL, c3 as ShadowServerAppActorRef, c4 as ShadowServerAppBridgeAuthorizeOAuthRequest, c5 as ShadowServerAppBridgeCapabilitiesRequest, c6 as ShadowServerAppBridgeFailureResponse, c7 as ShadowServerAppBridgeOpenCopilotRequest, e9 as ShadowServerAppBridgeOpenWorkspaceResourceRequest, c8 as ShadowServerAppBridgeRequest, c9 as ShadowServerAppBridgeResponse, ca as ShadowServerAppBridgeResponseType, cb as ShadowServerAppBridgeRouteChangedEvent, cc as ShadowServerAppBridgeRouteNavigateAck, cd as ShadowServerAppBridgeRouteNavigateRequest, ce as ShadowServerAppBridgeShareAppRequest, cf as ShadowServerAppBridgeSuccessResponse, cg as ShadowServerAppChannelMessageDelivery, ch as ShadowServerAppChannelMessageDeliveryError, ci as ShadowServerAppChannelMessageOutbox, cj as ShadowServerAppCollaborationEvent, ck as ShadowServerAppCollaborationMutation, cl as ShadowServerAppCollaborationResource, co as ShadowServerAppCommandContext, cp as ShadowServerAppCommandEnvelope, cq as ShadowServerAppCommandError, cr as ShadowServerAppCommandEventType, cs as ShadowServerAppCommandFailureResponse, ct as ShadowServerAppCommandHandler, cu as ShadowServerAppCommandHandlerContext, cv as ShadowServerAppCommandHandlers, cw as ShadowServerAppCommandInput, cx as ShadowServerAppCommandName, cy as ShadowServerAppCommandParseError, cz as ShadowServerAppCommandParseResult, cA as ShadowServerAppCommandParseSuccess, cB as ShadowServerAppCommandRequestInput, cC as ShadowServerAppCommandResponse, ea as ShadowServerAppCommandRuntimeRequest, cD as ShadowServerAppCommandSuccessResponse, cE as ShadowServerAppEnvironment, cF as ShadowServerAppExecutionFailure, cG as ShadowServerAppExecutionResult, cH as ShadowServerAppExecutionSuccess, cI as ShadowServerAppFetch, cJ as ShadowServerAppHostAppRef, cK as ShadowServerAppHostInboxTaskRequestInput, cL as ShadowServerAppHttpError, cM as ShadowServerAppIdentitySnapshot, cN as ShadowServerAppIdentitySubjectKind, cO as ShadowServerAppInboxDelivery, cP as ShadowServerAppInboxDeliveryError, cQ as ShadowServerAppInboxDeliveryFromMessageInput, cR as ShadowServerAppInboxTarget, cS as ShadowServerAppInboxTaskOutbox, cT as ShadowServerAppInboxTaskPriority, cU as ShadowServerAppInboxTaskResource, cV as ShadowServerAppIntrospectionInput, cW as ShadowServerAppLaunchCommandContextOptions, cX as ShadowServerAppLaunchCommandContextResolution, cY as ShadowServerAppLaunchFetchOptions, cZ as ShadowServerAppLaunchIntrospection, c_ as ShadowServerAppLaunchOutboxDeliveryOptions, c$ as ShadowServerAppLaunchTokenHint, d0 as ShadowServerAppManifestOptions, d5 as ShadowServerAppOutbox, d6 as ShadowServerAppOutboxPayload, d8 as ShadowServerAppResolvedInboxTaskRequest, d9 as ShadowServerAppResultShadow, da as ShadowServerAppResultWithShadow, db as ShadowServerAppRuntime, dc as ShadowServerAppRuntimeOptions, dd as ShadowServerAppValidationIssue, dw as buildShadowServerAppInboxDelivery, dx as buildShadowServerAppInboxTaskRequest, dy as createShadowServerAppCollaborationCursor, dz as createShadowServerAppCollaborationEvent, dA as createShadowServerAppCollaborationResource, dB as createShadowServerAppManifest, dC as createShadowServerAppRuntime, dD as decodeShadowServerAppLaunchTokenHint, dE as defineShadowServerApp, dF as deliverShadowServerAppLaunchOutbox, dG as extractShadowServerAppBearerToken, dH as fetchShadowServerAppLaunchInboxes, dI as getShadowServerAppChannelMessageDeliveries, dJ as getShadowServerAppChannelMessageErrors, eb as getShadowServerAppInboxDeliveries, ec as getShadowServerAppInboxErrors, ed as getShadowServerAppPendingChannelMessages, ee as getShadowServerAppPendingInboxTasks, dK as getShadowServerAppTaskCardId, dL as hasShadowServerAppPendingOutbox, dM as introspectShadowServerAppLaunchToken, dN as introspectShadowServerAppToken, dO as isShadowServerAppSignedMediaUrl, dP as normalizeShadowServerAppAvatarUrl, dQ as normalizeShadowServerAppBaseCursor, dR as normalizeShadowServerAppClientMutationId, dS as normalizeShadowServerAppCommandInput, dT as parseShadowServerAppCommandRequest, dU as readShadowServerAppCommandResponse, dV as resolveShadowServerAppLaunchCommandContext, dW as resolveShadowServerAppLaunchCommandContextResolution, dX as shadowServerAppActorAvatarUrl, dY as shadowServerAppActorDisplayName, dZ as shadowServerAppActorRef, d_ as shadowServerAppApiBaseUrl, d$ as shadowServerAppAvatarRedirectUrl, e0 as shadowServerAppDisplayIdentity, e1 as shadowServerAppError, e2 as shadowServerAppIdentityKey, e3 as shadowServerAppIdentitySnapshot, e4 as shadowServerAppInboxTaskEndpoint, e5 as shadowServerAppLaunchIntrospectionError, e6 as shadowServerAppPublicBaseUrl, e7 as shadowServerAppPublicUrl, ef as unwrapShadowServerAppCommandPayload, e8 as validateShadowServerAppJsonSchema } from './server-app-DCoGGI9M.js';
export { BUDDY_INBOX_DELIVERY_PERMISSION, BuddyInboxPlatformPermission } from '@shadowob/shared';

@@ -7,2 +7,3 @@ import {

SHADOW_SERVER_APP_PROTOCOL,
SHADOW_SERVER_APP_PUBLIC_AVATAR_CACHE_CONTROL,
ShadowServerAppCommandError,

@@ -34,2 +35,4 @@ ShadowServerAppHttpError,

introspectShadowServerAppToken,
isShadowServerAppSignedMediaUrl,
normalizeShadowServerAppAvatarUrl,
normalizeShadowServerAppBaseCursor,

@@ -41,5 +44,8 @@ normalizeShadowServerAppClientMutationId,

resolveShadowServerAppLaunchCommandContext,
resolveShadowServerAppLaunchCommandContextResolution,
shadowServerAppActorAvatarUrl,
shadowServerAppActorDisplayName,
shadowServerAppActorRef,
shadowServerAppApiBaseUrl,
shadowServerAppAvatarRedirectUrl,
shadowServerAppDisplayIdentity,

@@ -50,5 +56,8 @@ shadowServerAppError,

shadowServerAppInboxTaskEndpoint,
shadowServerAppLaunchIntrospectionError,
shadowServerAppPublicBaseUrl,
shadowServerAppPublicUrl,
unwrapShadowServerAppCommandPayload,
validateShadowServerAppJsonSchema
} from "./chunk-X2M6ERZU.js";
} from "./chunk-62WH33SI.js";
export {

@@ -60,2 +69,3 @@ BUDDY_INBOX_DELIVERY_PERMISSION,

SHADOW_SERVER_APP_PROTOCOL,
SHADOW_SERVER_APP_PUBLIC_AVATAR_CACHE_CONTROL,
ShadowServerAppCommandError,

@@ -87,2 +97,4 @@ ShadowServerAppHttpError,

introspectShadowServerAppToken,
isShadowServerAppSignedMediaUrl,
normalizeShadowServerAppAvatarUrl,
normalizeShadowServerAppBaseCursor,

@@ -94,5 +106,8 @@ normalizeShadowServerAppClientMutationId,

resolveShadowServerAppLaunchCommandContext,
resolveShadowServerAppLaunchCommandContextResolution,
shadowServerAppActorAvatarUrl,
shadowServerAppActorDisplayName,
shadowServerAppActorRef,
shadowServerAppApiBaseUrl,
shadowServerAppAvatarRedirectUrl,
shadowServerAppDisplayIdentity,

@@ -103,4 +118,7 @@ shadowServerAppError,

shadowServerAppInboxTaskEndpoint,
shadowServerAppLaunchIntrospectionError,
shadowServerAppPublicBaseUrl,
shadowServerAppPublicUrl,
unwrapShadowServerAppCommandPayload,
validateShadowServerAppJsonSchema
};
{
"name": "@shadowob/sdk",
"version": "1.1.63",
"version": "1.1.64",
"description": "Shadow SDK — typed REST client and real-time Socket.IO event listener for Shadow servers",

@@ -51,3 +51,3 @@ "license": "MIT",

"socket.io-client": "^4.8.1",
"@shadowob/shared": "1.1.63"
"@shadowob/shared": "1.1.64"
},

@@ -54,0 +54,0 @@ "peerDependencies": {

import {
BUDDY_INBOX_DELIVERY_PERMISSION,
decodeShadowServerAppLaunchTokenHint,
deliverShadowServerAppLaunchOutbox,
getShadowServerAppChannelMessageDeliveries,
getShadowServerAppChannelMessageErrors,
getShadowServerAppInboxDeliveries,
getShadowServerAppInboxErrors,
hasShadowServerAppPendingOutbox,
readShadowServerAppCommandResponse,
unwrapShadowServerAppCommandPayload
} from "./chunk-X2M6ERZU.js";
// src/bridge.ts
var SHADOW_BRIDGE_CAPABILITIES = [
"copilot.open",
"workspace.open",
"buddy.create.open",
"buddy.inboxes.list",
"buddy.grant.ensure",
"oauth.authorize",
"launch.refresh",
"route.navigate",
"route.report",
"app.share.open"
];
function commandPath(basePath, commandName) {
return `${basePath.replace(/\/+$/u, "")}/${encodeURIComponent(commandName)}`;
}
function shadowServerAppMountedPathPrefix(windowRef) {
const win = windowRef ?? (typeof window === "undefined" ? null : window);
const pathname = win?.location?.pathname ?? "";
const segments = pathname.split("/").filter(Boolean);
const shadowIndex = segments.indexOf("shadow");
if (shadowIndex <= 0 || segments[shadowIndex + 1] !== "server") return "";
return `/${segments.slice(0, shadowIndex).join("/")}`;
}
function shadowServerAppMountedPath(path, windowRef) {
const normalized = path.startsWith("/") ? path : `/${path}`;
return `${shadowServerAppMountedPathPrefix(windowRef)}${normalized}`;
}
function isRecord(value) {
return !!value && typeof value === "object" && !Array.isArray(value);
}
function withoutUndefined(value) {
if (value === void 0) return {};
if (Array.isArray(value)) return value.map(withoutUndefined);
if (!isRecord(value)) return value;
return Object.fromEntries(
Object.entries(value).filter(([, entry]) => entry !== void 0).map(([key, entry]) => [key, withoutUndefined(entry)])
);
}
var ShadowServerAppBrowserClient = class {
bridge;
commandBasePath;
inboxesPath;
shadowApiBaseUrl;
fetchFn;
deliverLaunchOutboxFromBrowser;
win;
launchTokenValue;
launchEventStreamUrlValue;
launchExpiresInValue;
launchContextHandlers = /* @__PURE__ */ new Set();
unsubscribeLaunchUpdate;
constructor(options = {}) {
this.bridge = new ShadowBridge(options);
const windowRef = options.windowRef ?? (typeof window === "undefined" ? null : window);
this.commandBasePath = options.commandBasePath ?? shadowServerAppMountedPath("/api/runtime/commands", windowRef);
this.inboxesPath = options.inboxesPath ?? shadowServerAppMountedPath("/api/runtime/inboxes", windowRef);
this.shadowApiBaseUrl = options.shadowApiBaseUrl;
this.fetchFn = options.fetch;
this.deliverLaunchOutboxFromBrowser = options.deliverLaunchOutboxFromBrowser ?? false;
this.win = windowRef;
this.launchTokenValue = this.launchTokenFromLocation();
this.launchEventStreamUrlValue = this.launchEventStreamUrlFromLocation();
this.unsubscribeLaunchUpdate = this.bridge.onLaunchUpdate((context) => {
this.applyLaunchUpdate(context);
const snapshot = this.launchContext();
for (const handler of this.launchContextHandlers) {
void Promise.resolve(handler(snapshot)).catch(() => void 0);
}
});
}
bridgeAvailable() {
return this.bridge.isAvailable();
}
launchToken(param = "shadow_launch") {
return this.launchTokenValue ?? this.launchTokenFromLocation(param);
}
launchEventStreamUrl(param = "shadow_event_stream") {
return this.launchEventStreamUrlValue ?? this.launchEventStreamUrlFromLocation(param);
}
launchContext() {
return {
launchToken: this.launchToken(),
eventStreamUrl: this.launchEventStreamUrl(),
eventStreamPath: this.launchEventStreamUrl(),
...typeof this.launchExpiresInValue === "number" ? { expiresIn: this.launchExpiresInValue } : {}
};
}
onLaunchContextChange(handler) {
this.launchContextHandlers.add(handler);
return () => {
this.launchContextHandlers.delete(handler);
};
}
launchHeaders(headers = {}, options = {}) {
const token = this.launchToken(options.launchTokenParam);
return token ? { ...headers, "X-Shadow-Launch-Token": token } : headers;
}
async command(commandName, input = {}) {
const path = commandPath(this.commandBasePath, commandName);
const init = {
method: "POST",
headers: this.launchHeaders({ "Content-Type": "application/json" }),
body: JSON.stringify({ input: withoutUndefined(input) })
};
let response = await this.fetch(path, init);
if (response.status === 401 && await this.refreshLaunch({ reason: "command_unauthorized" })) {
response = await this.fetch(path, {
...init,
headers: this.launchHeaders({ "Content-Type": "application/json" })
});
}
const result = await readShadowServerAppCommandResponse(response);
return this.deliverLaunchOutbox(commandName, result);
}
async refreshLaunch(input = {}) {
if (!this.bridge.isAvailable()) return null;
try {
const context = await this.bridge.refreshLaunch(input);
if (context?.launchToken) {
this.applyLaunchUpdate({
launchToken: context.launchToken,
eventStreamUrl: context.eventStreamUrl,
eventStreamPath: context.eventStreamPath,
expiresIn: context.expiresIn
});
const snapshot = this.launchContext();
for (const handler of this.launchContextHandlers) {
void Promise.resolve(handler(snapshot)).catch(() => void 0);
}
}
return context;
} catch {
return null;
}
}
async fetchWithLaunch(input, init = {}, options = {}) {
if (options.refresh) {
const refreshInput = options.refresh === true ? { reason: "fetch" } : options.refresh;
await this.refreshLaunch(refreshInput);
} else if (!this.launchToken()) {
await this.refreshLaunch({ reason: "missing_launch" });
}
let response = await this.fetch(input, this.withLaunchHeaders(init));
if (response.status !== 401 || !await this.refreshLaunch({ reason: "fetch_unauthorized" })) {
return response;
}
return this.fetch(input, this.withLaunchHeaders(init));
}
async listBuddyInboxes(options = {}) {
if (this.bridge.isAvailable()) {
try {
return await this.bridge.listBuddyInboxes({ refresh: options.refresh });
} catch {
}
}
let response = await this.fetch(this.inboxesPath, { headers: this.launchHeaders() });
if (response.status === 401 && await this.refreshLaunch({ reason: "inboxes_unauthorized" })) {
response = await this.fetch(this.inboxesPath, { headers: this.launchHeaders() });
}
if (!response.ok) {
if (options.emptyOnError) return { inboxes: [] };
const message = await response.text().catch(() => "");
throw new Error(message || `Buddy inbox lookup failed (${response.status})`);
}
return await response.json();
}
async ensureBuddyTaskGrant(input) {
const buddyAgentId = input.agentId?.trim();
if (!buddyAgentId || !this.bridge.isAvailable()) return { granted: false, skipped: true };
return this.bridge.ensureBuddyGrant(
{
buddyAgentId,
permissions: input.permissions ?? [BUDDY_INBOX_DELIVERY_PERMISSION],
reason: input.reason
},
{ timeoutMs: input.timeoutMs ?? 6e4 }
);
}
openBuddyCreator(input = {}, options = {}) {
if (!this.bridge.isAvailable()) return Promise.resolve({ opened: false, agent: null });
return this.bridge.openBuddyCreator(input, options);
}
openCopilot(delivery, options = {}) {
return this.bridge.openCopilot(delivery, options);
}
openWorkspaceResource(input, options = {}) {
return this.bridge.openWorkspaceResource(input, options);
}
authorizeOAuth(input, options = {}) {
if (!this.bridge.isAvailable()) return Promise.resolve({ opened: false });
return this.bridge.authorizeOAuth(input, options);
}
routeChanged(path) {
return this.bridge.routeChanged(path);
}
onRouteNavigate(handler) {
return this.bridge.onRouteNavigate(handler);
}
shareApp(input = {}, options = {}) {
if (!this.bridge.isAvailable()) return Promise.resolve({ opened: false });
return this.bridge.shareApp(input, options);
}
inboxDeliveries(payload) {
return this.bridge.inboxDeliveries(payload);
}
inboxErrors(payload) {
return this.bridge.inboxErrors(payload);
}
channelMessageDeliveries(payload) {
return this.bridge.channelMessageDeliveries(payload);
}
channelMessageErrors(payload) {
return this.bridge.channelMessageErrors(payload);
}
async deliverLaunchOutbox(commandName, result) {
if (!this.deliverLaunchOutboxFromBrowser || !hasShadowServerAppPendingOutbox(result)) {
return result;
}
const launchToken = this.launchToken();
if (!decodeShadowServerAppLaunchTokenHint(launchToken)) return result;
return await deliverShadowServerAppLaunchOutbox({
commandName,
result,
launchToken,
shadowApiBaseUrl: this.shadowApiBaseUrl,
fetch: this.fetch.bind(this)
});
}
fetch(input, init) {
if (this.fetchFn) return this.fetchFn(input, init);
return globalThis.fetch(input, init);
}
dispose() {
this.unsubscribeLaunchUpdate();
this.launchContextHandlers.clear();
this.bridge.dispose();
}
launchTokenFromLocation(param = "shadow_launch") {
if (!this.win) return null;
return new URLSearchParams(this.win.location.search).get(param);
}
launchEventStreamUrlFromLocation(param = "shadow_event_stream") {
if (!this.win) return null;
return new URLSearchParams(this.win.location.search).get(param);
}
applyLaunchUpdate(context) {
this.launchTokenValue = context.launchToken;
this.launchEventStreamUrlValue = context.eventStreamUrl ?? context.eventStreamPath ?? null;
this.launchExpiresInValue = typeof context.expiresIn === "number" ? context.expiresIn : void 0;
}
withLaunchHeaders(init) {
const headers = new Headers(init.headers);
const token = this.launchToken();
if (token) headers.set("X-Shadow-Launch-Token", token);
return { ...init, headers };
}
};
function createShadowServerAppClient(options = {}) {
return new ShadowServerAppBrowserClient(options);
}
var createShadowServerAppBrowserClient = createShadowServerAppClient;
function createShadowServerAppRuntimeClient(options = {}) {
const windowRef = options.windowRef ?? (typeof window === "undefined" ? void 0 : window);
return new ShadowServerAppBrowserClient({
...options,
windowRef,
commandBasePath: options.commandBasePath ?? shadowServerAppMountedPath("/api/runtime/commands", windowRef),
inboxesPath: options.inboxesPath ?? shadowServerAppMountedPath("/api/runtime/inboxes", windowRef),
deliverLaunchOutboxFromBrowser: false
});
}
var ShadowBridge = class _ShadowBridge {
static capabilitiesRequestType = "shadow.app.capabilities.request";
static capabilitiesResponseType = "shadow.app.capabilities.response";
static openCopilotRequestType = "shadow.app.copilot.open.request";
static openCopilotResponseType = "shadow.app.copilot.open.response";
static openWorkspaceResourceRequestType = "shadow.app.workspace.open.request";
static openWorkspaceResourceResponseType = "shadow.app.workspace.open.response";
static openBuddyCreatorRequestType = "shadow.app.buddy.create.request";
static openBuddyCreatorResponseType = "shadow.app.buddy.create.response";
static listBuddyInboxesRequestType = "shadow.app.buddy.inboxes.request";
static listBuddyInboxesResponseType = "shadow.app.buddy.inboxes.response";
static ensureBuddyGrantRequestType = "shadow.app.buddy.grant.request";
static ensureBuddyGrantResponseType = "shadow.app.buddy.grant.response";
static authorizeOAuthRequestType = "shadow.app.oauth.authorize.request";
static authorizeOAuthResponseType = "shadow.app.oauth.authorize.response";
static launchUpdateType = "shadow.app.launch.update";
static routeNavigateType = "shadow.app.navigate";
static routeNavigateAckType = "shadow.app.navigate.ack";
static routeChangedType = "shadow.app.route.changed";
static shareAppRequestType = "shadow.app.share.request";
static shareAppResponseType = "shadow.app.share.response";
static refreshLaunchRequestType = "shadow.app.launch.refresh.request";
static refreshLaunchResponseType = "shadow.app.launch.refresh.response";
static launchUpdatedEventType = "shadow.app.launch.updated";
static inboxDeliveries(payload) {
return getShadowServerAppInboxDeliveries(payload);
}
static inboxErrors(payload) {
return getShadowServerAppInboxErrors(payload);
}
static channelMessageDeliveries(payload) {
return getShadowServerAppChannelMessageDeliveries(payload);
}
static channelMessageErrors(payload) {
return getShadowServerAppChannelMessageErrors(payload);
}
static unwrapCommandPayload(payload) {
return unwrapShadowServerAppCommandPayload(payload);
}
appKey;
targetOrigin;
timeoutMs;
win;
hasLaunchContext;
launchTokenValue = null;
pending = /* @__PURE__ */ new Map();
onMessage = (event) => {
let data = event.data;
if (typeof data === "string") {
try {
data = JSON.parse(data || "{}");
} catch {
return;
}
}
if (!data || typeof data !== "object") return;
const record = data;
if (record.type === _ShadowBridge.launchUpdatedEventType) {
this.applyLaunchContext(record.result ?? record.launch);
return;
}
if (typeof record.requestId !== "string" || typeof record.type !== "string") return;
const entry = this.pending.get(record.requestId);
if (!entry || record.type !== entry.responseType) return;
this.pending.delete(record.requestId);
if (record.ok) {
if (record.type === _ShadowBridge.refreshLaunchResponseType) {
this.applyLaunchContext(record.result);
}
entry.resolve(record.result);
} else
entry.reject(
new Error(typeof record.error === "string" ? record.error : "Bridge request failed")
);
};
constructor(options = {}) {
this.win = options.windowRef ?? (typeof window === "undefined" ? null : window);
this.appKey = options.appKey ?? this.resolveLaunchAppKey();
this.targetOrigin = options.targetOrigin ?? "*";
this.timeoutMs = options.timeoutMs ?? 6e4;
this.launchTokenValue = this.resolveLaunchToken();
this.hasLaunchContext = this.resolveLaunchContext();
this.win?.addEventListener("message", this.onMessage);
}
dispose() {
this.win?.removeEventListener("message", this.onMessage);
for (const entry of this.pending.values()) {
entry.reject(new Error("ShadowBridge disposed"));
}
this.pending.clear();
}
isAvailable() {
if (!this.win) return false;
return (this.hasLaunchContext || !!this.appKey) && (this.win.parent !== this.win || !!this.win.ReactNativeWebView);
}
launchToken(param = "shadow_launch") {
if (!this.win) return null;
if (param !== "shadow_launch") {
return new URLSearchParams(this.win.location.search).get(param);
}
return this.launchTokenValue ?? this.resolveLaunchToken();
}
launchHeaders(headers = {}, options = {}) {
const token = this.launchToken(options.launchTokenParam);
return token ? { ...headers, "X-Shadow-Launch-Token": token } : headers;
}
capabilities(options = {}) {
return this.request(
_ShadowBridge.capabilitiesRequestType,
_ShadowBridge.capabilitiesResponseType,
{},
options.timeoutMs ?? 15e3
);
}
openCopilot(deliveryOrInput, options = {}) {
const input = "delivery" in deliveryOrInput ? deliveryOrInput : { delivery: deliveryOrInput };
return this.request(
_ShadowBridge.openCopilotRequestType,
_ShadowBridge.openCopilotResponseType,
input,
options.timeoutMs ?? 15e3
);
}
openWorkspaceResource(input, options = {}) {
return this.request(
_ShadowBridge.openWorkspaceResourceRequestType,
_ShadowBridge.openWorkspaceResourceResponseType,
input,
options.timeoutMs ?? 15e3
);
}
openBuddyCreator(input = {}, options = {}) {
return this.request(
_ShadowBridge.openBuddyCreatorRequestType,
_ShadowBridge.openBuddyCreatorResponseType,
input,
options.timeoutMs ?? 10 * 60 * 1e3
);
}
listBuddyInboxes(input = {}, options = {}) {
return this.request(
_ShadowBridge.listBuddyInboxesRequestType,
_ShadowBridge.listBuddyInboxesResponseType,
input,
options.timeoutMs ?? 15e3
);
}
ensureBuddyGrant(input, options = {}) {
return this.request(
_ShadowBridge.ensureBuddyGrantRequestType,
_ShadowBridge.ensureBuddyGrantResponseType,
input,
options.timeoutMs ?? 3e4
);
}
authorizeOAuth(input, options = {}) {
const payload = typeof input === "string" ? { authorizeUrl: input } : input;
return this.request(
_ShadowBridge.authorizeOAuthRequestType,
_ShadowBridge.authorizeOAuthResponseType,
payload,
options.timeoutMs ?? 10 * 60 * 1e3
);
}
routeChanged(path) {
if (!this.isAvailable()) return false;
this.postMessage({
type: _ShadowBridge.routeChangedType,
...this.appKey ? { appKey: this.appKey } : {},
path
});
return true;
}
onRouteNavigate(handler) {
const win = this.win;
if (!win) return () => void 0;
const listener = (event) => {
let data = event.data;
if (typeof data === "string") {
try {
data = JSON.parse(data || "{}");
} catch {
return;
}
}
if (!data || typeof data !== "object") return;
const record = data;
if (record.type !== _ShadowBridge.routeNavigateType) return;
if (this.appKey && typeof record.appKey === "string" && record.appKey !== this.appKey) {
return;
}
if (typeof record.requestId !== "string" || typeof record.path !== "string") return;
const eventPayload = { path: record.path, requestId: record.requestId };
void Promise.resolve(handler(record.path, eventPayload)).catch(() => void 0).finally(() => {
this.postMessage({
type: _ShadowBridge.routeNavigateAckType,
requestId: record.requestId,
...this.appKey ? { appKey: this.appKey } : {}
});
});
};
win.addEventListener("message", listener);
return () => win.removeEventListener("message", listener);
}
onLaunchUpdate(handler) {
const win = this.win;
if (!win) return () => void 0;
const listener = (event) => {
let data = event.data;
if (typeof data === "string") {
try {
data = JSON.parse(data || "{}");
} catch {
return;
}
}
if (!data || typeof data !== "object") return;
const record = data;
if (record.type !== _ShadowBridge.launchUpdateType && record.type !== _ShadowBridge.launchUpdatedEventType) {
return;
}
if (this.appKey && typeof record.appKey === "string" && record.appKey !== this.appKey) {
return;
}
if (typeof record.launchToken !== "string" || !record.launchToken) return;
const eventStreamUrl = typeof record.eventStreamUrl === "string" && record.eventStreamUrl ? record.eventStreamUrl : typeof record.eventStreamPath === "string" && record.eventStreamPath ? record.eventStreamPath : null;
const eventStreamPath = typeof record.eventStreamPath === "string" && record.eventStreamPath ? record.eventStreamPath : typeof record.eventStreamUrl === "string" && record.eventStreamUrl ? record.eventStreamUrl : null;
void Promise.resolve(
handler({
launchToken: record.launchToken,
eventStreamUrl,
eventStreamPath,
...typeof record.expiresIn === "number" ? { expiresIn: record.expiresIn } : {}
})
).catch(() => void 0);
};
win.addEventListener("message", listener);
return () => win.removeEventListener("message", listener);
}
shareApp(input = {}, options = {}) {
return this.request(
_ShadowBridge.shareAppRequestType,
_ShadowBridge.shareAppResponseType,
input,
options.timeoutMs ?? 5 * 60 * 1e3
);
}
refreshLaunch(input = {}, options = {}) {
return this.request(
_ShadowBridge.refreshLaunchRequestType,
_ShadowBridge.refreshLaunchResponseType,
input,
options.timeoutMs ?? 15e3
);
}
unwrapCommandPayload(payload) {
return unwrapShadowServerAppCommandPayload(payload);
}
inboxDeliveries(payload) {
return getShadowServerAppInboxDeliveries(payload);
}
inboxErrors(payload) {
return getShadowServerAppInboxErrors(payload);
}
channelMessageDeliveries(payload) {
return getShadowServerAppChannelMessageDeliveries(payload);
}
channelMessageErrors(payload) {
return getShadowServerAppChannelMessageErrors(payload);
}
request(requestType, responseType, payload, timeoutMs = this.timeoutMs) {
if (!this.isAvailable()) {
return Promise.reject(
new Error("ShadowBridge is not available outside a Shadow launch frame")
);
}
const requestId = `req_${Math.random().toString(36).slice(2)}`;
return new Promise((resolve, reject) => {
this.pending.set(requestId, {
responseType,
resolve,
reject
});
this.postMessage({
type: requestType,
requestId,
...this.appKey ? { appKey: this.appKey } : {},
...payload
});
this.win?.setTimeout(() => {
if (!this.pending.has(requestId)) return;
this.pending.delete(requestId);
reject(new Error("Bridge request timed out"));
}, timeoutMs);
});
}
postMessage(message) {
if (!this.win) return;
if (this.win.ReactNativeWebView) {
this.win.ReactNativeWebView.postMessage(JSON.stringify(message));
return;
}
this.win.parent.postMessage(message, this.targetOrigin);
}
launchContextStorageKey() {
return this.appKey ? `shadow.bridge.launch:${this.appKey}` : null;
}
launchTokenStorageKey() {
return this.appKey ? `shadow.bridge.launch-token:${this.appKey}` : null;
}
rememberLaunchToken(token) {
if (!token) return;
const hint = decodeShadowServerAppLaunchTokenHint(token);
if (!this.appKey && hint?.appKey) this.appKey = hint.appKey;
this.launchTokenValue = token;
this.hasLaunchContext = true;
if (this.appKey) {
const memoryContexts = this.win.__shadowBridgeLaunchContexts ??= {};
const memoryTokens = this.win.__shadowBridgeLaunchTokens ??= {};
memoryContexts[this.appKey] = true;
memoryTokens[this.appKey] = token;
}
try {
const contextKey = this.launchContextStorageKey();
const tokenKey = this.launchTokenStorageKey();
if (contextKey) this.win?.sessionStorage?.setItem(contextKey, "1");
if (tokenKey) this.win?.sessionStorage?.setItem(tokenKey, token);
} catch {
}
}
resolveLaunchToken() {
if (!this.win) return null;
const urlToken = new URLSearchParams(this.win.location.search).get("shadow_launch");
if (urlToken) {
this.rememberLaunchToken(urlToken);
return urlToken;
}
const memoryToken = this.appKey ? this.win.__shadowBridgeLaunchTokens?.[this.appKey] : null;
if (memoryToken) return memoryToken;
try {
const tokenKey = this.launchTokenStorageKey();
return tokenKey ? this.win.sessionStorage?.getItem(tokenKey) ?? null : null;
} catch {
return null;
}
}
applyLaunchContext(value) {
if (!isRecord(value) || typeof value.launchToken !== "string") return false;
this.rememberLaunchToken(value.launchToken);
return true;
}
resolveLaunchContext() {
if (!this.win) return false;
const token = this.launchTokenValue ?? this.resolveLaunchToken();
if (token) {
this.rememberLaunchToken(token);
return true;
}
const storageKey = this.launchContextStorageKey();
const memoryContexts = this.win.__shadowBridgeLaunchContexts ??= {};
const hasLaunchToken = new URLSearchParams(this.win.location.search).has("shadow_launch");
if (hasLaunchToken) {
if (this.appKey) memoryContexts[this.appKey] = true;
try {
if (storageKey) this.win.sessionStorage?.setItem(storageKey, "1");
} catch {
}
return true;
}
if (this.appKey && memoryContexts[this.appKey]) return true;
try {
return storageKey ? this.win.sessionStorage?.getItem(storageKey) === "1" : false;
} catch {
return false;
}
}
resolveLaunchAppKey() {
if (!this.win) return void 0;
const token = new URLSearchParams(this.win.location.search).get("shadow_launch");
return decodeShadowServerAppLaunchTokenHint(token)?.appKey;
}
};
export {
SHADOW_BRIDGE_CAPABILITIES,
shadowServerAppMountedPathPrefix,
shadowServerAppMountedPath,
ShadowServerAppBrowserClient,
createShadowServerAppClient,
createShadowServerAppBrowserClient,
createShadowServerAppRuntimeClient,
ShadowBridge
};
// src/server-app.ts
import {
BUDDY_INBOX_DELIVERY_PERMISSION
} from "@shadowob/shared";
var SHADOW_SERVER_APP_PROTOCOL = "shadow.app/1";
var SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT = "server_app.command.completed";
var SHADOW_SERVER_APP_COMMAND_FAILED_EVENT = "server_app.command.failed";
var SHADOW_SERVER_APP_COMMAND_EVENTS = [
SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT,
SHADOW_SERVER_APP_COMMAND_FAILED_EVENT
];
var ShadowServerAppHttpError = class extends Error {
status;
payload;
constructor(status, message, payload) {
super(message);
this.name = "ShadowServerAppHttpError";
this.status = status;
this.payload = payload;
}
};
function isProtocolRecord(value) {
return !!value && typeof value === "object" && !Array.isArray(value);
}
function optionalProtocolString(value) {
return typeof value === "string" && value ? value : void 0;
}
function protocolPathSegment(value) {
return encodeURIComponent(value);
}
function shadowServerAppInboxTaskEndpoint(serverIdOrSlug, target) {
if ("channelId" in target && target.channelId) {
return `/api/channels/${protocolPathSegment(target.channelId)}/inbox/tasks`;
}
if ("agentId" in target && target.agentId) {
return `/api/servers/${protocolPathSegment(serverIdOrSlug)}/inboxes/${protocolPathSegment(
target.agentId
)}/tasks`;
}
throw new Error("Missing Inbox task target");
}
function buildShadowServerAppInboxTaskRequest(input) {
const appId = input.app.id ?? input.app.appId ?? input.app.appKey;
const serverAppData = isProtocolRecord(input.task.data?.serverApp) ? input.task.data.serverApp : {};
return {
endpoint: shadowServerAppInboxTaskEndpoint(input.serverIdOrSlug, input.target),
body: {
title: input.task.title,
body: input.task.body,
priority: input.task.priority,
tags: input.task.tags,
idempotencyKey: input.task.idempotencyKey,
requirements: input.task.requirements,
outputContract: input.task.outputContract,
privacy: input.task.privacy,
app: {
id: appId,
appId,
appKey: input.app.appKey,
name: input.app.name ?? input.app.label ?? input.app.appKey,
label: input.app.label ?? input.app.name ?? input.app.appKey,
...input.app.iconUrl ? { iconUrl: input.app.iconUrl } : {}
},
source: {
kind: "server_app",
id: appId,
appId,
appKey: input.app.appKey,
...input.app.name ? { appName: input.app.name } : {},
...input.app.iconUrl ? { iconUrl: input.app.iconUrl } : {},
...input.app.serverId ? { serverId: input.app.serverId } : {},
...input.commandName ? { command: input.commandName } : {},
label: input.app.label ?? input.app.name ?? input.app.appKey,
...input.task.resource ? { resource: input.task.resource } : {}
},
data: {
...input.task.data ?? {},
serverApp: {
...serverAppData,
appKey: input.app.appKey,
name: input.app.name ?? input.app.label ?? input.app.appKey,
label: input.app.label ?? input.app.name ?? input.app.appKey,
...input.app.iconUrl ? { iconUrl: input.app.iconUrl } : {},
...input.commandName ? { command: input.commandName } : {}
}
}
}
};
}
function getShadowServerAppTaskCardId(message) {
const metadata = isProtocolRecord(message) ? message.metadata : null;
const cards = isProtocolRecord(metadata) && Array.isArray(metadata.cards) ? metadata.cards : [];
for (const item of cards) {
if (!isProtocolRecord(item)) continue;
if (item.kind === "task" && typeof item.id === "string" && item.id) return item.id;
}
return null;
}
function buildShadowServerAppInboxDelivery(input) {
const message = isProtocolRecord(input.message) ? input.message : {};
return {
..."agentId" in input.target && input.target.agentId ? { agentId: input.target.agentId } : {},
channelId: optionalProtocolString(message.channelId),
messageId: optionalProtocolString(message.id),
cardId: getShadowServerAppTaskCardId(message),
idempotencyKey: input.idempotencyKey
};
}
function shadowFromPayload(payload) {
if (payload.protocol === SHADOW_SERVER_APP_PROTOCOL) {
return payload;
}
const shadow = isProtocolRecord(payload.shadow) ? payload.shadow : null;
if (shadow?.protocol === SHADOW_SERVER_APP_PROTOCOL) {
return shadow;
}
return null;
}
function mergeShadowResult(value, shadow) {
if (!shadow) return value;
const existing = shadowFromPayload(value);
if (!existing) return { ...value, shadow };
return {
...value,
shadow: {
protocol: SHADOW_SERVER_APP_PROTOCOL,
outbox: {
...existing.outbox ?? {},
...shadow.outbox ?? {}
}
}
};
}
function getShadowServerAppInboxDeliveries(payload) {
if (!isProtocolRecord(payload)) return [];
const shadow = shadowFromPayload(payload);
return shadow?.outbox?.deliveries ?? [];
}
function getShadowServerAppInboxErrors(payload) {
if (!isProtocolRecord(payload)) return [];
const shadow = shadowFromPayload(payload);
return shadow?.outbox?.errors ?? [];
}
function getShadowServerAppPendingInboxTasks(payload, depth = 0) {
if (!isProtocolRecord(payload) || depth > 4) return [];
const shadow = shadowFromPayload(payload);
const tasks = shadow?.outbox?.inboxTasks ?? [];
return "result" in payload && payload.result !== void 0 ? [...tasks, ...getShadowServerAppPendingInboxTasks(payload.result, depth + 1)] : tasks;
}
function getShadowServerAppChannelMessageDeliveries(payload) {
if (!isProtocolRecord(payload)) return [];
const shadow = shadowFromPayload(payload);
return shadow?.outbox?.channelMessageDeliveries ?? [];
}
function getShadowServerAppChannelMessageErrors(payload) {
if (!isProtocolRecord(payload)) return [];
const shadow = shadowFromPayload(payload);
return shadow?.outbox?.channelMessageErrors ?? [];
}
function getShadowServerAppPendingChannelMessages(payload, depth = 0) {
if (!isProtocolRecord(payload) || depth > 4) return [];
const shadow = shadowFromPayload(payload);
const messages = shadow?.outbox?.channelMessages ?? [];
return "result" in payload && payload.result !== void 0 ? [...messages, ...getShadowServerAppPendingChannelMessages(payload.result, depth + 1)] : messages;
}
function hasShadowServerAppPendingOutbox(payload) {
return getShadowServerAppPendingInboxTasks(payload).length > 0 || getShadowServerAppPendingChannelMessages(payload).length > 0;
}
function isDomainResultWithEvents(payload) {
return Array.isArray(payload.events) && ("cursor" in payload || "result" in payload);
}
function isCommandPayloadEnvelope(payload) {
if (isDomainResultWithEvents(payload)) return false;
return payload.ok === true || payload.ok === false || shadowFromPayload(payload) !== null;
}
function unwrapShadowServerAppCommandPayload(payload) {
if (isProtocolRecord(payload) && payload.ok === false) {
throw new Error(typeof payload.error === "string" ? payload.error : "Command failed");
}
if (isProtocolRecord(payload) && "result" in payload && payload.result !== void 0 && isCommandPayloadEnvelope(payload)) {
const nested = unwrapShadowServerAppCommandPayload(payload.result);
const shadow = shadowFromPayload(payload);
if (isProtocolRecord(nested)) return mergeShadowResult(nested, shadow);
return nested;
}
return payload;
}
async function readShadowServerAppResponsePayload(response) {
const text = await response.text().catch(() => "");
if (!text.trim()) return null;
try {
return JSON.parse(text);
} catch {
if (!response.ok) return { ok: false, error: text };
throw new ShadowServerAppHttpError(response.status, "Command returned invalid JSON", text);
}
}
function shadowServerAppResponseErrorMessage(status, payload, fallback = "Command failed") {
if (isProtocolRecord(payload) && typeof payload.error === "string" && payload.error) {
return payload.error;
}
if (typeof payload === "string" && payload.trim()) return payload;
return status ? `${fallback} (${status})` : fallback;
}
async function readShadowServerAppCommandResponse(response) {
const payload = await readShadowServerAppResponsePayload(response);
if (!response.ok || isProtocolRecord(payload) && payload.ok === false) {
throw new ShadowServerAppHttpError(
response.status,
shadowServerAppResponseErrorMessage(response.status, payload),
payload
);
}
return unwrapShadowServerAppCommandPayload(payload);
}
var ShadowServerAppOutbox = class {
inboxTasks = [];
channelMessages = [];
enqueueInboxTask(task) {
this.inboxTasks.push(task);
return this;
}
enqueueInboxTasks(tasks) {
for (const task of tasks) this.enqueueInboxTask(task);
return this;
}
sendChannelMessage(message) {
this.channelMessages.push(message);
return this;
}
sendChannelMessages(messages) {
for (const message of messages) this.sendChannelMessage(message);
return this;
}
toShadow() {
return {
protocol: SHADOW_SERVER_APP_PROTOCOL,
outbox: {
...this.inboxTasks.length > 0 ? { inboxTasks: [...this.inboxTasks] } : {},
...this.channelMessages.length > 0 ? { channelMessages: [...this.channelMessages] } : {}
}
};
}
attachTo(result) {
return { ...result, shadow: this.toShadow() };
}
};
function trimTrailingSlash(value) {
return value.replace(/\/$/, "");
}
function joinBasePath(baseUrl, path) {
const cleanBase = trimTrailingSlash(baseUrl);
const cleanPath = path.startsWith("/") ? path : `/${path}`;
return `${cleanBase}${cleanPath}`;
}
function urlOrigin(value) {
try {
return new URL(value).origin;
} catch {
return null;
}
}
function rebasePublicAssetUrl(value, sourceOrigin, publicBaseUrl) {
if (!sourceOrigin) return value;
try {
const url = new URL(value);
if (url.origin !== sourceOrigin) return value;
return joinBasePath(publicBaseUrl, `${url.pathname}${url.search}${url.hash}`);
} catch {
return value;
}
}
function extractShadowServerAppBearerToken(value) {
if (!value) return null;
return value.toLowerCase().startsWith("bearer ") ? value.slice(7).trim() : null;
}
function decodeBase64UrlJson(value) {
try {
const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, "=");
const binary = globalThis.atob(padded);
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
return JSON.parse(new TextDecoder().decode(bytes));
} catch {
return null;
}
}
function decodeShadowServerAppLaunchTokenHint(token) {
if (!token) return null;
const parts = token.split(".");
if (parts.length !== 3 || parts[0] !== "sat_v1") return null;
const payload = decodeBase64UrlJson(parts[1]);
if (typeof payload?.serverId !== "string" || typeof payload.appKey !== "string") return null;
return { serverId: payload.serverId, appKey: payload.appKey };
}
async function fetchShadowServerAppLaunchInboxes(options) {
const hint = decodeShadowServerAppLaunchTokenHint(options.launchToken);
if (!hint || !options.launchToken) return { inboxes: [] };
const fetchFn = options.fetch ?? fetch;
const baseUrl = trimTrailingSlash(options.shadowApiBaseUrl ?? "http://localhost:3002");
const response = await fetchFn(
`${baseUrl}/api/servers/${encodeURIComponent(hint.serverId)}/apps/${encodeURIComponent(
hint.appKey
)}/launch/inboxes`,
{ headers: { Authorization: `Bearer ${options.launchToken}` } }
);
if (!response.ok) {
const message = await response.text().catch(() => "");
throw new Error(`Shadow launch inbox lookup failed (${response.status}): ${message}`);
}
return await response.json();
}
async function introspectShadowServerAppLaunchToken(options) {
const hint = decodeShadowServerAppLaunchTokenHint(options.launchToken);
if (!hint || !options.launchToken) return null;
const fetchFn = options.fetch ?? fetch;
const baseUrl = trimTrailingSlash(options.shadowApiBaseUrl ?? "http://localhost:3002");
const response = await fetchFn(
`${baseUrl}/api/servers/${encodeURIComponent(hint.serverId)}/apps/${encodeURIComponent(
hint.appKey
)}/launch/introspect`,
{
method: "POST",
headers: { Authorization: `Bearer ${options.launchToken}` }
}
);
if (!response.ok) return null;
const payload = await response.json().catch(() => null);
return payload?.active ? payload : null;
}
async function resolveShadowServerAppLaunchCommandContext(options) {
const introspection = await introspectShadowServerAppLaunchToken(options);
const shadow = introspection?.shadow;
if (!shadow) return null;
const command = options.manifest.commands.find((item) => item.name === options.commandName);
return {
protocol: SHADOW_SERVER_APP_PROTOCOL,
serverId: shadow.serverId,
serverAppId: shadow.serverAppId ?? "launch",
appKey: shadow.appKey || options.manifest.appKey,
command: options.commandName,
actor: shadow.actor,
channelId: shadow.channelId ?? null,
resources: shadow.resources ?? null,
task: shadow.task,
permission: command?.permission ?? shadow.permission ?? "server_app.runtime",
action: command?.action ?? shadow.action ?? "read",
dataClass: command?.dataClass ?? shadow.dataClass ?? "server-private"
};
}
async function deliverShadowServerAppLaunchOutbox(options) {
const hint = decodeShadowServerAppLaunchTokenHint(options.launchToken);
if (!hint || !options.launchToken) return options.result;
const fetchFn = options.fetch ?? fetch;
const baseUrl = trimTrailingSlash(options.shadowApiBaseUrl ?? "http://localhost:3002");
const response = await fetchFn(
`${baseUrl}/api/servers/${encodeURIComponent(hint.serverId)}/apps/${encodeURIComponent(
hint.appKey
)}/launch/outbox`,
{
method: "POST",
headers: {
Authorization: `Bearer ${options.launchToken}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
commandName: options.commandName,
result: options.result
})
}
);
if (!response.ok) {
const payload = await readShadowServerAppResponsePayload(response);
throw new ShadowServerAppHttpError(
response.status,
shadowServerAppResponseErrorMessage(
response.status,
payload,
"Shadow launch outbox delivery failed"
),
payload
);
}
return readShadowServerAppResponsePayload(response);
}
function normalizeShadowServerAppCommandInput(value) {
if (value && typeof value === "object" && !Array.isArray(value) && "input" in value && Object.keys(value).every((key) => key === "input" || key === "channelId")) {
return value.input ?? {};
}
return value;
}
function createShadowServerAppManifest(manifest, options = {}) {
const publicBaseUrl = trimTrailingSlash(
options.publicBaseUrl ?? `http://localhost:${options.port ?? 4201}`
);
const apiBaseUrl = trimTrailingSlash(options.apiBaseUrl ?? publicBaseUrl);
const iframeAllowedOrigins = (options.allowedOrigins ?? [publicBaseUrl]).map(
(origin) => urlOrigin(origin)
);
const iframePath = options.iframePath ?? "/shadow/server";
const iconPath = options.iconPath ?? "/assets/icon.svg";
const sourceAssetOrigin = urlOrigin(manifest.iconUrl);
return {
...manifest,
iconUrl: joinBasePath(publicBaseUrl, iconPath),
marketplace: manifest.marketplace ? {
...manifest.marketplace,
coverImageUrl: manifest.marketplace.coverImageUrl ? rebasePublicAssetUrl(
manifest.marketplace.coverImageUrl,
sourceAssetOrigin,
publicBaseUrl
) : manifest.marketplace.coverImageUrl,
gallery: manifest.marketplace.gallery?.map((item) => ({
...item,
url: rebasePublicAssetUrl(item.url, sourceAssetOrigin, publicBaseUrl)
})),
links: manifest.marketplace.links?.map((item) => ({
...item,
url: rebasePublicAssetUrl(item.url, sourceAssetOrigin, publicBaseUrl)
}))
} : manifest.marketplace,
iframe: manifest.iframe ? {
...manifest.iframe,
entry: joinBasePath(publicBaseUrl, iframePath),
allowedOrigins: iframeAllowedOrigins
} : manifest.iframe,
api: {
...manifest.api,
baseUrl: apiBaseUrl
}
};
}
function defineShadowServerApp(manifest, options = {}) {
return new ShadowServerAppRuntime(manifest, options);
}
var createShadowServerAppRuntime = defineShadowServerApp;
var ShadowServerAppCommandError = class extends Error {
status;
issues;
constructor(status, error, issues) {
super(error);
this.name = "ShadowServerAppCommandError";
this.status = status;
this.issues = issues;
}
};
function shadowServerAppError(status, error, issues) {
return new ShadowServerAppCommandError(status, error, issues);
}
var ShadowServerAppRuntime = class {
constructor(sourceManifest, options = {}) {
this.sourceManifest = sourceManifest;
this.options = options;
}
sourceManifest;
options;
manifest(options = {}) {
return createShadowServerAppManifest(this.sourceManifest, options);
}
defineCommands(handlers) {
return handlers;
}
actor(envelopeOrContext) {
return shadowServerAppActorRef(envelopeOrContext);
}
error(status, error, issues) {
return shadowServerAppError(status, error, issues);
}
async parseCommand(commandName, request) {
return parseShadowServerAppCommandRequest(
{
...request,
expectedCommand: commandName,
shadowBaseUrl: this.options.shadowBaseUrl,
fetchImpl: this.options.fetchImpl
}
);
}
async executeCommand(commandName, request, handlers) {
const parsed = await this.parseCommand(commandName, request);
if (!parsed.ok) return parseErrorResult(parsed);
return this.executeEnvelope(commandName, parsed.envelope, handlers);
}
async executeLocal(commandName, input, context, handlers) {
return this.executeEnvelope(
commandName,
{
input,
context: {
...context,
command: commandName
}
},
handlers
);
}
async executeEnvelope(commandName, envelope, handlers) {
const command = this.sourceManifest.commands.find((item) => item.name === commandName);
if (!command) return failureResult(404, "command_not_found");
const validation = validateShadowServerAppJsonSchema(command.inputSchema, envelope.input);
if (!validation.ok) return failureResult(422, "invalid_input", validation.issues);
const handler = handlers[commandName];
if (!handler) return failureResult(404, "command_not_found");
try {
const result = await handler(envelope.input, {
context: envelope.context,
actor: this.actor(envelope)
});
return { ok: true, status: 200, body: { ok: true, result } };
} catch (error) {
if (error instanceof ShadowServerAppCommandError) {
return failureResult(error.status, error.message, error.issues);
}
throw error;
}
}
};
async function introspectShadowServerAppToken(input) {
const baseUrl = trimTrailingSlash(input.shadowBaseUrl ?? "http://localhost:3002");
const fetchImpl = input.fetchImpl ?? fetch;
const response = await fetchImpl(
`${baseUrl}/api/servers/${encodeURIComponent(input.serverId)}/apps/${encodeURIComponent(
input.appKey
)}/oauth/introspect`,
{
method: "POST",
headers: {
Authorization: `Bearer ${input.token}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ token: input.token })
}
);
if (!response.ok) return null;
const payload = await response.json();
return payload.active ? payload : null;
}
async function parseShadowServerAppCommandRequest(input) {
const token = extractShadowServerAppBearerToken(input.authorizationHeader);
const serverId = input.serverIdHeader;
const appKey = input.appKeyHeader;
if (!token || !serverId || !appKey) {
return { ok: false, status: 401, error: "missing_oauth" };
}
const introspection = await introspectShadowServerAppToken({
token,
serverId,
appKey,
shadowBaseUrl: input.shadowBaseUrl,
fetchImpl: input.fetchImpl
}).catch(() => null);
const context = introspection?.shadow;
if (!context) return { ok: false, status: 401, error: "invalid_token" };
if (context.command !== input.expectedCommand) {
return { ok: false, status: 403, error: "wrong_command" };
}
let commandInput;
if (input.requestInput !== void 0) {
commandInput = input.requestInput;
} else {
let body;
try {
body = JSON.parse(input.requestBody ?? "{}");
} catch {
return { ok: false, status: 400, error: "invalid_json" };
}
commandInput = body.input ?? {};
}
return {
ok: true,
envelope: {
input: commandInput,
context
}
};
}
function validateShadowServerAppJsonSchema(schema, value) {
if (!schema) return { ok: true };
const issues = [];
validateJsonSchemaValue(schema, value, "", issues);
return issues.length ? { ok: false, issues } : { ok: true };
}
function shadowServerAppActorDisplayName(envelopeOrContext) {
const context = "context" in envelopeOrContext ? envelopeOrContext.context : envelopeOrContext;
const actor = context.actor;
const profile = actor.profile;
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`;
}
function shadowServerAppActorAvatarUrl(envelopeOrContext) {
const context = "context" in envelopeOrContext ? envelopeOrContext.context : envelopeOrContext;
return context.actor.profile?.avatarUrl ?? null;
}
function shadowServerAppActorRef(envelopeOrContext) {
const context = "context" in envelopeOrContext ? envelopeOrContext.context : envelopeOrContext;
const actor = context.actor;
return {
kind: actor.kind,
id: actor.buddyAgentId ?? actor.userId ?? actor.ownerId ?? "unknown",
userId: actor.userId ?? null,
buddyAgentId: actor.buddyAgentId ?? null,
ownerId: actor.ownerId ?? null,
displayName: shadowServerAppActorDisplayName(context),
avatarUrl: shadowServerAppActorAvatarUrl(context)
};
}
function isShadowServerAppActorRef(value) {
return !!value && typeof value === "object" && !Array.isArray(value) && typeof value.kind === "string" && typeof value.id === "string" && typeof value.displayName === "string";
}
function shadowServerAppIdentitySubjectKind(actor) {
if (actor.kind === "system") return "system";
if (actor.kind === "local") return "local";
if (actor.buddyAgentId) return "buddy";
if (actor.kind === "agent") return "agent";
if (actor.userId) return "user";
return "unknown";
}
function shadowServerAppIdentityKey(actorOrIdentity) {
const actor = isShadowServerAppActorRef(actorOrIdentity) ? actorOrIdentity : shadowServerAppActorRef(actorOrIdentity);
const subjectKind = shadowServerAppIdentitySubjectKind(actor);
if (subjectKind === "buddy" && actor.buddyAgentId) return `buddy:${actor.buddyAgentId}`;
if (actor.userId) return `user:${actor.userId}`;
if (actor.ownerId) return `owner:${actor.ownerId}`;
return `${subjectKind}:${actor.id || "unknown"}`;
}
function shadowServerAppIdentitySnapshot(actorOrContext) {
const actor = isShadowServerAppActorRef(actorOrContext) ? actorOrContext : shadowServerAppActorRef(actorOrContext);
return {
...actor,
subjectKind: shadowServerAppIdentitySubjectKind(actor),
stableKey: shadowServerAppIdentityKey(actor)
};
}
var shadowServerAppDisplayIdentity = shadowServerAppIdentitySnapshot;
function normalizeShadowServerAppClientMutationId(value) {
if (typeof value !== "string") return null;
const clean = value.trim();
if (!clean) return null;
return clean.slice(0, 160);
}
function normalizeShadowServerAppBaseCursor(value) {
if (typeof value !== "string") return null;
const clean = value.trim();
if (!clean) return null;
return clean.slice(0, 240);
}
function createShadowServerAppCollaborationResource(context, resource) {
return {
appKey: resource.appKey ?? context.appKey,
serverId: resource.serverId ?? context.serverId,
kind: resource.kind,
id: resource.id,
...resource.label !== void 0 ? { label: resource.label } : {},
...resource.projectId !== void 0 ? { projectId: resource.projectId } : {},
...resource.boardId !== void 0 ? { boardId: resource.boardId } : {}
};
}
function createShadowServerAppCollaborationCursor(input) {
const sequence = input.sequence ?? Date.now();
const occurredAt = input.occurredAt ?? (/* @__PURE__ */ new Date()).toISOString();
return `${input.resource.kind}:${input.resource.id}:${sequence}:${occurredAt}`;
}
function createShadowServerAppCollaborationEvent(input) {
const occurredAt = input.occurredAt ?? (/* @__PURE__ */ new Date()).toISOString();
const cursor = input.cursor ?? createShadowServerAppCollaborationCursor({
resource: input.resource,
occurredAt
});
return {
protocol: SHADOW_SERVER_APP_PROTOCOL,
type: input.type,
cursor,
occurredAt,
resource: input.resource,
actor: shadowServerAppIdentitySnapshot(input.actor),
payload: input.payload,
clientMutationId: normalizeShadowServerAppClientMutationId(input.clientMutationId),
baseCursor: normalizeShadowServerAppBaseCursor(input.baseCursor)
};
}
function parseErrorResult(error) {
return failureResult(error.status, error.error, error.issues);
}
function failureResult(status, error, issues) {
return {
ok: false,
status,
body: issues === void 0 ? { ok: false, error } : { ok: false, error, issues }
};
}
function validateJsonSchemaValue(schema, value, path, issues) {
if (Array.isArray(schema.oneOf)) {
const matches = schema.oneOf.some((option) => {
const nestedIssues = [];
if (option && typeof option === "object" && !Array.isArray(option)) {
validateJsonSchemaValue(
option,
value,
path,
nestedIssues
);
}
return nestedIssues.length === 0;
});
if (!matches) issues.push({ path, message: "Expected value matching one schema option" });
return;
}
const enumValues = schema.enum;
if (Array.isArray(enumValues) && !enumValues.includes(value)) {
issues.push({ path, message: `Expected one of ${enumValues.map(String).join(", ")}` });
return;
}
const type = schema.type;
if (type === "object") {
if (!value || typeof value !== "object" || Array.isArray(value)) {
issues.push({ path, message: "Expected object" });
return;
}
const record = value;
const properties = schema.properties && typeof schema.properties === "object" && !Array.isArray(schema.properties) ? schema.properties : {};
const required = Array.isArray(schema.required) ? schema.required.map(String) : [];
for (const key of required) {
if (!(key in record)) issues.push({ path: joinJsonPath(path, key), message: "Required" });
}
for (const [key, propertySchema] of Object.entries(properties)) {
if (record[key] !== void 0) {
validateJsonSchemaValue(propertySchema, record[key], joinJsonPath(path, key), issues);
}
}
const additionalProperties = schema.additionalProperties && typeof schema.additionalProperties === "object" && !Array.isArray(schema.additionalProperties) ? schema.additionalProperties : null;
if (additionalProperties) {
for (const [key, nestedValue] of Object.entries(record)) {
if (!(key in properties)) {
validateJsonSchemaValue(
additionalProperties,
nestedValue,
joinJsonPath(path, key),
issues
);
}
}
} else if (schema.additionalProperties === false) {
for (const key of Object.keys(record)) {
if (!(key in properties)) {
issues.push({ path: joinJsonPath(path, key), message: "Unknown property" });
}
}
}
return;
}
if (type === "array") {
if (!Array.isArray(value)) {
issues.push({ path, message: "Expected array" });
return;
}
const maxItems = typeof schema.maxItems === "number" ? schema.maxItems : null;
if (maxItems !== null && value.length > maxItems) {
issues.push({ path, message: `Expected at most ${maxItems} items` });
}
const itemSchema = schema.items && typeof schema.items === "object" && !Array.isArray(schema.items) ? schema.items : null;
if (itemSchema) {
value.forEach(
(item, index) => validateJsonSchemaValue(itemSchema, item, `${path}[${index}]`, issues)
);
}
return;
}
if (type === "string") {
if (typeof value !== "string") {
issues.push({ path, message: "Expected string" });
return;
}
const maxLength = typeof schema.maxLength === "number" ? schema.maxLength : null;
const minLength = typeof schema.minLength === "number" ? schema.minLength : null;
if (minLength !== null && value.length < minLength) {
issues.push({ path, message: `Expected at least ${minLength} characters` });
}
if (maxLength !== null && value.length > maxLength) {
issues.push({ path, message: `Expected at most ${maxLength} characters` });
}
return;
}
if (type === "number" || type === "integer") {
if (typeof value !== "number" || !Number.isFinite(value)) {
issues.push({ path, message: "Expected number" });
return;
}
if (type === "integer" && !Number.isInteger(value)) {
issues.push({ path, message: "Expected integer" });
}
return;
}
if (type === "boolean" && typeof value !== "boolean") {
issues.push({ path, message: "Expected boolean" });
}
}
function joinJsonPath(parent, key) {
return parent ? `${parent}.${key}` : key;
}
export {
SHADOW_SERVER_APP_PROTOCOL,
SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT,
SHADOW_SERVER_APP_COMMAND_FAILED_EVENT,
SHADOW_SERVER_APP_COMMAND_EVENTS,
ShadowServerAppHttpError,
shadowServerAppInboxTaskEndpoint,
buildShadowServerAppInboxTaskRequest,
getShadowServerAppTaskCardId,
buildShadowServerAppInboxDelivery,
getShadowServerAppInboxDeliveries,
getShadowServerAppInboxErrors,
getShadowServerAppPendingInboxTasks,
getShadowServerAppChannelMessageDeliveries,
getShadowServerAppChannelMessageErrors,
getShadowServerAppPendingChannelMessages,
hasShadowServerAppPendingOutbox,
unwrapShadowServerAppCommandPayload,
readShadowServerAppCommandResponse,
ShadowServerAppOutbox,
extractShadowServerAppBearerToken,
decodeShadowServerAppLaunchTokenHint,
fetchShadowServerAppLaunchInboxes,
introspectShadowServerAppLaunchToken,
resolveShadowServerAppLaunchCommandContext,
deliverShadowServerAppLaunchOutbox,
normalizeShadowServerAppCommandInput,
createShadowServerAppManifest,
defineShadowServerApp,
createShadowServerAppRuntime,
ShadowServerAppCommandError,
shadowServerAppError,
ShadowServerAppRuntime,
introspectShadowServerAppToken,
parseShadowServerAppCommandRequest,
validateShadowServerAppJsonSchema,
shadowServerAppActorDisplayName,
shadowServerAppActorAvatarUrl,
shadowServerAppActorRef,
shadowServerAppIdentityKey,
shadowServerAppIdentitySnapshot,
shadowServerAppDisplayIdentity,
normalizeShadowServerAppClientMutationId,
normalizeShadowServerAppBaseCursor,
createShadowServerAppCollaborationResource,
createShadowServerAppCollaborationCursor,
createShadowServerAppCollaborationEvent,
BUDDY_INBOX_DELIVERY_PERMISSION
};

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display