🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@shadowob/sdk

Package Overview
Dependencies
Maintainers
1
Versions
80
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.65
to
1.1.66
+58
bin/shadow-space-app.mjs
#!/usr/bin/env node
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'
import { dirname, relative, resolve } from 'node:path'
function usage(exitCode = 1) {
const out = exitCode === 0 ? console.log : console.error
out(`Usage:
shadow-space-app typegen <manifest.json> <output.ts>
Generates a typed TypeScript manifest module from a Space App JSON manifest.`)
process.exit(exitCode)
}
function readJson(filePath) {
try {
return JSON.parse(readFileSync(filePath, 'utf8'))
} catch (error) {
throw new Error(`Failed to read JSON manifest at ${filePath}: ${error.message}`)
}
}
function importPath(fromFile, toFile) {
let value = relative(dirname(fromFile), toFile).replaceAll('\\', '/')
if (!value.startsWith('.')) value = `./${value}`
return value.replace(/\.[cm]?ts$/, '.js')
}
function generateTypeModule(manifestPath, outputPath) {
const manifest = readJson(manifestPath)
const sdkImport = '@shadowob/sdk'
const sourceImport = importPath(outputPath, manifestPath)
const contents = `// Generated by shadow-space-app typegen from ${sourceImport}.
// Do not edit by hand. Update the JSON manifest and regenerate this file.
import type { ShadowSpaceAppManifest } from '${sdkImport}'
export const shadowSpaceAppManifest = ${JSON.stringify(manifest, null, 2)} as const satisfies ShadowSpaceAppManifest
export type ShadowSpaceAppManifestDefinition = typeof shadowSpaceAppManifest
`
mkdirSync(dirname(outputPath), { recursive: true })
writeFileSync(outputPath, contents)
}
const [, , command, manifestArg, outputArg] = process.argv
if (!command || command === '--help' || command === '-h') usage(command ? 0 : 1)
if (command !== 'typegen' || !manifestArg || !outputArg) usage()
try {
const manifestPath = resolve(process.cwd(), manifestArg)
const outputPath = resolve(process.cwd(), outputArg)
generateTypeModule(manifestPath, outputPath)
console.log(`Generated ${relative(process.cwd(), outputPath)}`)
} catch (error) {
console.error(error instanceof Error ? error.message : String(error))
process.exit(1)
}
import {
decodeShadowSpaceAppLaunchTokenHint,
getShadowSpaceAppChannelMessageDeliveries,
getShadowSpaceAppChannelMessageErrors,
getShadowSpaceAppInboxDeliveries,
getShadowSpaceAppInboxErrors,
readShadowSpaceAppCommandResponse
} from "./chunk-NADGSKKC.js";
// src/bridge-authorization-element.ts
var defaultScopeLabels = {
"user:read": "Read your basic profile",
"user:email": "Read your email address",
"servers:read": "Read server information",
"servers:write": "Manage server information",
"channels:read": "Read channels",
"channels:write": "Manage channels",
"messages:read": "Read messages",
"messages:write": "Send and manage messages",
"attachments:read": "Read attachments",
"attachments:write": "Upload and manage attachments",
"workspaces:read": "Read workspace files",
"workspaces:write": "Manage workspace files",
"buddies:create": "Create Buddies",
"buddies:manage": "Manage Buddies",
"commerce:read": "Read commerce data",
"commerce:write": "Manage commerce data"
};
function boolAttr(value) {
return value === "" || value === "true";
}
function splitScopes(value) {
return (value ?? "user:read").split(/\s+/u).map((scope) => scope.trim()).filter(Boolean);
}
function escapeHtml(value) {
return String(value ?? "").replace(/[&<>"']/gu, (char) => {
switch (char) {
case "&":
return "&amp;";
case "<":
return "&lt;";
case ">":
return "&gt;";
case '"':
return "&quot;";
case "'":
return "&#39;";
default:
return char;
}
});
}
function defineShadowSpaceAppAuthorizeElement(tagName = "shadow-space-app-authorize") {
if (typeof window === "undefined" || typeof HTMLElement === "undefined") return null;
if (!window.customElements) return null;
const existing = window.customElements.get(tagName);
if (existing) return existing;
class ShadowSpaceAppAuthorizeElement extends HTMLElement {
static observedAttributes = [
"app-name",
"app-logo-url",
"app-origin",
"title",
"subtitle",
"permissions-label",
"approve-label",
"deny-label",
"approving-label",
"loading",
"approving",
"error",
"scopes"
];
dataValue = {};
constructor() {
super();
this.attachShadow({ mode: "open" });
}
set data(value) {
this.dataValue = value;
this.render();
}
get data() {
return this.dataValue;
}
connectedCallback() {
this.render();
}
attributeChangedCallback() {
this.render();
}
value(name, fallback = "") {
return this.getAttribute(name) ?? fallback;
}
mergedData() {
return {
appName: this.dataValue.appName ?? this.value("app-name", "Application"),
appLogoUrl: this.dataValue.appLogoUrl ?? this.getAttribute("app-logo-url"),
appOrigin: this.dataValue.appOrigin ?? this.getAttribute("app-origin"),
title: this.dataValue.title ?? this.value("title", "Authorize application"),
subtitle: this.dataValue.subtitle ?? this.value("subtitle", "This application is requesting access to your Shadow account."),
permissionsLabel: this.dataValue.permissionsLabel ?? this.value("permissions-label", "Requested access"),
approveLabel: this.dataValue.approveLabel ?? this.value("approve-label", "Authorize"),
denyLabel: this.dataValue.denyLabel ?? this.value("deny-label", "Deny"),
approvingLabel: this.dataValue.approvingLabel ?? this.value("approving-label", "Authorizing"),
loading: this.dataValue.loading ?? boolAttr(this.getAttribute("loading")),
approving: this.dataValue.approving ?? boolAttr(this.getAttribute("approving")),
error: this.dataValue.error ?? this.getAttribute("error"),
scopes: this.dataValue.scopes ?? splitScopes(this.getAttribute("scopes")),
scopeLabels: { ...defaultScopeLabels, ...this.dataValue.scopeLabels ?? {} }
};
}
render() {
if (!this.shadowRoot) return;
const data = this.mergedData();
const initial = escapeHtml(data.appName[0]?.toUpperCase() ?? "A");
const scopes = data.scopes.length ? data.scopes : ["user:read"];
const logoUrl = data.appLogoUrl ? escapeHtml(data.appLogoUrl) : null;
const title = escapeHtml(data.title);
this.shadowRoot.innerHTML = `
<style>
:host {
display: block;
color: #f8fafc;
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", sans-serif;
}
.card {
width: min(460px, calc(100vw - 48px));
border: 1px solid rgba(255, 255, 255, 0.14);
border-radius: 24px;
background:
linear-gradient(180deg, rgba(20, 24, 33, 0.96), rgba(11, 15, 23, 0.98)),
#0b0f17;
box-shadow: 0 28px 90px rgba(0, 0, 0, 0.48);
overflow: hidden;
}
.top {
padding: 24px 24px 18px;
text-align: center;
}
.logo {
width: 54px;
height: 54px;
margin: 0 auto 14px;
display: grid;
place-items: center;
border-radius: 16px;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.12);
color: white;
font-weight: 900;
overflow: hidden;
}
.logo img {
width: 100%;
height: 100%;
object-fit: cover;
}
h2 {
margin: 0;
font-size: 20px;
line-height: 1.15;
letter-spacing: 0;
font-weight: 850;
}
.subtitle {
margin: 8px auto 0;
max-width: 34ch;
color: rgba(248, 250, 252, 0.68);
font-size: 13px;
line-height: 1.55;
font-weight: 600;
}
.app {
display: flex;
gap: 12px;
align-items: center;
margin: 0 20px 18px;
padding: 14px;
border-radius: 16px;
background: rgba(255, 255, 255, 0.07);
}
.app-name {
min-width: 0;
font-size: 14px;
font-weight: 800;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.app-origin {
margin-top: 3px;
min-width: 0;
color: rgba(248, 250, 252, 0.52);
font-size: 12px;
font-weight: 650;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.body {
padding: 0 24px 22px;
}
.label {
margin: 0 0 10px;
color: rgba(248, 250, 252, 0.7);
font-size: 13px;
font-weight: 750;
}
ul {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 8px;
}
li {
display: flex;
align-items: center;
gap: 9px;
color: rgba(248, 250, 252, 0.88);
font-size: 13px;
font-weight: 650;
}
.check {
width: 20px;
height: 20px;
flex: 0 0 auto;
display: grid;
place-items: center;
border-radius: 999px;
background: rgba(52, 211, 153, 0.16);
color: #34d399;
font-size: 13px;
font-weight: 950;
}
.error {
margin: 0 24px 16px;
padding: 11px 12px;
border: 1px solid rgba(248, 113, 113, 0.28);
border-radius: 14px;
background: rgba(248, 113, 113, 0.1);
color: #fecaca;
font-size: 13px;
font-weight: 700;
}
.actions {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
padding: 0 24px 24px;
}
button {
min-height: 44px;
border: 0;
border-radius: 14px;
font: inherit;
font-size: 14px;
font-weight: 850;
cursor: pointer;
}
button:disabled {
cursor: default;
opacity: 0.56;
}
.deny {
color: #f8fafc;
background: rgba(255, 255, 255, 0.09);
}
.approve {
color: #061018;
background: #f8fafc;
}
.loading {
display: grid;
min-height: 180px;
place-items: center;
}
.spinner {
width: 24px;
height: 24px;
border-radius: 999px;
border: 3px solid rgba(255, 255, 255, 0.16);
border-top-color: #f8fafc;
animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
</style>
<section class="card" role="dialog" aria-modal="true" aria-label="${title}">
<div class="top">
<div class="logo">
${logoUrl ? `<img alt="" src="${logoUrl}" />` : `<span>${initial}</span>`}
</div>
<h2>${title}</h2>
<p class="subtitle">${escapeHtml(data.subtitle)}</p>
</div>
${data.loading ? '<div class="loading"><div class="spinner"></div></div>' : `
<div class="app">
<div class="logo" style="width:42px;height:42px;margin:0;border-radius:13px">
${logoUrl ? `<img alt="" src="${logoUrl}" />` : `<span>${initial}</span>`}
</div>
<div style="min-width:0">
<div class="app-name">${escapeHtml(data.appName)}</div>
${data.appOrigin ? `<div class="app-origin">${escapeHtml(data.appOrigin)}</div>` : ""}
</div>
</div>
${data.error ? `<div class="error">${escapeHtml(data.error)}</div>` : ""}
<div class="body">
<p class="label">${escapeHtml(data.permissionsLabel)}</p>
<ul>
${scopes.map(
(scope) => `<li><span class="check">\u2713</span><span>${escapeHtml(
data.scopeLabels[scope] ?? scope
)}</span></li>`
).join("")}
</ul>
</div>
<div class="actions">
<button class="deny" ${data.approving ? "disabled" : ""}>${escapeHtml(
data.denyLabel
)}</button>
<button class="approve" ${data.approving ? "disabled" : ""}>${data.approving ? escapeHtml(data.approvingLabel) : escapeHtml(data.approveLabel)}</button>
</div>
`}
</section>
`;
this.shadowRoot.querySelector(".approve")?.addEventListener("click", () => this.emit("shadow-authorize-approve"));
this.shadowRoot.querySelector(".deny")?.addEventListener("click", () => this.emit("shadow-authorize-deny"));
}
emit(type) {
this.dispatchEvent(new CustomEvent(type, { bubbles: true, composed: true }));
}
}
window.customElements.define(tagName, ShadowSpaceAppAuthorizeElement);
return ShadowSpaceAppAuthorizeElement;
}
// src/bridge.ts
var SHADOW_BRIDGE_CAPABILITIES = [
"copilot.open",
"channel.open",
"workspace.open",
"buddy.create.open",
"oauth.authorize",
"launch.refresh",
"route.navigate",
"route.report",
"space-app.share.open"
];
function commandPath(basePath, commandName) {
return `${basePath.replace(/\/+$/u, "")}/${encodeURIComponent(commandName)}`;
}
function shadowSpaceAppMountedPathPrefix(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 shadowSpaceAppMountedPath(path, windowRef) {
const normalized = path.startsWith("/") ? path : `/${path}`;
return `${shadowSpaceAppMountedPathPrefix(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 ShadowSpaceAppBrowserClient = class {
bridge;
commandBasePath;
sessionPath;
inboxesPath;
buddyGrantPath;
fetchFn;
win;
launchTokenValue;
launchEventStreamUrlValue;
launchExpiresInValue;
sessionLaunchToken = null;
sessionCsrfToken = null;
sessionExchangePromise = null;
launchRefreshPromise = null;
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 ?? shadowSpaceAppMountedPath("/api/commands", windowRef);
this.sessionPath = options.sessionPath ?? shadowSpaceAppMountedPath("/api/shadow/session", windowRef);
this.inboxesPath = options.inboxesPath ?? shadowSpaceAppMountedPath("/api/inboxes", windowRef);
this.buddyGrantPath = options.buddyGrantPath ?? shadowSpaceAppMountedPath("/api/shadow/buddy-grants/ensure", windowRef);
this.fetchFn = options.fetch;
this.win = windowRef;
this.launchTokenValue = this.bridge.launchToken();
this.launchEventStreamUrlValue = shadowSpaceAppMountedPath("/api/shadow/events", windowRef);
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() {
return this.launchTokenValue;
}
launchEventStreamUrl() {
return this.launchEventStreamUrlValue;
}
async prepareEventStream() {
try {
const ready = await this.ensureSession({ reason: "events_missing_session" });
return ready ? this.launchEventStreamUrl() : null;
} catch {
return null;
}
}
launchContext() {
return {
launchToken: this.launchToken(),
...typeof this.launchExpiresInValue === "number" ? { expiresIn: this.launchExpiresInValue } : {}
};
}
onLaunchContextChange(handler) {
this.launchContextHandlers.add(handler);
return () => {
this.launchContextHandlers.delete(handler);
};
}
async command(commandName, input = {}) {
await this.ensureSession({ reason: "command_missing_session" });
const path = commandPath(this.commandBasePath, commandName);
const init = {
method: "POST",
headers: this.sessionHeaders({ "Content-Type": "application/json" }),
body: JSON.stringify({ input: withoutUndefined(input) }),
credentials: "include"
};
let response = await this.fetch(path, init);
if (response.status === 401 && await this.recoverSession("command_unauthorized")) {
response = await this.fetch(path, {
...init,
headers: this.sessionHeaders({ "Content-Type": "application/json" })
});
}
return readShadowSpaceAppCommandResponse(response);
}
async commandForm(commandName, formData) {
await this.ensureSession({ reason: "command_missing_session" });
const path = commandPath(this.commandBasePath, commandName);
const init = {
method: "POST",
headers: this.sessionHeaders(),
body: formData,
credentials: "include"
};
let response = await this.fetch(path, init);
if (response.status === 401 && await this.recoverSession("command_unauthorized")) {
response = await this.fetch(path, {
...init,
headers: this.sessionHeaders()
});
}
return readShadowSpaceAppCommandResponse(response);
}
async refreshLaunch(input = {}) {
if (!this.bridge.isAvailable()) return null;
if (this.launchRefreshPromise) return this.launchRefreshPromise;
const request = (async () => {
try {
const context = await this.bridge.refreshLaunch(input, { timeoutMs: 8e3 });
if (context?.launchToken) {
this.applyLaunchUpdate({
launchToken: context.launchToken,
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;
}
})().finally(() => {
this.launchRefreshPromise = null;
});
this.launchRefreshPromise = request;
return request;
}
async fetchWithSession(input, init = {}, options = {}) {
if (options.refresh) {
const refreshInput = options.refresh === true ? { reason: "fetch" } : options.refresh;
await this.refreshLaunch(refreshInput);
}
await this.ensureSession({ reason: "fetch_missing_session" });
let response = await this.fetch(input, this.withSession(init));
if (response.status !== 401 || !await this.recoverSession("fetch_unauthorized")) {
return response;
}
return this.fetch(input, this.withSession(init));
}
async listBuddyInboxes(options = {}) {
try {
if (options.refresh) await this.refreshLaunch({ reason: "inboxes_refresh" });
await this.ensureSession({ reason: "inboxes_missing_session" });
let response = await this.fetch(this.inboxesPath, this.withSession({ method: "GET" }));
if (response.status === 401 && await this.recoverSession("inboxes_unauthorized")) {
response = await this.fetch(this.inboxesPath, this.withSession({ method: "GET" }));
}
if (!response.ok) throw new Error(`Buddy inboxes failed (${response.status})`);
return await response.json();
} catch (error) {
if (options.emptyOnError) return { inboxes: [] };
throw error;
}
}
async ensureBuddyTaskGrant(input) {
const buddyAgentId = input.agentId?.trim();
if (!buddyAgentId) return { granted: false, skipped: true };
await this.ensureSession({ reason: "buddy_grant_missing_session" });
const request = () => this.fetch(
this.buddyGrantPath,
this.withSession({
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
buddyAgentId,
permissions: input.permissions,
reason: input.reason
}),
signal: AbortSignal.timeout(input.timeoutMs ?? 6e3)
})
);
let response = await request();
if (response.status === 401 && await this.recoverSession("buddy_grant_unauthorized")) {
response = await request();
}
if (!response.ok) throw new Error(`Buddy grant failed (${response.status})`);
return response.json();
}
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);
}
openChannel(input, options = {}) {
return this.bridge.openChannel(input, options);
}
openWorkspaceResource(input, options = {}) {
return this.bridge.openWorkspaceResource(input, options);
}
async authorizeOAuth(input, options = {}) {
const authorizeUrl = typeof input === "string" ? input : input.authorizeUrl;
if (!this.bridge.isAvailable()) {
if (options.fallback === "redirect") return this.redirectToAuthorizeUrl(authorizeUrl);
return { opened: false, status: "unavailable" };
}
try {
return await this.bridge.authorizeOAuth(input, {
// Authorization may include a first-time consent decision in the host.
// Keep the browser client aligned with the bridge's interactive timeout
// instead of treating a normal approval pause as a failed request.
timeoutMs: options.timeoutMs ?? 10 * 60 * 1e3
});
} catch (error) {
if (options.fallback === "redirect") return this.redirectToAuthorizeUrl(authorizeUrl);
return {
opened: false,
status: error instanceof Error && error.message.includes("timed out") ? "timeout" : "unavailable",
error: error instanceof Error ? error.message : "OAuth authorization failed"
};
}
}
routeChanged(path) {
return this.bridge.routeChanged(path);
}
onRouteNavigate(handler) {
return this.bridge.onRouteNavigate(handler);
}
shareSpaceApp(input = {}, options = {}) {
if (!this.bridge.isAvailable()) return Promise.resolve({ opened: false });
return this.bridge.shareSpaceApp(input, options);
}
inboxDeliveries(payload) {
return getShadowSpaceAppInboxDeliveries(payload);
}
inboxErrors(payload) {
return getShadowSpaceAppInboxErrors(payload);
}
channelMessageDeliveries(payload) {
return getShadowSpaceAppChannelMessageDeliveries(payload);
}
channelMessageErrors(payload) {
return getShadowSpaceAppChannelMessageErrors(payload);
}
fetch(input, init) {
if (this.fetchFn) return this.fetchFn(input, init);
return globalThis.fetch(input, init);
}
redirectToAuthorizeUrl(authorizeUrl) {
if (!this.win) return { opened: false, status: "unavailable" };
this.win.location.assign(authorizeUrl);
return { opened: true, status: "redirected", redirectUrl: authorizeUrl };
}
dispose() {
this.unsubscribeLaunchUpdate();
this.launchContextHandlers.clear();
this.bridge.dispose();
}
applyLaunchUpdate(context) {
if (context.launchToken !== this.launchTokenValue) {
this.sessionLaunchToken = null;
this.sessionCsrfToken = null;
}
this.launchTokenValue = context.launchToken;
this.launchExpiresInValue = typeof context.expiresIn === "number" ? context.expiresIn : void 0;
}
sessionHeaders(headersInit) {
const headers = new Headers(headersInit);
if (this.sessionCsrfToken) headers.set("X-Shadow-Space-App-CSRF", this.sessionCsrfToken);
return headers;
}
withSession(init) {
const headers = new Headers(init.headers);
if (this.sessionCsrfToken) headers.set("X-Shadow-Space-App-CSRF", this.sessionCsrfToken);
return { ...init, headers, credentials: "include" };
}
async ensureSession(input = {}) {
if (!this.bridge.isAvailable()) return false;
if (!this.launchToken()) await this.refreshLaunch(input);
const launchToken = this.launchToken();
if (!launchToken) return false;
if (this.sessionLaunchToken === launchToken && this.sessionCsrfToken) return true;
if (this.sessionExchangePromise) return this.sessionExchangePromise;
const exchange = (async () => {
const response = await this.fetch(this.sessionPath, {
method: "POST",
headers: { Authorization: `Bearer ${launchToken}` },
credentials: "include"
});
if (!response.ok) return false;
const payload = await response.json().catch(() => null);
if (payload?.ok !== true || typeof payload.csrfToken !== "string") return false;
this.sessionLaunchToken = launchToken;
this.sessionCsrfToken = payload.csrfToken;
return true;
})().finally(() => {
this.sessionExchangePromise = null;
});
this.sessionExchangePromise = exchange;
return exchange;
}
async recoverSession(reason) {
this.sessionLaunchToken = null;
this.sessionCsrfToken = null;
await this.refreshLaunch({ reason });
return this.ensureSession({ reason });
}
};
function createShadowSpaceAppClient(options = {}) {
return new ShadowSpaceAppBrowserClient(options);
}
var createShadowSpaceAppBrowserClient = createShadowSpaceAppClient;
var ShadowBridge = class _ShadowBridge {
static capabilitiesRequestType = "shadow.space-app.capabilities.request";
static capabilitiesResponseType = "shadow.space-app.capabilities.response";
static openCopilotRequestType = "shadow.space-app.copilot.open.request";
static openCopilotResponseType = "shadow.space-app.copilot.open.response";
static openChannelRequestType = "shadow.space-app.channel.open.request";
static openChannelResponseType = "shadow.space-app.channel.open.response";
static openWorkspaceResourceRequestType = "shadow.space-app.workspace.open.request";
static openWorkspaceResourceResponseType = "shadow.space-app.workspace.open.response";
static openBuddyCreatorRequestType = "shadow.space-app.buddy.create.request";
static openBuddyCreatorResponseType = "shadow.space-app.buddy.create.response";
static authorizeOAuthRequestType = "shadow.space-app.oauth.authorize.request";
static authorizeOAuthResponseType = "shadow.space-app.oauth.authorize.response";
static routeNavigateType = "shadow.space-app.navigate";
static routeNavigateAckType = "shadow.space-app.navigate.ack";
static routeChangedType = "shadow.space-app.route.changed";
static shareSpaceAppRequestType = "shadow.space-app.share.request";
static shareSpaceAppResponseType = "shadow.space-app.share.response";
static refreshLaunchRequestType = "shadow.space-app.launch.refresh.request";
static refreshLaunchResponseType = "shadow.space-app.launch.refresh.response";
static launchUpdatedEventType = "shadow.space-app.launch.updated";
appKey;
targetOrigin;
timeoutMs;
win;
launchTokenValue = null;
pending = /* @__PURE__ */ new Map();
onMessage = (event) => {
if (!this.isTrustedHostMessage(event)) return;
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);
this.win?.clearTimeout(entry.timeoutId);
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.resolveHostOrigin();
this.timeoutMs = options.timeoutMs ?? 6e4;
this.launchTokenValue = this.resolveLaunchToken();
this.win?.addEventListener("message", this.onMessage);
}
dispose() {
this.win?.removeEventListener("message", this.onMessage);
for (const entry of this.pending.values()) {
this.win?.clearTimeout(entry.timeoutId);
entry.reject(new Error("ShadowBridge disposed"));
}
this.pending.clear();
}
isAvailable() {
if (!this.win) return false;
return this.win.parent !== this.win || !!this.win.ReactNativeWebView;
}
launchToken() {
if (!this.win) return null;
return this.launchTokenValue ?? this.resolveLaunchToken();
}
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
);
}
openChannel(input, options = {}) {
return this.request(
_ShadowBridge.openChannelRequestType,
_ShadowBridge.openChannelResponseType,
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
);
}
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) => {
if (!this.isTrustedHostMessage(event)) return;
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) => {
if (!this.isTrustedHostMessage(event)) return;
let data = event.data;
if (typeof data === "string") {
try {
data = JSON.parse(data || "{}");
} catch {
return;
}
}
if (!data || typeof data !== "object") return;
const envelope = data;
if (envelope.type !== _ShadowBridge.launchUpdatedEventType) return;
const record = isRecord(envelope.result) ? envelope.result : isRecord(envelope.launch) ? envelope.launch : envelope;
const appKey = typeof envelope.appKey === "string" ? envelope.appKey : typeof record.appKey === "string" ? record.appKey : void 0;
if (this.appKey && appKey && appKey !== this.appKey) {
return;
}
if (typeof record.launchToken !== "string" || !record.launchToken) return;
void Promise.resolve(
handler({
launchToken: record.launchToken,
...typeof record.expiresIn === "number" ? { expiresIn: record.expiresIn } : {}
})
).catch(() => void 0);
};
win.addEventListener("message", listener);
return () => win.removeEventListener("message", listener);
}
shareSpaceApp(input = {}, options = {}) {
return this.request(
_ShadowBridge.shareSpaceAppRequestType,
_ShadowBridge.shareSpaceAppResponseType,
input,
options.timeoutMs ?? 5 * 60 * 1e3
);
}
refreshLaunch(input = {}, options = {}) {
return this.request(
_ShadowBridge.refreshLaunchRequestType,
_ShadowBridge.refreshLaunchResponseType,
input,
options.timeoutMs ?? 15e3
);
}
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 = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? `req_${crypto.randomUUID()}` : `req_${Date.now().toString(36)}_${this.pending.size.toString(36)}`;
return new Promise((resolve, reject) => {
const timeoutId = this.win.setTimeout(() => {
if (!this.pending.has(requestId)) return;
this.pending.delete(requestId);
reject(new Error(`${requestType} timed out after ${timeoutMs}ms`));
}, timeoutMs);
this.pending.set(requestId, {
responseType,
resolve,
reject,
timeoutId
});
try {
this.postMessage({
type: requestType,
requestId,
...this.appKey ? { appKey: this.appKey } : {},
...payload
});
} catch (error) {
this.pending.delete(requestId);
this.win?.clearTimeout(timeoutId);
reject(error instanceof Error ? error : new Error("Bridge request failed to send"));
}
});
}
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);
}
rememberLaunchToken(token) {
if (!token) return;
const hint = decodeShadowSpaceAppLaunchTokenHint(token);
if (!this.appKey && hint?.appKey) this.appKey = hint.appKey;
this.launchTokenValue = token;
if (this.appKey) {
const memoryTokens = this.win.__shadowBridgeLaunchTokens ??= {};
memoryTokens[this.appKey] = token;
}
}
resolveLaunchToken() {
if (!this.win) return null;
const memoryToken = this.appKey ? this.win.__shadowBridgeLaunchTokens?.[this.appKey] : null;
if (memoryToken) return memoryToken;
return null;
}
applyLaunchContext(value) {
if (!isRecord(value) || typeof value.launchToken !== "string") return false;
this.rememberLaunchToken(value.launchToken);
return true;
}
resolveLaunchAppKey() {
return void 0;
}
resolveHostOrigin() {
if (!this.win?.document?.referrer) return "*";
try {
return new URL(this.win.document.referrer).origin;
} catch {
return "*";
}
}
isTrustedHostMessage(event) {
if (!this.win) return false;
if (this.win.parent !== this.win && event.source !== this.win.parent) return false;
return this.targetOrigin === "*" || event.origin === this.targetOrigin;
}
};
export {
defineShadowSpaceAppAuthorizeElement,
SHADOW_BRIDGE_CAPABILITIES,
shadowSpaceAppMountedPathPrefix,
shadowSpaceAppMountedPath,
ShadowSpaceAppBrowserClient,
createShadowSpaceAppClient,
createShadowSpaceAppBrowserClient,
ShadowBridge
};
// src/space-app.ts
import {
BUDDY_INBOX_DELIVERY_PERMISSION
} from "@shadowob/shared";
var SHADOW_LAUNCH_INTROSPECTION_CACHE_TTL_MS = 5e3;
var SHADOW_LAUNCH_INTROSPECTION_TIMEOUT_MS = 2500;
var SHADOW_LAUNCH_INTROSPECTION_CACHE_LIMIT = 256;
var shadowLaunchIntrospectionCache = /* @__PURE__ */ new Map();
var shadowLaunchIntrospectionRequests = /* @__PURE__ */ new Map();
var shadowLaunchFetchIds = /* @__PURE__ */ new WeakMap();
var nextShadowLaunchFetchId = 1;
var SHADOW_SPACE_APP_PROTOCOL = "shadow.space-app/1";
var SHADOW_SPACE_APP_COMMAND_COMPLETED_EVENT = "space_app.command.completed";
var SHADOW_SPACE_APP_COMMAND_FAILED_EVENT = "space_app.command.failed";
var SHADOW_SPACE_APP_COMMAND_EVENTS = [
SHADOW_SPACE_APP_COMMAND_COMPLETED_EVENT,
SHADOW_SPACE_APP_COMMAND_FAILED_EVENT
];
var ShadowSpaceAppHttpError = class extends Error {
status;
payload;
constructor(status, message, payload) {
super(message);
this.name = "ShadowSpaceAppHttpError";
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 shadowSpaceAppInboxTaskEndpoint(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 buildShadowSpaceAppInboxTaskRequest(input) {
const appId = input.app.id ?? input.app.appId ?? input.app.appKey;
const spaceAppData = isProtocolRecord(input.task.data?.spaceApp) ? input.task.data.spaceApp : {};
return {
endpoint: shadowSpaceAppInboxTaskEndpoint(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: "space_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 ?? {},
spaceApp: {
...spaceAppData,
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 getShadowSpaceAppTaskCardId(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 buildShadowSpaceAppInboxDelivery(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: getShadowSpaceAppTaskCardId(message),
idempotencyKey: input.idempotencyKey
};
}
function shadowFromPayload(payload) {
if (payload.protocol === SHADOW_SPACE_APP_PROTOCOL) {
return payload;
}
const shadow = isProtocolRecord(payload.shadow) ? payload.shadow : null;
if (shadow?.protocol === SHADOW_SPACE_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_SPACE_APP_PROTOCOL,
outbox: {
...existing.outbox ?? {},
...shadow.outbox ?? {}
}
}
};
}
function getShadowSpaceAppInboxDeliveries(payload) {
if (!isProtocolRecord(payload)) return [];
const shadow = shadowFromPayload(payload);
return shadow?.outbox?.deliveries ?? [];
}
function getShadowSpaceAppInboxErrors(payload) {
if (!isProtocolRecord(payload)) return [];
const shadow = shadowFromPayload(payload);
return shadow?.outbox?.errors ?? [];
}
function getShadowSpaceAppPendingInboxTasks(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, ...getShadowSpaceAppPendingInboxTasks(payload.result, depth + 1)] : tasks;
}
function getShadowSpaceAppChannelMessageDeliveries(payload) {
if (!isProtocolRecord(payload)) return [];
const shadow = shadowFromPayload(payload);
return shadow?.outbox?.channelMessageDeliveries ?? [];
}
function getShadowSpaceAppChannelMessageErrors(payload) {
if (!isProtocolRecord(payload)) return [];
const shadow = shadowFromPayload(payload);
return shadow?.outbox?.channelMessageErrors ?? [];
}
function getShadowSpaceAppPendingChannelMessages(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, ...getShadowSpaceAppPendingChannelMessages(payload.result, depth + 1)] : messages;
}
function hasShadowSpaceAppPendingOutbox(payload) {
return getShadowSpaceAppPendingInboxTasks(payload).length > 0 || getShadowSpaceAppPendingChannelMessages(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 unwrapShadowSpaceAppCommandPayload(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 = unwrapShadowSpaceAppCommandPayload(payload.result);
const shadow = shadowFromPayload(payload);
if (isProtocolRecord(nested)) return mergeShadowResult(nested, shadow);
return nested;
}
return payload;
}
async function readShadowSpaceAppResponsePayload(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 ShadowSpaceAppHttpError(response.status, "Command returned invalid JSON", text);
}
}
function shadowSpaceAppResponseErrorMessage(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 readShadowSpaceAppCommandResponse(response) {
const payload = await readShadowSpaceAppResponsePayload(response);
if (!response.ok || isProtocolRecord(payload) && payload.ok === false) {
throw new ShadowSpaceAppHttpError(
response.status,
shadowSpaceAppResponseErrorMessage(response.status, payload),
payload
);
}
return unwrapShadowSpaceAppCommandPayload(payload);
}
var ShadowSpaceAppOutbox = 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_SPACE_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_SPACE_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 shadowSpaceAppApiBaseUrl(env = {}) {
return trimTrailingSlash(
firstEnvironmentValue(
env,
["SHADOWOB_INTERNAL_SERVER_URL", "SHADOWOB_SERVER_URL"],
"http://localhost:3002"
)
);
}
function shadowSpaceAppPublicBaseUrl(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 shadowSpaceAppPublicUrl(pathOrUrl, env = {}) {
if (!pathOrUrl.startsWith("/")) return pathOrUrl;
return joinBasePath(shadowSpaceAppPublicBaseUrl(env), pathOrUrl);
}
function isShadowSpaceAppSignedMediaUrl(value, env = {}) {
const mediaUrl = value.trim();
if (mediaUrl.startsWith("/api/media/signed/")) return true;
try {
return new URL(mediaUrl, shadowSpaceAppPublicBaseUrl(env)).pathname.startsWith(
"/api/media/signed/"
);
} catch {
return false;
}
}
function normalizeShadowSpaceAppAvatarUrl(value, env = {}) {
if (typeof value !== "string") return null;
const avatarUrl = value.trim();
if (!avatarUrl || avatarUrl.length > 500) return null;
if (isShadowSpaceAppSignedMediaUrl(avatarUrl, env)) return null;
return shadowSpaceAppPublicUrl(avatarUrl, env);
}
function shadowSpaceAppAvatarRedirectUrl(requestUrl, env = {}) {
return shadowSpaceAppPublicUrl(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 extractShadowSpaceAppBearerToken(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 decodeShadowSpaceAppLaunchTokenPayload(token) {
if (!token) return null;
const parts = token.split(".");
if (parts.length !== 3 || parts[0] !== "sat_v1") return null;
return decodeBase64UrlJson(parts[1]);
}
function decodeShadowSpaceAppLaunchTokenHint(token) {
const payload = decodeShadowSpaceAppLaunchTokenPayload(token);
if (typeof payload?.serverId !== "string" || typeof payload.appKey !== "string") return null;
return { serverId: payload.serverId, appKey: payload.appKey };
}
function shadowLaunchFetchId(fetchFn) {
const existing = shadowLaunchFetchIds.get(fetchFn);
if (existing) return existing;
const id = nextShadowLaunchFetchId++;
shadowLaunchFetchIds.set(fetchFn, id);
return id;
}
function shadowLaunchIntrospectionCacheKey(fetchFn, baseUrl, launchToken) {
return `${shadowLaunchFetchId(fetchFn)}:${baseUrl}:${launchToken}`;
}
function pruneShadowLaunchIntrospectionCache(now) {
for (const [key, entry] of shadowLaunchIntrospectionCache) {
if (entry.expiresAt <= now) shadowLaunchIntrospectionCache.delete(key);
}
while (shadowLaunchIntrospectionCache.size >= SHADOW_LAUNCH_INTROSPECTION_CACHE_LIMIT) {
const oldestKey = shadowLaunchIntrospectionCache.keys().next().value;
if (typeof oldestKey !== "string") break;
shadowLaunchIntrospectionCache.delete(oldestKey);
}
}
function shadowLaunchIntrospectionExpiry(launchToken, now) {
const payload = decodeShadowSpaceAppLaunchTokenPayload(launchToken);
const tokenExpiresAt = typeof payload?.exp === "number" && Number.isFinite(payload.exp) ? payload.exp * 1e3 : Number.POSITIVE_INFINITY;
return Math.min(now + SHADOW_LAUNCH_INTROSPECTION_CACHE_TTL_MS, tokenExpiresAt);
}
async function fetchShadowSpaceAppLaunchInboxes(options) {
const hint = decodeShadowSpaceAppLaunchTokenHint(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)}/space-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 fetchShadowSpaceAppLaunchMembers(options) {
const hint = decodeShadowSpaceAppLaunchTokenHint(options.launchToken);
if (!hint || !options.launchToken) return { members: [] };
const fetchFn = options.fetch ?? fetch;
const baseUrl = trimTrailingSlash(options.shadowApiBaseUrl ?? "http://localhost:3002");
const response = await fetchFn(
`${baseUrl}/api/servers/${encodeURIComponent(hint.serverId)}/space-apps/${encodeURIComponent(
hint.appKey
)}/launch/members`,
{
headers: { Authorization: `Bearer ${options.launchToken}` },
signal: AbortSignal.timeout(SHADOW_LAUNCH_INTROSPECTION_TIMEOUT_MS)
}
);
if (!response.ok) {
const message = await response.text().catch(() => "");
throw new Error(`Shadow launch member lookup failed (${response.status}): ${message}`);
}
return await response.json();
}
async function fetchShadowSpaceAppLaunchChannels(options) {
const hint = decodeShadowSpaceAppLaunchTokenHint(options.launchToken);
if (!hint || !options.launchToken) return { channels: [] };
const fetchFn = options.fetch ?? fetch;
const baseUrl = trimTrailingSlash(options.shadowApiBaseUrl ?? "http://localhost:3002");
const response = await fetchFn(
`${baseUrl}/api/servers/${encodeURIComponent(hint.serverId)}/space-apps/${encodeURIComponent(
hint.appKey
)}/launch/channels`,
{
headers: { Authorization: `Bearer ${options.launchToken}` },
signal: AbortSignal.timeout(SHADOW_LAUNCH_INTROSPECTION_TIMEOUT_MS)
}
);
if (!response.ok) {
const message = await response.text().catch(() => "");
throw new Error(`Shadow launch channel lookup failed (${response.status}): ${message}`);
}
return await response.json();
}
async function fetchShadowSpaceAppLaunchMessage(options) {
const hint = decodeShadowSpaceAppLaunchTokenHint(options.launchToken);
if (!hint || !options.launchToken) throw new Error("Shadow launch token is required");
const fetchFn = options.fetch ?? fetch;
const baseUrl = trimTrailingSlash(options.shadowApiBaseUrl ?? "http://localhost:3002");
const response = await fetchFn(
`${baseUrl}/api/servers/${encodeURIComponent(hint.serverId)}/space-apps/${encodeURIComponent(
hint.appKey
)}/launch/messages/${encodeURIComponent(options.messageId)}`,
{
headers: { Authorization: `Bearer ${options.launchToken}` },
signal: AbortSignal.timeout(SHADOW_LAUNCH_INTROSPECTION_TIMEOUT_MS)
}
);
if (!response.ok) {
const message = await response.text().catch(() => "");
throw new Error(`Shadow launch message lookup failed (${response.status}): ${message}`);
}
return await response.json();
}
async function ensureShadowSpaceAppLaunchChannel(options) {
const hint = decodeShadowSpaceAppLaunchTokenHint(options.launchToken);
if (!hint || !options.launchToken) throw new Error("Shadow launch token is required");
const fetchFn = options.fetch ?? fetch;
const baseUrl = trimTrailingSlash(options.shadowApiBaseUrl ?? "http://localhost:3002");
const response = await fetchFn(
`${baseUrl}/api/servers/${encodeURIComponent(hint.serverId)}/space-apps/${encodeURIComponent(
hint.appKey
)}/launch/channels/ensure`,
{
method: "POST",
headers: {
Authorization: `Bearer ${options.launchToken}`,
"Content-Type": "application/json"
},
body: JSON.stringify(options.input),
signal: AbortSignal.timeout(SHADOW_LAUNCH_INTROSPECTION_TIMEOUT_MS)
}
);
if (!response.ok) {
const message = await response.text().catch(() => "");
throw new Error(`Shadow launch channel ensure failed (${response.status}): ${message}`);
}
return await response.json();
}
async function createShadowSpaceAppLaunchPoll(options) {
const hint = decodeShadowSpaceAppLaunchTokenHint(options.launchToken);
if (!hint || !options.launchToken) throw new Error("Shadow launch token is required");
const fetchFn = options.fetch ?? fetch;
const baseUrl = trimTrailingSlash(options.shadowApiBaseUrl ?? "http://localhost:3002");
const response = await fetchFn(
`${baseUrl}/api/servers/${encodeURIComponent(hint.serverId)}/space-apps/${encodeURIComponent(
hint.appKey
)}/launch/polls`,
{
method: "POST",
headers: {
Authorization: `Bearer ${options.launchToken}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
...options.input,
answers: options.input.answers.map(
(answer) => typeof answer === "string" ? { text: answer } : answer
)
}),
signal: AbortSignal.timeout(SHADOW_LAUNCH_INTROSPECTION_TIMEOUT_MS)
}
);
if (!response.ok) {
const message = await response.text().catch(() => "");
throw new Error(`Shadow launch poll creation failed (${response.status}): ${message}`);
}
return await response.json();
}
async function ensureShadowSpaceAppLaunchBuddyTaskGrant(options) {
const hint = decodeShadowSpaceAppLaunchTokenHint(options.launchToken);
if (!hint || !options.launchToken) throw new Error("Shadow launch token is required");
const fetchFn = options.fetch ?? fetch;
const baseUrl = trimTrailingSlash(options.shadowApiBaseUrl ?? "http://localhost:3002");
const response = await fetchFn(
`${baseUrl}/api/servers/${encodeURIComponent(hint.serverId)}/space-apps/${encodeURIComponent(
hint.appKey
)}/launch/buddy-grants/ensure`,
{
method: "POST",
headers: {
Authorization: `Bearer ${options.launchToken}`,
"Content-Type": "application/json"
},
body: JSON.stringify(options.input),
signal: AbortSignal.timeout(SHADOW_LAUNCH_INTROSPECTION_TIMEOUT_MS)
}
);
if (!response.ok) {
const message = await response.text().catch(() => "");
throw new Error(`Shadow launch Buddy grant failed (${response.status}): ${message}`);
}
return await response.json();
}
async function introspectShadowSpaceAppLaunchToken(options) {
const hint = decodeShadowSpaceAppLaunchTokenHint(options.launchToken);
if (!hint || !options.launchToken) return null;
const fetchFn = options.fetch ?? fetch;
const baseUrl = trimTrailingSlash(options.shadowApiBaseUrl ?? "http://localhost:3002");
const cacheKey = shadowLaunchIntrospectionCacheKey(fetchFn, baseUrl, options.launchToken);
const now = Date.now();
const cached = shadowLaunchIntrospectionCache.get(cacheKey);
if (cached && cached.expiresAt > now) return cached.value;
shadowLaunchIntrospectionCache.delete(cacheKey);
const inFlight = shadowLaunchIntrospectionRequests.get(cacheKey);
if (inFlight) return inFlight;
const request = (async () => {
const response = await fetchFn(
`${baseUrl}/api/servers/${encodeURIComponent(hint.serverId)}/space-apps/${encodeURIComponent(
hint.appKey
)}/launch/introspect`,
{
method: "POST",
headers: { Authorization: `Bearer ${options.launchToken}` },
signal: AbortSignal.timeout(SHADOW_LAUNCH_INTROSPECTION_TIMEOUT_MS)
}
);
if (!response.ok) {
const payload2 = await readShadowSpaceAppResponsePayload(response).catch(() => null);
return {
active: false,
error: shadowSpaceAppResponseErrorMessage(response.status, payload2, "invalid_launch_token")
};
}
const payload = await response.json().catch(() => null);
const introspection = typeof payload?.active === "boolean" ? payload : null;
if (introspection?.active) {
const cachedAt = Date.now();
const expiresAt = shadowLaunchIntrospectionExpiry(options.launchToken, cachedAt);
if (expiresAt > cachedAt) {
pruneShadowLaunchIntrospectionCache(cachedAt);
shadowLaunchIntrospectionCache.set(cacheKey, { expiresAt, value: introspection });
}
}
return introspection;
})().finally(() => shadowLaunchIntrospectionRequests.delete(cacheKey));
shadowLaunchIntrospectionRequests.set(cacheKey, request);
return request;
}
function shadowSpaceAppLaunchIntrospectionError(introspection) {
return introspection?.error ?? introspection?.reason ?? introspection?.error_description ?? "invalid_launch_token";
}
function shadowSpaceAppLaunchCommandContextFromIntrospection(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_SPACE_APP_PROTOCOL,
serverId: shadow.serverId,
spaceAppId: shadow.spaceAppId ?? "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 ?? "space_app.runtime",
action: command?.action ?? shadow.action ?? "read",
dataClass: command?.dataClass ?? shadow.dataClass ?? "server-private"
};
}
async function resolveShadowSpaceAppLaunchCommandContextResolution(options) {
const introspection = await introspectShadowSpaceAppLaunchToken(options);
if (!introspection?.active) {
return {
context: null,
introspection,
error: shadowSpaceAppLaunchIntrospectionError(introspection)
};
}
const context = shadowSpaceAppLaunchCommandContextFromIntrospection(options, introspection);
return {
context,
introspection,
error: context ? null : shadowSpaceAppLaunchIntrospectionError(introspection)
};
}
async function resolveShadowSpaceAppLaunchCommandContext(options) {
const resolution = await resolveShadowSpaceAppLaunchCommandContextResolution(options);
return resolution.context;
}
async function deliverShadowSpaceAppLaunchOutbox(options) {
const hint = decodeShadowSpaceAppLaunchTokenHint(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)}/space-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 readShadowSpaceAppResponsePayload(response);
throw new ShadowSpaceAppHttpError(
response.status,
shadowSpaceAppResponseErrorMessage(
response.status,
payload,
"Shadow launch outbox delivery failed"
),
payload
);
}
return readShadowSpaceAppResponsePayload(response);
}
async function publishShadowSpaceAppNotification(options) {
const hint = decodeShadowSpaceAppLaunchTokenHint(options.launchToken);
if (!hint || !options.launchToken) {
throw new Error("A Shadow launch token is required to publish a Space App notification");
}
const fetchFn = options.fetch ?? fetch;
const baseUrl = trimTrailingSlash(options.shadowApiBaseUrl ?? "http://localhost:3002");
const response = await fetchFn(
`${baseUrl}/api/servers/${encodeURIComponent(hint.serverId)}/space-apps/${encodeURIComponent(
hint.appKey
)}/notifications`,
{
method: "POST",
headers: {
Authorization: `Bearer ${options.launchToken}`,
"Content-Type": "application/json"
},
body: JSON.stringify(options.notification)
}
);
if (!response.ok) {
const payload = await readShadowSpaceAppResponsePayload(response);
throw new ShadowSpaceAppHttpError(
response.status,
shadowSpaceAppResponseErrorMessage(
response.status,
payload,
"Space App notification publish failed"
),
payload
);
}
return readShadowSpaceAppResponsePayload(response);
}
function normalizeShadowSpaceAppCommandInput(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 createShadowSpaceAppManifest(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 defineShadowSpaceApp(manifest, options = {}) {
return new ShadowSpaceAppRuntime(manifest, options);
}
var createShadowSpaceAppRuntime = defineShadowSpaceApp;
var ShadowSpaceAppCommandError = class extends Error {
status;
issues;
constructor(status, error, issues) {
super(error);
this.name = "ShadowSpaceAppCommandError";
this.status = status;
this.issues = issues;
}
};
function shadowSpaceAppError(status, error, issues) {
return new ShadowSpaceAppCommandError(status, error, issues);
}
var ShadowSpaceAppRuntime = class {
constructor(sourceManifest, options = {}) {
this.sourceManifest = sourceManifest;
this.options = options;
}
sourceManifest;
options;
manifest(options = {}) {
return createShadowSpaceAppManifest(this.sourceManifest, options);
}
defineCommands(handlers) {
return handlers;
}
actor(envelopeOrContext) {
return shadowSpaceAppActorRef(envelopeOrContext);
}
error(status, error, issues) {
return shadowSpaceAppError(status, error, issues);
}
async parseCommand(commandName, request) {
return parseShadowSpaceAppCommandRequest({
...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 = validateShadowSpaceAppJsonSchema(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 ShadowSpaceAppCommandError) {
return failureResult(error.status, error.message, error.issues);
}
throw error;
}
}
};
async function introspectShadowSpaceAppToken(input) {
const baseUrl = trimTrailingSlash(input.shadowBaseUrl ?? "http://localhost:3002");
const fetchImpl = input.fetchImpl ?? fetch;
const response = await fetchImpl(`${baseUrl}/api/space-apps/commands/introspect`, {
method: "POST",
headers: { Authorization: `Bearer ${input.token}` }
});
if (!response.ok) return null;
const payload = await response.json();
return payload.active ? payload : null;
}
async function parseShadowSpaceAppCommandRequest(input) {
const token = extractShadowSpaceAppBearerToken(input.authorizationHeader);
if (!token) {
return { ok: false, status: 401, error: "missing_command_token" };
}
const introspection = await introspectShadowSpaceAppToken({
token,
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 validateShadowSpaceAppJsonSchema(schema, value) {
if (!schema) return { ok: true };
const issues = [];
validateJsonSchemaValue(schema, value, "", issues);
return issues.length ? { ok: false, issues } : { ok: true };
}
function shadowSpaceAppActorDisplayName(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 shadowSpaceAppActorAvatarUrl(envelopeOrContext) {
const context = "context" in envelopeOrContext ? envelopeOrContext.context : envelopeOrContext;
return context.actor.profile?.avatarUrl ?? null;
}
function shadowSpaceAppActorRef(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: shadowSpaceAppActorDisplayName(context),
avatarUrl: shadowSpaceAppActorAvatarUrl(context)
};
}
function isShadowSpaceAppActorRef(value) {
return !!value && typeof value === "object" && !Array.isArray(value) && typeof value.kind === "string" && typeof value.id === "string" && typeof value.displayName === "string";
}
function shadowSpaceAppIdentitySubjectKind(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 shadowSpaceAppIdentityKey(actorOrIdentity) {
const actor = isShadowSpaceAppActorRef(actorOrIdentity) ? actorOrIdentity : shadowSpaceAppActorRef(actorOrIdentity);
const subjectKind = shadowSpaceAppIdentitySubjectKind(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 shadowSpaceAppIdentitySnapshot(actorOrContext) {
const actor = isShadowSpaceAppActorRef(actorOrContext) ? actorOrContext : shadowSpaceAppActorRef(actorOrContext);
return {
...actor,
subjectKind: shadowSpaceAppIdentitySubjectKind(actor),
stableKey: shadowSpaceAppIdentityKey(actor)
};
}
var shadowSpaceAppDisplayIdentity = shadowSpaceAppIdentitySnapshot;
function normalizeShadowSpaceAppClientMutationId(value) {
if (typeof value !== "string") return null;
const clean = value.trim();
if (!clean) return null;
return clean.slice(0, 160);
}
function normalizeShadowSpaceAppBaseCursor(value) {
if (typeof value !== "string") return null;
const clean = value.trim();
if (!clean) return null;
return clean.slice(0, 240);
}
function createShadowSpaceAppCollaborationResource(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 createShadowSpaceAppCollaborationCursor(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 createShadowSpaceAppCollaborationEvent(input) {
const occurredAt = input.occurredAt ?? (/* @__PURE__ */ new Date()).toISOString();
const cursor = input.cursor ?? createShadowSpaceAppCollaborationCursor({
resource: input.resource,
occurredAt
});
return {
protocol: SHADOW_SPACE_APP_PROTOCOL,
type: input.type,
cursor,
occurredAt,
resource: input.resource,
actor: shadowSpaceAppIdentitySnapshot(input.actor),
payload: input.payload,
clientMutationId: normalizeShadowSpaceAppClientMutationId(input.clientMutationId),
baseCursor: normalizeShadowSpaceAppBaseCursor(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_SPACE_APP_PROTOCOL,
SHADOW_SPACE_APP_COMMAND_COMPLETED_EVENT,
SHADOW_SPACE_APP_COMMAND_FAILED_EVENT,
SHADOW_SPACE_APP_COMMAND_EVENTS,
ShadowSpaceAppHttpError,
shadowSpaceAppInboxTaskEndpoint,
buildShadowSpaceAppInboxTaskRequest,
getShadowSpaceAppTaskCardId,
buildShadowSpaceAppInboxDelivery,
getShadowSpaceAppInboxDeliveries,
getShadowSpaceAppInboxErrors,
getShadowSpaceAppPendingInboxTasks,
getShadowSpaceAppChannelMessageDeliveries,
getShadowSpaceAppChannelMessageErrors,
getShadowSpaceAppPendingChannelMessages,
hasShadowSpaceAppPendingOutbox,
unwrapShadowSpaceAppCommandPayload,
readShadowSpaceAppCommandResponse,
ShadowSpaceAppOutbox,
SHADOW_SPACE_APP_PUBLIC_AVATAR_CACHE_CONTROL,
shadowSpaceAppApiBaseUrl,
shadowSpaceAppPublicBaseUrl,
shadowSpaceAppPublicUrl,
isShadowSpaceAppSignedMediaUrl,
normalizeShadowSpaceAppAvatarUrl,
shadowSpaceAppAvatarRedirectUrl,
extractShadowSpaceAppBearerToken,
decodeShadowSpaceAppLaunchTokenHint,
fetchShadowSpaceAppLaunchInboxes,
fetchShadowSpaceAppLaunchMembers,
fetchShadowSpaceAppLaunchChannels,
fetchShadowSpaceAppLaunchMessage,
ensureShadowSpaceAppLaunchChannel,
createShadowSpaceAppLaunchPoll,
ensureShadowSpaceAppLaunchBuddyTaskGrant,
introspectShadowSpaceAppLaunchToken,
shadowSpaceAppLaunchIntrospectionError,
shadowSpaceAppLaunchCommandContextFromIntrospection,
resolveShadowSpaceAppLaunchCommandContextResolution,
resolveShadowSpaceAppLaunchCommandContext,
deliverShadowSpaceAppLaunchOutbox,
publishShadowSpaceAppNotification,
normalizeShadowSpaceAppCommandInput,
createShadowSpaceAppManifest,
defineShadowSpaceApp,
createShadowSpaceAppRuntime,
ShadowSpaceAppCommandError,
shadowSpaceAppError,
ShadowSpaceAppRuntime,
introspectShadowSpaceAppToken,
parseShadowSpaceAppCommandRequest,
validateShadowSpaceAppJsonSchema,
shadowSpaceAppActorDisplayName,
shadowSpaceAppActorAvatarUrl,
shadowSpaceAppActorRef,
shadowSpaceAppIdentityKey,
shadowSpaceAppIdentitySnapshot,
shadowSpaceAppDisplayIdentity,
normalizeShadowSpaceAppClientMutationId,
normalizeShadowSpaceAppBaseCursor,
createShadowSpaceAppCollaborationResource,
createShadowSpaceAppCollaborationCursor,
createShadowSpaceAppCollaborationEvent,
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

"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/space-app-node.ts
var space_app_node_exports = {};
__export(space_app_node_exports, {
ShadowSpaceAppJsonStore: () => ShadowSpaceAppJsonStore,
ShadowSpaceAppSessionManager: () => ShadowSpaceAppSessionManager,
createShadowSpaceAppJsonStore: () => createShadowSpaceAppJsonStore,
createShadowSpaceAppSessionManager: () => createShadowSpaceAppSessionManager
});
module.exports = __toCommonJS(space_app_node_exports);
var import_node_crypto = require("crypto");
var import_node_fs = require("fs");
var import_node_path = require("path");
// src/space-app.ts
var import_shared = require("@shadowob/shared");
var SHADOW_LAUNCH_INTROSPECTION_CACHE_TTL_MS = 5e3;
var SHADOW_LAUNCH_INTROSPECTION_TIMEOUT_MS = 2500;
var SHADOW_LAUNCH_INTROSPECTION_CACHE_LIMIT = 256;
var shadowLaunchIntrospectionCache = /* @__PURE__ */ new Map();
var shadowLaunchIntrospectionRequests = /* @__PURE__ */ new Map();
var shadowLaunchFetchIds = /* @__PURE__ */ new WeakMap();
var nextShadowLaunchFetchId = 1;
var SHADOW_SPACE_APP_PROTOCOL = "shadow.space-app/1";
var ShadowSpaceAppHttpError = class extends Error {
status;
payload;
constructor(status, message, payload) {
super(message);
this.name = "ShadowSpaceAppHttpError";
this.status = status;
this.payload = payload;
}
};
function isProtocolRecord(value) {
return !!value && typeof value === "object" && !Array.isArray(value);
}
async function readShadowSpaceAppResponsePayload(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 ShadowSpaceAppHttpError(response.status, "Command returned invalid JSON", text);
}
}
function shadowSpaceAppResponseErrorMessage(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;
}
function trimTrailingSlash(value) {
return value.replace(/\/+$/, "");
}
function extractShadowSpaceAppBearerToken(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 decodeShadowSpaceAppLaunchTokenPayload(token) {
if (!token) return null;
const parts = token.split(".");
if (parts.length !== 3 || parts[0] !== "sat_v1") return null;
return decodeBase64UrlJson(parts[1]);
}
function decodeShadowSpaceAppLaunchTokenHint(token) {
const payload = decodeShadowSpaceAppLaunchTokenPayload(token);
if (typeof payload?.serverId !== "string" || typeof payload.appKey !== "string") return null;
return { serverId: payload.serverId, appKey: payload.appKey };
}
function shadowLaunchFetchId(fetchFn) {
const existing = shadowLaunchFetchIds.get(fetchFn);
if (existing) return existing;
const id = nextShadowLaunchFetchId++;
shadowLaunchFetchIds.set(fetchFn, id);
return id;
}
function shadowLaunchIntrospectionCacheKey(fetchFn, baseUrl, launchToken) {
return `${shadowLaunchFetchId(fetchFn)}:${baseUrl}:${launchToken}`;
}
function pruneShadowLaunchIntrospectionCache(now) {
for (const [key, entry] of shadowLaunchIntrospectionCache) {
if (entry.expiresAt <= now) shadowLaunchIntrospectionCache.delete(key);
}
while (shadowLaunchIntrospectionCache.size >= SHADOW_LAUNCH_INTROSPECTION_CACHE_LIMIT) {
const oldestKey = shadowLaunchIntrospectionCache.keys().next().value;
if (typeof oldestKey !== "string") break;
shadowLaunchIntrospectionCache.delete(oldestKey);
}
}
function shadowLaunchIntrospectionExpiry(launchToken, now) {
const payload = decodeShadowSpaceAppLaunchTokenPayload(launchToken);
const tokenExpiresAt = typeof payload?.exp === "number" && Number.isFinite(payload.exp) ? payload.exp * 1e3 : Number.POSITIVE_INFINITY;
return Math.min(now + SHADOW_LAUNCH_INTROSPECTION_CACHE_TTL_MS, tokenExpiresAt);
}
async function introspectShadowSpaceAppLaunchToken(options) {
const hint = decodeShadowSpaceAppLaunchTokenHint(options.launchToken);
if (!hint || !options.launchToken) return null;
const fetchFn = options.fetch ?? fetch;
const baseUrl = trimTrailingSlash(options.shadowApiBaseUrl ?? "http://localhost:3002");
const cacheKey = shadowLaunchIntrospectionCacheKey(fetchFn, baseUrl, options.launchToken);
const now = Date.now();
const cached = shadowLaunchIntrospectionCache.get(cacheKey);
if (cached && cached.expiresAt > now) return cached.value;
shadowLaunchIntrospectionCache.delete(cacheKey);
const inFlight = shadowLaunchIntrospectionRequests.get(cacheKey);
if (inFlight) return inFlight;
const request = (async () => {
const response = await fetchFn(
`${baseUrl}/api/servers/${encodeURIComponent(hint.serverId)}/space-apps/${encodeURIComponent(
hint.appKey
)}/launch/introspect`,
{
method: "POST",
headers: { Authorization: `Bearer ${options.launchToken}` },
signal: AbortSignal.timeout(SHADOW_LAUNCH_INTROSPECTION_TIMEOUT_MS)
}
);
if (!response.ok) {
const payload2 = await readShadowSpaceAppResponsePayload(response).catch(() => null);
return {
active: false,
error: shadowSpaceAppResponseErrorMessage(response.status, payload2, "invalid_launch_token")
};
}
const payload = await response.json().catch(() => null);
const introspection = typeof payload?.active === "boolean" ? payload : null;
if (introspection?.active) {
const cachedAt = Date.now();
const expiresAt = shadowLaunchIntrospectionExpiry(options.launchToken, cachedAt);
if (expiresAt > cachedAt) {
pruneShadowLaunchIntrospectionCache(cachedAt);
shadowLaunchIntrospectionCache.set(cacheKey, { expiresAt, value: introspection });
}
}
return introspection;
})().finally(() => shadowLaunchIntrospectionRequests.delete(cacheKey));
shadowLaunchIntrospectionRequests.set(cacheKey, request);
return request;
}
function shadowSpaceAppLaunchCommandContextFromIntrospection(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_SPACE_APP_PROTOCOL,
serverId: shadow.serverId,
spaceAppId: shadow.spaceAppId ?? "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 ?? "space_app.runtime",
action: command?.action ?? shadow.action ?? "read",
dataClass: command?.dataClass ?? shadow.dataClass ?? "server-private"
};
}
// src/space-app-node.ts
var DEFAULT_SESSION_COOKIE = "space_app_session";
var DEFAULT_SESSION_LIMIT = 4096;
var InMemoryShadowSpaceAppSessionStore = class {
constructor(limit) {
this.limit = limit;
}
limit;
sessions = /* @__PURE__ */ new Map();
get(id) {
return this.sessions.get(id) ?? null;
}
set(session) {
this.sessions.delete(session.id);
this.sessions.set(session.id, session);
while (this.sessions.size > this.limit) {
const oldest = this.sessions.keys().next().value;
if (typeof oldest !== "string") break;
this.sessions.delete(oldest);
}
}
delete(id) {
this.sessions.delete(id);
}
};
var ShadowSpaceAppSessionManager = class {
appKey;
cookieName;
fetchFn;
shadowApiBaseUrl;
store;
now;
constructor(options) {
this.appKey = options.appKey;
this.cookieName = options.cookieName ?? `${DEFAULT_SESSION_COOKIE}_${safeCookieSegment(options.appKey)}`;
this.fetchFn = options.fetch;
this.shadowApiBaseUrl = options.shadowApiBaseUrl;
this.store = options.store ?? new InMemoryShadowSpaceAppSessionStore(options.maxSessions ?? DEFAULT_SESSION_LIMIT);
this.now = options.now ?? Date.now;
}
async exchange(input) {
const launchToken = extractShadowSpaceAppBearerToken(input.authorizationHeader);
if (!launchToken) {
return { ok: false, status: 401, body: { ok: false, error: "launch_required" } };
}
const hint = decodeShadowSpaceAppLaunchTokenHint(launchToken);
if (!hint || hint.appKey !== this.appKey) {
return { ok: false, status: 403, body: { ok: false, error: "wrong_app" } };
}
const launch = await introspectShadowSpaceAppLaunchToken({
launchToken,
shadowApiBaseUrl: this.shadowApiBaseUrl,
fetch: this.fetchFn
}).catch(() => null);
if (!launch?.active || !launch.shadow || launch.shadow.appKey !== this.appKey) {
return { ok: false, status: 401, body: { ok: false, error: "invalid_launch_token" } };
}
const now = this.now();
const expiresAt = typeof launch.exp === "number" && Number.isFinite(launch.exp) ? Math.max(now + 1e3, launch.exp * 1e3) : now + 10 * 60 * 1e3;
const previousId = cookieValue(input.cookieHeader, this.cookieName);
if (previousId) await this.store.delete(previousId);
const session = {
id: (0, import_node_crypto.randomBytes)(32).toString("base64url"),
csrfToken: (0, import_node_crypto.randomBytes)(24).toString("base64url"),
launchToken,
launch,
createdAt: now,
expiresAt
};
await this.store.set(session);
return {
ok: true,
status: 200,
body: { ok: true, expiresAt, csrfToken: session.csrfToken },
setCookie: sessionCookie({
name: this.cookieName,
value: session.id,
requestUrl: input.requestUrl,
maxAgeSeconds: Math.max(1, Math.ceil((expiresAt - now) / 1e3))
})
};
}
async session(cookieHeader) {
const id = cookieValue(cookieHeader, this.cookieName);
if (!id) return null;
const session = await this.store.get(id);
if (!session) return null;
if (session.expiresAt <= this.now()) {
await this.store.delete(id);
return null;
}
return session;
}
hasSessionCookie(cookieHeader) {
return Boolean(cookieValue(cookieHeader, this.cookieName));
}
async authorizedSession(input) {
const session = await this.session(input.cookieHeader);
if (!session) return null;
if (input.requireCsrf !== false && input.csrfToken !== session.csrfToken) return null;
return session;
}
async commandContext(input) {
const session = await this.authorizedSession(input);
if (!session) {
return {
context: null,
session: null,
error: this.hasSessionCookie(input.cookieHeader) ? "invalid_session" : "session_required"
};
}
const context = shadowSpaceAppLaunchCommandContextFromIntrospection(
{
launchToken: session.launchToken,
commandName: input.commandName,
manifest: input.manifest
},
session.launch
);
return context ? { context, session, error: null } : { context: null, session: null, error: "invalid_session" };
}
async eventStream(input) {
const session = await this.authorizedSession({
cookieHeader: input.cookieHeader,
requireCsrf: false
});
if (!session) return null;
const shadow = session.launch.shadow;
if (!shadow || shadow.appKey !== this.appKey) return null;
const baseUrl = (this.shadowApiBaseUrl ?? "http://localhost:3002").replace(/\/+$/u, "");
const headers = {
Accept: "text/event-stream",
Authorization: `Bearer ${session.launchToken}`
};
if (input.lastEventId) headers["Last-Event-ID"] = input.lastEventId;
return (this.fetchFn ?? fetch)(
`${baseUrl}/api/servers/${encodeURIComponent(shadow.serverId)}/space-apps/${encodeURIComponent(
shadow.appKey
)}/events`,
{ headers }
);
}
clearCookie(requestUrl) {
return sessionCookie({
name: this.cookieName,
value: "",
requestUrl,
maxAgeSeconds: 0
});
}
};
function createShadowSpaceAppSessionManager(options) {
return new ShadowSpaceAppSessionManager(options);
}
function safeCookieSegment(value) {
return value.replace(/[^a-zA-Z0-9_-]/gu, "_");
}
function cookieValue(header, name) {
if (!header) return null;
for (const part of header.split(";")) {
const separator = part.indexOf("=");
if (separator < 0) continue;
if (part.slice(0, separator).trim() !== name) continue;
return decodeURIComponent(part.slice(separator + 1).trim());
}
return null;
}
function sessionCookie(input) {
const secure = new URL(input.requestUrl).protocol === "https:";
return [
`${input.name}=${encodeURIComponent(input.value)}`,
"Path=/",
"HttpOnly",
secure ? "SameSite=None" : "SameSite=Lax",
secure ? "Secure" : null,
`Max-Age=${input.maxAgeSeconds}`
].filter(Boolean).join("; ");
}
var ShadowSpaceAppJsonStore = class {
constructor(options) {
this.options = options;
}
options;
read() {
if (!(0, import_node_fs.existsSync)(this.options.filePath)) {
const value = this.defaultValue();
if (this.options.persistDefault !== false) this.write(value);
return value;
}
try {
const parsed = JSON.parse((0, import_node_fs.readFileSync)(this.options.filePath, "utf8"));
if (this.options.validate && !this.options.validate(parsed)) return this.defaultValue();
return this.normalize(parsed);
} catch {
return this.defaultValue();
}
}
write(value) {
const normalized = this.normalize(value);
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(this.options.filePath), { recursive: true });
const tempPath = `${this.options.filePath}.${process.pid}.${Date.now()}.tmp`;
(0, import_node_fs.writeFileSync)(tempPath, `${JSON.stringify(normalized, null, 2)}
`);
(0, import_node_fs.renameSync)(tempPath, this.options.filePath);
return normalized;
}
update(mutator) {
const current = this.clone(this.read());
const next = mutator(current) ?? current;
return this.write(next);
}
reset(nextValue) {
return this.write(nextValue ?? this.defaultValue());
}
defaultValue() {
const value = typeof this.options.defaultValue === "function" ? this.options.defaultValue() : this.options.defaultValue;
return this.normalize(this.clone(value));
}
normalize(value) {
return this.options.normalize ? this.options.normalize(value) : value;
}
clone(value) {
return structuredClone(value);
}
};
function createShadowSpaceAppJsonStore(options) {
return new ShadowSpaceAppJsonStore(options);
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ShadowSpaceAppJsonStore,
ShadowSpaceAppSessionManager,
createShadowSpaceAppJsonStore,
createShadowSpaceAppSessionManager
});
import { e2 as ShadowSpaceAppLaunchIntrospection, dr as ShadowSpaceAppCommandContext, w as ShadowSpaceAppManifest } from './space-app-BhlEB4pg.cjs';
import '@shadowob/shared';
interface ShadowSpaceAppSession {
id: string;
csrfToken: string;
launchToken: string;
launch: ShadowSpaceAppLaunchIntrospection;
createdAt: number;
expiresAt: number;
}
interface ShadowSpaceAppSessionStore {
get(id: string): ShadowSpaceAppSession | null | Promise<ShadowSpaceAppSession | null>;
set(session: ShadowSpaceAppSession): void | Promise<void>;
delete(id: string): void | Promise<void>;
}
interface ShadowSpaceAppSessionManagerOptions {
appKey: string;
shadowApiBaseUrl?: string;
cookieName?: string;
fetch?: typeof fetch;
store?: ShadowSpaceAppSessionStore;
maxSessions?: number;
now?: () => number;
}
type ShadowSpaceAppSessionExchangeResult = {
ok: true;
status: 200;
body: {
ok: true;
expiresAt: number;
csrfToken: string;
};
setCookie: string;
} | {
ok: false;
status: 401 | 403;
body: {
ok: false;
error: 'launch_required' | 'invalid_launch_token' | 'wrong_app';
};
};
type ShadowSpaceAppSessionContextResolution = {
context: ShadowSpaceAppCommandContext;
session: ShadowSpaceAppSession;
error: null;
} | {
context: null;
session: null;
error: 'session_required' | 'invalid_session';
};
/**
* Space App-owned, opaque launch sessions for embedded Space Apps.
*
* A launch token is accepted exactly once at the exchange endpoint and then stays
* server-side. Space App requests use an HttpOnly cookie plus a per-session CSRF token,
* so launch credentials never become a general-purpose request header.
*/
declare class ShadowSpaceAppSessionManager {
private readonly appKey;
private readonly cookieName;
private readonly fetchFn?;
private readonly shadowApiBaseUrl?;
private readonly store;
private readonly now;
constructor(options: ShadowSpaceAppSessionManagerOptions);
exchange(input: {
authorizationHeader?: string | null;
cookieHeader?: string | null;
requestUrl: string;
}): Promise<ShadowSpaceAppSessionExchangeResult>;
session(cookieHeader?: string | null): Promise<ShadowSpaceAppSession | null>;
hasSessionCookie(cookieHeader?: string | null): boolean;
authorizedSession(input: {
cookieHeader?: string | null;
csrfToken?: string | null;
requireCsrf?: boolean;
}): Promise<ShadowSpaceAppSession | null>;
commandContext(input: {
cookieHeader?: string | null;
csrfToken?: string | null;
commandName: string;
manifest: Pick<ShadowSpaceAppManifest, 'appKey' | 'commands'>;
}): Promise<ShadowSpaceAppSessionContextResolution>;
eventStream(input: {
cookieHeader?: string | null;
lastEventId?: string | null;
}): Promise<Response | null>;
clearCookie(requestUrl: string): string;
}
declare function createShadowSpaceAppSessionManager(options: ShadowSpaceAppSessionManagerOptions): ShadowSpaceAppSessionManager;
interface ShadowSpaceAppJsonStoreOptions<T> {
filePath: string;
defaultValue: T | (() => T);
validate?: (value: unknown) => value is T;
normalize?: (value: T) => T;
persistDefault?: boolean;
}
declare class ShadowSpaceAppJsonStore<T> {
private readonly options;
constructor(options: ShadowSpaceAppJsonStoreOptions<T>);
read(): T;
write(value: T): T;
update(mutator: (value: T) => T | void): T;
reset(nextValue?: T): T;
private defaultValue;
private normalize;
private clone;
}
declare function createShadowSpaceAppJsonStore<T>(options: ShadowSpaceAppJsonStoreOptions<T>): ShadowSpaceAppJsonStore<T>;
export { ShadowSpaceAppJsonStore, type ShadowSpaceAppJsonStoreOptions, type ShadowSpaceAppSession, type ShadowSpaceAppSessionContextResolution, type ShadowSpaceAppSessionExchangeResult, ShadowSpaceAppSessionManager, type ShadowSpaceAppSessionManagerOptions, type ShadowSpaceAppSessionStore, createShadowSpaceAppJsonStore, createShadowSpaceAppSessionManager };
import { e2 as ShadowSpaceAppLaunchIntrospection, dr as ShadowSpaceAppCommandContext, w as ShadowSpaceAppManifest } from './space-app-BhlEB4pg.js';
import '@shadowob/shared';
interface ShadowSpaceAppSession {
id: string;
csrfToken: string;
launchToken: string;
launch: ShadowSpaceAppLaunchIntrospection;
createdAt: number;
expiresAt: number;
}
interface ShadowSpaceAppSessionStore {
get(id: string): ShadowSpaceAppSession | null | Promise<ShadowSpaceAppSession | null>;
set(session: ShadowSpaceAppSession): void | Promise<void>;
delete(id: string): void | Promise<void>;
}
interface ShadowSpaceAppSessionManagerOptions {
appKey: string;
shadowApiBaseUrl?: string;
cookieName?: string;
fetch?: typeof fetch;
store?: ShadowSpaceAppSessionStore;
maxSessions?: number;
now?: () => number;
}
type ShadowSpaceAppSessionExchangeResult = {
ok: true;
status: 200;
body: {
ok: true;
expiresAt: number;
csrfToken: string;
};
setCookie: string;
} | {
ok: false;
status: 401 | 403;
body: {
ok: false;
error: 'launch_required' | 'invalid_launch_token' | 'wrong_app';
};
};
type ShadowSpaceAppSessionContextResolution = {
context: ShadowSpaceAppCommandContext;
session: ShadowSpaceAppSession;
error: null;
} | {
context: null;
session: null;
error: 'session_required' | 'invalid_session';
};
/**
* Space App-owned, opaque launch sessions for embedded Space Apps.
*
* A launch token is accepted exactly once at the exchange endpoint and then stays
* server-side. Space App requests use an HttpOnly cookie plus a per-session CSRF token,
* so launch credentials never become a general-purpose request header.
*/
declare class ShadowSpaceAppSessionManager {
private readonly appKey;
private readonly cookieName;
private readonly fetchFn?;
private readonly shadowApiBaseUrl?;
private readonly store;
private readonly now;
constructor(options: ShadowSpaceAppSessionManagerOptions);
exchange(input: {
authorizationHeader?: string | null;
cookieHeader?: string | null;
requestUrl: string;
}): Promise<ShadowSpaceAppSessionExchangeResult>;
session(cookieHeader?: string | null): Promise<ShadowSpaceAppSession | null>;
hasSessionCookie(cookieHeader?: string | null): boolean;
authorizedSession(input: {
cookieHeader?: string | null;
csrfToken?: string | null;
requireCsrf?: boolean;
}): Promise<ShadowSpaceAppSession | null>;
commandContext(input: {
cookieHeader?: string | null;
csrfToken?: string | null;
commandName: string;
manifest: Pick<ShadowSpaceAppManifest, 'appKey' | 'commands'>;
}): Promise<ShadowSpaceAppSessionContextResolution>;
eventStream(input: {
cookieHeader?: string | null;
lastEventId?: string | null;
}): Promise<Response | null>;
clearCookie(requestUrl: string): string;
}
declare function createShadowSpaceAppSessionManager(options: ShadowSpaceAppSessionManagerOptions): ShadowSpaceAppSessionManager;
interface ShadowSpaceAppJsonStoreOptions<T> {
filePath: string;
defaultValue: T | (() => T);
validate?: (value: unknown) => value is T;
normalize?: (value: T) => T;
persistDefault?: boolean;
}
declare class ShadowSpaceAppJsonStore<T> {
private readonly options;
constructor(options: ShadowSpaceAppJsonStoreOptions<T>);
read(): T;
write(value: T): T;
update(mutator: (value: T) => T | void): T;
reset(nextValue?: T): T;
private defaultValue;
private normalize;
private clone;
}
declare function createShadowSpaceAppJsonStore<T>(options: ShadowSpaceAppJsonStoreOptions<T>): ShadowSpaceAppJsonStore<T>;
export { ShadowSpaceAppJsonStore, type ShadowSpaceAppJsonStoreOptions, type ShadowSpaceAppSession, type ShadowSpaceAppSessionContextResolution, type ShadowSpaceAppSessionExchangeResult, ShadowSpaceAppSessionManager, type ShadowSpaceAppSessionManagerOptions, type ShadowSpaceAppSessionStore, createShadowSpaceAppJsonStore, createShadowSpaceAppSessionManager };
import {
decodeShadowSpaceAppLaunchTokenHint,
extractShadowSpaceAppBearerToken,
introspectShadowSpaceAppLaunchToken,
shadowSpaceAppLaunchCommandContextFromIntrospection
} from "./chunk-NADGSKKC.js";
// src/space-app-node.ts
import { randomBytes } from "crypto";
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
import { dirname } from "path";
var DEFAULT_SESSION_COOKIE = "space_app_session";
var DEFAULT_SESSION_LIMIT = 4096;
var InMemoryShadowSpaceAppSessionStore = class {
constructor(limit) {
this.limit = limit;
}
limit;
sessions = /* @__PURE__ */ new Map();
get(id) {
return this.sessions.get(id) ?? null;
}
set(session) {
this.sessions.delete(session.id);
this.sessions.set(session.id, session);
while (this.sessions.size > this.limit) {
const oldest = this.sessions.keys().next().value;
if (typeof oldest !== "string") break;
this.sessions.delete(oldest);
}
}
delete(id) {
this.sessions.delete(id);
}
};
var ShadowSpaceAppSessionManager = class {
appKey;
cookieName;
fetchFn;
shadowApiBaseUrl;
store;
now;
constructor(options) {
this.appKey = options.appKey;
this.cookieName = options.cookieName ?? `${DEFAULT_SESSION_COOKIE}_${safeCookieSegment(options.appKey)}`;
this.fetchFn = options.fetch;
this.shadowApiBaseUrl = options.shadowApiBaseUrl;
this.store = options.store ?? new InMemoryShadowSpaceAppSessionStore(options.maxSessions ?? DEFAULT_SESSION_LIMIT);
this.now = options.now ?? Date.now;
}
async exchange(input) {
const launchToken = extractShadowSpaceAppBearerToken(input.authorizationHeader);
if (!launchToken) {
return { ok: false, status: 401, body: { ok: false, error: "launch_required" } };
}
const hint = decodeShadowSpaceAppLaunchTokenHint(launchToken);
if (!hint || hint.appKey !== this.appKey) {
return { ok: false, status: 403, body: { ok: false, error: "wrong_app" } };
}
const launch = await introspectShadowSpaceAppLaunchToken({
launchToken,
shadowApiBaseUrl: this.shadowApiBaseUrl,
fetch: this.fetchFn
}).catch(() => null);
if (!launch?.active || !launch.shadow || launch.shadow.appKey !== this.appKey) {
return { ok: false, status: 401, body: { ok: false, error: "invalid_launch_token" } };
}
const now = this.now();
const expiresAt = typeof launch.exp === "number" && Number.isFinite(launch.exp) ? Math.max(now + 1e3, launch.exp * 1e3) : now + 10 * 60 * 1e3;
const previousId = cookieValue(input.cookieHeader, this.cookieName);
if (previousId) await this.store.delete(previousId);
const session = {
id: randomBytes(32).toString("base64url"),
csrfToken: randomBytes(24).toString("base64url"),
launchToken,
launch,
createdAt: now,
expiresAt
};
await this.store.set(session);
return {
ok: true,
status: 200,
body: { ok: true, expiresAt, csrfToken: session.csrfToken },
setCookie: sessionCookie({
name: this.cookieName,
value: session.id,
requestUrl: input.requestUrl,
maxAgeSeconds: Math.max(1, Math.ceil((expiresAt - now) / 1e3))
})
};
}
async session(cookieHeader) {
const id = cookieValue(cookieHeader, this.cookieName);
if (!id) return null;
const session = await this.store.get(id);
if (!session) return null;
if (session.expiresAt <= this.now()) {
await this.store.delete(id);
return null;
}
return session;
}
hasSessionCookie(cookieHeader) {
return Boolean(cookieValue(cookieHeader, this.cookieName));
}
async authorizedSession(input) {
const session = await this.session(input.cookieHeader);
if (!session) return null;
if (input.requireCsrf !== false && input.csrfToken !== session.csrfToken) return null;
return session;
}
async commandContext(input) {
const session = await this.authorizedSession(input);
if (!session) {
return {
context: null,
session: null,
error: this.hasSessionCookie(input.cookieHeader) ? "invalid_session" : "session_required"
};
}
const context = shadowSpaceAppLaunchCommandContextFromIntrospection(
{
launchToken: session.launchToken,
commandName: input.commandName,
manifest: input.manifest
},
session.launch
);
return context ? { context, session, error: null } : { context: null, session: null, error: "invalid_session" };
}
async eventStream(input) {
const session = await this.authorizedSession({
cookieHeader: input.cookieHeader,
requireCsrf: false
});
if (!session) return null;
const shadow = session.launch.shadow;
if (!shadow || shadow.appKey !== this.appKey) return null;
const baseUrl = (this.shadowApiBaseUrl ?? "http://localhost:3002").replace(/\/+$/u, "");
const headers = {
Accept: "text/event-stream",
Authorization: `Bearer ${session.launchToken}`
};
if (input.lastEventId) headers["Last-Event-ID"] = input.lastEventId;
return (this.fetchFn ?? fetch)(
`${baseUrl}/api/servers/${encodeURIComponent(shadow.serverId)}/space-apps/${encodeURIComponent(
shadow.appKey
)}/events`,
{ headers }
);
}
clearCookie(requestUrl) {
return sessionCookie({
name: this.cookieName,
value: "",
requestUrl,
maxAgeSeconds: 0
});
}
};
function createShadowSpaceAppSessionManager(options) {
return new ShadowSpaceAppSessionManager(options);
}
function safeCookieSegment(value) {
return value.replace(/[^a-zA-Z0-9_-]/gu, "_");
}
function cookieValue(header, name) {
if (!header) return null;
for (const part of header.split(";")) {
const separator = part.indexOf("=");
if (separator < 0) continue;
if (part.slice(0, separator).trim() !== name) continue;
return decodeURIComponent(part.slice(separator + 1).trim());
}
return null;
}
function sessionCookie(input) {
const secure = new URL(input.requestUrl).protocol === "https:";
return [
`${input.name}=${encodeURIComponent(input.value)}`,
"Path=/",
"HttpOnly",
secure ? "SameSite=None" : "SameSite=Lax",
secure ? "Secure" : null,
`Max-Age=${input.maxAgeSeconds}`
].filter(Boolean).join("; ");
}
var ShadowSpaceAppJsonStore = class {
constructor(options) {
this.options = options;
}
options;
read() {
if (!existsSync(this.options.filePath)) {
const value = this.defaultValue();
if (this.options.persistDefault !== false) this.write(value);
return value;
}
try {
const parsed = JSON.parse(readFileSync(this.options.filePath, "utf8"));
if (this.options.validate && !this.options.validate(parsed)) return this.defaultValue();
return this.normalize(parsed);
} catch {
return this.defaultValue();
}
}
write(value) {
const normalized = this.normalize(value);
mkdirSync(dirname(this.options.filePath), { recursive: true });
const tempPath = `${this.options.filePath}.${process.pid}.${Date.now()}.tmp`;
writeFileSync(tempPath, `${JSON.stringify(normalized, null, 2)}
`);
renameSync(tempPath, this.options.filePath);
return normalized;
}
update(mutator) {
const current = this.clone(this.read());
const next = mutator(current) ?? current;
return this.write(next);
}
reset(nextValue) {
return this.write(nextValue ?? this.defaultValue());
}
defaultValue() {
const value = typeof this.options.defaultValue === "function" ? this.options.defaultValue() : this.options.defaultValue;
return this.normalize(this.clone(value));
}
normalize(value) {
return this.options.normalize ? this.options.normalize(value) : value;
}
clone(value) {
return structuredClone(value);
}
};
function createShadowSpaceAppJsonStore(options) {
return new ShadowSpaceAppJsonStore(options);
}
export {
ShadowSpaceAppJsonStore,
ShadowSpaceAppSessionManager,
createShadowSpaceAppJsonStore,
createShadowSpaceAppSessionManager
};
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/space-app.ts
var space_app_exports = {};
__export(space_app_exports, {
BUDDY_INBOX_DELIVERY_PERMISSION: () => import_shared.BUDDY_INBOX_DELIVERY_PERMISSION,
SHADOW_SPACE_APP_COMMAND_COMPLETED_EVENT: () => SHADOW_SPACE_APP_COMMAND_COMPLETED_EVENT,
SHADOW_SPACE_APP_COMMAND_EVENTS: () => SHADOW_SPACE_APP_COMMAND_EVENTS,
SHADOW_SPACE_APP_COMMAND_FAILED_EVENT: () => SHADOW_SPACE_APP_COMMAND_FAILED_EVENT,
SHADOW_SPACE_APP_PROTOCOL: () => SHADOW_SPACE_APP_PROTOCOL,
SHADOW_SPACE_APP_PUBLIC_AVATAR_CACHE_CONTROL: () => SHADOW_SPACE_APP_PUBLIC_AVATAR_CACHE_CONTROL,
ShadowSpaceAppCommandError: () => ShadowSpaceAppCommandError,
ShadowSpaceAppHttpError: () => ShadowSpaceAppHttpError,
ShadowSpaceAppOutbox: () => ShadowSpaceAppOutbox,
ShadowSpaceAppRuntime: () => ShadowSpaceAppRuntime,
buildShadowSpaceAppInboxDelivery: () => buildShadowSpaceAppInboxDelivery,
buildShadowSpaceAppInboxTaskRequest: () => buildShadowSpaceAppInboxTaskRequest,
createShadowSpaceAppCollaborationCursor: () => createShadowSpaceAppCollaborationCursor,
createShadowSpaceAppCollaborationEvent: () => createShadowSpaceAppCollaborationEvent,
createShadowSpaceAppCollaborationResource: () => createShadowSpaceAppCollaborationResource,
createShadowSpaceAppLaunchPoll: () => createShadowSpaceAppLaunchPoll,
createShadowSpaceAppManifest: () => createShadowSpaceAppManifest,
createShadowSpaceAppRuntime: () => createShadowSpaceAppRuntime,
decodeShadowSpaceAppLaunchTokenHint: () => decodeShadowSpaceAppLaunchTokenHint,
defineShadowSpaceApp: () => defineShadowSpaceApp,
deliverShadowSpaceAppLaunchOutbox: () => deliverShadowSpaceAppLaunchOutbox,
ensureShadowSpaceAppLaunchBuddyTaskGrant: () => ensureShadowSpaceAppLaunchBuddyTaskGrant,
ensureShadowSpaceAppLaunchChannel: () => ensureShadowSpaceAppLaunchChannel,
extractShadowSpaceAppBearerToken: () => extractShadowSpaceAppBearerToken,
fetchShadowSpaceAppLaunchChannels: () => fetchShadowSpaceAppLaunchChannels,
fetchShadowSpaceAppLaunchInboxes: () => fetchShadowSpaceAppLaunchInboxes,
fetchShadowSpaceAppLaunchMembers: () => fetchShadowSpaceAppLaunchMembers,
fetchShadowSpaceAppLaunchMessage: () => fetchShadowSpaceAppLaunchMessage,
getShadowSpaceAppChannelMessageDeliveries: () => getShadowSpaceAppChannelMessageDeliveries,
getShadowSpaceAppChannelMessageErrors: () => getShadowSpaceAppChannelMessageErrors,
getShadowSpaceAppInboxDeliveries: () => getShadowSpaceAppInboxDeliveries,
getShadowSpaceAppInboxErrors: () => getShadowSpaceAppInboxErrors,
getShadowSpaceAppPendingChannelMessages: () => getShadowSpaceAppPendingChannelMessages,
getShadowSpaceAppPendingInboxTasks: () => getShadowSpaceAppPendingInboxTasks,
getShadowSpaceAppTaskCardId: () => getShadowSpaceAppTaskCardId,
hasShadowSpaceAppPendingOutbox: () => hasShadowSpaceAppPendingOutbox,
introspectShadowSpaceAppLaunchToken: () => introspectShadowSpaceAppLaunchToken,
introspectShadowSpaceAppToken: () => introspectShadowSpaceAppToken,
isShadowSpaceAppSignedMediaUrl: () => isShadowSpaceAppSignedMediaUrl,
normalizeShadowSpaceAppAvatarUrl: () => normalizeShadowSpaceAppAvatarUrl,
normalizeShadowSpaceAppBaseCursor: () => normalizeShadowSpaceAppBaseCursor,
normalizeShadowSpaceAppClientMutationId: () => normalizeShadowSpaceAppClientMutationId,
normalizeShadowSpaceAppCommandInput: () => normalizeShadowSpaceAppCommandInput,
parseShadowSpaceAppCommandRequest: () => parseShadowSpaceAppCommandRequest,
publishShadowSpaceAppNotification: () => publishShadowSpaceAppNotification,
readShadowSpaceAppCommandResponse: () => readShadowSpaceAppCommandResponse,
resolveShadowSpaceAppLaunchCommandContext: () => resolveShadowSpaceAppLaunchCommandContext,
resolveShadowSpaceAppLaunchCommandContextResolution: () => resolveShadowSpaceAppLaunchCommandContextResolution,
shadowSpaceAppActorAvatarUrl: () => shadowSpaceAppActorAvatarUrl,
shadowSpaceAppActorDisplayName: () => shadowSpaceAppActorDisplayName,
shadowSpaceAppActorRef: () => shadowSpaceAppActorRef,
shadowSpaceAppApiBaseUrl: () => shadowSpaceAppApiBaseUrl,
shadowSpaceAppAvatarRedirectUrl: () => shadowSpaceAppAvatarRedirectUrl,
shadowSpaceAppDisplayIdentity: () => shadowSpaceAppDisplayIdentity,
shadowSpaceAppError: () => shadowSpaceAppError,
shadowSpaceAppIdentityKey: () => shadowSpaceAppIdentityKey,
shadowSpaceAppIdentitySnapshot: () => shadowSpaceAppIdentitySnapshot,
shadowSpaceAppInboxTaskEndpoint: () => shadowSpaceAppInboxTaskEndpoint,
shadowSpaceAppLaunchCommandContextFromIntrospection: () => shadowSpaceAppLaunchCommandContextFromIntrospection,
shadowSpaceAppLaunchIntrospectionError: () => shadowSpaceAppLaunchIntrospectionError,
shadowSpaceAppPublicBaseUrl: () => shadowSpaceAppPublicBaseUrl,
shadowSpaceAppPublicUrl: () => shadowSpaceAppPublicUrl,
unwrapShadowSpaceAppCommandPayload: () => unwrapShadowSpaceAppCommandPayload,
validateShadowSpaceAppJsonSchema: () => validateShadowSpaceAppJsonSchema
});
module.exports = __toCommonJS(space_app_exports);
var import_shared = require("@shadowob/shared");
var SHADOW_LAUNCH_INTROSPECTION_CACHE_TTL_MS = 5e3;
var SHADOW_LAUNCH_INTROSPECTION_TIMEOUT_MS = 2500;
var SHADOW_LAUNCH_INTROSPECTION_CACHE_LIMIT = 256;
var shadowLaunchIntrospectionCache = /* @__PURE__ */ new Map();
var shadowLaunchIntrospectionRequests = /* @__PURE__ */ new Map();
var shadowLaunchFetchIds = /* @__PURE__ */ new WeakMap();
var nextShadowLaunchFetchId = 1;
var SHADOW_SPACE_APP_PROTOCOL = "shadow.space-app/1";
var SHADOW_SPACE_APP_COMMAND_COMPLETED_EVENT = "space_app.command.completed";
var SHADOW_SPACE_APP_COMMAND_FAILED_EVENT = "space_app.command.failed";
var SHADOW_SPACE_APP_COMMAND_EVENTS = [
SHADOW_SPACE_APP_COMMAND_COMPLETED_EVENT,
SHADOW_SPACE_APP_COMMAND_FAILED_EVENT
];
var ShadowSpaceAppHttpError = class extends Error {
status;
payload;
constructor(status, message, payload) {
super(message);
this.name = "ShadowSpaceAppHttpError";
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 shadowSpaceAppInboxTaskEndpoint(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 buildShadowSpaceAppInboxTaskRequest(input) {
const appId = input.app.id ?? input.app.appId ?? input.app.appKey;
const spaceAppData = isProtocolRecord(input.task.data?.spaceApp) ? input.task.data.spaceApp : {};
return {
endpoint: shadowSpaceAppInboxTaskEndpoint(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: "space_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 ?? {},
spaceApp: {
...spaceAppData,
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 getShadowSpaceAppTaskCardId(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 buildShadowSpaceAppInboxDelivery(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: getShadowSpaceAppTaskCardId(message),
idempotencyKey: input.idempotencyKey
};
}
function shadowFromPayload(payload) {
if (payload.protocol === SHADOW_SPACE_APP_PROTOCOL) {
return payload;
}
const shadow = isProtocolRecord(payload.shadow) ? payload.shadow : null;
if (shadow?.protocol === SHADOW_SPACE_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_SPACE_APP_PROTOCOL,
outbox: {
...existing.outbox ?? {},
...shadow.outbox ?? {}
}
}
};
}
function getShadowSpaceAppInboxDeliveries(payload) {
if (!isProtocolRecord(payload)) return [];
const shadow = shadowFromPayload(payload);
return shadow?.outbox?.deliveries ?? [];
}
function getShadowSpaceAppInboxErrors(payload) {
if (!isProtocolRecord(payload)) return [];
const shadow = shadowFromPayload(payload);
return shadow?.outbox?.errors ?? [];
}
function getShadowSpaceAppPendingInboxTasks(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, ...getShadowSpaceAppPendingInboxTasks(payload.result, depth + 1)] : tasks;
}
function getShadowSpaceAppChannelMessageDeliveries(payload) {
if (!isProtocolRecord(payload)) return [];
const shadow = shadowFromPayload(payload);
return shadow?.outbox?.channelMessageDeliveries ?? [];
}
function getShadowSpaceAppChannelMessageErrors(payload) {
if (!isProtocolRecord(payload)) return [];
const shadow = shadowFromPayload(payload);
return shadow?.outbox?.channelMessageErrors ?? [];
}
function getShadowSpaceAppPendingChannelMessages(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, ...getShadowSpaceAppPendingChannelMessages(payload.result, depth + 1)] : messages;
}
function hasShadowSpaceAppPendingOutbox(payload) {
return getShadowSpaceAppPendingInboxTasks(payload).length > 0 || getShadowSpaceAppPendingChannelMessages(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 unwrapShadowSpaceAppCommandPayload(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 = unwrapShadowSpaceAppCommandPayload(payload.result);
const shadow = shadowFromPayload(payload);
if (isProtocolRecord(nested)) return mergeShadowResult(nested, shadow);
return nested;
}
return payload;
}
async function readShadowSpaceAppResponsePayload(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 ShadowSpaceAppHttpError(response.status, "Command returned invalid JSON", text);
}
}
function shadowSpaceAppResponseErrorMessage(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 readShadowSpaceAppCommandResponse(response) {
const payload = await readShadowSpaceAppResponsePayload(response);
if (!response.ok || isProtocolRecord(payload) && payload.ok === false) {
throw new ShadowSpaceAppHttpError(
response.status,
shadowSpaceAppResponseErrorMessage(response.status, payload),
payload
);
}
return unwrapShadowSpaceAppCommandPayload(payload);
}
var ShadowSpaceAppOutbox = 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_SPACE_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_SPACE_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 shadowSpaceAppApiBaseUrl(env = {}) {
return trimTrailingSlash(
firstEnvironmentValue(
env,
["SHADOWOB_INTERNAL_SERVER_URL", "SHADOWOB_SERVER_URL"],
"http://localhost:3002"
)
);
}
function shadowSpaceAppPublicBaseUrl(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 shadowSpaceAppPublicUrl(pathOrUrl, env = {}) {
if (!pathOrUrl.startsWith("/")) return pathOrUrl;
return joinBasePath(shadowSpaceAppPublicBaseUrl(env), pathOrUrl);
}
function isShadowSpaceAppSignedMediaUrl(value, env = {}) {
const mediaUrl = value.trim();
if (mediaUrl.startsWith("/api/media/signed/")) return true;
try {
return new URL(mediaUrl, shadowSpaceAppPublicBaseUrl(env)).pathname.startsWith(
"/api/media/signed/"
);
} catch {
return false;
}
}
function normalizeShadowSpaceAppAvatarUrl(value, env = {}) {
if (typeof value !== "string") return null;
const avatarUrl = value.trim();
if (!avatarUrl || avatarUrl.length > 500) return null;
if (isShadowSpaceAppSignedMediaUrl(avatarUrl, env)) return null;
return shadowSpaceAppPublicUrl(avatarUrl, env);
}
function shadowSpaceAppAvatarRedirectUrl(requestUrl, env = {}) {
return shadowSpaceAppPublicUrl(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 extractShadowSpaceAppBearerToken(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 decodeShadowSpaceAppLaunchTokenPayload(token) {
if (!token) return null;
const parts = token.split(".");
if (parts.length !== 3 || parts[0] !== "sat_v1") return null;
return decodeBase64UrlJson(parts[1]);
}
function decodeShadowSpaceAppLaunchTokenHint(token) {
const payload = decodeShadowSpaceAppLaunchTokenPayload(token);
if (typeof payload?.serverId !== "string" || typeof payload.appKey !== "string") return null;
return { serverId: payload.serverId, appKey: payload.appKey };
}
function shadowLaunchFetchId(fetchFn) {
const existing = shadowLaunchFetchIds.get(fetchFn);
if (existing) return existing;
const id = nextShadowLaunchFetchId++;
shadowLaunchFetchIds.set(fetchFn, id);
return id;
}
function shadowLaunchIntrospectionCacheKey(fetchFn, baseUrl, launchToken) {
return `${shadowLaunchFetchId(fetchFn)}:${baseUrl}:${launchToken}`;
}
function pruneShadowLaunchIntrospectionCache(now) {
for (const [key, entry] of shadowLaunchIntrospectionCache) {
if (entry.expiresAt <= now) shadowLaunchIntrospectionCache.delete(key);
}
while (shadowLaunchIntrospectionCache.size >= SHADOW_LAUNCH_INTROSPECTION_CACHE_LIMIT) {
const oldestKey = shadowLaunchIntrospectionCache.keys().next().value;
if (typeof oldestKey !== "string") break;
shadowLaunchIntrospectionCache.delete(oldestKey);
}
}
function shadowLaunchIntrospectionExpiry(launchToken, now) {
const payload = decodeShadowSpaceAppLaunchTokenPayload(launchToken);
const tokenExpiresAt = typeof payload?.exp === "number" && Number.isFinite(payload.exp) ? payload.exp * 1e3 : Number.POSITIVE_INFINITY;
return Math.min(now + SHADOW_LAUNCH_INTROSPECTION_CACHE_TTL_MS, tokenExpiresAt);
}
async function fetchShadowSpaceAppLaunchInboxes(options) {
const hint = decodeShadowSpaceAppLaunchTokenHint(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)}/space-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 fetchShadowSpaceAppLaunchMembers(options) {
const hint = decodeShadowSpaceAppLaunchTokenHint(options.launchToken);
if (!hint || !options.launchToken) return { members: [] };
const fetchFn = options.fetch ?? fetch;
const baseUrl = trimTrailingSlash(options.shadowApiBaseUrl ?? "http://localhost:3002");
const response = await fetchFn(
`${baseUrl}/api/servers/${encodeURIComponent(hint.serverId)}/space-apps/${encodeURIComponent(
hint.appKey
)}/launch/members`,
{
headers: { Authorization: `Bearer ${options.launchToken}` },
signal: AbortSignal.timeout(SHADOW_LAUNCH_INTROSPECTION_TIMEOUT_MS)
}
);
if (!response.ok) {
const message = await response.text().catch(() => "");
throw new Error(`Shadow launch member lookup failed (${response.status}): ${message}`);
}
return await response.json();
}
async function fetchShadowSpaceAppLaunchChannels(options) {
const hint = decodeShadowSpaceAppLaunchTokenHint(options.launchToken);
if (!hint || !options.launchToken) return { channels: [] };
const fetchFn = options.fetch ?? fetch;
const baseUrl = trimTrailingSlash(options.shadowApiBaseUrl ?? "http://localhost:3002");
const response = await fetchFn(
`${baseUrl}/api/servers/${encodeURIComponent(hint.serverId)}/space-apps/${encodeURIComponent(
hint.appKey
)}/launch/channels`,
{
headers: { Authorization: `Bearer ${options.launchToken}` },
signal: AbortSignal.timeout(SHADOW_LAUNCH_INTROSPECTION_TIMEOUT_MS)
}
);
if (!response.ok) {
const message = await response.text().catch(() => "");
throw new Error(`Shadow launch channel lookup failed (${response.status}): ${message}`);
}
return await response.json();
}
async function fetchShadowSpaceAppLaunchMessage(options) {
const hint = decodeShadowSpaceAppLaunchTokenHint(options.launchToken);
if (!hint || !options.launchToken) throw new Error("Shadow launch token is required");
const fetchFn = options.fetch ?? fetch;
const baseUrl = trimTrailingSlash(options.shadowApiBaseUrl ?? "http://localhost:3002");
const response = await fetchFn(
`${baseUrl}/api/servers/${encodeURIComponent(hint.serverId)}/space-apps/${encodeURIComponent(
hint.appKey
)}/launch/messages/${encodeURIComponent(options.messageId)}`,
{
headers: { Authorization: `Bearer ${options.launchToken}` },
signal: AbortSignal.timeout(SHADOW_LAUNCH_INTROSPECTION_TIMEOUT_MS)
}
);
if (!response.ok) {
const message = await response.text().catch(() => "");
throw new Error(`Shadow launch message lookup failed (${response.status}): ${message}`);
}
return await response.json();
}
async function ensureShadowSpaceAppLaunchChannel(options) {
const hint = decodeShadowSpaceAppLaunchTokenHint(options.launchToken);
if (!hint || !options.launchToken) throw new Error("Shadow launch token is required");
const fetchFn = options.fetch ?? fetch;
const baseUrl = trimTrailingSlash(options.shadowApiBaseUrl ?? "http://localhost:3002");
const response = await fetchFn(
`${baseUrl}/api/servers/${encodeURIComponent(hint.serverId)}/space-apps/${encodeURIComponent(
hint.appKey
)}/launch/channels/ensure`,
{
method: "POST",
headers: {
Authorization: `Bearer ${options.launchToken}`,
"Content-Type": "application/json"
},
body: JSON.stringify(options.input),
signal: AbortSignal.timeout(SHADOW_LAUNCH_INTROSPECTION_TIMEOUT_MS)
}
);
if (!response.ok) {
const message = await response.text().catch(() => "");
throw new Error(`Shadow launch channel ensure failed (${response.status}): ${message}`);
}
return await response.json();
}
async function createShadowSpaceAppLaunchPoll(options) {
const hint = decodeShadowSpaceAppLaunchTokenHint(options.launchToken);
if (!hint || !options.launchToken) throw new Error("Shadow launch token is required");
const fetchFn = options.fetch ?? fetch;
const baseUrl = trimTrailingSlash(options.shadowApiBaseUrl ?? "http://localhost:3002");
const response = await fetchFn(
`${baseUrl}/api/servers/${encodeURIComponent(hint.serverId)}/space-apps/${encodeURIComponent(
hint.appKey
)}/launch/polls`,
{
method: "POST",
headers: {
Authorization: `Bearer ${options.launchToken}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
...options.input,
answers: options.input.answers.map(
(answer) => typeof answer === "string" ? { text: answer } : answer
)
}),
signal: AbortSignal.timeout(SHADOW_LAUNCH_INTROSPECTION_TIMEOUT_MS)
}
);
if (!response.ok) {
const message = await response.text().catch(() => "");
throw new Error(`Shadow launch poll creation failed (${response.status}): ${message}`);
}
return await response.json();
}
async function ensureShadowSpaceAppLaunchBuddyTaskGrant(options) {
const hint = decodeShadowSpaceAppLaunchTokenHint(options.launchToken);
if (!hint || !options.launchToken) throw new Error("Shadow launch token is required");
const fetchFn = options.fetch ?? fetch;
const baseUrl = trimTrailingSlash(options.shadowApiBaseUrl ?? "http://localhost:3002");
const response = await fetchFn(
`${baseUrl}/api/servers/${encodeURIComponent(hint.serverId)}/space-apps/${encodeURIComponent(
hint.appKey
)}/launch/buddy-grants/ensure`,
{
method: "POST",
headers: {
Authorization: `Bearer ${options.launchToken}`,
"Content-Type": "application/json"
},
body: JSON.stringify(options.input),
signal: AbortSignal.timeout(SHADOW_LAUNCH_INTROSPECTION_TIMEOUT_MS)
}
);
if (!response.ok) {
const message = await response.text().catch(() => "");
throw new Error(`Shadow launch Buddy grant failed (${response.status}): ${message}`);
}
return await response.json();
}
async function introspectShadowSpaceAppLaunchToken(options) {
const hint = decodeShadowSpaceAppLaunchTokenHint(options.launchToken);
if (!hint || !options.launchToken) return null;
const fetchFn = options.fetch ?? fetch;
const baseUrl = trimTrailingSlash(options.shadowApiBaseUrl ?? "http://localhost:3002");
const cacheKey = shadowLaunchIntrospectionCacheKey(fetchFn, baseUrl, options.launchToken);
const now = Date.now();
const cached = shadowLaunchIntrospectionCache.get(cacheKey);
if (cached && cached.expiresAt > now) return cached.value;
shadowLaunchIntrospectionCache.delete(cacheKey);
const inFlight = shadowLaunchIntrospectionRequests.get(cacheKey);
if (inFlight) return inFlight;
const request = (async () => {
const response = await fetchFn(
`${baseUrl}/api/servers/${encodeURIComponent(hint.serverId)}/space-apps/${encodeURIComponent(
hint.appKey
)}/launch/introspect`,
{
method: "POST",
headers: { Authorization: `Bearer ${options.launchToken}` },
signal: AbortSignal.timeout(SHADOW_LAUNCH_INTROSPECTION_TIMEOUT_MS)
}
);
if (!response.ok) {
const payload2 = await readShadowSpaceAppResponsePayload(response).catch(() => null);
return {
active: false,
error: shadowSpaceAppResponseErrorMessage(response.status, payload2, "invalid_launch_token")
};
}
const payload = await response.json().catch(() => null);
const introspection = typeof payload?.active === "boolean" ? payload : null;
if (introspection?.active) {
const cachedAt = Date.now();
const expiresAt = shadowLaunchIntrospectionExpiry(options.launchToken, cachedAt);
if (expiresAt > cachedAt) {
pruneShadowLaunchIntrospectionCache(cachedAt);
shadowLaunchIntrospectionCache.set(cacheKey, { expiresAt, value: introspection });
}
}
return introspection;
})().finally(() => shadowLaunchIntrospectionRequests.delete(cacheKey));
shadowLaunchIntrospectionRequests.set(cacheKey, request);
return request;
}
function shadowSpaceAppLaunchIntrospectionError(introspection) {
return introspection?.error ?? introspection?.reason ?? introspection?.error_description ?? "invalid_launch_token";
}
function shadowSpaceAppLaunchCommandContextFromIntrospection(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_SPACE_APP_PROTOCOL,
serverId: shadow.serverId,
spaceAppId: shadow.spaceAppId ?? "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 ?? "space_app.runtime",
action: command?.action ?? shadow.action ?? "read",
dataClass: command?.dataClass ?? shadow.dataClass ?? "server-private"
};
}
async function resolveShadowSpaceAppLaunchCommandContextResolution(options) {
const introspection = await introspectShadowSpaceAppLaunchToken(options);
if (!introspection?.active) {
return {
context: null,
introspection,
error: shadowSpaceAppLaunchIntrospectionError(introspection)
};
}
const context = shadowSpaceAppLaunchCommandContextFromIntrospection(options, introspection);
return {
context,
introspection,
error: context ? null : shadowSpaceAppLaunchIntrospectionError(introspection)
};
}
async function resolveShadowSpaceAppLaunchCommandContext(options) {
const resolution = await resolveShadowSpaceAppLaunchCommandContextResolution(options);
return resolution.context;
}
async function deliverShadowSpaceAppLaunchOutbox(options) {
const hint = decodeShadowSpaceAppLaunchTokenHint(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)}/space-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 readShadowSpaceAppResponsePayload(response);
throw new ShadowSpaceAppHttpError(
response.status,
shadowSpaceAppResponseErrorMessage(
response.status,
payload,
"Shadow launch outbox delivery failed"
),
payload
);
}
return readShadowSpaceAppResponsePayload(response);
}
async function publishShadowSpaceAppNotification(options) {
const hint = decodeShadowSpaceAppLaunchTokenHint(options.launchToken);
if (!hint || !options.launchToken) {
throw new Error("A Shadow launch token is required to publish a Space App notification");
}
const fetchFn = options.fetch ?? fetch;
const baseUrl = trimTrailingSlash(options.shadowApiBaseUrl ?? "http://localhost:3002");
const response = await fetchFn(
`${baseUrl}/api/servers/${encodeURIComponent(hint.serverId)}/space-apps/${encodeURIComponent(
hint.appKey
)}/notifications`,
{
method: "POST",
headers: {
Authorization: `Bearer ${options.launchToken}`,
"Content-Type": "application/json"
},
body: JSON.stringify(options.notification)
}
);
if (!response.ok) {
const payload = await readShadowSpaceAppResponsePayload(response);
throw new ShadowSpaceAppHttpError(
response.status,
shadowSpaceAppResponseErrorMessage(
response.status,
payload,
"Space App notification publish failed"
),
payload
);
}
return readShadowSpaceAppResponsePayload(response);
}
function normalizeShadowSpaceAppCommandInput(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 createShadowSpaceAppManifest(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 defineShadowSpaceApp(manifest, options = {}) {
return new ShadowSpaceAppRuntime(manifest, options);
}
var createShadowSpaceAppRuntime = defineShadowSpaceApp;
var ShadowSpaceAppCommandError = class extends Error {
status;
issues;
constructor(status, error, issues) {
super(error);
this.name = "ShadowSpaceAppCommandError";
this.status = status;
this.issues = issues;
}
};
function shadowSpaceAppError(status, error, issues) {
return new ShadowSpaceAppCommandError(status, error, issues);
}
var ShadowSpaceAppRuntime = class {
constructor(sourceManifest, options = {}) {
this.sourceManifest = sourceManifest;
this.options = options;
}
sourceManifest;
options;
manifest(options = {}) {
return createShadowSpaceAppManifest(this.sourceManifest, options);
}
defineCommands(handlers) {
return handlers;
}
actor(envelopeOrContext) {
return shadowSpaceAppActorRef(envelopeOrContext);
}
error(status, error, issues) {
return shadowSpaceAppError(status, error, issues);
}
async parseCommand(commandName, request) {
return parseShadowSpaceAppCommandRequest({
...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 = validateShadowSpaceAppJsonSchema(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 ShadowSpaceAppCommandError) {
return failureResult(error.status, error.message, error.issues);
}
throw error;
}
}
};
async function introspectShadowSpaceAppToken(input) {
const baseUrl = trimTrailingSlash(input.shadowBaseUrl ?? "http://localhost:3002");
const fetchImpl = input.fetchImpl ?? fetch;
const response = await fetchImpl(`${baseUrl}/api/space-apps/commands/introspect`, {
method: "POST",
headers: { Authorization: `Bearer ${input.token}` }
});
if (!response.ok) return null;
const payload = await response.json();
return payload.active ? payload : null;
}
async function parseShadowSpaceAppCommandRequest(input) {
const token = extractShadowSpaceAppBearerToken(input.authorizationHeader);
if (!token) {
return { ok: false, status: 401, error: "missing_command_token" };
}
const introspection = await introspectShadowSpaceAppToken({
token,
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 validateShadowSpaceAppJsonSchema(schema, value) {
if (!schema) return { ok: true };
const issues = [];
validateJsonSchemaValue(schema, value, "", issues);
return issues.length ? { ok: false, issues } : { ok: true };
}
function shadowSpaceAppActorDisplayName(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 shadowSpaceAppActorAvatarUrl(envelopeOrContext) {
const context = "context" in envelopeOrContext ? envelopeOrContext.context : envelopeOrContext;
return context.actor.profile?.avatarUrl ?? null;
}
function shadowSpaceAppActorRef(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: shadowSpaceAppActorDisplayName(context),
avatarUrl: shadowSpaceAppActorAvatarUrl(context)
};
}
function isShadowSpaceAppActorRef(value) {
return !!value && typeof value === "object" && !Array.isArray(value) && typeof value.kind === "string" && typeof value.id === "string" && typeof value.displayName === "string";
}
function shadowSpaceAppIdentitySubjectKind(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 shadowSpaceAppIdentityKey(actorOrIdentity) {
const actor = isShadowSpaceAppActorRef(actorOrIdentity) ? actorOrIdentity : shadowSpaceAppActorRef(actorOrIdentity);
const subjectKind = shadowSpaceAppIdentitySubjectKind(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 shadowSpaceAppIdentitySnapshot(actorOrContext) {
const actor = isShadowSpaceAppActorRef(actorOrContext) ? actorOrContext : shadowSpaceAppActorRef(actorOrContext);
return {
...actor,
subjectKind: shadowSpaceAppIdentitySubjectKind(actor),
stableKey: shadowSpaceAppIdentityKey(actor)
};
}
var shadowSpaceAppDisplayIdentity = shadowSpaceAppIdentitySnapshot;
function normalizeShadowSpaceAppClientMutationId(value) {
if (typeof value !== "string") return null;
const clean = value.trim();
if (!clean) return null;
return clean.slice(0, 160);
}
function normalizeShadowSpaceAppBaseCursor(value) {
if (typeof value !== "string") return null;
const clean = value.trim();
if (!clean) return null;
return clean.slice(0, 240);
}
function createShadowSpaceAppCollaborationResource(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 createShadowSpaceAppCollaborationCursor(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 createShadowSpaceAppCollaborationEvent(input) {
const occurredAt = input.occurredAt ?? (/* @__PURE__ */ new Date()).toISOString();
const cursor = input.cursor ?? createShadowSpaceAppCollaborationCursor({
resource: input.resource,
occurredAt
});
return {
protocol: SHADOW_SPACE_APP_PROTOCOL,
type: input.type,
cursor,
occurredAt,
resource: input.resource,
actor: shadowSpaceAppIdentitySnapshot(input.actor),
payload: input.payload,
clientMutationId: normalizeShadowSpaceAppClientMutationId(input.clientMutationId),
baseCursor: normalizeShadowSpaceAppBaseCursor(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;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
BUDDY_INBOX_DELIVERY_PERMISSION,
SHADOW_SPACE_APP_COMMAND_COMPLETED_EVENT,
SHADOW_SPACE_APP_COMMAND_EVENTS,
SHADOW_SPACE_APP_COMMAND_FAILED_EVENT,
SHADOW_SPACE_APP_PROTOCOL,
SHADOW_SPACE_APP_PUBLIC_AVATAR_CACHE_CONTROL,
ShadowSpaceAppCommandError,
ShadowSpaceAppHttpError,
ShadowSpaceAppOutbox,
ShadowSpaceAppRuntime,
buildShadowSpaceAppInboxDelivery,
buildShadowSpaceAppInboxTaskRequest,
createShadowSpaceAppCollaborationCursor,
createShadowSpaceAppCollaborationEvent,
createShadowSpaceAppCollaborationResource,
createShadowSpaceAppLaunchPoll,
createShadowSpaceAppManifest,
createShadowSpaceAppRuntime,
decodeShadowSpaceAppLaunchTokenHint,
defineShadowSpaceApp,
deliverShadowSpaceAppLaunchOutbox,
ensureShadowSpaceAppLaunchBuddyTaskGrant,
ensureShadowSpaceAppLaunchChannel,
extractShadowSpaceAppBearerToken,
fetchShadowSpaceAppLaunchChannels,
fetchShadowSpaceAppLaunchInboxes,
fetchShadowSpaceAppLaunchMembers,
fetchShadowSpaceAppLaunchMessage,
getShadowSpaceAppChannelMessageDeliveries,
getShadowSpaceAppChannelMessageErrors,
getShadowSpaceAppInboxDeliveries,
getShadowSpaceAppInboxErrors,
getShadowSpaceAppPendingChannelMessages,
getShadowSpaceAppPendingInboxTasks,
getShadowSpaceAppTaskCardId,
hasShadowSpaceAppPendingOutbox,
introspectShadowSpaceAppLaunchToken,
introspectShadowSpaceAppToken,
isShadowSpaceAppSignedMediaUrl,
normalizeShadowSpaceAppAvatarUrl,
normalizeShadowSpaceAppBaseCursor,
normalizeShadowSpaceAppClientMutationId,
normalizeShadowSpaceAppCommandInput,
parseShadowSpaceAppCommandRequest,
publishShadowSpaceAppNotification,
readShadowSpaceAppCommandResponse,
resolveShadowSpaceAppLaunchCommandContext,
resolveShadowSpaceAppLaunchCommandContextResolution,
shadowSpaceAppActorAvatarUrl,
shadowSpaceAppActorDisplayName,
shadowSpaceAppActorRef,
shadowSpaceAppApiBaseUrl,
shadowSpaceAppAvatarRedirectUrl,
shadowSpaceAppDisplayIdentity,
shadowSpaceAppError,
shadowSpaceAppIdentityKey,
shadowSpaceAppIdentitySnapshot,
shadowSpaceAppInboxTaskEndpoint,
shadowSpaceAppLaunchCommandContextFromIntrospection,
shadowSpaceAppLaunchIntrospectionError,
shadowSpaceAppPublicBaseUrl,
shadowSpaceAppPublicUrl,
unwrapShadowSpaceAppCommandPayload,
validateShadowSpaceAppJsonSchema
});
export { b$ as JsonSchemaToType, c7 as SHADOW_SPACE_APP_COMMAND_COMPLETED_EVENT, c8 as SHADOW_SPACE_APP_COMMAND_EVENTS, c9 as SHADOW_SPACE_APP_COMMAND_FAILED_EVENT, ca as SHADOW_SPACE_APP_PROTOCOL, cb as SHADOW_SPACE_APP_PUBLIC_AVATAR_CACHE_CONTROL, d5 as ShadowSpaceAppActorRef, d6 as ShadowSpaceAppBridgeAuthorizeOAuthRequest, d7 as ShadowSpaceAppBridgeCapabilitiesRequest, d8 as ShadowSpaceAppBridgeFailureResponse, d9 as ShadowSpaceAppBridgeOpenCopilotRequest, ff as ShadowSpaceAppBridgeOpenWorkspaceResourceRequest, da as ShadowSpaceAppBridgeRequest, db as ShadowSpaceAppBridgeResponse, dc as ShadowSpaceAppBridgeResponseType, dd as ShadowSpaceAppBridgeRouteChangedEvent, de as ShadowSpaceAppBridgeRouteNavigateAck, df as ShadowSpaceAppBridgeRouteNavigateRequest, dg as ShadowSpaceAppBridgeShareRequest, dh as ShadowSpaceAppBridgeSuccessResponse, di as ShadowSpaceAppChannelMessageDelivery, dj as ShadowSpaceAppChannelMessageDeliveryError, dk as ShadowSpaceAppChannelMessageOutbox, dl as ShadowSpaceAppCollaborationEvent, dm as ShadowSpaceAppCollaborationMutation, dn as ShadowSpaceAppCollaborationResource, dr as ShadowSpaceAppCommandContext, ds as ShadowSpaceAppCommandEnvelope, dt as ShadowSpaceAppCommandError, du as ShadowSpaceAppCommandEventType, dv as ShadowSpaceAppCommandFailureResponse, dw as ShadowSpaceAppCommandHandler, dx as ShadowSpaceAppCommandHandlerContext, dy as ShadowSpaceAppCommandHandlers, dz as ShadowSpaceAppCommandInput, dA as ShadowSpaceAppCommandName, dB as ShadowSpaceAppCommandParseError, dC as ShadowSpaceAppCommandParseResult, dD as ShadowSpaceAppCommandParseSuccess, dE as ShadowSpaceAppCommandRequestInput, dF as ShadowSpaceAppCommandResponse, fg as ShadowSpaceAppCommandRuntimeRequest, dG as ShadowSpaceAppCommandSuccessResponse, dH as ShadowSpaceAppEnvironment, dI as ShadowSpaceAppExecutionFailure, dJ as ShadowSpaceAppExecutionResult, dK as ShadowSpaceAppExecutionSuccess, dL as ShadowSpaceAppFetch, dM as ShadowSpaceAppHostInboxTaskRequestInput, dN as ShadowSpaceAppHostRef, dO as ShadowSpaceAppHttpError, dP as ShadowSpaceAppIdentitySnapshot, dQ as ShadowSpaceAppIdentitySubjectKind, dR as ShadowSpaceAppInboxDelivery, dS as ShadowSpaceAppInboxDeliveryError, dT as ShadowSpaceAppInboxDeliveryFromMessageInput, dU as ShadowSpaceAppInboxTarget, dV as ShadowSpaceAppInboxTaskOutbox, dW as ShadowSpaceAppInboxTaskPriority, dX as ShadowSpaceAppInboxTaskResource, dY as ShadowSpaceAppIntrospectionInput, C as ShadowSpaceAppLaunchChannel, dZ as ShadowSpaceAppLaunchCommandContextOptions, d_ as ShadowSpaceAppLaunchCommandContextResolution, F as ShadowSpaceAppLaunchCreatePollInput, G as ShadowSpaceAppLaunchCreatePollResult, d$ as ShadowSpaceAppLaunchEnsureBuddyTaskGrantInput, e0 as ShadowSpaceAppLaunchEnsureBuddyTaskGrantResult, D as ShadowSpaceAppLaunchEnsureChannelInput, E as ShadowSpaceAppLaunchEnsureChannelResult, e1 as ShadowSpaceAppLaunchFetchOptions, e2 as ShadowSpaceAppLaunchIntrospection, e3 as ShadowSpaceAppLaunchMember, e4 as ShadowSpaceAppLaunchOutboxDeliveryOptions, e5 as ShadowSpaceAppLaunchTokenHint, e6 as ShadowSpaceAppManifestOptions, eb as ShadowSpaceAppNotificationPublishInput, ec as ShadowSpaceAppNotificationPublishOptions, ee as ShadowSpaceAppOutbox, ef as ShadowSpaceAppOutboxPayload, eh as ShadowSpaceAppResolvedInboxTaskRequest, ei as ShadowSpaceAppResultShadow, ej as ShadowSpaceAppResultWithShadow, ek as ShadowSpaceAppRuntime, el as ShadowSpaceAppRuntimeOptions, em as ShadowSpaceAppValidationIssue, et as buildShadowSpaceAppInboxDelivery, eu as buildShadowSpaceAppInboxTaskRequest, ev as createShadowSpaceAppCollaborationCursor, ew as createShadowSpaceAppCollaborationEvent, ex as createShadowSpaceAppCollaborationResource, ey as createShadowSpaceAppLaunchPoll, ez as createShadowSpaceAppManifest, eA as createShadowSpaceAppRuntime, eB as decodeShadowSpaceAppLaunchTokenHint, eC as defineShadowSpaceApp, eD as deliverShadowSpaceAppLaunchOutbox, eE as ensureShadowSpaceAppLaunchBuddyTaskGrant, eF as ensureShadowSpaceAppLaunchChannel, eG as extractShadowSpaceAppBearerToken, eH as fetchShadowSpaceAppLaunchChannels, eI as fetchShadowSpaceAppLaunchInboxes, eJ as fetchShadowSpaceAppLaunchMembers, eK as fetchShadowSpaceAppLaunchMessage, eL as getShadowSpaceAppChannelMessageDeliveries, eM as getShadowSpaceAppChannelMessageErrors, eN as getShadowSpaceAppInboxDeliveries, eO as getShadowSpaceAppInboxErrors, fh as getShadowSpaceAppPendingChannelMessages, fi as getShadowSpaceAppPendingInboxTasks, eP as getShadowSpaceAppTaskCardId, eQ as hasShadowSpaceAppPendingOutbox, eR as introspectShadowSpaceAppLaunchToken, eS as introspectShadowSpaceAppToken, eT as isShadowSpaceAppSignedMediaUrl, eU as normalizeShadowSpaceAppAvatarUrl, eV as normalizeShadowSpaceAppBaseCursor, eW as normalizeShadowSpaceAppClientMutationId, eX as normalizeShadowSpaceAppCommandInput, eY as parseShadowSpaceAppCommandRequest, eZ as publishShadowSpaceAppNotification, e_ as readShadowSpaceAppCommandResponse, e$ as resolveShadowSpaceAppLaunchCommandContext, f0 as resolveShadowSpaceAppLaunchCommandContextResolution, f1 as shadowSpaceAppActorAvatarUrl, f2 as shadowSpaceAppActorDisplayName, f3 as shadowSpaceAppActorRef, f4 as shadowSpaceAppApiBaseUrl, f5 as shadowSpaceAppAvatarRedirectUrl, f6 as shadowSpaceAppDisplayIdentity, f7 as shadowSpaceAppError, f8 as shadowSpaceAppIdentityKey, f9 as shadowSpaceAppIdentitySnapshot, fa as shadowSpaceAppInboxTaskEndpoint, fj as shadowSpaceAppLaunchCommandContextFromIntrospection, fb as shadowSpaceAppLaunchIntrospectionError, fc as shadowSpaceAppPublicBaseUrl, fd as shadowSpaceAppPublicUrl, fk as unwrapShadowSpaceAppCommandPayload, fe as validateShadowSpaceAppJsonSchema } from './space-app-BhlEB4pg.cjs';
export { BUDDY_INBOX_DELIVERY_PERMISSION, BuddyInboxPlatformPermission } from '@shadowob/shared';
export { b$ as JsonSchemaToType, c7 as SHADOW_SPACE_APP_COMMAND_COMPLETED_EVENT, c8 as SHADOW_SPACE_APP_COMMAND_EVENTS, c9 as SHADOW_SPACE_APP_COMMAND_FAILED_EVENT, ca as SHADOW_SPACE_APP_PROTOCOL, cb as SHADOW_SPACE_APP_PUBLIC_AVATAR_CACHE_CONTROL, d5 as ShadowSpaceAppActorRef, d6 as ShadowSpaceAppBridgeAuthorizeOAuthRequest, d7 as ShadowSpaceAppBridgeCapabilitiesRequest, d8 as ShadowSpaceAppBridgeFailureResponse, d9 as ShadowSpaceAppBridgeOpenCopilotRequest, ff as ShadowSpaceAppBridgeOpenWorkspaceResourceRequest, da as ShadowSpaceAppBridgeRequest, db as ShadowSpaceAppBridgeResponse, dc as ShadowSpaceAppBridgeResponseType, dd as ShadowSpaceAppBridgeRouteChangedEvent, de as ShadowSpaceAppBridgeRouteNavigateAck, df as ShadowSpaceAppBridgeRouteNavigateRequest, dg as ShadowSpaceAppBridgeShareRequest, dh as ShadowSpaceAppBridgeSuccessResponse, di as ShadowSpaceAppChannelMessageDelivery, dj as ShadowSpaceAppChannelMessageDeliveryError, dk as ShadowSpaceAppChannelMessageOutbox, dl as ShadowSpaceAppCollaborationEvent, dm as ShadowSpaceAppCollaborationMutation, dn as ShadowSpaceAppCollaborationResource, dr as ShadowSpaceAppCommandContext, ds as ShadowSpaceAppCommandEnvelope, dt as ShadowSpaceAppCommandError, du as ShadowSpaceAppCommandEventType, dv as ShadowSpaceAppCommandFailureResponse, dw as ShadowSpaceAppCommandHandler, dx as ShadowSpaceAppCommandHandlerContext, dy as ShadowSpaceAppCommandHandlers, dz as ShadowSpaceAppCommandInput, dA as ShadowSpaceAppCommandName, dB as ShadowSpaceAppCommandParseError, dC as ShadowSpaceAppCommandParseResult, dD as ShadowSpaceAppCommandParseSuccess, dE as ShadowSpaceAppCommandRequestInput, dF as ShadowSpaceAppCommandResponse, fg as ShadowSpaceAppCommandRuntimeRequest, dG as ShadowSpaceAppCommandSuccessResponse, dH as ShadowSpaceAppEnvironment, dI as ShadowSpaceAppExecutionFailure, dJ as ShadowSpaceAppExecutionResult, dK as ShadowSpaceAppExecutionSuccess, dL as ShadowSpaceAppFetch, dM as ShadowSpaceAppHostInboxTaskRequestInput, dN as ShadowSpaceAppHostRef, dO as ShadowSpaceAppHttpError, dP as ShadowSpaceAppIdentitySnapshot, dQ as ShadowSpaceAppIdentitySubjectKind, dR as ShadowSpaceAppInboxDelivery, dS as ShadowSpaceAppInboxDeliveryError, dT as ShadowSpaceAppInboxDeliveryFromMessageInput, dU as ShadowSpaceAppInboxTarget, dV as ShadowSpaceAppInboxTaskOutbox, dW as ShadowSpaceAppInboxTaskPriority, dX as ShadowSpaceAppInboxTaskResource, dY as ShadowSpaceAppIntrospectionInput, C as ShadowSpaceAppLaunchChannel, dZ as ShadowSpaceAppLaunchCommandContextOptions, d_ as ShadowSpaceAppLaunchCommandContextResolution, F as ShadowSpaceAppLaunchCreatePollInput, G as ShadowSpaceAppLaunchCreatePollResult, d$ as ShadowSpaceAppLaunchEnsureBuddyTaskGrantInput, e0 as ShadowSpaceAppLaunchEnsureBuddyTaskGrantResult, D as ShadowSpaceAppLaunchEnsureChannelInput, E as ShadowSpaceAppLaunchEnsureChannelResult, e1 as ShadowSpaceAppLaunchFetchOptions, e2 as ShadowSpaceAppLaunchIntrospection, e3 as ShadowSpaceAppLaunchMember, e4 as ShadowSpaceAppLaunchOutboxDeliveryOptions, e5 as ShadowSpaceAppLaunchTokenHint, e6 as ShadowSpaceAppManifestOptions, eb as ShadowSpaceAppNotificationPublishInput, ec as ShadowSpaceAppNotificationPublishOptions, ee as ShadowSpaceAppOutbox, ef as ShadowSpaceAppOutboxPayload, eh as ShadowSpaceAppResolvedInboxTaskRequest, ei as ShadowSpaceAppResultShadow, ej as ShadowSpaceAppResultWithShadow, ek as ShadowSpaceAppRuntime, el as ShadowSpaceAppRuntimeOptions, em as ShadowSpaceAppValidationIssue, et as buildShadowSpaceAppInboxDelivery, eu as buildShadowSpaceAppInboxTaskRequest, ev as createShadowSpaceAppCollaborationCursor, ew as createShadowSpaceAppCollaborationEvent, ex as createShadowSpaceAppCollaborationResource, ey as createShadowSpaceAppLaunchPoll, ez as createShadowSpaceAppManifest, eA as createShadowSpaceAppRuntime, eB as decodeShadowSpaceAppLaunchTokenHint, eC as defineShadowSpaceApp, eD as deliverShadowSpaceAppLaunchOutbox, eE as ensureShadowSpaceAppLaunchBuddyTaskGrant, eF as ensureShadowSpaceAppLaunchChannel, eG as extractShadowSpaceAppBearerToken, eH as fetchShadowSpaceAppLaunchChannels, eI as fetchShadowSpaceAppLaunchInboxes, eJ as fetchShadowSpaceAppLaunchMembers, eK as fetchShadowSpaceAppLaunchMessage, eL as getShadowSpaceAppChannelMessageDeliveries, eM as getShadowSpaceAppChannelMessageErrors, eN as getShadowSpaceAppInboxDeliveries, eO as getShadowSpaceAppInboxErrors, fh as getShadowSpaceAppPendingChannelMessages, fi as getShadowSpaceAppPendingInboxTasks, eP as getShadowSpaceAppTaskCardId, eQ as hasShadowSpaceAppPendingOutbox, eR as introspectShadowSpaceAppLaunchToken, eS as introspectShadowSpaceAppToken, eT as isShadowSpaceAppSignedMediaUrl, eU as normalizeShadowSpaceAppAvatarUrl, eV as normalizeShadowSpaceAppBaseCursor, eW as normalizeShadowSpaceAppClientMutationId, eX as normalizeShadowSpaceAppCommandInput, eY as parseShadowSpaceAppCommandRequest, eZ as publishShadowSpaceAppNotification, e_ as readShadowSpaceAppCommandResponse, e$ as resolveShadowSpaceAppLaunchCommandContext, f0 as resolveShadowSpaceAppLaunchCommandContextResolution, f1 as shadowSpaceAppActorAvatarUrl, f2 as shadowSpaceAppActorDisplayName, f3 as shadowSpaceAppActorRef, f4 as shadowSpaceAppApiBaseUrl, f5 as shadowSpaceAppAvatarRedirectUrl, f6 as shadowSpaceAppDisplayIdentity, f7 as shadowSpaceAppError, f8 as shadowSpaceAppIdentityKey, f9 as shadowSpaceAppIdentitySnapshot, fa as shadowSpaceAppInboxTaskEndpoint, fj as shadowSpaceAppLaunchCommandContextFromIntrospection, fb as shadowSpaceAppLaunchIntrospectionError, fc as shadowSpaceAppPublicBaseUrl, fd as shadowSpaceAppPublicUrl, fk as unwrapShadowSpaceAppCommandPayload, fe as validateShadowSpaceAppJsonSchema } from './space-app-BhlEB4pg.js';
export { BUDDY_INBOX_DELIVERY_PERMISSION, BuddyInboxPlatformPermission } from '@shadowob/shared';
import {
BUDDY_INBOX_DELIVERY_PERMISSION,
SHADOW_SPACE_APP_COMMAND_COMPLETED_EVENT,
SHADOW_SPACE_APP_COMMAND_EVENTS,
SHADOW_SPACE_APP_COMMAND_FAILED_EVENT,
SHADOW_SPACE_APP_PROTOCOL,
SHADOW_SPACE_APP_PUBLIC_AVATAR_CACHE_CONTROL,
ShadowSpaceAppCommandError,
ShadowSpaceAppHttpError,
ShadowSpaceAppOutbox,
ShadowSpaceAppRuntime,
buildShadowSpaceAppInboxDelivery,
buildShadowSpaceAppInboxTaskRequest,
createShadowSpaceAppCollaborationCursor,
createShadowSpaceAppCollaborationEvent,
createShadowSpaceAppCollaborationResource,
createShadowSpaceAppLaunchPoll,
createShadowSpaceAppManifest,
createShadowSpaceAppRuntime,
decodeShadowSpaceAppLaunchTokenHint,
defineShadowSpaceApp,
deliverShadowSpaceAppLaunchOutbox,
ensureShadowSpaceAppLaunchBuddyTaskGrant,
ensureShadowSpaceAppLaunchChannel,
extractShadowSpaceAppBearerToken,
fetchShadowSpaceAppLaunchChannels,
fetchShadowSpaceAppLaunchInboxes,
fetchShadowSpaceAppLaunchMembers,
fetchShadowSpaceAppLaunchMessage,
getShadowSpaceAppChannelMessageDeliveries,
getShadowSpaceAppChannelMessageErrors,
getShadowSpaceAppInboxDeliveries,
getShadowSpaceAppInboxErrors,
getShadowSpaceAppPendingChannelMessages,
getShadowSpaceAppPendingInboxTasks,
getShadowSpaceAppTaskCardId,
hasShadowSpaceAppPendingOutbox,
introspectShadowSpaceAppLaunchToken,
introspectShadowSpaceAppToken,
isShadowSpaceAppSignedMediaUrl,
normalizeShadowSpaceAppAvatarUrl,
normalizeShadowSpaceAppBaseCursor,
normalizeShadowSpaceAppClientMutationId,
normalizeShadowSpaceAppCommandInput,
parseShadowSpaceAppCommandRequest,
publishShadowSpaceAppNotification,
readShadowSpaceAppCommandResponse,
resolveShadowSpaceAppLaunchCommandContext,
resolveShadowSpaceAppLaunchCommandContextResolution,
shadowSpaceAppActorAvatarUrl,
shadowSpaceAppActorDisplayName,
shadowSpaceAppActorRef,
shadowSpaceAppApiBaseUrl,
shadowSpaceAppAvatarRedirectUrl,
shadowSpaceAppDisplayIdentity,
shadowSpaceAppError,
shadowSpaceAppIdentityKey,
shadowSpaceAppIdentitySnapshot,
shadowSpaceAppInboxTaskEndpoint,
shadowSpaceAppLaunchCommandContextFromIntrospection,
shadowSpaceAppLaunchIntrospectionError,
shadowSpaceAppPublicBaseUrl,
shadowSpaceAppPublicUrl,
unwrapShadowSpaceAppCommandPayload,
validateShadowSpaceAppJsonSchema
} from "./chunk-NADGSKKC.js";
export {
BUDDY_INBOX_DELIVERY_PERMISSION,
SHADOW_SPACE_APP_COMMAND_COMPLETED_EVENT,
SHADOW_SPACE_APP_COMMAND_EVENTS,
SHADOW_SPACE_APP_COMMAND_FAILED_EVENT,
SHADOW_SPACE_APP_PROTOCOL,
SHADOW_SPACE_APP_PUBLIC_AVATAR_CACHE_CONTROL,
ShadowSpaceAppCommandError,
ShadowSpaceAppHttpError,
ShadowSpaceAppOutbox,
ShadowSpaceAppRuntime,
buildShadowSpaceAppInboxDelivery,
buildShadowSpaceAppInboxTaskRequest,
createShadowSpaceAppCollaborationCursor,
createShadowSpaceAppCollaborationEvent,
createShadowSpaceAppCollaborationResource,
createShadowSpaceAppLaunchPoll,
createShadowSpaceAppManifest,
createShadowSpaceAppRuntime,
decodeShadowSpaceAppLaunchTokenHint,
defineShadowSpaceApp,
deliverShadowSpaceAppLaunchOutbox,
ensureShadowSpaceAppLaunchBuddyTaskGrant,
ensureShadowSpaceAppLaunchChannel,
extractShadowSpaceAppBearerToken,
fetchShadowSpaceAppLaunchChannels,
fetchShadowSpaceAppLaunchInboxes,
fetchShadowSpaceAppLaunchMembers,
fetchShadowSpaceAppLaunchMessage,
getShadowSpaceAppChannelMessageDeliveries,
getShadowSpaceAppChannelMessageErrors,
getShadowSpaceAppInboxDeliveries,
getShadowSpaceAppInboxErrors,
getShadowSpaceAppPendingChannelMessages,
getShadowSpaceAppPendingInboxTasks,
getShadowSpaceAppTaskCardId,
hasShadowSpaceAppPendingOutbox,
introspectShadowSpaceAppLaunchToken,
introspectShadowSpaceAppToken,
isShadowSpaceAppSignedMediaUrl,
normalizeShadowSpaceAppAvatarUrl,
normalizeShadowSpaceAppBaseCursor,
normalizeShadowSpaceAppClientMutationId,
normalizeShadowSpaceAppCommandInput,
parseShadowSpaceAppCommandRequest,
publishShadowSpaceAppNotification,
readShadowSpaceAppCommandResponse,
resolveShadowSpaceAppLaunchCommandContext,
resolveShadowSpaceAppLaunchCommandContextResolution,
shadowSpaceAppActorAvatarUrl,
shadowSpaceAppActorDisplayName,
shadowSpaceAppActorRef,
shadowSpaceAppApiBaseUrl,
shadowSpaceAppAvatarRedirectUrl,
shadowSpaceAppDisplayIdentity,
shadowSpaceAppError,
shadowSpaceAppIdentityKey,
shadowSpaceAppIdentitySnapshot,
shadowSpaceAppInboxTaskEndpoint,
shadowSpaceAppLaunchCommandContextFromIntrospection,
shadowSpaceAppLaunchIntrospectionError,
shadowSpaceAppPublicBaseUrl,
shadowSpaceAppPublicUrl,
unwrapShadowSpaceAppCommandPayload,
validateShadowSpaceAppJsonSchema
};
+659
-415

@@ -24,31 +24,34 @@ "use strict";

SHADOW_BRIDGE_CAPABILITIES: () => SHADOW_BRIDGE_CAPABILITIES,
SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT: () => SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT,
SHADOW_SERVER_APP_COMMAND_EVENTS: () => SHADOW_SERVER_APP_COMMAND_EVENTS,
SHADOW_SERVER_APP_COMMAND_FAILED_EVENT: () => SHADOW_SERVER_APP_COMMAND_FAILED_EVENT,
SHADOW_SPACE_APP_COMMAND_COMPLETED_EVENT: () => SHADOW_SPACE_APP_COMMAND_COMPLETED_EVENT,
SHADOW_SPACE_APP_COMMAND_EVENTS: () => SHADOW_SPACE_APP_COMMAND_EVENTS,
SHADOW_SPACE_APP_COMMAND_FAILED_EVENT: () => SHADOW_SPACE_APP_COMMAND_FAILED_EVENT,
ShadowBridge: () => ShadowBridge,
ShadowServerAppBrowserClient: () => ShadowServerAppBrowserClient,
buildShadowServerAppInboxDelivery: () => buildShadowServerAppInboxDelivery,
buildShadowServerAppInboxTaskRequest: () => buildShadowServerAppInboxTaskRequest,
createShadowServerAppBrowserClient: () => createShadowServerAppBrowserClient,
createShadowServerAppClient: () => createShadowServerAppClient,
getShadowServerAppChannelMessageDeliveries: () => getShadowServerAppChannelMessageDeliveries,
getShadowServerAppChannelMessageErrors: () => getShadowServerAppChannelMessageErrors,
getShadowServerAppTaskCardId: () => getShadowServerAppTaskCardId,
readShadowServerAppCommandResponse: () => readShadowServerAppCommandResponse,
shadowServerAppInboxTaskEndpoint: () => shadowServerAppInboxTaskEndpoint,
shadowServerAppMountedPath: () => shadowServerAppMountedPath,
shadowServerAppMountedPathPrefix: () => shadowServerAppMountedPathPrefix
ShadowSpaceAppBrowserClient: () => ShadowSpaceAppBrowserClient,
buildShadowSpaceAppInboxDelivery: () => buildShadowSpaceAppInboxDelivery,
buildShadowSpaceAppInboxTaskRequest: () => buildShadowSpaceAppInboxTaskRequest,
createShadowSpaceAppBrowserClient: () => createShadowSpaceAppBrowserClient,
createShadowSpaceAppClient: () => createShadowSpaceAppClient,
defineShadowSpaceAppAuthorizeElement: () => defineShadowSpaceAppAuthorizeElement,
getShadowSpaceAppChannelMessageDeliveries: () => getShadowSpaceAppChannelMessageDeliveries,
getShadowSpaceAppChannelMessageErrors: () => getShadowSpaceAppChannelMessageErrors,
getShadowSpaceAppInboxDeliveries: () => getShadowSpaceAppInboxDeliveries,
getShadowSpaceAppInboxErrors: () => getShadowSpaceAppInboxErrors,
getShadowSpaceAppTaskCardId: () => getShadowSpaceAppTaskCardId,
readShadowSpaceAppCommandResponse: () => readShadowSpaceAppCommandResponse,
shadowSpaceAppInboxTaskEndpoint: () => shadowSpaceAppInboxTaskEndpoint,
shadowSpaceAppMountedPath: () => shadowSpaceAppMountedPath,
shadowSpaceAppMountedPathPrefix: () => shadowSpaceAppMountedPathPrefix
});
module.exports = __toCommonJS(bridge_exports);
// src/server-app.ts
// src/space-app.ts
var import_shared = require("@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 SHADOW_SPACE_APP_PROTOCOL = "shadow.space-app/1";
var SHADOW_SPACE_APP_COMMAND_COMPLETED_EVENT = "space_app.command.completed";
var SHADOW_SPACE_APP_COMMAND_FAILED_EVENT = "space_app.command.failed";
var SHADOW_SPACE_APP_COMMAND_EVENTS = [
SHADOW_SPACE_APP_COMMAND_COMPLETED_EVENT,
SHADOW_SPACE_APP_COMMAND_FAILED_EVENT
];
var ShadowServerAppHttpError = class extends Error {
var ShadowSpaceAppHttpError = class extends Error {
status;

@@ -58,3 +61,3 @@ payload;

super(message);
this.name = "ShadowServerAppHttpError";
this.name = "ShadowSpaceAppHttpError";
this.status = status;

@@ -73,3 +76,3 @@ this.payload = payload;

}
function shadowServerAppInboxTaskEndpoint(serverIdOrSlug, target) {
function shadowSpaceAppInboxTaskEndpoint(serverIdOrSlug, target) {
if ("channelId" in target && target.channelId) {

@@ -85,7 +88,7 @@ return `/api/channels/${protocolPathSegment(target.channelId)}/inbox/tasks`;

}
function buildShadowServerAppInboxTaskRequest(input) {
function buildShadowSpaceAppInboxTaskRequest(input) {
const appId = input.app.id ?? input.app.appId ?? input.app.appKey;
const serverAppData = isProtocolRecord(input.task.data?.serverApp) ? input.task.data.serverApp : {};
const spaceAppData = isProtocolRecord(input.task.data?.spaceApp) ? input.task.data.spaceApp : {};
return {
endpoint: shadowServerAppInboxTaskEndpoint(input.serverIdOrSlug, input.target),
endpoint: shadowSpaceAppInboxTaskEndpoint(input.serverIdOrSlug, input.target),
body: {

@@ -109,3 +112,3 @@ title: input.task.title,

source: {
kind: "server_app",
kind: "space_app",
id: appId,

@@ -123,4 +126,4 @@ appId,

...input.task.data ?? {},
serverApp: {
...serverAppData,
spaceApp: {
...spaceAppData,
appKey: input.app.appKey,

@@ -136,3 +139,3 @@ name: input.app.name ?? input.app.label ?? input.app.appKey,

}
function getShadowServerAppTaskCardId(message) {
function getShadowSpaceAppTaskCardId(message) {
const metadata = isProtocolRecord(message) ? message.metadata : null;

@@ -146,3 +149,3 @@ const cards = isProtocolRecord(metadata) && Array.isArray(metadata.cards) ? metadata.cards : [];

}
function buildShadowServerAppInboxDelivery(input) {
function buildShadowSpaceAppInboxDelivery(input) {
const message = isProtocolRecord(input.message) ? input.message : {};

@@ -153,3 +156,3 @@ return {

messageId: optionalProtocolString(message.id),
cardId: getShadowServerAppTaskCardId(message),
cardId: getShadowSpaceAppTaskCardId(message),
idempotencyKey: input.idempotencyKey

@@ -159,7 +162,7 @@ };

function shadowFromPayload(payload) {
if (payload.protocol === SHADOW_SERVER_APP_PROTOCOL) {
if (payload.protocol === SHADOW_SPACE_APP_PROTOCOL) {
return payload;
}
const shadow = isProtocolRecord(payload.shadow) ? payload.shadow : null;
if (shadow?.protocol === SHADOW_SERVER_APP_PROTOCOL) {
if (shadow?.protocol === SHADOW_SPACE_APP_PROTOCOL) {
return shadow;

@@ -176,3 +179,3 @@ }

shadow: {
protocol: SHADOW_SERVER_APP_PROTOCOL,
protocol: SHADOW_SPACE_APP_PROTOCOL,
outbox: {

@@ -185,3 +188,3 @@ ...existing.outbox ?? {},

}
function getShadowServerAppInboxDeliveries(payload) {
function getShadowSpaceAppInboxDeliveries(payload) {
if (!isProtocolRecord(payload)) return [];

@@ -191,3 +194,3 @@ const shadow = shadowFromPayload(payload);

}
function getShadowServerAppInboxErrors(payload) {
function getShadowSpaceAppInboxErrors(payload) {
if (!isProtocolRecord(payload)) return [];

@@ -197,9 +200,3 @@ const shadow = shadowFromPayload(payload);

}
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) {
function getShadowSpaceAppChannelMessageDeliveries(payload) {
if (!isProtocolRecord(payload)) return [];

@@ -209,3 +206,3 @@ const shadow = shadowFromPayload(payload);

}
function getShadowServerAppChannelMessageErrors(payload) {
function getShadowSpaceAppChannelMessageErrors(payload) {
if (!isProtocolRecord(payload)) return [];

@@ -215,11 +212,2 @@ const shadow = shadowFromPayload(payload);

}
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) {

@@ -232,3 +220,3 @@ return Array.isArray(payload.events) && ("cursor" in payload || "result" in payload);

}
function unwrapShadowServerAppCommandPayload(payload) {
function unwrapShadowSpaceAppCommandPayload(payload) {
if (isProtocolRecord(payload) && payload.ok === false) {

@@ -238,3 +226,3 @@ 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 nested = unwrapShadowSpaceAppCommandPayload(payload.result);
const shadow = shadowFromPayload(payload);

@@ -246,3 +234,3 @@ if (isProtocolRecord(nested)) return mergeShadowResult(nested, shadow);

}
async function readShadowServerAppResponsePayload(response) {
async function readShadowSpaceAppResponsePayload(response) {
const text = await response.text().catch(() => "");

@@ -254,6 +242,6 @@ if (!text.trim()) return null;

if (!response.ok) return { ok: false, error: text };
throw new ShadowServerAppHttpError(response.status, "Command returned invalid JSON", text);
throw new ShadowSpaceAppHttpError(response.status, "Command returned invalid JSON", text);
}
}
function shadowServerAppResponseErrorMessage(status, payload, fallback = "Command failed") {
function shadowSpaceAppResponseErrorMessage(status, payload, fallback = "Command failed") {
if (isProtocolRecord(payload) && typeof payload.error === "string" && payload.error) {

@@ -265,16 +253,13 @@ return payload.error;

}
async function readShadowServerAppCommandResponse(response) {
const payload = await readShadowServerAppResponsePayload(response);
async function readShadowSpaceAppCommandResponse(response) {
const payload = await readShadowSpaceAppResponsePayload(response);
if (!response.ok || isProtocolRecord(payload) && payload.ok === false) {
throw new ShadowServerAppHttpError(
throw new ShadowSpaceAppHttpError(
response.status,
shadowServerAppResponseErrorMessage(response.status, payload),
shadowSpaceAppResponseErrorMessage(response.status, payload),
payload
);
}
return unwrapShadowServerAppCommandPayload(payload);
return unwrapShadowSpaceAppCommandPayload(payload);
}
function trimTrailingSlash(value) {
return value.replace(/\/+$/, "");
}
function decodeBase64UrlJson(value) {

@@ -291,44 +276,340 @@ try {

}
function decodeShadowServerAppLaunchTokenHint(token) {
function decodeShadowSpaceAppLaunchTokenPayload(token) {
if (!token) return null;
const parts = token.split(".");
if (parts.length !== 3 || parts[0] !== "sat_v1") return null;
const payload = decodeBase64UrlJson(parts[1]);
return decodeBase64UrlJson(parts[1]);
}
function decodeShadowSpaceAppLaunchTokenHint(token) {
const payload = decodeShadowSpaceAppLaunchTokenPayload(token);
if (typeof payload?.serverId !== "string" || typeof payload.appKey !== "string") return null;
return { serverId: payload.serverId, appKey: payload.appKey };
}
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
})
// src/bridge-authorization-element.ts
var defaultScopeLabels = {
"user:read": "Read your basic profile",
"user:email": "Read your email address",
"servers:read": "Read server information",
"servers:write": "Manage server information",
"channels:read": "Read channels",
"channels:write": "Manage channels",
"messages:read": "Read messages",
"messages:write": "Send and manage messages",
"attachments:read": "Read attachments",
"attachments:write": "Upload and manage attachments",
"workspaces:read": "Read workspace files",
"workspaces:write": "Manage workspace files",
"buddies:create": "Create Buddies",
"buddies:manage": "Manage Buddies",
"commerce:read": "Read commerce data",
"commerce:write": "Manage commerce data"
};
function boolAttr(value) {
return value === "" || value === "true";
}
function splitScopes(value) {
return (value ?? "user:read").split(/\s+/u).map((scope) => scope.trim()).filter(Boolean);
}
function escapeHtml(value) {
return String(value ?? "").replace(/[&<>"']/gu, (char) => {
switch (char) {
case "&":
return "&amp;";
case "<":
return "&lt;";
case ">":
return "&gt;";
case '"':
return "&quot;";
case "'":
return "&#39;";
default:
return char;
}
);
if (!response.ok) {
const payload = await readShadowServerAppResponsePayload(response);
throw new ShadowServerAppHttpError(
response.status,
shadowServerAppResponseErrorMessage(
response.status,
payload,
"Shadow launch outbox delivery failed"
),
payload
);
});
}
function defineShadowSpaceAppAuthorizeElement(tagName = "shadow-space-app-authorize") {
if (typeof window === "undefined" || typeof HTMLElement === "undefined") return null;
if (!window.customElements) return null;
const existing = window.customElements.get(tagName);
if (existing) return existing;
class ShadowSpaceAppAuthorizeElement extends HTMLElement {
static observedAttributes = [
"app-name",
"app-logo-url",
"app-origin",
"title",
"subtitle",
"permissions-label",
"approve-label",
"deny-label",
"approving-label",
"loading",
"approving",
"error",
"scopes"
];
dataValue = {};
constructor() {
super();
this.attachShadow({ mode: "open" });
}
set data(value) {
this.dataValue = value;
this.render();
}
get data() {
return this.dataValue;
}
connectedCallback() {
this.render();
}
attributeChangedCallback() {
this.render();
}
value(name, fallback = "") {
return this.getAttribute(name) ?? fallback;
}
mergedData() {
return {
appName: this.dataValue.appName ?? this.value("app-name", "Application"),
appLogoUrl: this.dataValue.appLogoUrl ?? this.getAttribute("app-logo-url"),
appOrigin: this.dataValue.appOrigin ?? this.getAttribute("app-origin"),
title: this.dataValue.title ?? this.value("title", "Authorize application"),
subtitle: this.dataValue.subtitle ?? this.value("subtitle", "This application is requesting access to your Shadow account."),
permissionsLabel: this.dataValue.permissionsLabel ?? this.value("permissions-label", "Requested access"),
approveLabel: this.dataValue.approveLabel ?? this.value("approve-label", "Authorize"),
denyLabel: this.dataValue.denyLabel ?? this.value("deny-label", "Deny"),
approvingLabel: this.dataValue.approvingLabel ?? this.value("approving-label", "Authorizing"),
loading: this.dataValue.loading ?? boolAttr(this.getAttribute("loading")),
approving: this.dataValue.approving ?? boolAttr(this.getAttribute("approving")),
error: this.dataValue.error ?? this.getAttribute("error"),
scopes: this.dataValue.scopes ?? splitScopes(this.getAttribute("scopes")),
scopeLabels: { ...defaultScopeLabels, ...this.dataValue.scopeLabels ?? {} }
};
}
render() {
if (!this.shadowRoot) return;
const data = this.mergedData();
const initial = escapeHtml(data.appName[0]?.toUpperCase() ?? "A");
const scopes = data.scopes.length ? data.scopes : ["user:read"];
const logoUrl = data.appLogoUrl ? escapeHtml(data.appLogoUrl) : null;
const title = escapeHtml(data.title);
this.shadowRoot.innerHTML = `
<style>
:host {
display: block;
color: #f8fafc;
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", sans-serif;
}
.card {
width: min(460px, calc(100vw - 48px));
border: 1px solid rgba(255, 255, 255, 0.14);
border-radius: 24px;
background:
linear-gradient(180deg, rgba(20, 24, 33, 0.96), rgba(11, 15, 23, 0.98)),
#0b0f17;
box-shadow: 0 28px 90px rgba(0, 0, 0, 0.48);
overflow: hidden;
}
.top {
padding: 24px 24px 18px;
text-align: center;
}
.logo {
width: 54px;
height: 54px;
margin: 0 auto 14px;
display: grid;
place-items: center;
border-radius: 16px;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.12);
color: white;
font-weight: 900;
overflow: hidden;
}
.logo img {
width: 100%;
height: 100%;
object-fit: cover;
}
h2 {
margin: 0;
font-size: 20px;
line-height: 1.15;
letter-spacing: 0;
font-weight: 850;
}
.subtitle {
margin: 8px auto 0;
max-width: 34ch;
color: rgba(248, 250, 252, 0.68);
font-size: 13px;
line-height: 1.55;
font-weight: 600;
}
.app {
display: flex;
gap: 12px;
align-items: center;
margin: 0 20px 18px;
padding: 14px;
border-radius: 16px;
background: rgba(255, 255, 255, 0.07);
}
.app-name {
min-width: 0;
font-size: 14px;
font-weight: 800;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.app-origin {
margin-top: 3px;
min-width: 0;
color: rgba(248, 250, 252, 0.52);
font-size: 12px;
font-weight: 650;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.body {
padding: 0 24px 22px;
}
.label {
margin: 0 0 10px;
color: rgba(248, 250, 252, 0.7);
font-size: 13px;
font-weight: 750;
}
ul {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 8px;
}
li {
display: flex;
align-items: center;
gap: 9px;
color: rgba(248, 250, 252, 0.88);
font-size: 13px;
font-weight: 650;
}
.check {
width: 20px;
height: 20px;
flex: 0 0 auto;
display: grid;
place-items: center;
border-radius: 999px;
background: rgba(52, 211, 153, 0.16);
color: #34d399;
font-size: 13px;
font-weight: 950;
}
.error {
margin: 0 24px 16px;
padding: 11px 12px;
border: 1px solid rgba(248, 113, 113, 0.28);
border-radius: 14px;
background: rgba(248, 113, 113, 0.1);
color: #fecaca;
font-size: 13px;
font-weight: 700;
}
.actions {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
padding: 0 24px 24px;
}
button {
min-height: 44px;
border: 0;
border-radius: 14px;
font: inherit;
font-size: 14px;
font-weight: 850;
cursor: pointer;
}
button:disabled {
cursor: default;
opacity: 0.56;
}
.deny {
color: #f8fafc;
background: rgba(255, 255, 255, 0.09);
}
.approve {
color: #061018;
background: #f8fafc;
}
.loading {
display: grid;
min-height: 180px;
place-items: center;
}
.spinner {
width: 24px;
height: 24px;
border-radius: 999px;
border: 3px solid rgba(255, 255, 255, 0.16);
border-top-color: #f8fafc;
animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
</style>
<section class="card" role="dialog" aria-modal="true" aria-label="${title}">
<div class="top">
<div class="logo">
${logoUrl ? `<img alt="" src="${logoUrl}" />` : `<span>${initial}</span>`}
</div>
<h2>${title}</h2>
<p class="subtitle">${escapeHtml(data.subtitle)}</p>
</div>
${data.loading ? '<div class="loading"><div class="spinner"></div></div>' : `
<div class="app">
<div class="logo" style="width:42px;height:42px;margin:0;border-radius:13px">
${logoUrl ? `<img alt="" src="${logoUrl}" />` : `<span>${initial}</span>`}
</div>
<div style="min-width:0">
<div class="app-name">${escapeHtml(data.appName)}</div>
${data.appOrigin ? `<div class="app-origin">${escapeHtml(data.appOrigin)}</div>` : ""}
</div>
</div>
${data.error ? `<div class="error">${escapeHtml(data.error)}</div>` : ""}
<div class="body">
<p class="label">${escapeHtml(data.permissionsLabel)}</p>
<ul>
${scopes.map(
(scope) => `<li><span class="check">\u2713</span><span>${escapeHtml(
data.scopeLabels[scope] ?? scope
)}</span></li>`
).join("")}
</ul>
</div>
<div class="actions">
<button class="deny" ${data.approving ? "disabled" : ""}>${escapeHtml(
data.denyLabel
)}</button>
<button class="approve" ${data.approving ? "disabled" : ""}>${data.approving ? escapeHtml(data.approvingLabel) : escapeHtml(data.approveLabel)}</button>
</div>
`}
</section>
`;
this.shadowRoot.querySelector(".approve")?.addEventListener("click", () => this.emit("shadow-authorize-approve"));
this.shadowRoot.querySelector(".deny")?.addEventListener("click", () => this.emit("shadow-authorize-deny"));
}
emit(type) {
this.dispatchEvent(new CustomEvent(type, { bubbles: true, composed: true }));
}
}
return readShadowServerAppResponsePayload(response);
window.customElements.define(tagName, ShadowSpaceAppAuthorizeElement);
return ShadowSpaceAppAuthorizeElement;
}

@@ -339,6 +620,5 @@

"copilot.open",
"channel.open",
"workspace.open",
"buddy.create.open",
"buddy.inboxes.list",
"buddy.grant.ensure",
"oauth.authorize",

@@ -348,3 +628,3 @@ "launch.refresh",

"route.report",
"app.share.open"
"space-app.share.open"
];

@@ -354,3 +634,3 @@ function commandPath(basePath, commandName) {

}
function shadowServerAppMountedPathPrefix(windowRef) {
function shadowSpaceAppMountedPathPrefix(windowRef) {
const win = windowRef ?? (typeof window === "undefined" ? null : window);

@@ -363,5 +643,5 @@ const pathname = win?.location?.pathname ?? "";

}
function shadowServerAppMountedPath(path, windowRef) {
function shadowSpaceAppMountedPath(path, windowRef) {
const normalized = path.startsWith("/") ? path : `/${path}`;
return `${shadowServerAppMountedPathPrefix(windowRef)}${normalized}`;
return `${shadowSpaceAppMountedPathPrefix(windowRef)}${normalized}`;
}

@@ -379,9 +659,9 @@ function isRecord(value) {

}
var ShadowServerAppBrowserClient = class {
var ShadowSpaceAppBrowserClient = class {
bridge;
commandBasePath;
sessionPath;
inboxesPath;
shadowApiBaseUrl;
buddyGrantPath;
fetchFn;
deliverLaunchOutboxFromBrowser;
win;

@@ -391,2 +671,6 @@ launchTokenValue;

launchExpiresInValue;
sessionLaunchToken = null;
sessionCsrfToken = null;
sessionExchangePromise = null;
launchRefreshPromise = null;
launchContextHandlers = /* @__PURE__ */ new Set();

@@ -397,10 +681,10 @@ unsubscribeLaunchUpdate;

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.commandBasePath = options.commandBasePath ?? shadowSpaceAppMountedPath("/api/commands", windowRef);
this.sessionPath = options.sessionPath ?? shadowSpaceAppMountedPath("/api/shadow/session", windowRef);
this.inboxesPath = options.inboxesPath ?? shadowSpaceAppMountedPath("/api/inboxes", windowRef);
this.buddyGrantPath = options.buddyGrantPath ?? shadowSpaceAppMountedPath("/api/shadow/buddy-grants/ensure", windowRef);
this.fetchFn = options.fetch;
this.deliverLaunchOutboxFromBrowser = options.deliverLaunchOutboxFromBrowser ?? false;
this.win = windowRef;
this.launchTokenValue = this.launchTokenFromLocation();
this.launchEventStreamUrlValue = this.launchEventStreamUrlFromLocation();
this.launchTokenValue = this.bridge.launchToken();
this.launchEventStreamUrlValue = shadowSpaceAppMountedPath("/api/shadow/events", windowRef);
this.unsubscribeLaunchUpdate = this.bridge.onLaunchUpdate((context) => {

@@ -417,13 +701,19 @@ this.applyLaunchUpdate(context);

}
launchToken(param = "shadow_launch") {
return this.launchTokenValue ?? this.launchTokenFromLocation(param);
launchToken() {
return this.launchTokenValue;
}
launchEventStreamUrl(param = "shadow_event_stream") {
return this.launchEventStreamUrlValue ?? this.launchEventStreamUrlFromLocation(param);
launchEventStreamUrl() {
return this.launchEventStreamUrlValue;
}
async prepareEventStream() {
try {
const ready = await this.ensureSession({ reason: "events_missing_session" });
return ready ? this.launchEventStreamUrl() : null;
} catch {
return null;
}
}
launchContext() {
return {
launchToken: this.launchToken(),
eventStreamUrl: this.launchEventStreamUrl(),
eventStreamPath: this.launchEventStreamUrl(),
...typeof this.launchExpiresInValue === "number" ? { expiresIn: this.launchExpiresInValue } : {}

@@ -438,123 +728,114 @@ };

}
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" });
}
await this.ensureSession({ reason: "command_missing_session" });
const path = commandPath(this.commandBasePath, commandName);
const init = {
method: "POST",
headers: this.launchHeaders({ "Content-Type": "application/json" }),
body: JSON.stringify({ input: withoutUndefined(input) })
headers: this.sessionHeaders({ "Content-Type": "application/json" }),
body: JSON.stringify({ input: withoutUndefined(input) }),
credentials: "include"
};
let response = await this.fetch(path, init);
if (response.status === 401 && await this.refreshLaunch({ reason: "command_unauthorized" })) {
if (response.status === 401 && await this.recoverSession("command_unauthorized")) {
response = await this.fetch(path, {
...init,
headers: this.launchHeaders({ "Content-Type": "application/json" })
headers: this.sessionHeaders({ "Content-Type": "application/json" })
});
}
const result = await readShadowServerAppCommandResponse(response);
return this.deliverLaunchOutbox(commandName, result);
return readShadowSpaceAppCommandResponse(response);
}
async commandForm(commandName, formData) {
if (!this.launchToken()) {
await this.refreshLaunch({ reason: "command_missing_launch" });
}
await this.ensureSession({ reason: "command_missing_session" });
const path = commandPath(this.commandBasePath, commandName);
const init = {
method: "POST",
headers: this.launchHeaders(),
body: formData
headers: this.sessionHeaders(),
body: formData,
credentials: "include"
};
let response = await this.fetch(path, init);
if (response.status === 401 && await this.refreshLaunch({ reason: "command_unauthorized" })) {
if (response.status === 401 && await this.recoverSession("command_unauthorized")) {
response = await this.fetch(path, {
...init,
headers: this.launchHeaders()
headers: this.sessionHeaders()
});
}
const result = await readShadowServerAppCommandResponse(response);
return this.deliverLaunchOutbox(commandName, result);
return readShadowSpaceAppCommandResponse(response);
}
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);
if (this.launchRefreshPromise) return this.launchRefreshPromise;
const request = (async () => {
try {
const context = await this.bridge.refreshLaunch(input, { timeoutMs: 8e3 });
if (context?.launchToken) {
this.applyLaunchUpdate({
launchToken: context.launchToken,
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;
}
return context;
} catch {
return null;
}
})().finally(() => {
this.launchRefreshPromise = null;
});
this.launchRefreshPromise = request;
return request;
}
async fetchWithLaunch(input, init = {}, options = {}) {
async fetchWithSession(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" })) {
await this.ensureSession({ reason: "fetch_missing_session" });
let response = await this.fetch(input, this.withSession(init));
if (response.status !== 401 || !await this.recoverSession("fetch_unauthorized")) {
return response;
}
return this.fetch(input, this.withLaunchHeaders(init));
return this.fetch(input, this.withSession(init));
}
async listBuddyInboxes(options = {}) {
if (this.bridge.isAvailable()) {
try {
return await this.bridge.listBuddyInboxes(
{ refresh: options.refresh },
{ timeoutMs: options.timeoutMs ?? 4e3 }
);
} catch {
try {
if (options.refresh) await this.refreshLaunch({ reason: "inboxes_refresh" });
await this.ensureSession({ reason: "inboxes_missing_session" });
let response = await this.fetch(this.inboxesPath, this.withSession({ method: "GET" }));
if (response.status === 401 && await this.recoverSession("inboxes_unauthorized")) {
response = await this.fetch(this.inboxesPath, this.withSession({ method: "GET" }));
}
}
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 (!response.ok) throw new Error(`Buddy inboxes failed (${response.status})`);
return await response.json();
} catch (error) {
if (options.emptyOnError) return { inboxes: [] };
const message = await response.text().catch(() => "");
throw new Error(message || `Buddy inbox lookup failed (${response.status})`);
throw error;
}
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 ?? [import_shared.BUDDY_INBOX_DELIVERY_PERMISSION],
reason: input.reason
},
{ timeoutMs: input.timeoutMs ?? 6e3 }
if (!buddyAgentId) return { granted: false, skipped: true };
await this.ensureSession({ reason: "buddy_grant_missing_session" });
const request = () => this.fetch(
this.buddyGrantPath,
this.withSession({
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
buddyAgentId,
permissions: input.permissions,
reason: input.reason
}),
signal: AbortSignal.timeout(input.timeoutMs ?? 6e3)
})
);
let response = await request();
if (response.status === 401 && await this.recoverSession("buddy_grant_unauthorized")) {
response = await request();
}
if (!response.ok) throw new Error(`Buddy grant failed (${response.status})`);
return response.json();
}

@@ -568,8 +849,29 @@ openBuddyCreator(input = {}, options = {}) {

}
openChannel(input, options = {}) {
return this.bridge.openChannel(input, 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);
async authorizeOAuth(input, options = {}) {
const authorizeUrl = typeof input === "string" ? input : input.authorizeUrl;
if (!this.bridge.isAvailable()) {
if (options.fallback === "redirect") return this.redirectToAuthorizeUrl(authorizeUrl);
return { opened: false, status: "unavailable" };
}
try {
return await this.bridge.authorizeOAuth(input, {
// Authorization may include a first-time consent decision in the host.
// Keep the browser client aligned with the bridge's interactive timeout
// instead of treating a normal approval pause as a failed request.
timeoutMs: options.timeoutMs ?? 10 * 60 * 1e3
});
} catch (error) {
if (options.fallback === "redirect") return this.redirectToAuthorizeUrl(authorizeUrl);
return {
opened: false,
status: error instanceof Error && error.message.includes("timed out") ? "timeout" : "unavailable",
error: error instanceof Error ? error.message : "OAuth authorization failed"
};
}
}

@@ -582,32 +884,18 @@ routeChanged(path) {

}
shareApp(input = {}, options = {}) {
shareSpaceApp(input = {}, options = {}) {
if (!this.bridge.isAvailable()) return Promise.resolve({ opened: false });
return this.bridge.shareApp(input, options);
return this.bridge.shareSpaceApp(input, options);
}
inboxDeliveries(payload) {
return this.bridge.inboxDeliveries(payload);
return getShadowSpaceAppInboxDeliveries(payload);
}
inboxErrors(payload) {
return this.bridge.inboxErrors(payload);
return getShadowSpaceAppInboxErrors(payload);
}
channelMessageDeliveries(payload) {
return this.bridge.channelMessageDeliveries(payload);
return getShadowSpaceAppChannelMessageDeliveries(payload);
}
channelMessageErrors(payload) {
return this.bridge.channelMessageErrors(payload);
return getShadowSpaceAppChannelMessageErrors(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) {

@@ -617,2 +905,7 @@ if (this.fetchFn) return this.fetchFn(input, init);

}
redirectToAuthorizeUrl(authorizeUrl) {
if (!this.win) return { opened: false, status: "unavailable" };
this.win.location.assign(authorizeUrl);
return { opened: true, status: "redirected", redirectUrl: authorizeUrl };
}
dispose() {

@@ -623,65 +916,77 @@ this.unsubscribeLaunchUpdate();

}
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) {
if (context.launchToken !== this.launchTokenValue) {
this.sessionLaunchToken = null;
this.sessionCsrfToken = null;
}
this.launchTokenValue = context.launchToken;
this.launchEventStreamUrlValue = context.eventStreamUrl ?? context.eventStreamPath ?? null;
this.launchExpiresInValue = typeof context.expiresIn === "number" ? context.expiresIn : void 0;
}
withLaunchHeaders(init) {
sessionHeaders(headersInit) {
const headers = new Headers(headersInit);
if (this.sessionCsrfToken) headers.set("X-Shadow-Space-App-CSRF", this.sessionCsrfToken);
return headers;
}
withSession(init) {
const headers = new Headers(init.headers);
const token = this.launchToken();
if (token) headers.set("X-Shadow-Launch-Token", token);
return { ...init, headers };
if (this.sessionCsrfToken) headers.set("X-Shadow-Space-App-CSRF", this.sessionCsrfToken);
return { ...init, headers, credentials: "include" };
}
async ensureSession(input = {}) {
if (!this.bridge.isAvailable()) return false;
if (!this.launchToken()) await this.refreshLaunch(input);
const launchToken = this.launchToken();
if (!launchToken) return false;
if (this.sessionLaunchToken === launchToken && this.sessionCsrfToken) return true;
if (this.sessionExchangePromise) return this.sessionExchangePromise;
const exchange = (async () => {
const response = await this.fetch(this.sessionPath, {
method: "POST",
headers: { Authorization: `Bearer ${launchToken}` },
credentials: "include"
});
if (!response.ok) return false;
const payload = await response.json().catch(() => null);
if (payload?.ok !== true || typeof payload.csrfToken !== "string") return false;
this.sessionLaunchToken = launchToken;
this.sessionCsrfToken = payload.csrfToken;
return true;
})().finally(() => {
this.sessionExchangePromise = null;
});
this.sessionExchangePromise = exchange;
return exchange;
}
async recoverSession(reason) {
this.sessionLaunchToken = null;
this.sessionCsrfToken = null;
await this.refreshLaunch({ reason });
return this.ensureSession({ reason });
}
};
function createShadowServerAppClient(options = {}) {
return new ShadowServerAppBrowserClient(options);
function createShadowSpaceAppClient(options = {}) {
return new ShadowSpaceAppBrowserClient(options);
}
var createShadowServerAppBrowserClient = createShadowServerAppClient;
var createShadowSpaceAppBrowserClient = createShadowSpaceAppClient;
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);
}
static capabilitiesRequestType = "shadow.space-app.capabilities.request";
static capabilitiesResponseType = "shadow.space-app.capabilities.response";
static openCopilotRequestType = "shadow.space-app.copilot.open.request";
static openCopilotResponseType = "shadow.space-app.copilot.open.response";
static openChannelRequestType = "shadow.space-app.channel.open.request";
static openChannelResponseType = "shadow.space-app.channel.open.response";
static openWorkspaceResourceRequestType = "shadow.space-app.workspace.open.request";
static openWorkspaceResourceResponseType = "shadow.space-app.workspace.open.response";
static openBuddyCreatorRequestType = "shadow.space-app.buddy.create.request";
static openBuddyCreatorResponseType = "shadow.space-app.buddy.create.response";
static authorizeOAuthRequestType = "shadow.space-app.oauth.authorize.request";
static authorizeOAuthResponseType = "shadow.space-app.oauth.authorize.response";
static routeNavigateType = "shadow.space-app.navigate";
static routeNavigateAckType = "shadow.space-app.navigate.ack";
static routeChangedType = "shadow.space-app.route.changed";
static shareSpaceAppRequestType = "shadow.space-app.share.request";
static shareSpaceAppResponseType = "shadow.space-app.share.response";
static refreshLaunchRequestType = "shadow.space-app.launch.refresh.request";
static refreshLaunchResponseType = "shadow.space-app.launch.refresh.response";
static launchUpdatedEventType = "shadow.space-app.launch.updated";
appKey;

@@ -691,6 +996,6 @@ targetOrigin;

win;
hasLaunchContext;
launchTokenValue = null;
pending = /* @__PURE__ */ new Map();
onMessage = (event) => {
if (!this.isTrustedHostMessage(event)) return;
let data = event.data;

@@ -714,2 +1019,3 @@ if (typeof data === "string") {

this.pending.delete(record.requestId);
this.win?.clearTimeout(entry.timeoutId);
if (record.ok) {

@@ -728,6 +1034,5 @@ if (record.type === _ShadowBridge.refreshLaunchResponseType) {

this.appKey = options.appKey ?? this.resolveLaunchAppKey();
this.targetOrigin = options.targetOrigin ?? "*";
this.targetOrigin = options.targetOrigin ?? this.resolveHostOrigin();
this.timeoutMs = options.timeoutMs ?? 6e4;
this.launchTokenValue = this.resolveLaunchToken();
this.hasLaunchContext = this.resolveLaunchContext();
this.win?.addEventListener("message", this.onMessage);

@@ -738,2 +1043,3 @@ }

for (const entry of this.pending.values()) {
this.win?.clearTimeout(entry.timeoutId);
entry.reject(new Error("ShadowBridge disposed"));

@@ -745,15 +1051,8 @@ }

if (!this.win) return false;
return (this.hasLaunchContext || !!this.appKey) && (this.win.parent !== this.win || !!this.win.ReactNativeWebView);
return this.win.parent !== this.win || !!this.win.ReactNativeWebView;
}
launchToken(param = "shadow_launch") {
launchToken() {
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 = {}) {

@@ -776,2 +1075,10 @@ return this.request(

}
openChannel(input, options = {}) {
return this.request(
_ShadowBridge.openChannelRequestType,
_ShadowBridge.openChannelResponseType,
input,
options.timeoutMs ?? 15e3
);
}
openWorkspaceResource(input, options = {}) {

@@ -793,18 +1100,2 @@ return this.request(

}
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 = {}) {

@@ -832,2 +1123,3 @@ const payload = typeof input === "string" ? { authorizeUrl: input } : input;

const listener = (event) => {
if (!this.isTrustedHostMessage(event)) return;
let data = event.data;

@@ -864,2 +1156,3 @@ if (typeof data === "string") {

const listener = (event) => {
if (!this.isTrustedHostMessage(event)) return;
let data = event.data;

@@ -874,17 +1167,13 @@ if (typeof data === "string") {

if (!data || typeof data !== "object") return;
const record = data;
if (record.type !== _ShadowBridge.launchUpdateType && record.type !== _ShadowBridge.launchUpdatedEventType) {
const envelope = data;
if (envelope.type !== _ShadowBridge.launchUpdatedEventType) return;
const record = isRecord(envelope.result) ? envelope.result : isRecord(envelope.launch) ? envelope.launch : envelope;
const appKey = typeof envelope.appKey === "string" ? envelope.appKey : typeof record.appKey === "string" ? record.appKey : void 0;
if (this.appKey && appKey && appKey !== this.appKey) {
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 } : {}

@@ -897,6 +1186,6 @@ })

}
shareApp(input = {}, options = {}) {
shareSpaceApp(input = {}, options = {}) {
return this.request(
_ShadowBridge.shareAppRequestType,
_ShadowBridge.shareAppResponseType,
_ShadowBridge.shareSpaceAppRequestType,
_ShadowBridge.shareSpaceAppResponseType,
input,

@@ -914,17 +1203,2 @@ options.timeoutMs ?? 5 * 60 * 1e3

}
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) {

@@ -936,20 +1210,27 @@ if (!this.isAvailable()) {

}
const requestId = `req_${Math.random().toString(36).slice(2)}`;
const requestId = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? `req_${crypto.randomUUID()}` : `req_${Date.now().toString(36)}_${this.pending.size.toString(36)}`;
return new Promise((resolve, reject) => {
const timeoutId = this.win.setTimeout(() => {
if (!this.pending.has(requestId)) return;
this.pending.delete(requestId);
reject(new Error(`${requestType} timed out after ${timeoutMs}ms`));
}, timeoutMs);
this.pending.set(requestId, {
responseType,
resolve,
reject
reject,
timeoutId
});
this.postMessage({
type: requestType,
requestId,
...this.appKey ? { appKey: this.appKey } : {},
...payload
});
this.win?.setTimeout(() => {
if (!this.pending.has(requestId)) return;
try {
this.postMessage({
type: requestType,
requestId,
...this.appKey ? { appKey: this.appKey } : {},
...payload
});
} catch (error) {
this.pending.delete(requestId);
reject(new Error("Bridge request timed out"));
}, timeoutMs);
this.win?.clearTimeout(timeoutId);
reject(error instanceof Error ? error : new Error("Bridge request failed to send"));
}
});

@@ -965,43 +1246,17 @@ }

}
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);
const hint = decodeShadowSpaceAppLaunchTokenHint(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;
}
return null;
}

@@ -1013,31 +1268,17 @@ applyLaunchContext(value) {

}
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;
resolveLaunchAppKey() {
return void 0;
}
resolveHostOrigin() {
if (!this.win?.document?.referrer) return "*";
try {
return storageKey ? this.win.sessionStorage?.getItem(storageKey) === "1" : false;
return new URL(this.win.document.referrer).origin;
} catch {
return false;
return "*";
}
}
resolveLaunchAppKey() {
if (!this.win) return void 0;
const token = new URLSearchParams(this.win.location.search).get("shadow_launch");
return decodeShadowServerAppLaunchTokenHint(token)?.appKey;
isTrustedHostMessage(event) {
if (!this.win) return false;
if (this.win.parent !== this.win && event.source !== this.win.parent) return false;
return this.targetOrigin === "*" || event.origin === this.targetOrigin;
}

@@ -1048,18 +1289,21 @@ };

SHADOW_BRIDGE_CAPABILITIES,
SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT,
SHADOW_SERVER_APP_COMMAND_EVENTS,
SHADOW_SERVER_APP_COMMAND_FAILED_EVENT,
SHADOW_SPACE_APP_COMMAND_COMPLETED_EVENT,
SHADOW_SPACE_APP_COMMAND_EVENTS,
SHADOW_SPACE_APP_COMMAND_FAILED_EVENT,
ShadowBridge,
ShadowServerAppBrowserClient,
buildShadowServerAppInboxDelivery,
buildShadowServerAppInboxTaskRequest,
createShadowServerAppBrowserClient,
createShadowServerAppClient,
getShadowServerAppChannelMessageDeliveries,
getShadowServerAppChannelMessageErrors,
getShadowServerAppTaskCardId,
readShadowServerAppCommandResponse,
shadowServerAppInboxTaskEndpoint,
shadowServerAppMountedPath,
shadowServerAppMountedPathPrefix
ShadowSpaceAppBrowserClient,
buildShadowSpaceAppInboxDelivery,
buildShadowSpaceAppInboxTaskRequest,
createShadowSpaceAppBrowserClient,
createShadowSpaceAppClient,
defineShadowSpaceAppAuthorizeElement,
getShadowSpaceAppChannelMessageDeliveries,
getShadowSpaceAppChannelMessageErrors,
getShadowSpaceAppInboxDeliveries,
getShadowSpaceAppInboxErrors,
getShadowSpaceAppTaskCardId,
readShadowSpaceAppCommandResponse,
shadowSpaceAppInboxTaskEndpoint,
shadowSpaceAppMountedPath,
shadowSpaceAppMountedPathPrefix
});

@@ -1,8 +0,30 @@

import { d6 as ShadowServerAppInboxDelivery, d7 as ShadowServerAppInboxDeliveryError, cA as ShadowServerAppChannelMessageDelivery, cB as ShadowServerAppChannelMessageDeliveryError, W as ShadowBuddyInboxSummary } from './server-app-nz50wbnP.cjs';
export { bM as SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT, bN as SHADOW_SERVER_APP_COMMAND_EVENTS, bO as SHADOW_SERVER_APP_COMMAND_FAILED_EVENT, cC as ShadowServerAppChannelMessageOutbox, cL as ShadowServerAppCommandEventType, d1 as ShadowServerAppHostAppRef, d2 as ShadowServerAppHostInboxTaskRequestInput, d8 as ShadowServerAppInboxDeliveryFromMessageInput, d9 as ShadowServerAppInboxTarget, da as ShadowServerAppInboxTaskOutbox, dt as ShadowServerAppResolvedInboxTaskRequest, du as ShadowServerAppResultShadow, dX as buildShadowServerAppInboxDelivery, dY as buildShadowServerAppInboxTaskRequest, e7 as getShadowServerAppChannelMessageDeliveries, e8 as getShadowServerAppChannelMessageErrors, e9 as getShadowServerAppTaskCardId, ej as readShadowServerAppCommandResponse, ev as shadowServerAppInboxTaskEndpoint } from './server-app-nz50wbnP.cjs';
import { dR as ShadowSpaceAppInboxDelivery, a3 as ShadowBuddyInboxSummary, dS as ShadowSpaceAppInboxDeliveryError, di as ShadowSpaceAppChannelMessageDelivery, dj as ShadowSpaceAppChannelMessageDeliveryError } from './space-app-BhlEB4pg.cjs';
export { c7 as SHADOW_SPACE_APP_COMMAND_COMPLETED_EVENT, c8 as SHADOW_SPACE_APP_COMMAND_EVENTS, c9 as SHADOW_SPACE_APP_COMMAND_FAILED_EVENT, dk as ShadowSpaceAppChannelMessageOutbox, du as ShadowSpaceAppCommandEventType, dM as ShadowSpaceAppHostInboxTaskRequestInput, dN as ShadowSpaceAppHostRef, dT as ShadowSpaceAppInboxDeliveryFromMessageInput, dU as ShadowSpaceAppInboxTarget, dV as ShadowSpaceAppInboxTaskOutbox, eh as ShadowSpaceAppResolvedInboxTaskRequest, ei as ShadowSpaceAppResultShadow, et as buildShadowSpaceAppInboxDelivery, eu as buildShadowSpaceAppInboxTaskRequest, eL as getShadowSpaceAppChannelMessageDeliveries, eM as getShadowSpaceAppChannelMessageErrors, eN as getShadowSpaceAppInboxDeliveries, eO as getShadowSpaceAppInboxErrors, eP as getShadowSpaceAppTaskCardId, e_ as readShadowSpaceAppCommandResponse, fa as shadowSpaceAppInboxTaskEndpoint } from './space-app-BhlEB4pg.cjs';
import '@shadowob/shared';
interface ShadowSpaceAppAuthorizeElementData {
appName?: string;
appLogoUrl?: string | null;
appOrigin?: string | null;
title?: string;
subtitle?: string;
permissionsLabel?: string;
approveLabel?: string;
denyLabel?: string;
approvingLabel?: string;
loading?: boolean;
approving?: boolean;
error?: string | null;
scopes?: string[];
scopeLabels?: Record<string, string>;
}
declare function defineShadowSpaceAppAuthorizeElement(tagName?: string): CustomElementConstructor | null;
interface ShadowBridgeOpenCopilotInput {
delivery: ShadowServerAppInboxDelivery;
delivery: ShadowSpaceAppInboxDelivery;
}
interface ShadowBridgeOpenChannelInput {
channelId: string;
messageId?: string;
}
interface ShadowBridgeOpenWorkspaceResourceInput {

@@ -27,10 +49,2 @@ resource: {

}
interface ShadowBridgeListBuddyInboxesInput {
refresh?: boolean;
}
interface ShadowBridgeEnsureBuddyGrantInput {
buddyAgentId: string;
permissions: string[];
reason?: string;
}
interface ShadowBridgeAuthorizeOAuthInput {

@@ -41,5 +55,7 @@ authorizeUrl: string;

opened: boolean;
status?: 'opened' | 'redirected' | 'denied' | 'unsupported' | 'timeout' | 'unavailable';
redirectUrl?: string;
error?: string;
}
interface ShadowBridgeShareAppInput {
interface ShadowBridgeShareSpaceAppInput {
path?: string;

@@ -51,3 +67,3 @@ title?: string;

}
interface ShadowBridgeShareAppResult {
interface ShadowBridgeShareSpaceAppResult {
opened: boolean;

@@ -64,4 +80,2 @@ url?: string;

launchToken: string | null;
eventStreamUrl?: string | null;
eventStreamPath?: string | null;
expiresIn?: number | null;

@@ -71,4 +85,2 @@ }

launchToken: string;
eventStreamUrl?: string | null;
eventStreamPath?: string | null;
expiresIn?: number | null;

@@ -82,3 +94,3 @@ }

type ShadowBridgeLaunchUpdateHandler = (context: ShadowBridgeLaunchUpdateInput) => void | Promise<void>;
declare const SHADOW_BRIDGE_CAPABILITIES: readonly ["copilot.open", "workspace.open", "buddy.create.open", "buddy.inboxes.list", "buddy.grant.ensure", "oauth.authorize", "launch.refresh", "route.navigate", "route.report", "app.share.open"];
declare const SHADOW_BRIDGE_CAPABILITIES: readonly ["copilot.open", "channel.open", "workspace.open", "buddy.create.open", "oauth.authorize", "launch.refresh", "route.navigate", "route.report", "space-app.share.open"];
type ShadowBridgeCapability = (typeof SHADOW_BRIDGE_CAPABILITIES)[number];

@@ -91,15 +103,10 @@ interface ShadowBridgeOptions {

}
interface ShadowServerAppBrowserClientOptions extends ShadowBridgeOptions {
interface ShadowSpaceAppBrowserClientOptions extends ShadowBridgeOptions {
commandBasePath?: string;
sessionPath?: string;
inboxesPath?: string;
shadowApiBaseUrl?: string;
buddyGrantPath?: string;
fetch?: typeof fetch;
/**
* Browser outbox delivery is an opt-in fallback for standalone demos.
* Embedded Server Apps should let the App Backend or Shadow proxy deliver
* launch outbox on the server side to avoid cross-origin failures and duplicate deliveries.
*/
deliverLaunchOutboxFromBrowser?: boolean;
}
interface ShadowServerAppEnsureBuddyTaskGrantInput {
interface ShadowSpaceAppEnsureBuddyTaskGrantInput {
agentId?: string | null;

@@ -110,3 +117,3 @@ permissions?: string[];

}
interface ShadowServerAppListBuddyInboxesOptions {
interface ShadowSpaceAppListBuddyInboxesOptions {
refresh?: boolean;

@@ -116,17 +123,18 @@ emptyOnError?: boolean;

}
interface ShadowServerAppLaunchHeadersOptions {
launchTokenParam?: string;
}
interface ShadowServerAppFetchWithLaunchOptions {
interface ShadowSpaceAppFetchWithSessionOptions {
refresh?: boolean | ShadowBridgeRefreshLaunchInput;
}
declare function shadowServerAppMountedPathPrefix(windowRef?: Window | null): string;
declare function shadowServerAppMountedPath(path: string, windowRef?: Window | null): string;
declare class ShadowServerAppBrowserClient {
interface ShadowSpaceAppAuthorizeOAuthOptions {
fallback?: 'none' | 'redirect';
timeoutMs?: number;
}
declare function shadowSpaceAppMountedPathPrefix(windowRef?: Window | null): string;
declare function shadowSpaceAppMountedPath(path: string, windowRef?: Window | null): string;
declare class ShadowSpaceAppBrowserClient {
readonly bridge: ShadowBridge;
private readonly commandBasePath;
private readonly sessionPath;
private readonly inboxesPath;
private readonly shadowApiBaseUrl;
private readonly buddyGrantPath;
private readonly fetchFn;
private readonly deliverLaunchOutboxFromBrowser;
private readonly win;

@@ -136,26 +144,23 @@ private launchTokenValue;

private launchExpiresInValue;
private sessionLaunchToken;
private sessionCsrfToken;
private sessionExchangePromise;
private launchRefreshPromise;
private readonly launchContextHandlers;
private readonly unsubscribeLaunchUpdate;
constructor(options?: ShadowServerAppBrowserClientOptions);
constructor(options?: ShadowSpaceAppBrowserClientOptions);
bridgeAvailable(): boolean;
launchToken(param?: string): string | null;
launchEventStreamUrl(param?: string): string | null;
launchToken(): string | null;
launchEventStreamUrl(): string | null;
prepareEventStream(): Promise<string | null>;
launchContext(): ShadowBridgeLaunchContext;
onLaunchContextChange(handler: (context: ShadowBridgeLaunchContext) => void | Promise<void>): () => void;
launchHeaders(headers?: Record<string, string>, options?: ShadowServerAppLaunchHeadersOptions): Record<string, string>;
command<TResult = unknown>(commandName: string, input?: unknown): Promise<TResult>;
commandForm<TResult = unknown>(commandName: string, formData: FormData): Promise<TResult>;
refreshLaunch(input?: ShadowBridgeRefreshLaunchInput): Promise<ShadowBridgeLaunchContext | null>;
fetchWithLaunch(input: RequestInfo | URL, init?: RequestInit, options?: ShadowServerAppFetchWithLaunchOptions): Promise<Response>;
listBuddyInboxes<TInbox = ShadowBuddyInboxSummary>(options?: ShadowServerAppListBuddyInboxesOptions): Promise<{
fetchWithSession(input: RequestInfo | URL, init?: RequestInit, options?: ShadowSpaceAppFetchWithSessionOptions): Promise<Response>;
listBuddyInboxes<TInbox = ShadowBuddyInboxSummary>(options?: ShadowSpaceAppListBuddyInboxesOptions): Promise<{
inboxes: TInbox[];
}>;
private fetchLaunchBuddyInboxes;
ensureBuddyTaskGrant(input: ShadowServerAppEnsureBuddyTaskGrantInput): Promise<{
granted: boolean;
grant?: unknown;
} | {
granted: boolean;
skipped: boolean;
}>;
ensureBuddyTaskGrant(input: ShadowSpaceAppEnsureBuddyTaskGrantInput): Promise<any>;
openBuddyCreator(input?: ShadowBridgeOpenBuddyCreatorInput, options?: {

@@ -167,3 +172,3 @@ timeoutMs?: number;

}>;
openCopilot(delivery: ShadowServerAppInboxDelivery, options?: {
openCopilot(delivery: ShadowSpaceAppInboxDelivery, options?: {
timeoutMs?: number;

@@ -173,3 +178,3 @@ }): Promise<{

}>;
openWorkspaceResource(input: ShadowBridgeOpenWorkspaceResourceInput, options?: {
openChannel(input: ShadowBridgeOpenChannelInput, options?: {
timeoutMs?: number;

@@ -179,3 +184,3 @@ }): Promise<{

}>;
authorizeOAuth(input: ShadowBridgeAuthorizeOAuthInput | string, options?: {
openWorkspaceResource(input: ShadowBridgeOpenWorkspaceResourceInput, options?: {
timeoutMs?: number;

@@ -185,5 +190,6 @@ }): Promise<{

}>;
authorizeOAuth(input: ShadowBridgeAuthorizeOAuthInput | string, options?: ShadowSpaceAppAuthorizeOAuthOptions): Promise<ShadowBridgeAuthorizeOAuthResult>;
routeChanged(path: string): boolean;
onRouteNavigate(handler: ShadowBridgeRouteNavigateHandler): () => void;
shareApp(input?: ShadowBridgeShareAppInput, options?: {
shareSpaceApp(input?: ShadowBridgeShareSpaceAppInput, options?: {
timeoutMs?: number;

@@ -193,45 +199,38 @@ }): Promise<{

}>;
inboxDeliveries(payload: unknown): ShadowServerAppInboxDelivery[];
inboxErrors(payload: unknown): ShadowServerAppInboxDeliveryError[];
channelMessageDeliveries(payload: unknown): ShadowServerAppChannelMessageDelivery[];
channelMessageErrors(payload: unknown): ShadowServerAppChannelMessageDeliveryError[];
private deliverLaunchOutbox;
inboxDeliveries(payload: unknown): ShadowSpaceAppInboxDelivery[];
inboxErrors(payload: unknown): ShadowSpaceAppInboxDeliveryError[];
channelMessageDeliveries(payload: unknown): ShadowSpaceAppChannelMessageDelivery[];
channelMessageErrors(payload: unknown): ShadowSpaceAppChannelMessageDeliveryError[];
private fetch;
private redirectToAuthorizeUrl;
dispose(): void;
private launchTokenFromLocation;
private launchEventStreamUrlFromLocation;
private applyLaunchUpdate;
private withLaunchHeaders;
private sessionHeaders;
private withSession;
private ensureSession;
private recoverSession;
}
declare function createShadowServerAppClient(options?: ShadowServerAppBrowserClientOptions): ShadowServerAppBrowserClient;
declare const createShadowServerAppBrowserClient: typeof createShadowServerAppClient;
declare function createShadowSpaceAppClient(options?: ShadowSpaceAppBrowserClientOptions): ShadowSpaceAppBrowserClient;
declare const createShadowSpaceAppBrowserClient: typeof createShadowSpaceAppClient;
declare class ShadowBridge {
static readonly capabilitiesRequestType = "shadow.app.capabilities.request";
static readonly capabilitiesResponseType = "shadow.app.capabilities.response";
static readonly openCopilotRequestType = "shadow.app.copilot.open.request";
static readonly openCopilotResponseType = "shadow.app.copilot.open.response";
static readonly openWorkspaceResourceRequestType = "shadow.app.workspace.open.request";
static readonly openWorkspaceResourceResponseType = "shadow.app.workspace.open.response";
static readonly openBuddyCreatorRequestType = "shadow.app.buddy.create.request";
static readonly openBuddyCreatorResponseType = "shadow.app.buddy.create.response";
static readonly listBuddyInboxesRequestType = "shadow.app.buddy.inboxes.request";
static readonly listBuddyInboxesResponseType = "shadow.app.buddy.inboxes.response";
static readonly ensureBuddyGrantRequestType = "shadow.app.buddy.grant.request";
static readonly ensureBuddyGrantResponseType = "shadow.app.buddy.grant.response";
static readonly authorizeOAuthRequestType = "shadow.app.oauth.authorize.request";
static readonly authorizeOAuthResponseType = "shadow.app.oauth.authorize.response";
static readonly launchUpdateType = "shadow.app.launch.update";
static readonly routeNavigateType = "shadow.app.navigate";
static readonly routeNavigateAckType = "shadow.app.navigate.ack";
static readonly routeChangedType = "shadow.app.route.changed";
static readonly shareAppRequestType = "shadow.app.share.request";
static readonly shareAppResponseType = "shadow.app.share.response";
static readonly refreshLaunchRequestType = "shadow.app.launch.refresh.request";
static readonly refreshLaunchResponseType = "shadow.app.launch.refresh.response";
static readonly launchUpdatedEventType = "shadow.app.launch.updated";
static inboxDeliveries(payload: unknown): ShadowServerAppInboxDelivery[];
static inboxErrors(payload: unknown): ShadowServerAppInboxDeliveryError[];
static channelMessageDeliveries(payload: unknown): ShadowServerAppChannelMessageDelivery[];
static channelMessageErrors(payload: unknown): ShadowServerAppChannelMessageDeliveryError[];
static unwrapCommandPayload<TResult = unknown>(payload: unknown): TResult;
static readonly capabilitiesRequestType = "shadow.space-app.capabilities.request";
static readonly capabilitiesResponseType = "shadow.space-app.capabilities.response";
static readonly openCopilotRequestType = "shadow.space-app.copilot.open.request";
static readonly openCopilotResponseType = "shadow.space-app.copilot.open.response";
static readonly openChannelRequestType = "shadow.space-app.channel.open.request";
static readonly openChannelResponseType = "shadow.space-app.channel.open.response";
static readonly openWorkspaceResourceRequestType = "shadow.space-app.workspace.open.request";
static readonly openWorkspaceResourceResponseType = "shadow.space-app.workspace.open.response";
static readonly openBuddyCreatorRequestType = "shadow.space-app.buddy.create.request";
static readonly openBuddyCreatorResponseType = "shadow.space-app.buddy.create.response";
static readonly authorizeOAuthRequestType = "shadow.space-app.oauth.authorize.request";
static readonly authorizeOAuthResponseType = "shadow.space-app.oauth.authorize.response";
static readonly routeNavigateType = "shadow.space-app.navigate";
static readonly routeNavigateAckType = "shadow.space-app.navigate.ack";
static readonly routeChangedType = "shadow.space-app.route.changed";
static readonly shareSpaceAppRequestType = "shadow.space-app.share.request";
static readonly shareSpaceAppResponseType = "shadow.space-app.share.response";
static readonly refreshLaunchRequestType = "shadow.space-app.launch.refresh.request";
static readonly refreshLaunchResponseType = "shadow.space-app.launch.refresh.response";
static readonly launchUpdatedEventType = "shadow.space-app.launch.updated";
private appKey?;

@@ -241,3 +240,2 @@ private readonly targetOrigin;

private readonly win;
private hasLaunchContext;
private launchTokenValue;

@@ -249,4 +247,3 @@ private readonly pending;

isAvailable(): boolean;
launchToken(param?: string): string | null;
launchHeaders(headers?: Record<string, string>, options?: ShadowServerAppLaunchHeadersOptions): Record<string, string>;
launchToken(): string | null;
capabilities(options?: {

@@ -257,3 +254,3 @@ timeoutMs?: number;

}>;
openCopilot(deliveryOrInput: ShadowServerAppInboxDelivery | ShadowBridgeOpenCopilotInput, options?: {
openCopilot(deliveryOrInput: ShadowSpaceAppInboxDelivery | ShadowBridgeOpenCopilotInput, options?: {
timeoutMs?: number;

@@ -263,2 +260,7 @@ }): Promise<{

}>;
openChannel(input: ShadowBridgeOpenChannelInput, options?: {
timeoutMs?: number;
}): Promise<{
opened: boolean;
}>;
openWorkspaceResource(input: ShadowBridgeOpenWorkspaceResourceInput, options?: {

@@ -275,13 +277,2 @@ timeoutMs?: number;

}>;
listBuddyInboxes(input?: ShadowBridgeListBuddyInboxesInput, options?: {
timeoutMs?: number;
}): Promise<{
inboxes: ShadowBuddyInboxSummary[];
}>;
ensureBuddyGrant(input: ShadowBridgeEnsureBuddyGrantInput, options?: {
timeoutMs?: number;
}): Promise<{
granted: boolean;
grant?: unknown;
}>;
authorizeOAuth(input: ShadowBridgeAuthorizeOAuthInput | string, options?: {

@@ -293,24 +284,18 @@ timeoutMs?: number;

onLaunchUpdate(handler: ShadowBridgeLaunchUpdateHandler): () => void;
shareApp(input?: ShadowBridgeShareAppInput, options?: {
shareSpaceApp(input?: ShadowBridgeShareSpaceAppInput, options?: {
timeoutMs?: number;
}): Promise<ShadowBridgeShareAppResult>;
}): Promise<ShadowBridgeShareSpaceAppResult>;
refreshLaunch(input?: ShadowBridgeRefreshLaunchInput, options?: {
timeoutMs?: number;
}): Promise<ShadowBridgeLaunchContext>;
unwrapCommandPayload<TResult = unknown>(payload: unknown): TResult;
inboxDeliveries(payload: unknown): ShadowServerAppInboxDelivery[];
inboxErrors(payload: unknown): ShadowServerAppInboxDeliveryError[];
channelMessageDeliveries(payload: unknown): ShadowServerAppChannelMessageDelivery[];
channelMessageErrors(payload: unknown): ShadowServerAppChannelMessageDeliveryError[];
private request;
private postMessage;
private launchContextStorageKey;
private launchTokenStorageKey;
private rememberLaunchToken;
private resolveLaunchToken;
private applyLaunchContext;
private resolveLaunchContext;
private resolveLaunchAppKey;
private resolveHostOrigin;
private isTrustedHostMessage;
}
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 };
export { SHADOW_BRIDGE_CAPABILITIES, ShadowBridge, type ShadowBridgeAuthorizeOAuthInput, type ShadowBridgeAuthorizeOAuthResult, type ShadowBridgeCapability, type ShadowBridgeLaunchContext, type ShadowBridgeLaunchUpdateHandler, type ShadowBridgeLaunchUpdateInput, type ShadowBridgeOpenBuddyCreatorInput, type ShadowBridgeOpenChannelInput, type ShadowBridgeOpenCopilotInput, type ShadowBridgeOpenWorkspaceResourceInput, type ShadowBridgeOptions, type ShadowBridgeRefreshLaunchInput, type ShadowBridgeRouteNavigateEvent, type ShadowBridgeRouteNavigateHandler, type ShadowBridgeShareSpaceAppInput, type ShadowBridgeShareSpaceAppResult, ShadowBuddyInboxSummary, type ShadowSpaceAppAuthorizeElementData, type ShadowSpaceAppAuthorizeOAuthOptions, ShadowSpaceAppBrowserClient, type ShadowSpaceAppBrowserClientOptions, ShadowSpaceAppChannelMessageDelivery, ShadowSpaceAppChannelMessageDeliveryError, type ShadowSpaceAppEnsureBuddyTaskGrantInput, type ShadowSpaceAppFetchWithSessionOptions, ShadowSpaceAppInboxDelivery, ShadowSpaceAppInboxDeliveryError, type ShadowSpaceAppListBuddyInboxesOptions, createShadowSpaceAppBrowserClient, createShadowSpaceAppClient, defineShadowSpaceAppAuthorizeElement, shadowSpaceAppMountedPath, shadowSpaceAppMountedPathPrefix };

@@ -1,8 +0,30 @@

import { d6 as ShadowServerAppInboxDelivery, d7 as ShadowServerAppInboxDeliveryError, cA as ShadowServerAppChannelMessageDelivery, cB as ShadowServerAppChannelMessageDeliveryError, W as ShadowBuddyInboxSummary } from './server-app-nz50wbnP.js';
export { bM as SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT, bN as SHADOW_SERVER_APP_COMMAND_EVENTS, bO as SHADOW_SERVER_APP_COMMAND_FAILED_EVENT, cC as ShadowServerAppChannelMessageOutbox, cL as ShadowServerAppCommandEventType, d1 as ShadowServerAppHostAppRef, d2 as ShadowServerAppHostInboxTaskRequestInput, d8 as ShadowServerAppInboxDeliveryFromMessageInput, d9 as ShadowServerAppInboxTarget, da as ShadowServerAppInboxTaskOutbox, dt as ShadowServerAppResolvedInboxTaskRequest, du as ShadowServerAppResultShadow, dX as buildShadowServerAppInboxDelivery, dY as buildShadowServerAppInboxTaskRequest, e7 as getShadowServerAppChannelMessageDeliveries, e8 as getShadowServerAppChannelMessageErrors, e9 as getShadowServerAppTaskCardId, ej as readShadowServerAppCommandResponse, ev as shadowServerAppInboxTaskEndpoint } from './server-app-nz50wbnP.js';
import { dR as ShadowSpaceAppInboxDelivery, a3 as ShadowBuddyInboxSummary, dS as ShadowSpaceAppInboxDeliveryError, di as ShadowSpaceAppChannelMessageDelivery, dj as ShadowSpaceAppChannelMessageDeliveryError } from './space-app-BhlEB4pg.js';
export { c7 as SHADOW_SPACE_APP_COMMAND_COMPLETED_EVENT, c8 as SHADOW_SPACE_APP_COMMAND_EVENTS, c9 as SHADOW_SPACE_APP_COMMAND_FAILED_EVENT, dk as ShadowSpaceAppChannelMessageOutbox, du as ShadowSpaceAppCommandEventType, dM as ShadowSpaceAppHostInboxTaskRequestInput, dN as ShadowSpaceAppHostRef, dT as ShadowSpaceAppInboxDeliveryFromMessageInput, dU as ShadowSpaceAppInboxTarget, dV as ShadowSpaceAppInboxTaskOutbox, eh as ShadowSpaceAppResolvedInboxTaskRequest, ei as ShadowSpaceAppResultShadow, et as buildShadowSpaceAppInboxDelivery, eu as buildShadowSpaceAppInboxTaskRequest, eL as getShadowSpaceAppChannelMessageDeliveries, eM as getShadowSpaceAppChannelMessageErrors, eN as getShadowSpaceAppInboxDeliveries, eO as getShadowSpaceAppInboxErrors, eP as getShadowSpaceAppTaskCardId, e_ as readShadowSpaceAppCommandResponse, fa as shadowSpaceAppInboxTaskEndpoint } from './space-app-BhlEB4pg.js';
import '@shadowob/shared';
interface ShadowSpaceAppAuthorizeElementData {
appName?: string;
appLogoUrl?: string | null;
appOrigin?: string | null;
title?: string;
subtitle?: string;
permissionsLabel?: string;
approveLabel?: string;
denyLabel?: string;
approvingLabel?: string;
loading?: boolean;
approving?: boolean;
error?: string | null;
scopes?: string[];
scopeLabels?: Record<string, string>;
}
declare function defineShadowSpaceAppAuthorizeElement(tagName?: string): CustomElementConstructor | null;
interface ShadowBridgeOpenCopilotInput {
delivery: ShadowServerAppInboxDelivery;
delivery: ShadowSpaceAppInboxDelivery;
}
interface ShadowBridgeOpenChannelInput {
channelId: string;
messageId?: string;
}
interface ShadowBridgeOpenWorkspaceResourceInput {

@@ -27,10 +49,2 @@ resource: {

}
interface ShadowBridgeListBuddyInboxesInput {
refresh?: boolean;
}
interface ShadowBridgeEnsureBuddyGrantInput {
buddyAgentId: string;
permissions: string[];
reason?: string;
}
interface ShadowBridgeAuthorizeOAuthInput {

@@ -41,5 +55,7 @@ authorizeUrl: string;

opened: boolean;
status?: 'opened' | 'redirected' | 'denied' | 'unsupported' | 'timeout' | 'unavailable';
redirectUrl?: string;
error?: string;
}
interface ShadowBridgeShareAppInput {
interface ShadowBridgeShareSpaceAppInput {
path?: string;

@@ -51,3 +67,3 @@ title?: string;

}
interface ShadowBridgeShareAppResult {
interface ShadowBridgeShareSpaceAppResult {
opened: boolean;

@@ -64,4 +80,2 @@ url?: string;

launchToken: string | null;
eventStreamUrl?: string | null;
eventStreamPath?: string | null;
expiresIn?: number | null;

@@ -71,4 +85,2 @@ }

launchToken: string;
eventStreamUrl?: string | null;
eventStreamPath?: string | null;
expiresIn?: number | null;

@@ -82,3 +94,3 @@ }

type ShadowBridgeLaunchUpdateHandler = (context: ShadowBridgeLaunchUpdateInput) => void | Promise<void>;
declare const SHADOW_BRIDGE_CAPABILITIES: readonly ["copilot.open", "workspace.open", "buddy.create.open", "buddy.inboxes.list", "buddy.grant.ensure", "oauth.authorize", "launch.refresh", "route.navigate", "route.report", "app.share.open"];
declare const SHADOW_BRIDGE_CAPABILITIES: readonly ["copilot.open", "channel.open", "workspace.open", "buddy.create.open", "oauth.authorize", "launch.refresh", "route.navigate", "route.report", "space-app.share.open"];
type ShadowBridgeCapability = (typeof SHADOW_BRIDGE_CAPABILITIES)[number];

@@ -91,15 +103,10 @@ interface ShadowBridgeOptions {

}
interface ShadowServerAppBrowserClientOptions extends ShadowBridgeOptions {
interface ShadowSpaceAppBrowserClientOptions extends ShadowBridgeOptions {
commandBasePath?: string;
sessionPath?: string;
inboxesPath?: string;
shadowApiBaseUrl?: string;
buddyGrantPath?: string;
fetch?: typeof fetch;
/**
* Browser outbox delivery is an opt-in fallback for standalone demos.
* Embedded Server Apps should let the App Backend or Shadow proxy deliver
* launch outbox on the server side to avoid cross-origin failures and duplicate deliveries.
*/
deliverLaunchOutboxFromBrowser?: boolean;
}
interface ShadowServerAppEnsureBuddyTaskGrantInput {
interface ShadowSpaceAppEnsureBuddyTaskGrantInput {
agentId?: string | null;

@@ -110,3 +117,3 @@ permissions?: string[];

}
interface ShadowServerAppListBuddyInboxesOptions {
interface ShadowSpaceAppListBuddyInboxesOptions {
refresh?: boolean;

@@ -116,17 +123,18 @@ emptyOnError?: boolean;

}
interface ShadowServerAppLaunchHeadersOptions {
launchTokenParam?: string;
}
interface ShadowServerAppFetchWithLaunchOptions {
interface ShadowSpaceAppFetchWithSessionOptions {
refresh?: boolean | ShadowBridgeRefreshLaunchInput;
}
declare function shadowServerAppMountedPathPrefix(windowRef?: Window | null): string;
declare function shadowServerAppMountedPath(path: string, windowRef?: Window | null): string;
declare class ShadowServerAppBrowserClient {
interface ShadowSpaceAppAuthorizeOAuthOptions {
fallback?: 'none' | 'redirect';
timeoutMs?: number;
}
declare function shadowSpaceAppMountedPathPrefix(windowRef?: Window | null): string;
declare function shadowSpaceAppMountedPath(path: string, windowRef?: Window | null): string;
declare class ShadowSpaceAppBrowserClient {
readonly bridge: ShadowBridge;
private readonly commandBasePath;
private readonly sessionPath;
private readonly inboxesPath;
private readonly shadowApiBaseUrl;
private readonly buddyGrantPath;
private readonly fetchFn;
private readonly deliverLaunchOutboxFromBrowser;
private readonly win;

@@ -136,26 +144,23 @@ private launchTokenValue;

private launchExpiresInValue;
private sessionLaunchToken;
private sessionCsrfToken;
private sessionExchangePromise;
private launchRefreshPromise;
private readonly launchContextHandlers;
private readonly unsubscribeLaunchUpdate;
constructor(options?: ShadowServerAppBrowserClientOptions);
constructor(options?: ShadowSpaceAppBrowserClientOptions);
bridgeAvailable(): boolean;
launchToken(param?: string): string | null;
launchEventStreamUrl(param?: string): string | null;
launchToken(): string | null;
launchEventStreamUrl(): string | null;
prepareEventStream(): Promise<string | null>;
launchContext(): ShadowBridgeLaunchContext;
onLaunchContextChange(handler: (context: ShadowBridgeLaunchContext) => void | Promise<void>): () => void;
launchHeaders(headers?: Record<string, string>, options?: ShadowServerAppLaunchHeadersOptions): Record<string, string>;
command<TResult = unknown>(commandName: string, input?: unknown): Promise<TResult>;
commandForm<TResult = unknown>(commandName: string, formData: FormData): Promise<TResult>;
refreshLaunch(input?: ShadowBridgeRefreshLaunchInput): Promise<ShadowBridgeLaunchContext | null>;
fetchWithLaunch(input: RequestInfo | URL, init?: RequestInit, options?: ShadowServerAppFetchWithLaunchOptions): Promise<Response>;
listBuddyInboxes<TInbox = ShadowBuddyInboxSummary>(options?: ShadowServerAppListBuddyInboxesOptions): Promise<{
fetchWithSession(input: RequestInfo | URL, init?: RequestInit, options?: ShadowSpaceAppFetchWithSessionOptions): Promise<Response>;
listBuddyInboxes<TInbox = ShadowBuddyInboxSummary>(options?: ShadowSpaceAppListBuddyInboxesOptions): Promise<{
inboxes: TInbox[];
}>;
private fetchLaunchBuddyInboxes;
ensureBuddyTaskGrant(input: ShadowServerAppEnsureBuddyTaskGrantInput): Promise<{
granted: boolean;
grant?: unknown;
} | {
granted: boolean;
skipped: boolean;
}>;
ensureBuddyTaskGrant(input: ShadowSpaceAppEnsureBuddyTaskGrantInput): Promise<any>;
openBuddyCreator(input?: ShadowBridgeOpenBuddyCreatorInput, options?: {

@@ -167,3 +172,3 @@ timeoutMs?: number;

}>;
openCopilot(delivery: ShadowServerAppInboxDelivery, options?: {
openCopilot(delivery: ShadowSpaceAppInboxDelivery, options?: {
timeoutMs?: number;

@@ -173,3 +178,3 @@ }): Promise<{

}>;
openWorkspaceResource(input: ShadowBridgeOpenWorkspaceResourceInput, options?: {
openChannel(input: ShadowBridgeOpenChannelInput, options?: {
timeoutMs?: number;

@@ -179,3 +184,3 @@ }): Promise<{

}>;
authorizeOAuth(input: ShadowBridgeAuthorizeOAuthInput | string, options?: {
openWorkspaceResource(input: ShadowBridgeOpenWorkspaceResourceInput, options?: {
timeoutMs?: number;

@@ -185,5 +190,6 @@ }): Promise<{

}>;
authorizeOAuth(input: ShadowBridgeAuthorizeOAuthInput | string, options?: ShadowSpaceAppAuthorizeOAuthOptions): Promise<ShadowBridgeAuthorizeOAuthResult>;
routeChanged(path: string): boolean;
onRouteNavigate(handler: ShadowBridgeRouteNavigateHandler): () => void;
shareApp(input?: ShadowBridgeShareAppInput, options?: {
shareSpaceApp(input?: ShadowBridgeShareSpaceAppInput, options?: {
timeoutMs?: number;

@@ -193,45 +199,38 @@ }): Promise<{

}>;
inboxDeliveries(payload: unknown): ShadowServerAppInboxDelivery[];
inboxErrors(payload: unknown): ShadowServerAppInboxDeliveryError[];
channelMessageDeliveries(payload: unknown): ShadowServerAppChannelMessageDelivery[];
channelMessageErrors(payload: unknown): ShadowServerAppChannelMessageDeliveryError[];
private deliverLaunchOutbox;
inboxDeliveries(payload: unknown): ShadowSpaceAppInboxDelivery[];
inboxErrors(payload: unknown): ShadowSpaceAppInboxDeliveryError[];
channelMessageDeliveries(payload: unknown): ShadowSpaceAppChannelMessageDelivery[];
channelMessageErrors(payload: unknown): ShadowSpaceAppChannelMessageDeliveryError[];
private fetch;
private redirectToAuthorizeUrl;
dispose(): void;
private launchTokenFromLocation;
private launchEventStreamUrlFromLocation;
private applyLaunchUpdate;
private withLaunchHeaders;
private sessionHeaders;
private withSession;
private ensureSession;
private recoverSession;
}
declare function createShadowServerAppClient(options?: ShadowServerAppBrowserClientOptions): ShadowServerAppBrowserClient;
declare const createShadowServerAppBrowserClient: typeof createShadowServerAppClient;
declare function createShadowSpaceAppClient(options?: ShadowSpaceAppBrowserClientOptions): ShadowSpaceAppBrowserClient;
declare const createShadowSpaceAppBrowserClient: typeof createShadowSpaceAppClient;
declare class ShadowBridge {
static readonly capabilitiesRequestType = "shadow.app.capabilities.request";
static readonly capabilitiesResponseType = "shadow.app.capabilities.response";
static readonly openCopilotRequestType = "shadow.app.copilot.open.request";
static readonly openCopilotResponseType = "shadow.app.copilot.open.response";
static readonly openWorkspaceResourceRequestType = "shadow.app.workspace.open.request";
static readonly openWorkspaceResourceResponseType = "shadow.app.workspace.open.response";
static readonly openBuddyCreatorRequestType = "shadow.app.buddy.create.request";
static readonly openBuddyCreatorResponseType = "shadow.app.buddy.create.response";
static readonly listBuddyInboxesRequestType = "shadow.app.buddy.inboxes.request";
static readonly listBuddyInboxesResponseType = "shadow.app.buddy.inboxes.response";
static readonly ensureBuddyGrantRequestType = "shadow.app.buddy.grant.request";
static readonly ensureBuddyGrantResponseType = "shadow.app.buddy.grant.response";
static readonly authorizeOAuthRequestType = "shadow.app.oauth.authorize.request";
static readonly authorizeOAuthResponseType = "shadow.app.oauth.authorize.response";
static readonly launchUpdateType = "shadow.app.launch.update";
static readonly routeNavigateType = "shadow.app.navigate";
static readonly routeNavigateAckType = "shadow.app.navigate.ack";
static readonly routeChangedType = "shadow.app.route.changed";
static readonly shareAppRequestType = "shadow.app.share.request";
static readonly shareAppResponseType = "shadow.app.share.response";
static readonly refreshLaunchRequestType = "shadow.app.launch.refresh.request";
static readonly refreshLaunchResponseType = "shadow.app.launch.refresh.response";
static readonly launchUpdatedEventType = "shadow.app.launch.updated";
static inboxDeliveries(payload: unknown): ShadowServerAppInboxDelivery[];
static inboxErrors(payload: unknown): ShadowServerAppInboxDeliveryError[];
static channelMessageDeliveries(payload: unknown): ShadowServerAppChannelMessageDelivery[];
static channelMessageErrors(payload: unknown): ShadowServerAppChannelMessageDeliveryError[];
static unwrapCommandPayload<TResult = unknown>(payload: unknown): TResult;
static readonly capabilitiesRequestType = "shadow.space-app.capabilities.request";
static readonly capabilitiesResponseType = "shadow.space-app.capabilities.response";
static readonly openCopilotRequestType = "shadow.space-app.copilot.open.request";
static readonly openCopilotResponseType = "shadow.space-app.copilot.open.response";
static readonly openChannelRequestType = "shadow.space-app.channel.open.request";
static readonly openChannelResponseType = "shadow.space-app.channel.open.response";
static readonly openWorkspaceResourceRequestType = "shadow.space-app.workspace.open.request";
static readonly openWorkspaceResourceResponseType = "shadow.space-app.workspace.open.response";
static readonly openBuddyCreatorRequestType = "shadow.space-app.buddy.create.request";
static readonly openBuddyCreatorResponseType = "shadow.space-app.buddy.create.response";
static readonly authorizeOAuthRequestType = "shadow.space-app.oauth.authorize.request";
static readonly authorizeOAuthResponseType = "shadow.space-app.oauth.authorize.response";
static readonly routeNavigateType = "shadow.space-app.navigate";
static readonly routeNavigateAckType = "shadow.space-app.navigate.ack";
static readonly routeChangedType = "shadow.space-app.route.changed";
static readonly shareSpaceAppRequestType = "shadow.space-app.share.request";
static readonly shareSpaceAppResponseType = "shadow.space-app.share.response";
static readonly refreshLaunchRequestType = "shadow.space-app.launch.refresh.request";
static readonly refreshLaunchResponseType = "shadow.space-app.launch.refresh.response";
static readonly launchUpdatedEventType = "shadow.space-app.launch.updated";
private appKey?;

@@ -241,3 +240,2 @@ private readonly targetOrigin;

private readonly win;
private hasLaunchContext;
private launchTokenValue;

@@ -249,4 +247,3 @@ private readonly pending;

isAvailable(): boolean;
launchToken(param?: string): string | null;
launchHeaders(headers?: Record<string, string>, options?: ShadowServerAppLaunchHeadersOptions): Record<string, string>;
launchToken(): string | null;
capabilities(options?: {

@@ -257,3 +254,3 @@ timeoutMs?: number;

}>;
openCopilot(deliveryOrInput: ShadowServerAppInboxDelivery | ShadowBridgeOpenCopilotInput, options?: {
openCopilot(deliveryOrInput: ShadowSpaceAppInboxDelivery | ShadowBridgeOpenCopilotInput, options?: {
timeoutMs?: number;

@@ -263,2 +260,7 @@ }): Promise<{

}>;
openChannel(input: ShadowBridgeOpenChannelInput, options?: {
timeoutMs?: number;
}): Promise<{
opened: boolean;
}>;
openWorkspaceResource(input: ShadowBridgeOpenWorkspaceResourceInput, options?: {

@@ -275,13 +277,2 @@ timeoutMs?: number;

}>;
listBuddyInboxes(input?: ShadowBridgeListBuddyInboxesInput, options?: {
timeoutMs?: number;
}): Promise<{
inboxes: ShadowBuddyInboxSummary[];
}>;
ensureBuddyGrant(input: ShadowBridgeEnsureBuddyGrantInput, options?: {
timeoutMs?: number;
}): Promise<{
granted: boolean;
grant?: unknown;
}>;
authorizeOAuth(input: ShadowBridgeAuthorizeOAuthInput | string, options?: {

@@ -293,24 +284,18 @@ timeoutMs?: number;

onLaunchUpdate(handler: ShadowBridgeLaunchUpdateHandler): () => void;
shareApp(input?: ShadowBridgeShareAppInput, options?: {
shareSpaceApp(input?: ShadowBridgeShareSpaceAppInput, options?: {
timeoutMs?: number;
}): Promise<ShadowBridgeShareAppResult>;
}): Promise<ShadowBridgeShareSpaceAppResult>;
refreshLaunch(input?: ShadowBridgeRefreshLaunchInput, options?: {
timeoutMs?: number;
}): Promise<ShadowBridgeLaunchContext>;
unwrapCommandPayload<TResult = unknown>(payload: unknown): TResult;
inboxDeliveries(payload: unknown): ShadowServerAppInboxDelivery[];
inboxErrors(payload: unknown): ShadowServerAppInboxDeliveryError[];
channelMessageDeliveries(payload: unknown): ShadowServerAppChannelMessageDelivery[];
channelMessageErrors(payload: unknown): ShadowServerAppChannelMessageDeliveryError[];
private request;
private postMessage;
private launchContextStorageKey;
private launchTokenStorageKey;
private rememberLaunchToken;
private resolveLaunchToken;
private applyLaunchContext;
private resolveLaunchContext;
private resolveLaunchAppKey;
private resolveHostOrigin;
private isTrustedHostMessage;
}
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 };
export { SHADOW_BRIDGE_CAPABILITIES, ShadowBridge, type ShadowBridgeAuthorizeOAuthInput, type ShadowBridgeAuthorizeOAuthResult, type ShadowBridgeCapability, type ShadowBridgeLaunchContext, type ShadowBridgeLaunchUpdateHandler, type ShadowBridgeLaunchUpdateInput, type ShadowBridgeOpenBuddyCreatorInput, type ShadowBridgeOpenChannelInput, type ShadowBridgeOpenCopilotInput, type ShadowBridgeOpenWorkspaceResourceInput, type ShadowBridgeOptions, type ShadowBridgeRefreshLaunchInput, type ShadowBridgeRouteNavigateEvent, type ShadowBridgeRouteNavigateHandler, type ShadowBridgeShareSpaceAppInput, type ShadowBridgeShareSpaceAppResult, ShadowBuddyInboxSummary, type ShadowSpaceAppAuthorizeElementData, type ShadowSpaceAppAuthorizeOAuthOptions, ShadowSpaceAppBrowserClient, type ShadowSpaceAppBrowserClientOptions, ShadowSpaceAppChannelMessageDelivery, ShadowSpaceAppChannelMessageDeliveryError, type ShadowSpaceAppEnsureBuddyTaskGrantInput, type ShadowSpaceAppFetchWithSessionOptions, ShadowSpaceAppInboxDelivery, ShadowSpaceAppInboxDeliveryError, type ShadowSpaceAppListBuddyInboxesOptions, createShadowSpaceAppBrowserClient, createShadowSpaceAppClient, defineShadowSpaceAppAuthorizeElement, shadowSpaceAppMountedPath, shadowSpaceAppMountedPathPrefix };
import {
SHADOW_BRIDGE_CAPABILITIES,
ShadowBridge,
ShadowServerAppBrowserClient,
createShadowServerAppBrowserClient,
createShadowServerAppClient,
shadowServerAppMountedPath,
shadowServerAppMountedPathPrefix
} from "./chunk-EXPB3ASN.js";
ShadowSpaceAppBrowserClient,
createShadowSpaceAppBrowserClient,
createShadowSpaceAppClient,
defineShadowSpaceAppAuthorizeElement,
shadowSpaceAppMountedPath,
shadowSpaceAppMountedPathPrefix
} from "./chunk-6TSMS7HD.js";
import {
SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT,
SHADOW_SERVER_APP_COMMAND_EVENTS,
SHADOW_SERVER_APP_COMMAND_FAILED_EVENT,
buildShadowServerAppInboxDelivery,
buildShadowServerAppInboxTaskRequest,
getShadowServerAppChannelMessageDeliveries,
getShadowServerAppChannelMessageErrors,
getShadowServerAppTaskCardId,
readShadowServerAppCommandResponse,
shadowServerAppInboxTaskEndpoint
} from "./chunk-62WH33SI.js";
SHADOW_SPACE_APP_COMMAND_COMPLETED_EVENT,
SHADOW_SPACE_APP_COMMAND_EVENTS,
SHADOW_SPACE_APP_COMMAND_FAILED_EVENT,
buildShadowSpaceAppInboxDelivery,
buildShadowSpaceAppInboxTaskRequest,
getShadowSpaceAppChannelMessageDeliveries,
getShadowSpaceAppChannelMessageErrors,
getShadowSpaceAppInboxDeliveries,
getShadowSpaceAppInboxErrors,
getShadowSpaceAppTaskCardId,
readShadowSpaceAppCommandResponse,
shadowSpaceAppInboxTaskEndpoint
} from "./chunk-NADGSKKC.js";
export {
SHADOW_BRIDGE_CAPABILITIES,
SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT,
SHADOW_SERVER_APP_COMMAND_EVENTS,
SHADOW_SERVER_APP_COMMAND_FAILED_EVENT,
SHADOW_SPACE_APP_COMMAND_COMPLETED_EVENT,
SHADOW_SPACE_APP_COMMAND_EVENTS,
SHADOW_SPACE_APP_COMMAND_FAILED_EVENT,
ShadowBridge,
ShadowServerAppBrowserClient,
buildShadowServerAppInboxDelivery,
buildShadowServerAppInboxTaskRequest,
createShadowServerAppBrowserClient,
createShadowServerAppClient,
getShadowServerAppChannelMessageDeliveries,
getShadowServerAppChannelMessageErrors,
getShadowServerAppTaskCardId,
readShadowServerAppCommandResponse,
shadowServerAppInboxTaskEndpoint,
shadowServerAppMountedPath,
shadowServerAppMountedPathPrefix
ShadowSpaceAppBrowserClient,
buildShadowSpaceAppInboxDelivery,
buildShadowSpaceAppInboxTaskRequest,
createShadowSpaceAppBrowserClient,
createShadowSpaceAppClient,
defineShadowSpaceAppAuthorizeElement,
getShadowSpaceAppChannelMessageDeliveries,
getShadowSpaceAppChannelMessageErrors,
getShadowSpaceAppInboxDeliveries,
getShadowSpaceAppInboxErrors,
getShadowSpaceAppTaskCardId,
readShadowSpaceAppCommandResponse,
shadowSpaceAppInboxTaskEndpoint,
shadowSpaceAppMountedPath,
shadowSpaceAppMountedPathPrefix
};
{
"name": "@shadowob/sdk",
"version": "1.1.65",
"version": "1.1.66",
"description": "Shadow SDK — typed REST client and real-time Socket.IO event listener for Shadow servers",

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

"bin": {
"shadow-server-app": "./bin/shadow-server-app.mjs"
"shadow-space-app": "./bin/shadow-space-app.mjs"
},

@@ -23,15 +23,15 @@ "exports": {

},
"./server-app/node": {
"types": "./dist/server-app-node.d.ts",
"development": "./src/server-app-node.ts",
"import": "./dist/server-app-node.js",
"require": "./dist/server-app-node.cjs",
"default": "./dist/server-app-node.js"
"./space-app/node": {
"types": "./dist/space-app-node.d.ts",
"development": "./src/space-app-node.ts",
"import": "./dist/space-app-node.js",
"require": "./dist/space-app-node.cjs",
"default": "./dist/space-app-node.js"
},
"./server-app": {
"types": "./dist/server-app.d.ts",
"development": "./src/server-app.ts",
"import": "./dist/server-app.js",
"require": "./dist/server-app.cjs",
"default": "./dist/server-app.js"
"./space-app": {
"types": "./dist/space-app.d.ts",
"development": "./src/space-app.ts",
"import": "./dist/space-app.js",
"require": "./dist/space-app.cjs",
"default": "./dist/space-app.js"
},

@@ -53,3 +53,3 @@ "./bridge": {

"socket.io-client": "^4.8.1",
"@shadowob/shared": "1.1.65"
"@shadowob/shared": "1.1.66"
},

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

#!/usr/bin/env node
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'
import { dirname, relative, resolve } from 'node:path'
function usage(exitCode = 1) {
const out = exitCode === 0 ? console.log : console.error
out(`Usage:
shadow-server-app typegen <manifest.json> <output.ts>
Generates a typed TypeScript manifest module from a Shadow server app JSON manifest.`)
process.exit(exitCode)
}
function readJson(filePath) {
try {
return JSON.parse(readFileSync(filePath, 'utf8'))
} catch (error) {
throw new Error(`Failed to read JSON manifest at ${filePath}: ${error.message}`)
}
}
function importPath(fromFile, toFile) {
let value = relative(dirname(fromFile), toFile).replaceAll('\\', '/')
if (!value.startsWith('.')) value = `./${value}`
return value.replace(/\.[cm]?ts$/, '.js')
}
function generateTypeModule(manifestPath, outputPath) {
const manifest = readJson(manifestPath)
const sdkImport = '@shadowob/sdk'
const sourceImport = importPath(outputPath, manifestPath)
const contents = `// Generated by shadow-server-app typegen from ${sourceImport}.
// Do not edit by hand. Update the JSON manifest and regenerate this file.
import type { ShadowServerAppManifest } from '${sdkImport}'
export const shadowServerAppManifest = ${JSON.stringify(manifest, null, 2)} as const satisfies ShadowServerAppManifest
export type ShadowServerAppManifestDefinition = typeof shadowServerAppManifest
`
mkdirSync(dirname(outputPath), { recursive: true })
writeFileSync(outputPath, contents)
}
const [, , command, manifestArg, outputArg] = process.argv
if (!command || command === '--help' || command === '-h') usage(command ? 0 : 1)
if (command !== 'typegen' || !manifestArg || !outputArg) usage()
try {
const manifestPath = resolve(process.cwd(), manifestArg)
const outputPath = resolve(process.cwd(), outputArg)
generateTypeModule(manifestPath, outputPath)
console.log(`Generated ${relative(process.cwd(), outputPath)}`)
} catch (error) {
console.error(error instanceof Error ? error.message : String(error))
process.exit(1)
}
// 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
};
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/server-app-node.ts
var server_app_node_exports = {};
__export(server_app_node_exports, {
ShadowServerAppJsonStore: () => ShadowServerAppJsonStore,
createShadowServerAppJsonStore: () => createShadowServerAppJsonStore
});
module.exports = __toCommonJS(server_app_node_exports);
var import_node_fs = require("fs");
var import_node_path = require("path");
var ShadowServerAppJsonStore = class {
constructor(options) {
this.options = options;
}
options;
read() {
if (!(0, import_node_fs.existsSync)(this.options.filePath)) {
const value = this.defaultValue();
if (this.options.persistDefault !== false) this.write(value);
return value;
}
try {
const parsed = JSON.parse((0, import_node_fs.readFileSync)(this.options.filePath, "utf8"));
if (this.options.validate && !this.options.validate(parsed)) return this.defaultValue();
return this.normalize(parsed);
} catch {
return this.defaultValue();
}
}
write(value) {
const normalized = this.normalize(value);
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(this.options.filePath), { recursive: true });
const tempPath = `${this.options.filePath}.${process.pid}.${Date.now()}.tmp`;
(0, import_node_fs.writeFileSync)(tempPath, `${JSON.stringify(normalized, null, 2)}
`);
(0, import_node_fs.renameSync)(tempPath, this.options.filePath);
return normalized;
}
update(mutator) {
const current = this.clone(this.read());
const next = mutator(current) ?? current;
return this.write(next);
}
reset(nextValue) {
return this.write(nextValue ?? this.defaultValue());
}
defaultValue() {
const value = typeof this.options.defaultValue === "function" ? this.options.defaultValue() : this.options.defaultValue;
return this.normalize(this.clone(value));
}
normalize(value) {
return this.options.normalize ? this.options.normalize(value) : value;
}
clone(value) {
return structuredClone(value);
}
};
function createShadowServerAppJsonStore(options) {
return new ShadowServerAppJsonStore(options);
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ShadowServerAppJsonStore,
createShadowServerAppJsonStore
});
interface ShadowServerAppJsonStoreOptions<T> {
filePath: string;
defaultValue: T | (() => T);
validate?: (value: unknown) => value is T;
normalize?: (value: T) => T;
persistDefault?: boolean;
}
declare class ShadowServerAppJsonStore<T> {
private readonly options;
constructor(options: ShadowServerAppJsonStoreOptions<T>);
read(): T;
write(value: T): T;
update(mutator: (value: T) => T | void): T;
reset(nextValue?: T): T;
private defaultValue;
private normalize;
private clone;
}
declare function createShadowServerAppJsonStore<T>(options: ShadowServerAppJsonStoreOptions<T>): ShadowServerAppJsonStore<T>;
export { ShadowServerAppJsonStore, type ShadowServerAppJsonStoreOptions, createShadowServerAppJsonStore };
interface ShadowServerAppJsonStoreOptions<T> {
filePath: string;
defaultValue: T | (() => T);
validate?: (value: unknown) => value is T;
normalize?: (value: T) => T;
persistDefault?: boolean;
}
declare class ShadowServerAppJsonStore<T> {
private readonly options;
constructor(options: ShadowServerAppJsonStoreOptions<T>);
read(): T;
write(value: T): T;
update(mutator: (value: T) => T | void): T;
reset(nextValue?: T): T;
private defaultValue;
private normalize;
private clone;
}
declare function createShadowServerAppJsonStore<T>(options: ShadowServerAppJsonStoreOptions<T>): ShadowServerAppJsonStore<T>;
export { ShadowServerAppJsonStore, type ShadowServerAppJsonStoreOptions, createShadowServerAppJsonStore };
// src/server-app-node.ts
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
import { dirname } from "path";
var ShadowServerAppJsonStore = class {
constructor(options) {
this.options = options;
}
options;
read() {
if (!existsSync(this.options.filePath)) {
const value = this.defaultValue();
if (this.options.persistDefault !== false) this.write(value);
return value;
}
try {
const parsed = JSON.parse(readFileSync(this.options.filePath, "utf8"));
if (this.options.validate && !this.options.validate(parsed)) return this.defaultValue();
return this.normalize(parsed);
} catch {
return this.defaultValue();
}
}
write(value) {
const normalized = this.normalize(value);
mkdirSync(dirname(this.options.filePath), { recursive: true });
const tempPath = `${this.options.filePath}.${process.pid}.${Date.now()}.tmp`;
writeFileSync(tempPath, `${JSON.stringify(normalized, null, 2)}
`);
renameSync(tempPath, this.options.filePath);
return normalized;
}
update(mutator) {
const current = this.clone(this.read());
const next = mutator(current) ?? current;
return this.write(next);
}
reset(nextValue) {
return this.write(nextValue ?? this.defaultValue());
}
defaultValue() {
const value = typeof this.options.defaultValue === "function" ? this.options.defaultValue() : this.options.defaultValue;
return this.normalize(this.clone(value));
}
normalize(value) {
return this.options.normalize ? this.options.normalize(value) : value;
}
clone(value) {
return structuredClone(value);
}
};
function createShadowServerAppJsonStore(options) {
return new ShadowServerAppJsonStore(options);
}
export {
ShadowServerAppJsonStore,
createShadowServerAppJsonStore
};

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

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

"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/server-app.ts
var server_app_exports = {};
__export(server_app_exports, {
BUDDY_INBOX_DELIVERY_PERMISSION: () => import_shared.BUDDY_INBOX_DELIVERY_PERMISSION,
SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT: () => SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT,
SHADOW_SERVER_APP_COMMAND_EVENTS: () => SHADOW_SERVER_APP_COMMAND_EVENTS,
SHADOW_SERVER_APP_COMMAND_FAILED_EVENT: () => SHADOW_SERVER_APP_COMMAND_FAILED_EVENT,
SHADOW_SERVER_APP_PROTOCOL: () => SHADOW_SERVER_APP_PROTOCOL,
SHADOW_SERVER_APP_PUBLIC_AVATAR_CACHE_CONTROL: () => SHADOW_SERVER_APP_PUBLIC_AVATAR_CACHE_CONTROL,
ShadowServerAppCommandError: () => ShadowServerAppCommandError,
ShadowServerAppHttpError: () => ShadowServerAppHttpError,
ShadowServerAppOutbox: () => ShadowServerAppOutbox,
ShadowServerAppRuntime: () => ShadowServerAppRuntime,
buildShadowServerAppInboxDelivery: () => buildShadowServerAppInboxDelivery,
buildShadowServerAppInboxTaskRequest: () => buildShadowServerAppInboxTaskRequest,
createShadowServerAppCollaborationCursor: () => createShadowServerAppCollaborationCursor,
createShadowServerAppCollaborationEvent: () => createShadowServerAppCollaborationEvent,
createShadowServerAppCollaborationResource: () => createShadowServerAppCollaborationResource,
createShadowServerAppManifest: () => createShadowServerAppManifest,
createShadowServerAppRuntime: () => createShadowServerAppRuntime,
decodeShadowServerAppLaunchTokenHint: () => decodeShadowServerAppLaunchTokenHint,
defineShadowServerApp: () => defineShadowServerApp,
deliverShadowServerAppLaunchOutbox: () => deliverShadowServerAppLaunchOutbox,
extractShadowServerAppBearerToken: () => extractShadowServerAppBearerToken,
fetchShadowServerAppLaunchInboxes: () => fetchShadowServerAppLaunchInboxes,
getShadowServerAppChannelMessageDeliveries: () => getShadowServerAppChannelMessageDeliveries,
getShadowServerAppChannelMessageErrors: () => getShadowServerAppChannelMessageErrors,
getShadowServerAppInboxDeliveries: () => getShadowServerAppInboxDeliveries,
getShadowServerAppInboxErrors: () => getShadowServerAppInboxErrors,
getShadowServerAppPendingChannelMessages: () => getShadowServerAppPendingChannelMessages,
getShadowServerAppPendingInboxTasks: () => getShadowServerAppPendingInboxTasks,
getShadowServerAppTaskCardId: () => getShadowServerAppTaskCardId,
hasShadowServerAppPendingOutbox: () => hasShadowServerAppPendingOutbox,
introspectShadowServerAppLaunchToken: () => introspectShadowServerAppLaunchToken,
introspectShadowServerAppToken: () => introspectShadowServerAppToken,
isShadowServerAppSignedMediaUrl: () => isShadowServerAppSignedMediaUrl,
normalizeShadowServerAppAvatarUrl: () => normalizeShadowServerAppAvatarUrl,
normalizeShadowServerAppBaseCursor: () => normalizeShadowServerAppBaseCursor,
normalizeShadowServerAppClientMutationId: () => normalizeShadowServerAppClientMutationId,
normalizeShadowServerAppCommandInput: () => normalizeShadowServerAppCommandInput,
parseShadowServerAppCommandRequest: () => parseShadowServerAppCommandRequest,
readShadowServerAppCommandResponse: () => readShadowServerAppCommandResponse,
resolveShadowServerAppLaunchCommandContext: () => resolveShadowServerAppLaunchCommandContext,
resolveShadowServerAppLaunchCommandContextResolution: () => resolveShadowServerAppLaunchCommandContextResolution,
shadowServerAppActorAvatarUrl: () => shadowServerAppActorAvatarUrl,
shadowServerAppActorDisplayName: () => shadowServerAppActorDisplayName,
shadowServerAppActorRef: () => shadowServerAppActorRef,
shadowServerAppApiBaseUrl: () => shadowServerAppApiBaseUrl,
shadowServerAppAvatarRedirectUrl: () => shadowServerAppAvatarRedirectUrl,
shadowServerAppDisplayIdentity: () => shadowServerAppDisplayIdentity,
shadowServerAppError: () => shadowServerAppError,
shadowServerAppIdentityKey: () => shadowServerAppIdentityKey,
shadowServerAppIdentitySnapshot: () => shadowServerAppIdentitySnapshot,
shadowServerAppInboxTaskEndpoint: () => shadowServerAppInboxTaskEndpoint,
shadowServerAppLaunchIntrospectionError: () => shadowServerAppLaunchIntrospectionError,
shadowServerAppPublicBaseUrl: () => shadowServerAppPublicBaseUrl,
shadowServerAppPublicUrl: () => shadowServerAppPublicUrl,
unwrapShadowServerAppCommandPayload: () => unwrapShadowServerAppCommandPayload,
validateShadowServerAppJsonSchema: () => validateShadowServerAppJsonSchema
});
module.exports = __toCommonJS(server_app_exports);
var import_shared = require("@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;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
BUDDY_INBOX_DELIVERY_PERMISSION,
SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT,
SHADOW_SERVER_APP_COMMAND_EVENTS,
SHADOW_SERVER_APP_COMMAND_FAILED_EVENT,
SHADOW_SERVER_APP_PROTOCOL,
SHADOW_SERVER_APP_PUBLIC_AVATAR_CACHE_CONTROL,
ShadowServerAppCommandError,
ShadowServerAppHttpError,
ShadowServerAppOutbox,
ShadowServerAppRuntime,
buildShadowServerAppInboxDelivery,
buildShadowServerAppInboxTaskRequest,
createShadowServerAppCollaborationCursor,
createShadowServerAppCollaborationEvent,
createShadowServerAppCollaborationResource,
createShadowServerAppManifest,
createShadowServerAppRuntime,
decodeShadowServerAppLaunchTokenHint,
defineShadowServerApp,
deliverShadowServerAppLaunchOutbox,
extractShadowServerAppBearerToken,
fetchShadowServerAppLaunchInboxes,
getShadowServerAppChannelMessageDeliveries,
getShadowServerAppChannelMessageErrors,
getShadowServerAppInboxDeliveries,
getShadowServerAppInboxErrors,
getShadowServerAppPendingChannelMessages,
getShadowServerAppPendingInboxTasks,
getShadowServerAppTaskCardId,
hasShadowServerAppPendingOutbox,
introspectShadowServerAppLaunchToken,
introspectShadowServerAppToken,
isShadowServerAppSignedMediaUrl,
normalizeShadowServerAppAvatarUrl,
normalizeShadowServerAppBaseCursor,
normalizeShadowServerAppClientMutationId,
normalizeShadowServerAppCommandInput,
parseShadowServerAppCommandRequest,
readShadowServerAppCommandResponse,
resolveShadowServerAppLaunchCommandContext,
resolveShadowServerAppLaunchCommandContextResolution,
shadowServerAppActorAvatarUrl,
shadowServerAppActorDisplayName,
shadowServerAppActorRef,
shadowServerAppApiBaseUrl,
shadowServerAppAvatarRedirectUrl,
shadowServerAppDisplayIdentity,
shadowServerAppError,
shadowServerAppIdentityKey,
shadowServerAppIdentitySnapshot,
shadowServerAppInboxTaskEndpoint,
shadowServerAppLaunchIntrospectionError,
shadowServerAppPublicBaseUrl,
shadowServerAppPublicUrl,
unwrapShadowServerAppCommandPayload,
validateShadowServerAppJsonSchema
});
export { bE as JsonSchemaToType, bM as SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT, bN as SHADOW_SERVER_APP_COMMAND_EVENTS, bO as SHADOW_SERVER_APP_COMMAND_FAILED_EVENT, bP as SHADOW_SERVER_APP_PROTOCOL, bQ as SHADOW_SERVER_APP_PUBLIC_AVATAR_CACHE_CONTROL, cn as ShadowServerAppActorRef, co as ShadowServerAppBridgeAuthorizeOAuthRequest, cp as ShadowServerAppBridgeCapabilitiesRequest, cq as ShadowServerAppBridgeFailureResponse, cr as ShadowServerAppBridgeOpenCopilotRequest, eA as ShadowServerAppBridgeOpenWorkspaceResourceRequest, cs as ShadowServerAppBridgeRequest, ct as ShadowServerAppBridgeResponse, cu as ShadowServerAppBridgeResponseType, cv as ShadowServerAppBridgeRouteChangedEvent, cw as ShadowServerAppBridgeRouteNavigateAck, cx as ShadowServerAppBridgeRouteNavigateRequest, cy as ShadowServerAppBridgeShareAppRequest, cz as ShadowServerAppBridgeSuccessResponse, cA as ShadowServerAppChannelMessageDelivery, cB as ShadowServerAppChannelMessageDeliveryError, cC as ShadowServerAppChannelMessageOutbox, cD as ShadowServerAppCollaborationEvent, cE as ShadowServerAppCollaborationMutation, cF as ShadowServerAppCollaborationResource, cI as ShadowServerAppCommandContext, cJ as ShadowServerAppCommandEnvelope, cK as ShadowServerAppCommandError, cL as ShadowServerAppCommandEventType, cM as ShadowServerAppCommandFailureResponse, cN as ShadowServerAppCommandHandler, cO as ShadowServerAppCommandHandlerContext, cP as ShadowServerAppCommandHandlers, cQ as ShadowServerAppCommandInput, cR as ShadowServerAppCommandName, cS as ShadowServerAppCommandParseError, cT as ShadowServerAppCommandParseResult, cU as ShadowServerAppCommandParseSuccess, cV as ShadowServerAppCommandRequestInput, cW as ShadowServerAppCommandResponse, eB as ShadowServerAppCommandRuntimeRequest, cX as ShadowServerAppCommandSuccessResponse, cY as ShadowServerAppEnvironment, cZ as ShadowServerAppExecutionFailure, c_ as ShadowServerAppExecutionResult, c$ as ShadowServerAppExecutionSuccess, d0 as ShadowServerAppFetch, d1 as ShadowServerAppHostAppRef, d2 as ShadowServerAppHostInboxTaskRequestInput, d3 as ShadowServerAppHttpError, d4 as ShadowServerAppIdentitySnapshot, d5 as ShadowServerAppIdentitySubjectKind, d6 as ShadowServerAppInboxDelivery, d7 as ShadowServerAppInboxDeliveryError, d8 as ShadowServerAppInboxDeliveryFromMessageInput, d9 as ShadowServerAppInboxTarget, da as ShadowServerAppInboxTaskOutbox, db as ShadowServerAppInboxTaskPriority, dc as ShadowServerAppInboxTaskResource, dd as ShadowServerAppIntrospectionInput, de as ShadowServerAppLaunchCommandContextOptions, df as ShadowServerAppLaunchCommandContextResolution, dg as ShadowServerAppLaunchFetchOptions, dh as ShadowServerAppLaunchIntrospection, di as ShadowServerAppLaunchOutboxDeliveryOptions, dj as ShadowServerAppLaunchTokenHint, dk as ShadowServerAppManifestOptions, dq as ShadowServerAppOutbox, dr as ShadowServerAppOutboxPayload, dt as ShadowServerAppResolvedInboxTaskRequest, du as ShadowServerAppResultShadow, dv as ShadowServerAppResultWithShadow, dw as ShadowServerAppRuntime, dx as ShadowServerAppRuntimeOptions, dy as ShadowServerAppValidationIssue, dX as buildShadowServerAppInboxDelivery, dY as buildShadowServerAppInboxTaskRequest, dZ as createShadowServerAppCollaborationCursor, d_ as createShadowServerAppCollaborationEvent, d$ as createShadowServerAppCollaborationResource, e0 as createShadowServerAppManifest, e1 as createShadowServerAppRuntime, e2 as decodeShadowServerAppLaunchTokenHint, e3 as defineShadowServerApp, e4 as deliverShadowServerAppLaunchOutbox, e5 as extractShadowServerAppBearerToken, e6 as fetchShadowServerAppLaunchInboxes, e7 as getShadowServerAppChannelMessageDeliveries, e8 as getShadowServerAppChannelMessageErrors, eC as getShadowServerAppInboxDeliveries, eD as getShadowServerAppInboxErrors, eE as getShadowServerAppPendingChannelMessages, eF as getShadowServerAppPendingInboxTasks, e9 as getShadowServerAppTaskCardId, ea as hasShadowServerAppPendingOutbox, eb as introspectShadowServerAppLaunchToken, ec as introspectShadowServerAppToken, ed as isShadowServerAppSignedMediaUrl, ee as normalizeShadowServerAppAvatarUrl, ef as normalizeShadowServerAppBaseCursor, eg as normalizeShadowServerAppClientMutationId, eh as normalizeShadowServerAppCommandInput, ei as parseShadowServerAppCommandRequest, ej as readShadowServerAppCommandResponse, ek as resolveShadowServerAppLaunchCommandContext, el as resolveShadowServerAppLaunchCommandContextResolution, em as shadowServerAppActorAvatarUrl, en as shadowServerAppActorDisplayName, eo as shadowServerAppActorRef, ep as shadowServerAppApiBaseUrl, eq as shadowServerAppAvatarRedirectUrl, er as shadowServerAppDisplayIdentity, es as shadowServerAppError, et as shadowServerAppIdentityKey, eu as shadowServerAppIdentitySnapshot, ev as shadowServerAppInboxTaskEndpoint, ew as shadowServerAppLaunchIntrospectionError, ex as shadowServerAppPublicBaseUrl, ey as shadowServerAppPublicUrl, eG as unwrapShadowServerAppCommandPayload, ez as validateShadowServerAppJsonSchema } from './server-app-nz50wbnP.cjs';
export { BUDDY_INBOX_DELIVERY_PERMISSION, BuddyInboxPlatformPermission } from '@shadowob/shared';
export { bE as JsonSchemaToType, bM as SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT, bN as SHADOW_SERVER_APP_COMMAND_EVENTS, bO as SHADOW_SERVER_APP_COMMAND_FAILED_EVENT, bP as SHADOW_SERVER_APP_PROTOCOL, bQ as SHADOW_SERVER_APP_PUBLIC_AVATAR_CACHE_CONTROL, cn as ShadowServerAppActorRef, co as ShadowServerAppBridgeAuthorizeOAuthRequest, cp as ShadowServerAppBridgeCapabilitiesRequest, cq as ShadowServerAppBridgeFailureResponse, cr as ShadowServerAppBridgeOpenCopilotRequest, eA as ShadowServerAppBridgeOpenWorkspaceResourceRequest, cs as ShadowServerAppBridgeRequest, ct as ShadowServerAppBridgeResponse, cu as ShadowServerAppBridgeResponseType, cv as ShadowServerAppBridgeRouteChangedEvent, cw as ShadowServerAppBridgeRouteNavigateAck, cx as ShadowServerAppBridgeRouteNavigateRequest, cy as ShadowServerAppBridgeShareAppRequest, cz as ShadowServerAppBridgeSuccessResponse, cA as ShadowServerAppChannelMessageDelivery, cB as ShadowServerAppChannelMessageDeliveryError, cC as ShadowServerAppChannelMessageOutbox, cD as ShadowServerAppCollaborationEvent, cE as ShadowServerAppCollaborationMutation, cF as ShadowServerAppCollaborationResource, cI as ShadowServerAppCommandContext, cJ as ShadowServerAppCommandEnvelope, cK as ShadowServerAppCommandError, cL as ShadowServerAppCommandEventType, cM as ShadowServerAppCommandFailureResponse, cN as ShadowServerAppCommandHandler, cO as ShadowServerAppCommandHandlerContext, cP as ShadowServerAppCommandHandlers, cQ as ShadowServerAppCommandInput, cR as ShadowServerAppCommandName, cS as ShadowServerAppCommandParseError, cT as ShadowServerAppCommandParseResult, cU as ShadowServerAppCommandParseSuccess, cV as ShadowServerAppCommandRequestInput, cW as ShadowServerAppCommandResponse, eB as ShadowServerAppCommandRuntimeRequest, cX as ShadowServerAppCommandSuccessResponse, cY as ShadowServerAppEnvironment, cZ as ShadowServerAppExecutionFailure, c_ as ShadowServerAppExecutionResult, c$ as ShadowServerAppExecutionSuccess, d0 as ShadowServerAppFetch, d1 as ShadowServerAppHostAppRef, d2 as ShadowServerAppHostInboxTaskRequestInput, d3 as ShadowServerAppHttpError, d4 as ShadowServerAppIdentitySnapshot, d5 as ShadowServerAppIdentitySubjectKind, d6 as ShadowServerAppInboxDelivery, d7 as ShadowServerAppInboxDeliveryError, d8 as ShadowServerAppInboxDeliveryFromMessageInput, d9 as ShadowServerAppInboxTarget, da as ShadowServerAppInboxTaskOutbox, db as ShadowServerAppInboxTaskPriority, dc as ShadowServerAppInboxTaskResource, dd as ShadowServerAppIntrospectionInput, de as ShadowServerAppLaunchCommandContextOptions, df as ShadowServerAppLaunchCommandContextResolution, dg as ShadowServerAppLaunchFetchOptions, dh as ShadowServerAppLaunchIntrospection, di as ShadowServerAppLaunchOutboxDeliveryOptions, dj as ShadowServerAppLaunchTokenHint, dk as ShadowServerAppManifestOptions, dq as ShadowServerAppOutbox, dr as ShadowServerAppOutboxPayload, dt as ShadowServerAppResolvedInboxTaskRequest, du as ShadowServerAppResultShadow, dv as ShadowServerAppResultWithShadow, dw as ShadowServerAppRuntime, dx as ShadowServerAppRuntimeOptions, dy as ShadowServerAppValidationIssue, dX as buildShadowServerAppInboxDelivery, dY as buildShadowServerAppInboxTaskRequest, dZ as createShadowServerAppCollaborationCursor, d_ as createShadowServerAppCollaborationEvent, d$ as createShadowServerAppCollaborationResource, e0 as createShadowServerAppManifest, e1 as createShadowServerAppRuntime, e2 as decodeShadowServerAppLaunchTokenHint, e3 as defineShadowServerApp, e4 as deliverShadowServerAppLaunchOutbox, e5 as extractShadowServerAppBearerToken, e6 as fetchShadowServerAppLaunchInboxes, e7 as getShadowServerAppChannelMessageDeliveries, e8 as getShadowServerAppChannelMessageErrors, eC as getShadowServerAppInboxDeliveries, eD as getShadowServerAppInboxErrors, eE as getShadowServerAppPendingChannelMessages, eF as getShadowServerAppPendingInboxTasks, e9 as getShadowServerAppTaskCardId, ea as hasShadowServerAppPendingOutbox, eb as introspectShadowServerAppLaunchToken, ec as introspectShadowServerAppToken, ed as isShadowServerAppSignedMediaUrl, ee as normalizeShadowServerAppAvatarUrl, ef as normalizeShadowServerAppBaseCursor, eg as normalizeShadowServerAppClientMutationId, eh as normalizeShadowServerAppCommandInput, ei as parseShadowServerAppCommandRequest, ej as readShadowServerAppCommandResponse, ek as resolveShadowServerAppLaunchCommandContext, el as resolveShadowServerAppLaunchCommandContextResolution, em as shadowServerAppActorAvatarUrl, en as shadowServerAppActorDisplayName, eo as shadowServerAppActorRef, ep as shadowServerAppApiBaseUrl, eq as shadowServerAppAvatarRedirectUrl, er as shadowServerAppDisplayIdentity, es as shadowServerAppError, et as shadowServerAppIdentityKey, eu as shadowServerAppIdentitySnapshot, ev as shadowServerAppInboxTaskEndpoint, ew as shadowServerAppLaunchIntrospectionError, ex as shadowServerAppPublicBaseUrl, ey as shadowServerAppPublicUrl, eG as unwrapShadowServerAppCommandPayload, ez as validateShadowServerAppJsonSchema } from './server-app-nz50wbnP.js';
export { BUDDY_INBOX_DELIVERY_PERMISSION, BuddyInboxPlatformPermission } from '@shadowob/shared';
import {
BUDDY_INBOX_DELIVERY_PERMISSION,
SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT,
SHADOW_SERVER_APP_COMMAND_EVENTS,
SHADOW_SERVER_APP_COMMAND_FAILED_EVENT,
SHADOW_SERVER_APP_PROTOCOL,
SHADOW_SERVER_APP_PUBLIC_AVATAR_CACHE_CONTROL,
ShadowServerAppCommandError,
ShadowServerAppHttpError,
ShadowServerAppOutbox,
ShadowServerAppRuntime,
buildShadowServerAppInboxDelivery,
buildShadowServerAppInboxTaskRequest,
createShadowServerAppCollaborationCursor,
createShadowServerAppCollaborationEvent,
createShadowServerAppCollaborationResource,
createShadowServerAppManifest,
createShadowServerAppRuntime,
decodeShadowServerAppLaunchTokenHint,
defineShadowServerApp,
deliverShadowServerAppLaunchOutbox,
extractShadowServerAppBearerToken,
fetchShadowServerAppLaunchInboxes,
getShadowServerAppChannelMessageDeliveries,
getShadowServerAppChannelMessageErrors,
getShadowServerAppInboxDeliveries,
getShadowServerAppInboxErrors,
getShadowServerAppPendingChannelMessages,
getShadowServerAppPendingInboxTasks,
getShadowServerAppTaskCardId,
hasShadowServerAppPendingOutbox,
introspectShadowServerAppLaunchToken,
introspectShadowServerAppToken,
isShadowServerAppSignedMediaUrl,
normalizeShadowServerAppAvatarUrl,
normalizeShadowServerAppBaseCursor,
normalizeShadowServerAppClientMutationId,
normalizeShadowServerAppCommandInput,
parseShadowServerAppCommandRequest,
readShadowServerAppCommandResponse,
resolveShadowServerAppLaunchCommandContext,
resolveShadowServerAppLaunchCommandContextResolution,
shadowServerAppActorAvatarUrl,
shadowServerAppActorDisplayName,
shadowServerAppActorRef,
shadowServerAppApiBaseUrl,
shadowServerAppAvatarRedirectUrl,
shadowServerAppDisplayIdentity,
shadowServerAppError,
shadowServerAppIdentityKey,
shadowServerAppIdentitySnapshot,
shadowServerAppInboxTaskEndpoint,
shadowServerAppLaunchIntrospectionError,
shadowServerAppPublicBaseUrl,
shadowServerAppPublicUrl,
unwrapShadowServerAppCommandPayload,
validateShadowServerAppJsonSchema
} from "./chunk-62WH33SI.js";
export {
BUDDY_INBOX_DELIVERY_PERMISSION,
SHADOW_SERVER_APP_COMMAND_COMPLETED_EVENT,
SHADOW_SERVER_APP_COMMAND_EVENTS,
SHADOW_SERVER_APP_COMMAND_FAILED_EVENT,
SHADOW_SERVER_APP_PROTOCOL,
SHADOW_SERVER_APP_PUBLIC_AVATAR_CACHE_CONTROL,
ShadowServerAppCommandError,
ShadowServerAppHttpError,
ShadowServerAppOutbox,
ShadowServerAppRuntime,
buildShadowServerAppInboxDelivery,
buildShadowServerAppInboxTaskRequest,
createShadowServerAppCollaborationCursor,
createShadowServerAppCollaborationEvent,
createShadowServerAppCollaborationResource,
createShadowServerAppManifest,
createShadowServerAppRuntime,
decodeShadowServerAppLaunchTokenHint,
defineShadowServerApp,
deliverShadowServerAppLaunchOutbox,
extractShadowServerAppBearerToken,
fetchShadowServerAppLaunchInboxes,
getShadowServerAppChannelMessageDeliveries,
getShadowServerAppChannelMessageErrors,
getShadowServerAppInboxDeliveries,
getShadowServerAppInboxErrors,
getShadowServerAppPendingChannelMessages,
getShadowServerAppPendingInboxTasks,
getShadowServerAppTaskCardId,
hasShadowServerAppPendingOutbox,
introspectShadowServerAppLaunchToken,
introspectShadowServerAppToken,
isShadowServerAppSignedMediaUrl,
normalizeShadowServerAppAvatarUrl,
normalizeShadowServerAppBaseCursor,
normalizeShadowServerAppClientMutationId,
normalizeShadowServerAppCommandInput,
parseShadowServerAppCommandRequest,
readShadowServerAppCommandResponse,
resolveShadowServerAppLaunchCommandContext,
resolveShadowServerAppLaunchCommandContextResolution,
shadowServerAppActorAvatarUrl,
shadowServerAppActorDisplayName,
shadowServerAppActorRef,
shadowServerAppApiBaseUrl,
shadowServerAppAvatarRedirectUrl,
shadowServerAppDisplayIdentity,
shadowServerAppError,
shadowServerAppIdentityKey,
shadowServerAppIdentitySnapshot,
shadowServerAppInboxTaskEndpoint,
shadowServerAppLaunchIntrospectionError,
shadowServerAppPublicBaseUrl,
shadowServerAppPublicUrl,
unwrapShadowServerAppCommandPayload,
validateShadowServerAppJsonSchema
};

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